зеркало из 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) {
|
Android: function Android(aDetails, aBrowser) {
|
||||||
const ANDROID_VIEW_TEXT_CHANGED = 0x10;
|
const ANDROID_VIEW_TEXT_CHANGED = 0x10;
|
||||||
const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
|
const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
|
||||||
|
|
||||||
if (!this._bridge)
|
if (!this.androidBridge) {
|
||||||
this._bridge = Cc['@mozilla.org/android/bridge;1'].getService(Ci.nsIAndroidBridge);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for each (let androidEvent in aDetails) {
|
for each (let androidEvent in aDetails) {
|
||||||
androidEvent.type = 'Accessibility:Event';
|
androidEvent.type = 'Accessibility:Event';
|
||||||
|
@ -722,7 +734,7 @@ var Output = {
|
||||||
androidEvent.brailleOutput = this.brailleState.init(androidEvent.brailleOutput);
|
androidEvent.brailleOutput = this.brailleState.init(androidEvent.brailleOutput);
|
||||||
break;
|
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'];
|
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.ContentControl = function ContentControl(aContentScope) {
|
||||||
this._contentScope = Cu.getWeakReference(aContentScope);
|
this._contentScope = Cu.getWeakReference(aContentScope);
|
||||||
this._vcCache = new WeakMap();
|
this._vcCache = new WeakMap();
|
||||||
|
@ -31,16 +35,19 @@ this.ContentControl.prototype = {
|
||||||
messagesOfInterest: ['AccessFu:MoveCursor',
|
messagesOfInterest: ['AccessFu:MoveCursor',
|
||||||
'AccessFu:ClearCursor',
|
'AccessFu:ClearCursor',
|
||||||
'AccessFu:MoveToPoint',
|
'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();
|
let cs = this._contentScope.get();
|
||||||
for (let message of this.messagesOfInterest) {
|
for (let message of this.messagesOfInterest) {
|
||||||
cs.addMessageListener(message, this);
|
cs.addMessageListener(message, this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
stop: function ContentControl_stop() {
|
stop: function cc_stop() {
|
||||||
let cs = this._contentScope.get();
|
let cs = this._contentScope.get();
|
||||||
for (let message of this.messagesOfInterest) {
|
for (let message of this.messagesOfInterest) {
|
||||||
cs.removeMessageListener(message, this);
|
cs.removeMessageListener(message, this);
|
||||||
|
@ -59,29 +66,19 @@ this.ContentControl.prototype = {
|
||||||
return Utils.getVirtualCursor(this.document);
|
return Utils.getVirtualCursor(this.document);
|
||||||
},
|
},
|
||||||
|
|
||||||
receiveMessage: function ContentControl_receiveMessage(aMessage) {
|
receiveMessage: function cc_receiveMessage(aMessage) {
|
||||||
Logger.debug(() => {
|
Logger.debug(() => {
|
||||||
return ['ContentControl.receiveMessage',
|
return ['ContentControl.receiveMessage',
|
||||||
this.document.location.toString(),
|
aMessage.name,
|
||||||
JSON.stringify(aMessage.json)];
|
JSON.stringify(aMessage.json)];
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (aMessage.name) {
|
let func = this['handle' + aMessage.name.slice(9)]; // 'AccessFu:'.length
|
||||||
case 'AccessFu:MoveCursor':
|
if (func) {
|
||||||
this.handleMove(aMessage);
|
func.bind(this)(aMessage);
|
||||||
break;
|
} else {
|
||||||
case 'AccessFu:ClearCursor':
|
Logger.warning('ContentControl: Unhandled message:', aMessage.name);
|
||||||
this.handleClear(aMessage);
|
|
||||||
break;
|
|
||||||
case 'AccessFu:MoveToPoint':
|
|
||||||
this.handleMoveToPoint(aMessage);
|
|
||||||
break;
|
|
||||||
case 'AccessFu:AutoMove':
|
|
||||||
this.handleAutoMove(aMessage);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} catch (x) {
|
} catch (x) {
|
||||||
Logger.logException(
|
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 origin = aMessage.json.origin;
|
||||||
let action = aMessage.json.action;
|
let action = aMessage.json.action;
|
||||||
let vc = this.vc;
|
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 [x, y] = [aMessage.json.x, aMessage.json.y];
|
||||||
let rule = TraversalRules[aMessage.json.rule];
|
let rule = TraversalRules[aMessage.json.rule];
|
||||||
let vc = this.vc;
|
let vc = this.vc;
|
||||||
|
@ -141,16 +138,169 @@ this.ContentControl.prototype = {
|
||||||
this.sendToChild(vc, aMessage, delta);
|
this.sendToChild(vc, aMessage, delta);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleClear: function ContentControl_handleClear(aMessage) {
|
handleClearCursor: function cc_handleClearCursor(aMessage) {
|
||||||
this.sendToChild(this.vc, aMessage);
|
this.sendToChild(this.vc, aMessage);
|
||||||
this.vc.position = null;
|
this.vc.position = null;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleAutoMove: function ContentControl_handleAutoMove(aMessage) {
|
handleAutoMove: function cc_handleAutoMove(aMessage) {
|
||||||
this.autoMove(null, aMessage.json);
|
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;
|
let acc = aAccessible || this.vc.position;
|
||||||
if (Utils.isAliveAndVisible(acc) && acc.role === Roles.INTERNAL_FRAME) {
|
if (Utils.isAliveAndVisible(acc) && acc.role === Roles.INTERNAL_FRAME) {
|
||||||
let domNode = acc.DOMNode;
|
let domNode = acc.DOMNode;
|
||||||
|
@ -167,9 +317,7 @@ this.ContentControl.prototype = {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
sendToChild: function ContentControl_sendToChild(aVirtualCursor,
|
sendToChild: function cc_sendToChild(aVirtualCursor, aMessage, aReplacer) {
|
||||||
aMessage,
|
|
||||||
aReplacer) {
|
|
||||||
let mm = this.getChildCursor(aVirtualCursor.position);
|
let mm = this.getChildCursor(aVirtualCursor.position);
|
||||||
if (!mm) {
|
if (!mm) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -186,7 +334,7 @@ this.ContentControl.prototype = {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
sendToParent: function ContentControl_sendToParent(aMessage) {
|
sendToParent: function cc_sendToParent(aMessage) {
|
||||||
// XXX: This is a silly way to make a deep copy
|
// XXX: This is a silly way to make a deep copy
|
||||||
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
|
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
|
||||||
newJSON.origin = 'child';
|
newJSON.origin = 'child';
|
||||||
|
@ -205,7 +353,7 @@ this.ContentControl.prototype = {
|
||||||
* precedence over given anchor.
|
* precedence over given anchor.
|
||||||
* - moveMethod: pivot move method to use, default is 'moveNext',
|
* - 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;
|
let win = this.window;
|
||||||
win.clearTimeout(this._autoMove);
|
win.clearTimeout(this._autoMove);
|
||||||
|
|
||||||
|
@ -266,4 +414,4 @@ this.ContentControl.prototype = {
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
|
||||||
Ci.nsIMessageListener
|
Ci.nsIMessageListener
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|
|
@ -625,7 +625,8 @@ this.UtteranceGenerator = {
|
||||||
// This is because we expose the checked information on the node itself.
|
// 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
|
// XXX: this means the checked state is always appended to the end, regardless
|
||||||
// of the utterance ordering preference.
|
// 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) ?
|
let statetr = aState.contains(States.CHECKED) ?
|
||||||
'stateChecked' : 'stateNotChecked';
|
'stateChecked' : 'stateNotChecked';
|
||||||
stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr));
|
stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr));
|
||||||
|
|
|
@ -569,15 +569,13 @@ BraillePresenter.prototype = {
|
||||||
this.Presentation = {
|
this.Presentation = {
|
||||||
get presenters() {
|
get presenters() {
|
||||||
delete this.presenters;
|
delete this.presenters;
|
||||||
this.presenters = [new VisualPresenter()];
|
let presenterMap = {
|
||||||
|
'mobile/android': [VisualPresenter, AndroidPresenter],
|
||||||
if (Utils.MozBuildApp == 'mobile/android') {
|
'b2g': [VisualPresenter, SpeechPresenter, HapticPresenter],
|
||||||
this.presenters.push(new AndroidPresenter());
|
'browser': [VisualPresenter, SpeechPresenter, HapticPresenter,
|
||||||
} else {
|
AndroidPresenter]
|
||||||
this.presenters.push(new SpeechPresenter());
|
};
|
||||||
this.presenters.push(new HapticPresenter());
|
this.presenters = [new P() for (P of presenterMap[Utils.MozBuildApp])];
|
||||||
}
|
|
||||||
|
|
||||||
return this.presenters;
|
return this.presenters;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ this.Utils = {
|
||||||
this._AndroidSdkVersion = Services.sysinfo.getPropertyAsInt32('version');
|
this._AndroidSdkVersion = Services.sysinfo.getPropertyAsInt32('version');
|
||||||
} else {
|
} else {
|
||||||
// Most useful in desktop debugging.
|
// Most useful in desktop debugging.
|
||||||
this._AndroidSdkVersion = 15;
|
this._AndroidSdkVersion = 16;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this._AndroidSdkVersion;
|
return this._AndroidSdkVersion;
|
||||||
|
|
|
@ -5,10 +5,6 @@
|
||||||
let Ci = Components.interfaces;
|
let Ci = Components.interfaces;
|
||||||
let Cu = Components.utils;
|
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');
|
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
|
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
|
||||||
'resource://gre/modules/accessibility/Utils.jsm');
|
'resource://gre/modules/accessibility/Utils.jsm');
|
||||||
|
@ -68,86 +64,6 @@ function forwardToChild(aMessage, aListener, aVCPosition) {
|
||||||
return true;
|
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 activateContextMenu(aMessage) {
|
||||||
function sendContextMenuCoordinates(aAccessible) {
|
function sendContextMenuCoordinates(aAccessible) {
|
||||||
let bounds = Utils.getBounds(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) {
|
function presentCaretChange(aText, aOldOffset, aNewOffset) {
|
||||||
if (aOldOffset !== aNewOffset) {
|
if (aOldOffset !== aNewOffset) {
|
||||||
let msg = Presentation.textSelectionChanged(aText, aNewOffset, aNewOffset,
|
let msg = Presentation.textSelectionChanged(aText, aNewOffset, aNewOffset,
|
||||||
|
@ -282,12 +132,9 @@ addMessageListener(
|
||||||
if (m.json.buildApp)
|
if (m.json.buildApp)
|
||||||
Utils.MozBuildApp = m.json.buildApp;
|
Utils.MozBuildApp = m.json.buildApp;
|
||||||
|
|
||||||
addMessageListener('AccessFu:Activate', activateCurrent);
|
|
||||||
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
||||||
addMessageListener('AccessFu:Scroll', scroll);
|
addMessageListener('AccessFu:Scroll', scroll);
|
||||||
addMessageListener('AccessFu:AdjustRange', adjustRange);
|
addMessageListener('AccessFu:AdjustRange', adjustRange);
|
||||||
addMessageListener('AccessFu:MoveCaret', moveCaret);
|
|
||||||
addMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
|
|
||||||
|
|
||||||
if (!eventManager) {
|
if (!eventManager) {
|
||||||
eventManager = new EventManager(this);
|
eventManager = new EventManager(this);
|
||||||
|
@ -307,11 +154,8 @@ addMessageListener(
|
||||||
function(m) {
|
function(m) {
|
||||||
Logger.debug('AccessFu:Stop');
|
Logger.debug('AccessFu:Stop');
|
||||||
|
|
||||||
removeMessageListener('AccessFu:Activate', activateCurrent);
|
|
||||||
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
||||||
removeMessageListener('AccessFu:Scroll', scroll);
|
removeMessageListener('AccessFu:Scroll', scroll);
|
||||||
removeMessageListener('AccessFu:MoveCaret', moveCaret);
|
|
||||||
removeMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
|
|
||||||
|
|
||||||
eventManager.stop();
|
eventManager.stop();
|
||||||
contentControl.stop();
|
contentControl.stop();
|
||||||
|
|
|
@ -6,9 +6,11 @@ support-files =
|
||||||
output.js
|
output.js
|
||||||
doc_traversal.html
|
doc_traversal.html
|
||||||
doc_content_integration.html
|
doc_content_integration.html
|
||||||
|
doc_content_text.html
|
||||||
|
|
||||||
[test_alive.html]
|
[test_alive.html]
|
||||||
[test_content_integration.html]
|
[test_content_integration.html]
|
||||||
|
[test_content_text.html]
|
||||||
[test_explicit_names.html]
|
[test_explicit_names.html]
|
||||||
[test_landmarks.html]
|
[test_landmarks.html]
|
||||||
[test_live_regions.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 () {
|
this.setupMessageManager(this.mms[0], function () {
|
||||||
// Get child message managers and set them up
|
// Get child message managers and set them up
|
||||||
var frames = currentTabDocument().querySelectorAll('iframe');
|
var frames = currentTabDocument().querySelectorAll('iframe');
|
||||||
|
if (frames.length === 0) {
|
||||||
|
self.pump();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var toSetup = 0;
|
var toSetup = 0;
|
||||||
for (var i = 0; i < frames.length; i++ ) {
|
for (var i = 0; i < frames.length; i++ ) {
|
||||||
var mm = Utils.getMessageManager(frames[i]);
|
var mm = Utils.getMessageManager(frames[i]);
|
||||||
|
@ -206,7 +211,8 @@ AccessFuContentTest.prototype = {
|
||||||
aMessageManager.addMessageListener('AccessFu:Present', this);
|
aMessageManager.addMessageListener('AccessFu:Present', this);
|
||||||
aMessageManager.addMessageListener('AccessFu:Ready', function () {
|
aMessageManager.addMessageListener('AccessFu:Ready', function () {
|
||||||
aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
|
aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
|
||||||
aMessageManager.sendAsyncMessage('AccessFu:Start', { buildApp: 'browser' });
|
aMessageManager.sendAsyncMessage('AccessFu:Start',
|
||||||
|
{ buildApp: 'browser', androidSdkVersion: Utils.AndroidSdkVersion});
|
||||||
});
|
});
|
||||||
|
|
||||||
aMessageManager.loadFrameScript(
|
aMessageManager.loadFrameScript(
|
||||||
|
@ -242,21 +248,46 @@ AccessFuContentTest.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var expected = this.currentPair[1];
|
var expected = this.currentPair[1] || {};
|
||||||
|
var speech = this.extractUtterance(aMessage.json);
|
||||||
if (expected) {
|
var android = this.extractAndroid(aMessage.json, expected.android);
|
||||||
if (expected.speak !== undefined) {
|
if ((speech && expected.speak) || (android && expected.android)) {
|
||||||
var speech = this.extractUtterance(aMessage.json);
|
if (expected.speak) {
|
||||||
if (!speech) {
|
(SimpleTest[expected.speak_checkFunc] || is)(speech, expected.speak);
|
||||||
// Probably a visual highlight adjustment after a scroll.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var checkFunc = SimpleTest[expected.speak_checkFunc] || is;
|
|
||||||
checkFunc(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) {
|
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;
|
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");
|
var iframe = doc.createElement("iframe");
|
||||||
iframe.mozbrowser = true;
|
iframe.mozbrowser = true;
|
||||||
iframe.addEventListener("mozbrowserloadend", function () {
|
iframe.addEventListener("mozbrowserloadend", function () {
|
||||||
var simpleMoveNext = { name: 'AccessFu:MoveCursor',
|
var contentTest = new AccessFuContentTest(
|
||||||
json: { action: 'moveNext',
|
[
|
||||||
rule: 'Simple',
|
// Simple traversal forward
|
||||||
inputType: 'gesture',
|
[ContentMessages.simpleMoveNext, {
|
||||||
origin: 'top' }
|
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',
|
// Simple traversal backward
|
||||||
json: { action: 'movePrevious',
|
[ContentMessages.simpleMovePrevious, {
|
||||||
rule: 'Simple',
|
speak: 'many option checked check button such app'
|
||||||
inputType: 'gesture',
|
}],
|
||||||
origin: 'top' }
|
[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',
|
// Moving to the absolute last item from an embedded document
|
||||||
json: { origin: 'top' } };
|
// 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',
|
[ContentMessages.clearCursor, null], // Reset cursors
|
||||||
json: { action: 'moveLast',
|
|
||||||
rule: 'Simple',
|
|
||||||
inputType: 'gesture',
|
|
||||||
origin: 'top' }
|
|
||||||
};
|
|
||||||
|
|
||||||
var simpleMoveFirst = { name: 'AccessFu:MoveCursor',
|
// Moving to the absolute first item from an embedded document
|
||||||
json: { action: 'moveFirst',
|
// fails. Bug 972035.
|
||||||
rule: 'Simple',
|
[ContentMessages.simpleMoveNext, {
|
||||||
inputType: 'gesture',
|
speak: 'Phone status bar Traversal Rule test document'
|
||||||
origin: 'top' }
|
}],
|
||||||
};
|
[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',
|
// Reset cursors
|
||||||
json: { origin: 'top' }
|
[ContentMessages.clearCursor, null],
|
||||||
};
|
|
||||||
|
|
||||||
function focusFunc(aSelector, aBlur) {
|
// Move cursor with focus in outside document
|
||||||
return function (mm) {
|
[ContentMessages.simpleMoveNext, {
|
||||||
mm.sendAsyncMessage(
|
speak: 'Phone status bar Traversal Rule test document'
|
||||||
'AccessFuTest:Focus', { selector: aSelector, blur: aBlur });
|
}],
|
||||||
};
|
[ContentMessages.focusSelector('button#home', false), {
|
||||||
}
|
speak: 'Home button'
|
||||||
|
}],
|
||||||
|
|
||||||
var contentTest = new AccessFuContentTest(
|
// Blur button and reset cursor
|
||||||
[
|
[ContentMessages.focusSelector('button#home', true), null],
|
||||||
// Simple traversal forward
|
[ContentMessages.clearCursor, null],
|
||||||
[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' }],
|
|
||||||
|
|
||||||
// Simple traversal backward
|
// Set focus on element outside of embedded frame while
|
||||||
[simpleMovePrevious,
|
// cursor is in frame
|
||||||
{ speak: 'many option checked check button such app' }],
|
[ContentMessages.simpleMoveNext, {
|
||||||
[activateCurrent,
|
speak: 'Phone status bar Traversal Rule test document'
|
||||||
{ speak: 'unchecked' }],
|
}],
|
||||||
[null,
|
[ContentMessages.simpleMoveNext, {
|
||||||
{ speak: 'unchecked', speak_checkFunc: 'todo_isnot' }],
|
speak: 'wow heading level 1 such app'
|
||||||
[simpleMovePrevious,
|
}],
|
||||||
{ speak: 'wow heading level 1' }],
|
[ContentMessages.focusSelector('button#home', false), {
|
||||||
[simpleMovePrevious,
|
speak: 'Home button'
|
||||||
{ speak: 'Phone status bar' }],
|
}]
|
||||||
|
|
||||||
// Moving to the absolute last item from an embedded document fails.
|
// Blur button and reset cursor
|
||||||
// Bug 972035.
|
[ContentMessages.focusSelector('button#home', true), null],
|
||||||
[simpleMoveNext,
|
[ContentMessages.clearCursor, null],
|
||||||
{ 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' }],
|
|
||||||
|
|
||||||
[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.
|
// Open dialog in outer doc, while cursor is also in outer doc
|
||||||
// Bug 972035.
|
[ContentMessages.simpleMoveNext, {
|
||||||
[simpleMoveNext,
|
speak: 'Phone status bar Traversal Rule test document'
|
||||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
}],
|
||||||
[simpleMoveNext,
|
[doc.defaultView.showAlert, {
|
||||||
{ speak: 'wow heading level 1 such app' }],
|
speak: 'This is an alert! heading level 1 dialog'
|
||||||
[simpleMoveNext,
|
}],
|
||||||
{ speak: 'many option not checked check button' }],
|
|
||||||
[simpleMoveFirst,
|
|
||||||
{ speak: 'Phone status bar', speak_checkFunc: 'todo_is' }],
|
|
||||||
|
|
||||||
// Reset cursors
|
[function() {
|
||||||
[clearCursor, null],
|
doc.defaultView.hideAlert()
|
||||||
|
}, {
|
||||||
|
speak: 'wow heading level 1 such app'
|
||||||
|
}],
|
||||||
|
|
||||||
// Move cursor with focus in outside document
|
[ContentMessages.clearCursor, null],
|
||||||
[simpleMoveNext,
|
|
||||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
|
||||||
[ focusFunc('button#home', false), { speak: 'Home button' }],
|
|
||||||
|
|
||||||
// Blur button and reset cursor
|
// Open dialog in outer doc, while cursor is in inner frame
|
||||||
[focusFunc('button#home', true), null],
|
[ContentMessages.simpleMoveNext, {
|
||||||
[clearCursor, null],
|
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
|
// XXX: Place cursor back where it was.
|
||||||
[simpleMoveNext,
|
[doc.defaultView.hideAlert, {
|
||||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
speak: 'many option not checked check button such app'
|
||||||
[simpleMoveNext,
|
}],
|
||||||
{ speak: 'wow heading level 1 such app' }],
|
|
||||||
[focusFunc('button#home', false), { speak: 'Home button' }]
|
|
||||||
|
|
||||||
// Blur button and reset cursor
|
[ContentMessages.clearCursor, null],
|
||||||
[focusFunc('button#home', true), null],
|
|
||||||
[clearCursor, null],
|
|
||||||
|
|
||||||
// XXX: Set focus on iframe itself.
|
// Open dialog, then focus on something when closing
|
||||||
// XXX: Set focus on element in iframe when cursor is outside of it.
|
[ContentMessages.simpleMoveNext, {
|
||||||
// XXX: Set focus on element in iframe when cursor is in iframe.
|
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
|
[function() {
|
||||||
[simpleMoveNext,
|
doc.defaultView.hideAlert();
|
||||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
doc.querySelector('button#home').focus();
|
||||||
[doc.defaultView.showAlert,
|
}, {
|
||||||
{ speak: 'This is an alert! heading level 1 dialog' }],
|
speak: 'Home button Traversal Rule test document'
|
||||||
|
}]
|
||||||
[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' }]
|
|
||||||
]);
|
|
||||||
|
|
||||||
contentTest.start(function () {
|
contentTest.start(function () {
|
||||||
closeBrowserWindow();
|
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"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</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="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||||
|
|
|
@ -4,6 +4,6 @@
|
||||||
"branch": "",
|
"branch": "",
|
||||||
"revision": ""
|
"revision": ""
|
||||||
},
|
},
|
||||||
"revision": "ca4f67df676ef3ab0072b9e3fb97baee4d4bde36",
|
"revision": "91e5760ff948d3bef3959866c6a1744991560ef6",
|
||||||
"repo_path": "/integration/gaia-central"
|
"repo_path": "/integration/gaia-central"
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</project>
|
</project>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||||
</project>
|
</project>
|
||||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
<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="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
<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
|
// Delete HTTP cache v2 data of users that didn't opt-in manually
|
||||||
pref("browser.cache.auto_delete_cache_version", 1);
|
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();
|
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(() => {
|
SessionStore.promiseInitialized.then(() => {
|
||||||
// Bail out if the window has been closed in the meantime.
|
// Bail out if the window has been closed in the meantime.
|
||||||
if (window.closed) {
|
if (window.closed) {
|
||||||
|
|
|
@ -61,4 +61,5 @@ run-if = crashreporter
|
||||||
[browser_pluginplaypreview2.js]
|
[browser_pluginplaypreview2.js]
|
||||||
[browser_pluginCrashCommentAndURL.js]
|
[browser_pluginCrashCommentAndURL.js]
|
||||||
run-if = crashreporter
|
run-if = crashreporter
|
||||||
|
skip-if = os == "linux" && debug # bug 933680
|
||||||
[browser_plugins_added_dynamically.js]
|
[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 += [
|
PARALLEL_DIRS += [
|
||||||
'base',
|
'base',
|
||||||
'components',
|
'components',
|
||||||
|
'experiments',
|
||||||
'fuel',
|
'fuel',
|
||||||
'locales',
|
'locales',
|
||||||
'modules',
|
'modules',
|
||||||
|
|
|
@ -28,9 +28,11 @@ AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [
|
||||||
esac
|
esac
|
||||||
fi
|
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 \
|
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"
|
MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR"
|
||||||
else
|
else
|
||||||
MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR"
|
MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR"
|
||||||
|
|
|
@ -8933,9 +8933,9 @@ if test "$MOZ_TREE_FREETYPE"; then
|
||||||
export CXXFLAGS="$CXXFLAGS $MOZ_DEBUG_FLAGS"
|
export CXXFLAGS="$CXXFLAGS $MOZ_DEBUG_FLAGS"
|
||||||
export LDFLAGS="$LDFLAGS $MOZ_DEBUG_LDFLAGS"
|
export LDFLAGS="$LDFLAGS $MOZ_DEBUG_LDFLAGS"
|
||||||
export LIBPNG_CFLAGS="$MOZ_PNG_CFLAGS"
|
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"
|
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
|
if ! test -e modules; then
|
||||||
mkdir modules
|
mkdir modules
|
||||||
|
|
|
@ -5473,6 +5473,14 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
|
||||||
return true;
|
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
|
nsresult
|
||||||
nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
|
nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1421,6 +1421,8 @@ public:
|
||||||
uint32_t aNamespaceID,
|
uint32_t aNamespaceID,
|
||||||
mozilla::ErrorResult& rv);
|
mozilla::ErrorResult& rv);
|
||||||
|
|
||||||
|
static bool IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject);
|
||||||
|
|
||||||
// The "registry" from the web components spec.
|
// The "registry" from the web components spec.
|
||||||
nsRefPtr<Registry> mRegistry;
|
nsRefPtr<Registry> mRegistry;
|
||||||
|
|
||||||
|
|
|
@ -3577,7 +3577,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
|
||||||
thebes = new gfxContext(mTarget);
|
thebes = new gfxContext(mTarget);
|
||||||
thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
|
thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
|
||||||
matrix._22, matrix._31, matrix._32));
|
matrix._22, matrix._31, matrix._32));
|
||||||
} else if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
} else {
|
||||||
drawDT =
|
drawDT =
|
||||||
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
|
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
|
||||||
SurfaceFormat::B8G8R8A8);
|
SurfaceFormat::B8G8R8A8);
|
||||||
|
@ -3588,17 +3588,6 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
|
||||||
|
|
||||||
thebes = new gfxContext(drawDT);
|
thebes = new gfxContext(drawDT);
|
||||||
thebes->Scale(matrix._11, matrix._22);
|
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();
|
nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "DOMSVGPoint.h"
|
#include "DOMSVGPoint.h"
|
||||||
#include "DOMSVGPointList.h"
|
#include "DOMSVGPointList.h"
|
||||||
#include "SVGPoint.h"
|
#include "SVGPoint.h"
|
||||||
|
#include "gfx2DGlue.h"
|
||||||
#include "nsSVGElement.h"
|
#include "nsSVGElement.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "mozilla/dom/SVGMatrix.h"
|
#include "mozilla/dom/SVGMatrix.h"
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
// See the architecture comment in DOMSVGPointList.h.
|
// See the architecture comment in DOMSVGPointList.h.
|
||||||
|
|
||||||
using namespace mozilla;
|
using namespace mozilla;
|
||||||
|
using namespace mozilla::gfx;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ DOMSVGPoint::MatrixTransform(dom::SVGMatrix& matrix)
|
||||||
float x = HasOwner() ? InternalItem().mX : mPt.mX;
|
float x = HasOwner() ? InternalItem().mX : mPt.mX;
|
||||||
float y = HasOwner() ? InternalItem().mY : mPt.mY;
|
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);
|
nsCOMPtr<nsISVGPoint> newPoint = new DOMSVGPoint(pt);
|
||||||
return newPoint.forget();
|
return newPoint.forget();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#define MOZILLA_DOMSVGPOINT_H__
|
#define MOZILLA_DOMSVGPOINT_H__
|
||||||
|
|
||||||
#include "DOMSVGPointList.h"
|
#include "DOMSVGPointList.h"
|
||||||
#include "gfxPoint.h"
|
|
||||||
#include "mozilla/gfx/2D.h"
|
#include "mozilla/gfx/2D.h"
|
||||||
#include "nsAutoPtr.h"
|
#include "nsAutoPtr.h"
|
||||||
#include "nsDebug.h"
|
#include "nsDebug.h"
|
||||||
|
@ -87,15 +86,6 @@ public:
|
||||||
"DOMSVGPoint coords are not finite");
|
"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
|
// WebIDL
|
||||||
virtual float X();
|
virtual float X();
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "SVGMotionSMILType.h"
|
#include "SVGMotionSMILType.h"
|
||||||
|
|
||||||
#include "gfx2DGlue.h"
|
#include "gfx2DGlue.h"
|
||||||
|
#include "mozilla/gfx/Point.h"
|
||||||
#include "nsSMILValue.h"
|
#include "nsSMILValue.h"
|
||||||
#include "nsDebug.h"
|
#include "nsDebug.h"
|
||||||
#include "nsMathUtils.h"
|
#include "nsMathUtils.h"
|
||||||
|
@ -233,16 +234,16 @@ SVGMotionSMILType::IsEqual(const nsSMILValue& aLeft,
|
||||||
inline static void
|
inline static void
|
||||||
GetAngleAndPointAtDistance(Path* aPath, float aDistance,
|
GetAngleAndPointAtDistance(Path* aPath, float aDistance,
|
||||||
RotateType aRotateType,
|
RotateType aRotateType,
|
||||||
gfxFloat& aRotateAngle, // in & out-param.
|
float& aRotateAngle, // in & out-param.
|
||||||
gfxPoint& aPoint) // out-param.
|
Point& aPoint) // out-param.
|
||||||
{
|
{
|
||||||
if (aRotateType == eRotateType_Explicit) {
|
if (aRotateType == eRotateType_Explicit) {
|
||||||
// Leave aRotateAngle as-is.
|
// Leave aRotateAngle as-is.
|
||||||
aPoint = ThebesPoint(aPath->ComputePointAtLength(aDistance));
|
aPoint = aPath->ComputePointAtLength(aDistance);
|
||||||
} else {
|
} else {
|
||||||
Point tangent; // Unit vector tangent to the point we find.
|
Point tangent; // Unit vector tangent to the point we find.
|
||||||
aPoint = ThebesPoint(aPath->ComputePointAtLength(aDistance, &tangent));
|
aPoint = aPath->ComputePointAtLength(aDistance, &tangent);
|
||||||
gfxFloat tangentAngle = atan2(tangent.y, tangent.x);
|
float tangentAngle = atan2(tangent.y, tangent.x);
|
||||||
if (aRotateType == eRotateType_Auto) {
|
if (aRotateType == eRotateType_Auto) {
|
||||||
aRotateAngle = tangentAngle;
|
aRotateAngle = tangentAngle;
|
||||||
} else {
|
} else {
|
||||||
|
@ -291,8 +292,8 @@ SVGMotionSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
|
||||||
Path* path = srcParams.mPath;
|
Path* path = srcParams.mPath;
|
||||||
|
|
||||||
// Use destination to get our rotate angle.
|
// Use destination to get our rotate angle.
|
||||||
gfxFloat rotateAngle = dstSeg.mRotateAngle;
|
float rotateAngle = dstSeg.mRotateAngle;
|
||||||
gfxPoint dstPt;
|
Point dstPt;
|
||||||
GetAngleAndPointAtDistance(path, dstParams.mDistToPoint, dstSeg.mRotateType,
|
GetAngleAndPointAtDistance(path, dstParams.mDistToPoint, dstSeg.mRotateType,
|
||||||
rotateAngle, dstPt);
|
rotateAngle, dstPt);
|
||||||
|
|
||||||
|
@ -450,8 +451,8 @@ SVGMotionSMILType::CreateMatrix(const nsSMILValue& aSMILVal)
|
||||||
gfx::Matrix matrix;
|
gfx::Matrix matrix;
|
||||||
uint32_t length = arr.Length();
|
uint32_t length = arr.Length();
|
||||||
for (uint32_t i = 0; i < length; i++) {
|
for (uint32_t i = 0; i < length; i++) {
|
||||||
gfxPoint point; // initialized below
|
Point point; // initialized below
|
||||||
gfxFloat rotateAngle = arr[i].mRotateAngle; // might get updated below
|
float rotateAngle = arr[i].mRotateAngle; // might get updated below
|
||||||
if (arr[i].mSegmentType == eSegmentType_Translation) {
|
if (arr[i].mSegmentType == eSegmentType_Translation) {
|
||||||
point.x = arr[i].mU.mTranslationParams.mX;
|
point.x = arr[i].mU.mTranslationParams.mX;
|
||||||
point.y = arr[i].mU.mTranslationParams.mY;
|
point.y = arr[i].mU.mTranslationParams.mY;
|
||||||
|
|
|
@ -821,7 +821,7 @@ SVGPathData::ToPathForLengthOrPositionMeasuring() const
|
||||||
}
|
}
|
||||||
|
|
||||||
static double
|
static double
|
||||||
AngleOfVector(const gfxPoint& aVector)
|
AngleOfVector(const Point& aVector)
|
||||||
{
|
{
|
||||||
// C99 says about atan2 "A domain error may occur if both arguments are
|
// C99 says about atan2 "A domain error may occur if both arguments are
|
||||||
// zero" and "On a domain error, the function returns an implementation-
|
// 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.
|
// 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:
|
// 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
|
static float
|
||||||
AngleOfVectorF(const gfxPoint& aVector)
|
AngleOfVectorF(const Point& aVector)
|
||||||
{
|
{
|
||||||
return static_cast<float>(AngleOfVector(aVector));
|
return static_cast<float>(AngleOfVector(aVector));
|
||||||
}
|
}
|
||||||
|
@ -846,14 +846,14 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
// places, and repeat multiple times consecutively.
|
// places, and repeat multiple times consecutively.
|
||||||
|
|
||||||
// info on current [sub]path (reset every M command):
|
// 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;
|
float pathStartAngle = 0.0f;
|
||||||
|
|
||||||
// info on previous segment:
|
// info on previous segment:
|
||||||
uint16_t prevSegType = PATHSEG_UNKNOWN;
|
uint16_t prevSegType = PATHSEG_UNKNOWN;
|
||||||
gfxPoint prevSegEnd(0.0, 0.0);
|
Point prevSegEnd(0.0, 0.0);
|
||||||
float prevSegEndAngle = 0.0f;
|
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;
|
uint32_t i = 0;
|
||||||
while (i < mData.Length()) {
|
while (i < mData.Length()) {
|
||||||
|
@ -861,8 +861,8 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
// info on current segment:
|
// info on current segment:
|
||||||
uint16_t segType =
|
uint16_t segType =
|
||||||
SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args
|
SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args
|
||||||
gfxPoint &segStart = prevSegEnd;
|
Point& segStart = prevSegEnd;
|
||||||
gfxPoint segEnd;
|
Point segEnd;
|
||||||
float segStartAngle, segEndAngle;
|
float segStartAngle, segEndAngle;
|
||||||
|
|
||||||
switch (segType) // to find segStartAngle, segEnd and 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_ABS:
|
||||||
case PATHSEG_MOVETO_REL:
|
case PATHSEG_MOVETO_REL:
|
||||||
if (segType == PATHSEG_MOVETO_ABS) {
|
if (segType == PATHSEG_MOVETO_ABS) {
|
||||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
segEnd = Point(mData[i], mData[i+1]);
|
||||||
} else {
|
} else {
|
||||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||||
}
|
}
|
||||||
pathStart = segEnd;
|
pathStart = segEnd;
|
||||||
// If authors are going to specify multiple consecutive moveto commands
|
// 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_ABS:
|
||||||
case PATHSEG_LINETO_REL:
|
case PATHSEG_LINETO_REL:
|
||||||
if (segType == PATHSEG_LINETO_ABS) {
|
if (segType == PATHSEG_LINETO_ABS) {
|
||||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
segEnd = Point(mData[i], mData[i+1]);
|
||||||
} else {
|
} else {
|
||||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||||
}
|
}
|
||||||
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
||||||
i += 2;
|
i += 2;
|
||||||
|
@ -900,15 +900,15 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
case PATHSEG_CURVETO_CUBIC_ABS:
|
case PATHSEG_CURVETO_CUBIC_ABS:
|
||||||
case PATHSEG_CURVETO_CUBIC_REL:
|
case PATHSEG_CURVETO_CUBIC_REL:
|
||||||
{
|
{
|
||||||
gfxPoint cp1, cp2; // control points
|
Point cp1, cp2; // control points
|
||||||
if (segType == PATHSEG_CURVETO_CUBIC_ABS) {
|
if (segType == PATHSEG_CURVETO_CUBIC_ABS) {
|
||||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
cp1 = Point(mData[i], mData[i+1]);
|
||||||
cp2 = gfxPoint(mData[i+2], mData[i+3]);
|
cp2 = Point(mData[i+2], mData[i+3]);
|
||||||
segEnd = gfxPoint(mData[i+4], mData[i+5]);
|
segEnd = Point(mData[i+4], mData[i+5]);
|
||||||
} else {
|
} else {
|
||||||
cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
|
cp1 = segStart + Point(mData[i], mData[i+1]);
|
||||||
cp2 = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
cp2 = segStart + Point(mData[i+2], mData[i+3]);
|
||||||
segEnd = segStart + gfxPoint(mData[i+4], mData[i+5]);
|
segEnd = segStart + Point(mData[i+4], mData[i+5]);
|
||||||
}
|
}
|
||||||
prevCP = cp2;
|
prevCP = cp2;
|
||||||
if (cp1 == segStart) {
|
if (cp1 == segStart) {
|
||||||
|
@ -926,13 +926,13 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
case PATHSEG_CURVETO_QUADRATIC_ABS:
|
case PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||||
case PATHSEG_CURVETO_QUADRATIC_REL:
|
case PATHSEG_CURVETO_QUADRATIC_REL:
|
||||||
{
|
{
|
||||||
gfxPoint cp1, cp2; // control points
|
Point cp1, cp2; // control points
|
||||||
if (segType == PATHSEG_CURVETO_QUADRATIC_ABS) {
|
if (segType == PATHSEG_CURVETO_QUADRATIC_ABS) {
|
||||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
cp1 = Point(mData[i], mData[i+1]);
|
||||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
segEnd = Point(mData[i+2], mData[i+3]);
|
||||||
} else {
|
} else {
|
||||||
cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
|
cp1 = segStart + Point(mData[i], mData[i+1]);
|
||||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
segEnd = segStart + Point(mData[i+2], mData[i+3]);
|
||||||
}
|
}
|
||||||
prevCP = cp1;
|
prevCP = cp1;
|
||||||
segStartAngle = AngleOfVectorF(cp1 - segStart);
|
segStartAngle = AngleOfVectorF(cp1 - segStart);
|
||||||
|
@ -950,9 +950,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
bool largeArcFlag = mData[i+3] != 0.0f;
|
bool largeArcFlag = mData[i+3] != 0.0f;
|
||||||
bool sweepFlag = mData[i+4] != 0.0f;
|
bool sweepFlag = mData[i+4] != 0.0f;
|
||||||
if (segType == PATHSEG_ARC_ABS) {
|
if (segType == PATHSEG_ARC_ABS) {
|
||||||
segEnd = gfxPoint(mData[i+5], mData[i+6]);
|
segEnd = Point(mData[i+5], mData[i+6]);
|
||||||
} else {
|
} 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:
|
// 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 cyp = -root * ry * x1p / rx;
|
||||||
|
|
||||||
double theta, delta;
|
double theta, delta;
|
||||||
theta = AngleOfVector(gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry)); // F.6.5.5
|
theta = AngleOfVector(Point((x1p-cxp)/rx, (y1p-cyp)/ry)); // F.6.5.5
|
||||||
delta = AngleOfVector(gfxPoint((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6
|
delta = AngleOfVector(Point((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6
|
||||||
theta;
|
theta;
|
||||||
if (!sweepFlag && delta > 0)
|
if (!sweepFlag && delta > 0)
|
||||||
delta -= 2.0 * M_PI;
|
delta -= 2.0 * M_PI;
|
||||||
|
@ -1047,9 +1047,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
case PATHSEG_LINETO_HORIZONTAL_ABS:
|
case PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||||
case PATHSEG_LINETO_HORIZONTAL_REL:
|
case PATHSEG_LINETO_HORIZONTAL_REL:
|
||||||
if (segType == PATHSEG_LINETO_HORIZONTAL_ABS) {
|
if (segType == PATHSEG_LINETO_HORIZONTAL_ABS) {
|
||||||
segEnd = gfxPoint(mData[i++], segStart.y);
|
segEnd = Point(mData[i++], segStart.y);
|
||||||
} else {
|
} else {
|
||||||
segEnd = segStart + gfxPoint(mData[i++], 0.0f);
|
segEnd = segStart + Point(mData[i++], 0.0f);
|
||||||
}
|
}
|
||||||
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
||||||
break;
|
break;
|
||||||
|
@ -1057,9 +1057,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
case PATHSEG_LINETO_VERTICAL_ABS:
|
case PATHSEG_LINETO_VERTICAL_ABS:
|
||||||
case PATHSEG_LINETO_VERTICAL_REL:
|
case PATHSEG_LINETO_VERTICAL_REL:
|
||||||
if (segType == PATHSEG_LINETO_VERTICAL_ABS) {
|
if (segType == PATHSEG_LINETO_VERTICAL_ABS) {
|
||||||
segEnd = gfxPoint(segStart.x, mData[i++]);
|
segEnd = Point(segStart.x, mData[i++]);
|
||||||
} else {
|
} else {
|
||||||
segEnd = segStart + gfxPoint(0.0f, mData[i++]);
|
segEnd = segStart + Point(0.0f, mData[i++]);
|
||||||
}
|
}
|
||||||
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
||||||
break;
|
break;
|
||||||
|
@ -1067,15 +1067,15 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||||
case PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
case PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||||
{
|
{
|
||||||
gfxPoint cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ?
|
Point cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ?
|
||||||
segStart * 2 - prevCP : segStart;
|
segStart * 2 - prevCP : segStart;
|
||||||
gfxPoint cp2;
|
Point cp2;
|
||||||
if (segType == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) {
|
if (segType == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) {
|
||||||
cp2 = gfxPoint(mData[i], mData[i+1]);
|
cp2 = Point(mData[i], mData[i+1]);
|
||||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
segEnd = Point(mData[i+2], mData[i+3]);
|
||||||
} else {
|
} else {
|
||||||
cp2 = segStart + gfxPoint(mData[i], mData[i+1]);
|
cp2 = segStart + Point(mData[i], mData[i+1]);
|
||||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
segEnd = segStart + Point(mData[i+2], mData[i+3]);
|
||||||
}
|
}
|
||||||
prevCP = cp2;
|
prevCP = cp2;
|
||||||
if (cp1 == segStart) {
|
if (cp1 == segStart) {
|
||||||
|
@ -1093,13 +1093,13 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
||||||
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||||
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||||
{
|
{
|
||||||
gfxPoint cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ?
|
Point cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ?
|
||||||
segStart * 2 - prevCP : segStart;
|
segStart * 2 - prevCP : segStart;
|
||||||
gfxPoint cp2;
|
Point cp2;
|
||||||
if (segType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) {
|
if (segType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) {
|
||||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
segEnd = Point(mData[i], mData[i+1]);
|
||||||
} else {
|
} else {
|
||||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||||
}
|
}
|
||||||
prevCP = cp1;
|
prevCP = cp1;
|
||||||
segStartAngle = AngleOfVectorF(cp1 - segStart);
|
segStartAngle = AngleOfVectorF(cp1 - segStart);
|
||||||
|
|
|
@ -334,7 +334,7 @@ SVGPathElement::ConstructPath(gfxContext *aCtx)
|
||||||
mD.GetAnimValue().ConstructPath(aCtx);
|
mD.GetAnimValue().ConstructPath(aCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
gfxFloat
|
float
|
||||||
SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
|
SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
|
||||||
{
|
{
|
||||||
NS_ABORT_IF_FALSE(aFor == eForTextPath || aFor == eForStroking,
|
NS_ABORT_IF_FALSE(aFor == eForTextPath || aFor == eForStroking,
|
||||||
|
|
|
@ -82,7 +82,7 @@ public:
|
||||||
* length (as provided by the <path> element's 'pathLength' attribute). This
|
* length (as provided by the <path> element's 'pathLength' attribute). This
|
||||||
* is used to scale stroke dashing, and to scale offsets along a textPath.
|
* is used to scale stroke dashing, and to scale offsets along a textPath.
|
||||||
*/
|
*/
|
||||||
gfxFloat GetPathLengthScale(PathLengthScaleForType aFor);
|
float GetPathLengthScale(PathLengthScaleForType aFor);
|
||||||
|
|
||||||
// WebIDL
|
// WebIDL
|
||||||
already_AddRefed<SVGAnimatedNumber> PathLength();
|
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;
|
aLeft[3].y = aRight[0].y = (aLeft[2].y + aRight[1].y) / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gfxFloat
|
static float
|
||||||
CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts,
|
CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts,
|
||||||
uint32_t aRecursionCount,
|
uint32_t aRecursionCount,
|
||||||
void (*aSplit)(const Point*, Point*, Point*))
|
void (*aSplit)(const Point*, Point*, Point*))
|
||||||
{
|
{
|
||||||
Point left[4];
|
Point left[4];
|
||||||
Point right[4];
|
Point right[4];
|
||||||
gfxFloat length = 0, dist;
|
float length = 0, dist;
|
||||||
for (uint32_t i = 0; i < aNumPts - 1; i++) {
|
for (uint32_t i = 0; i < aNumPts - 1; i++) {
|
||||||
length += CalcDistanceBetweenPoints(aCurve[i], aCurve[i+1]);
|
length += CalcDistanceBetweenPoints(aCurve[i], aCurve[i+1]);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts,
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline gfxFloat
|
static inline float
|
||||||
CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1,
|
CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1,
|
||||||
const Point& aCP2, const Point &aTo)
|
const Point& aCP2, const Point &aTo)
|
||||||
{
|
{
|
||||||
|
@ -161,7 +161,7 @@ CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1,
|
||||||
return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
|
return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline gfxFloat
|
static inline float
|
||||||
CalcLengthOfQuadraticBezier(const Point& aPos, const Point& aCP,
|
CalcLengthOfQuadraticBezier(const Point& aPos, const Point& aCP,
|
||||||
const Point& aTo)
|
const Point& aTo)
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,3 +73,10 @@ if CONFIG['SOLARIS_SUNPRO_CC']:
|
||||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
RCFILE = 'sqlite.rc'
|
RCFILE = 'sqlite.rc'
|
||||||
RESFILE = 'sqlite.res'
|
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__();
|
__delete__();
|
||||||
|
|
||||||
|
ReplyKeyEvent(WidgetKeyboardEvent event);
|
||||||
|
|
||||||
child:
|
child:
|
||||||
/**
|
/**
|
||||||
* Notify the remote browser that it has been Show()n on this
|
* 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;
|
mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (localEvent.mFlags.mWantReplyFromContentProcess) {
|
||||||
|
SendReplyKeyEvent(localEvent);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1192,6 +1192,28 @@ TabParent::GetChildProcessOffset()
|
||||||
pt, targetFrame->PresContext()->AppUnitsPerDevPixel()));
|
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.
|
* Try to answer query event using cached text.
|
||||||
*
|
*
|
||||||
|
|
|
@ -114,6 +114,7 @@ public:
|
||||||
|
|
||||||
virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
|
virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
|
||||||
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) 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 RecvPRenderFrameConstructor(PRenderFrameParent* actor) MOZ_OVERRIDE;
|
||||||
virtual bool RecvInitRenderFrame(PRenderFrameParent* aFrame,
|
virtual bool RecvInitRenderFrame(PRenderFrameParent* aFrame,
|
||||||
ScrollingBehavior* scrolling,
|
ScrollingBehavior* scrolling,
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "nsPluginNativeWindow.h"
|
#include "nsPluginNativeWindow.h"
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
#include "nsPluginInstanceOwner.h"
|
#include "nsPluginInstanceOwner.h"
|
||||||
|
#include "nsDataHashtable.h"
|
||||||
|
|
||||||
#define MAGIC_REQUEST_CONTEXT 0x01020304
|
#define MAGIC_REQUEST_CONTEXT 0x01020304
|
||||||
|
|
||||||
|
@ -39,12 +40,12 @@ class nsPluginByteRangeStreamListener
|
||||||
public:
|
public:
|
||||||
nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
|
nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
|
||||||
virtual ~nsPluginByteRangeStreamListener();
|
virtual ~nsPluginByteRangeStreamListener();
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIREQUESTOBSERVER
|
NS_DECL_NSIREQUESTOBSERVER
|
||||||
NS_DECL_NSISTREAMLISTENER
|
NS_DECL_NSISTREAMLISTENER
|
||||||
NS_DECL_NSIINTERFACEREQUESTOR
|
NS_DECL_NSIINTERFACEREQUESTOR
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsCOMPtr<nsIStreamListener> mStreamConverter;
|
nsCOMPtr<nsIStreamListener> mStreamConverter;
|
||||||
nsWeakPtr mWeakPtrPluginStreamListenerPeer;
|
nsWeakPtr mWeakPtrPluginStreamListenerPeer;
|
||||||
|
@ -88,17 +89,17 @@ NS_IMETHODIMP
|
||||||
nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
|
nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
||||||
if (!finalStreamListener)
|
if (!finalStreamListener)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsPluginStreamListenerPeer *pslp =
|
nsPluginStreamListenerPeer *pslp =
|
||||||
static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
|
static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
|
||||||
|
|
||||||
NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
|
NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
|
||||||
"Untracked byte-range request?");
|
"Untracked byte-range request?");
|
||||||
|
|
||||||
nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
|
nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
|
rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
|
||||||
|
@ -113,18 +114,18 @@ nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mStreamConverter = 0;
|
mStreamConverter = 0;
|
||||||
|
|
||||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
||||||
if (!httpChannel) {
|
if (!httpChannel) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t responseCode = 0;
|
uint32_t responseCode = 0;
|
||||||
rv = httpChannel->GetResponseStatus(&responseCode);
|
rv = httpChannel->GetResponseStatus(&responseCode);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseCode != 200) {
|
if (responseCode != 200) {
|
||||||
uint32_t wantsAllNetworkStreams = 0;
|
uint32_t wantsAllNetworkStreams = 0;
|
||||||
rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
|
rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
|
||||||
|
@ -138,12 +139,12 @@ nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if server cannot continue with byte range (206 status) and sending us whole object (200 status)
|
// 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
|
// reset this seekable stream & try serve it to plugin instance as a file
|
||||||
mStreamConverter = finalStreamListener;
|
mStreamConverter = finalStreamListener;
|
||||||
mRemoveMagicNumber = true;
|
mRemoveMagicNumber = true;
|
||||||
|
|
||||||
rv = pslp->ServeStreamAsFile(request, ctxt);
|
rv = pslp->ServeStreamAsFile(request, ctxt);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -154,11 +155,11 @@ nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports
|
||||||
{
|
{
|
||||||
if (!mStreamConverter)
|
if (!mStreamConverter)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
||||||
if (!finalStreamListener)
|
if (!finalStreamListener)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsPluginStreamListenerPeer *pslp =
|
nsPluginStreamListenerPeer *pslp =
|
||||||
static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
|
static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
|
||||||
bool found = pslp->mRequests.RemoveObject(request);
|
bool found = pslp->mRequests.RemoveObject(request);
|
||||||
|
@ -181,7 +182,7 @@ nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports
|
||||||
NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
|
NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mStreamConverter->OnStopRequest(request, ctxt, status);
|
return mStreamConverter->OnStopRequest(request, ctxt, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,11 +224,11 @@ nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupport
|
||||||
{
|
{
|
||||||
if (!mStreamConverter)
|
if (!mStreamConverter)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
||||||
if (!finalStreamListener)
|
if (!finalStreamListener)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
|
return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,28 +242,6 @@ nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result)
|
||||||
|
|
||||||
return finalStreamListener->GetInterface(aIID, 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
|
// nsPluginStreamListenerPeer
|
||||||
|
|
||||||
|
@ -280,11 +259,11 @@ nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
|
||||||
mStartBinding = false;
|
mStartBinding = false;
|
||||||
mAbort = false;
|
mAbort = false;
|
||||||
mRequestFailed = false;
|
mRequestFailed = false;
|
||||||
|
|
||||||
mPendingRequests = 0;
|
mPendingRequests = 0;
|
||||||
mHaveFiredOnStartRequest = false;
|
mHaveFiredOnStartRequest = false;
|
||||||
mDataForwardToRequest = nullptr;
|
mDataForwardToRequest = nullptr;
|
||||||
|
|
||||||
mSeekable = false;
|
mSeekable = false;
|
||||||
mModified = 0;
|
mModified = 0;
|
||||||
mStreamOffset = 0;
|
mStreamOffset = 0;
|
||||||
|
@ -306,7 +285,7 @@ nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
|
||||||
// or we won't be able to remove the cache file
|
// or we won't be able to remove the cache file
|
||||||
if (mFileCacheOutputStream)
|
if (mFileCacheOutputStream)
|
||||||
mFileCacheOutputStream = nullptr;
|
mFileCacheOutputStream = nullptr;
|
||||||
|
|
||||||
delete mDataForwardToRequest;
|
delete mDataForwardToRequest;
|
||||||
|
|
||||||
if (mPluginInstance)
|
if (mPluginInstance)
|
||||||
|
@ -350,7 +329,7 @@ nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
|
||||||
|
|
||||||
mPendingRequests = 1;
|
mPendingRequests = 1;
|
||||||
|
|
||||||
mDataForwardToRequest = new nsHashtable();
|
mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -394,38 +373,38 @@ nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the filename from the channel
|
// Get the filename from the channel
|
||||||
nsCOMPtr<nsIURI> uri;
|
nsCOMPtr<nsIURI> uri;
|
||||||
rv = channel->GetURI(getter_AddRefs(uri));
|
rv = channel->GetURI(getter_AddRefs(uri));
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
||||||
if (!url)
|
if (!url)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsAutoCString filename;
|
nsAutoCString filename;
|
||||||
url->GetFileName(filename);
|
url->GetFileName(filename);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
// Create a file to save our stream into. Should we scramble the name?
|
// Create a file to save our stream into. Should we scramble the name?
|
||||||
filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
|
filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
|
||||||
rv = pluginTmp->AppendNative(filename);
|
rv = pluginTmp->AppendNative(filename);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
// Yes, make it unique.
|
// Yes, make it unique.
|
||||||
rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
|
rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
// create a file output stream to write to...
|
// create a file output stream to write to...
|
||||||
nsCOMPtr<nsIOutputStream> outstream;
|
nsCOMPtr<nsIOutputStream> outstream;
|
||||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
|
rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
// save the file.
|
// save the file.
|
||||||
mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
|
mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
|
||||||
}
|
}
|
||||||
|
@ -618,28 +597,28 @@ nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACStr
|
||||||
//the string should look like this: bytes=500-700,601-999
|
//the string should look like this: bytes=500-700,601-999
|
||||||
if (!aRangeList)
|
if (!aRangeList)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int32_t requestCnt = 0;
|
int32_t requestCnt = 0;
|
||||||
nsAutoCString string("bytes=");
|
nsAutoCString string("bytes=");
|
||||||
|
|
||||||
for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) {
|
for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) {
|
||||||
// XXX zero length?
|
// XXX zero length?
|
||||||
if (!range->length)
|
if (!range->length)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// XXX needs to be fixed for negative offsets
|
// XXX needs to be fixed for negative offsets
|
||||||
string.AppendInt(range->offset);
|
string.AppendInt(range->offset);
|
||||||
string.Append("-");
|
string.Append("-");
|
||||||
string.AppendInt(range->offset + range->length - 1);
|
string.AppendInt(range->offset + range->length - 1);
|
||||||
if (range->next)
|
if (range->next)
|
||||||
string += ",";
|
string += ",";
|
||||||
|
|
||||||
requestCnt++;
|
requestCnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get rid of possible trailing comma
|
// get rid of possible trailing comma
|
||||||
string.Trim(",", false);
|
string.Trim(",", false);
|
||||||
|
|
||||||
rangeRequest = string;
|
rangeRequest = string;
|
||||||
*numRequests = requestCnt;
|
*numRequests = requestCnt;
|
||||||
return;
|
return;
|
||||||
|
@ -650,32 +629,32 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
|
||||||
{
|
{
|
||||||
nsAutoCString rangeString;
|
nsAutoCString rangeString;
|
||||||
int32_t numRequests;
|
int32_t numRequests;
|
||||||
|
|
||||||
MakeByteRangeString(rangeList, rangeString, &numRequests);
|
MakeByteRangeString(rangeList, rangeString, &numRequests);
|
||||||
|
|
||||||
if (numRequests == 0)
|
if (numRequests == 0)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
|
nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
|
||||||
nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
|
nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
|
||||||
nsCOMPtr<nsIChannel> channel;
|
nsCOMPtr<nsIChannel> channel;
|
||||||
rv = NS_NewChannel(getter_AddRefs(channel), mURL, nullptr, loadGroup, callbacks);
|
rv = NS_NewChannel(getter_AddRefs(channel), mURL, nullptr, loadGroup, callbacks);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
||||||
if (!httpChannel)
|
if (!httpChannel)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
|
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
|
||||||
|
|
||||||
mAbort = true; // instruct old stream listener to cancel
|
mAbort = true; // instruct old stream listener to cancel
|
||||||
// the request on the next ODA.
|
// the request on the next ODA.
|
||||||
|
|
||||||
nsCOMPtr<nsIStreamListener> converter;
|
nsCOMPtr<nsIStreamListener> converter;
|
||||||
|
|
||||||
if (numRequests == 1) {
|
if (numRequests == 1) {
|
||||||
converter = this;
|
converter = this;
|
||||||
// set current stream offset equal to the first offset in the range list
|
// set current stream offset equal to the first offset in the range list
|
||||||
|
@ -692,16 +671,16 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
|
||||||
else
|
else
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPendingRequests += numRequests;
|
mPendingRequests += numRequests;
|
||||||
|
|
||||||
nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
|
nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
rv = container->SetData(MAGIC_REQUEST_CONTEXT);
|
rv = container->SetData(MAGIC_REQUEST_CONTEXT);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
rv = channel->AsyncOpen(converter, container);
|
rv = channel->AsyncOpen(converter, container);
|
||||||
if (NS_SUCCEEDED(rv))
|
if (NS_SUCCEEDED(rv))
|
||||||
TrackRequest(channel);
|
TrackRequest(channel);
|
||||||
|
@ -727,7 +706,7 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
|
||||||
{
|
{
|
||||||
if (!mPluginInstance)
|
if (!mPluginInstance)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
// mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
|
// mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
|
||||||
mPluginInstance->Stop();
|
mPluginInstance->Stop();
|
||||||
mPluginInstance->Start();
|
mPluginInstance->Start();
|
||||||
|
@ -737,7 +716,7 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
|
||||||
owner->GetWindow(window);
|
owner->GetWindow(window);
|
||||||
#if (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT)
|
#if (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT)
|
||||||
// Should call GetPluginPort() here.
|
// Should call GetPluginPort() here.
|
||||||
// This part is copied from nsPluginInstanceOwner::GetPluginPort().
|
// This part is copied from nsPluginInstanceOwner::GetPluginPort().
|
||||||
nsCOMPtr<nsIWidget> widget;
|
nsCOMPtr<nsIWidget> widget;
|
||||||
((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
|
((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
|
||||||
if (widget) {
|
if (widget) {
|
||||||
|
@ -746,22 +725,22 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
|
||||||
#endif
|
#endif
|
||||||
owner->CallSetWindow();
|
owner->CallSetWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
mSeekable = false;
|
mSeekable = false;
|
||||||
mPStreamListener->OnStartBinding(this);
|
mPStreamListener->OnStartBinding(this);
|
||||||
mStreamOffset = 0;
|
mStreamOffset = 0;
|
||||||
|
|
||||||
// force the plugin to use stream as file
|
// force the plugin to use stream as file
|
||||||
mStreamType = NP_ASFILE;
|
mStreamType = NP_ASFILE;
|
||||||
|
|
||||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||||
if (channel) {
|
if (channel) {
|
||||||
SetupPluginCacheFile(channel);
|
SetupPluginCacheFile(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unset mPendingRequests
|
// unset mPendingRequests
|
||||||
mPendingRequests = 0;
|
mPendingRequests = 0;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,7 +748,7 @@ bool
|
||||||
nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
|
nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
|
||||||
{
|
{
|
||||||
NS_ENSURE_TRUE(psi, false);
|
NS_ENSURE_TRUE(psi, false);
|
||||||
|
|
||||||
if (psi->mLength == mLength &&
|
if (psi->mLength == mLength &&
|
||||||
psi->mModified == mModified &&
|
psi->mModified == mModified &&
|
||||||
mStreamComplete &&
|
mStreamComplete &&
|
||||||
|
@ -793,32 +772,32 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
|
||||||
|
|
||||||
if (mRequestFailed)
|
if (mRequestFailed)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
if (mAbort) {
|
if (mAbort) {
|
||||||
uint32_t magicNumber = 0; // set it to something that is not the magic number.
|
uint32_t magicNumber = 0; // set it to something that is not the magic number.
|
||||||
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
|
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
|
||||||
if (container)
|
if (container)
|
||||||
container->GetData(&magicNumber);
|
container->GetData(&magicNumber);
|
||||||
|
|
||||||
if (magicNumber != MAGIC_REQUEST_CONTEXT) {
|
if (magicNumber != MAGIC_REQUEST_CONTEXT) {
|
||||||
// this is not one of our range requests
|
// this is not one of our range requests
|
||||||
mAbort = false;
|
mAbort = false;
|
||||||
return NS_BINDING_ABORTED;
|
return NS_BINDING_ABORTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
if (!mPStreamListener)
|
if (!mPStreamListener)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
const char * url = nullptr;
|
const char * url = nullptr;
|
||||||
GetURL(&url);
|
GetURL(&url);
|
||||||
|
|
||||||
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
||||||
("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n",
|
("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n",
|
||||||
this, request, sourceOffset, aLength, url ? url : "no url set"));
|
this, request, sourceOffset, aLength, url ? url : "no url set"));
|
||||||
|
|
||||||
// if the plugin has requested an AsFileOnly stream, then don't
|
// if the plugin has requested an AsFileOnly stream, then don't
|
||||||
// call OnDataAvailable
|
// call OnDataAvailable
|
||||||
if (mStreamType != NP_ASFILEONLY) {
|
if (mStreamType != NP_ASFILEONLY) {
|
||||||
|
@ -827,44 +806,42 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
|
||||||
if (brr) {
|
if (brr) {
|
||||||
if (!mDataForwardToRequest)
|
if (!mDataForwardToRequest)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
int64_t absoluteOffset64 = 0;
|
int64_t absoluteOffset64 = 0;
|
||||||
brr->GetStartRange(&absoluteOffset64);
|
brr->GetStartRange(&absoluteOffset64);
|
||||||
|
|
||||||
// XXX handle 64-bit for real
|
// XXX handle 64-bit for real
|
||||||
int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
|
int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
|
||||||
|
|
||||||
// we need to track how much data we have forwarded to the
|
// we need to track how much data we have forwarded to the
|
||||||
// plugin.
|
// plugin.
|
||||||
|
|
||||||
// FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
|
// FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
|
||||||
//
|
//
|
||||||
// Why couldn't this be tracked on the plugin info, and not in a
|
// Why couldn't this be tracked on the plugin info, and not in a
|
||||||
// *hash table*?
|
// *hash table*?
|
||||||
nsPRUintKey key(absoluteOffset);
|
int32_t amtForwardToPlugin = mDataForwardToRequest->Get(absoluteOffset);
|
||||||
int32_t amtForwardToPlugin =
|
mDataForwardToRequest->Put(absoluteOffset, (amtForwardToPlugin + aLength));
|
||||||
NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key));
|
|
||||||
mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength));
|
|
||||||
|
|
||||||
SetStreamOffset(absoluteOffset + amtForwardToPlugin);
|
SetStreamOffset(absoluteOffset + amtForwardToPlugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIInputStream> stream = aIStream;
|
nsCOMPtr<nsIInputStream> stream = aIStream;
|
||||||
|
|
||||||
// if we are caching the file ourselves to disk, we want to 'tee' off
|
// 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
|
// the data as the plugin read from the stream. We do this by the magic
|
||||||
// of an input stream tee.
|
// of an input stream tee.
|
||||||
|
|
||||||
if (mFileCacheOutputStream) {
|
if (mFileCacheOutputStream) {
|
||||||
rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
|
rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = mPStreamListener->OnDataAvailable(this,
|
rv = mPStreamListener->OnDataAvailable(this,
|
||||||
stream,
|
stream,
|
||||||
aLength);
|
aLength);
|
||||||
|
|
||||||
// if a plugin returns an error, the peer must kill the stream
|
// if a plugin returns an error, the peer must kill the stream
|
||||||
// else the stream and PluginStreamListener leak
|
// else the stream and PluginStreamListener leak
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
|
@ -876,7 +853,7 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
|
||||||
char* buffer = new char[aLength];
|
char* buffer = new char[aLength];
|
||||||
uint32_t amountRead, amountWrote = 0;
|
uint32_t amountRead, amountWrote = 0;
|
||||||
rv = aIStream->Read(buffer, aLength, &amountRead);
|
rv = aIStream->Read(buffer, aLength, &amountRead);
|
||||||
|
|
||||||
// if we are caching this to disk ourselves, lets write the bytes out.
|
// if we are caching this to disk ourselves, lets write the bytes out.
|
||||||
if (mFileCacheOutputStream) {
|
if (mFileCacheOutputStream) {
|
||||||
while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
|
while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
|
||||||
|
@ -901,11 +878,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
NS_ERROR("Received OnStopRequest for untracked request.");
|
NS_ERROR("Received OnStopRequest for untracked request.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
||||||
("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
|
("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
|
||||||
this, aStatus, request));
|
this, aStatus, request));
|
||||||
|
|
||||||
// for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
|
// for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
|
||||||
nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
|
nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
|
||||||
if (brr) {
|
if (brr) {
|
||||||
|
@ -913,13 +890,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
brr->GetStartRange(&absoluteOffset64);
|
brr->GetStartRange(&absoluteOffset64);
|
||||||
// XXX support 64-bit offsets
|
// XXX support 64-bit offsets
|
||||||
int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
|
int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
|
||||||
|
|
||||||
nsPRUintKey key(absoluteOffset);
|
|
||||||
|
|
||||||
// remove the request from our data forwarding count hash.
|
// remove the request from our data forwarding count hash.
|
||||||
mDataForwardToRequest->Remove(&key);
|
mDataForwardToRequest->Remove(absoluteOffset);
|
||||||
|
|
||||||
|
|
||||||
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
||||||
(" ::OnStopRequest for ByteRangeRequest Started=%d\n",
|
(" ::OnStopRequest for ByteRangeRequest Started=%d\n",
|
||||||
absoluteOffset));
|
absoluteOffset));
|
||||||
|
@ -929,11 +904,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
// close & tear it down here
|
// close & tear it down here
|
||||||
mFileCacheOutputStream = nullptr;
|
mFileCacheOutputStream = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we still have pending stuff to do, lets not close the plugin socket.
|
// if we still have pending stuff to do, lets not close the plugin socket.
|
||||||
if (--mPendingRequests > 0)
|
if (--mPendingRequests > 0)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
// we keep our connections around...
|
// we keep our connections around...
|
||||||
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
|
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
|
||||||
if (container) {
|
if (container) {
|
||||||
|
@ -944,10 +919,10 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mPStreamListener)
|
if (!mPStreamListener)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||||
if (!channel)
|
if (!channel)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
@ -956,21 +931,21 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
rv = channel->GetContentType(aContentType);
|
rv = channel->GetContentType(aContentType);
|
||||||
if (NS_FAILED(rv) && !mRequestFailed)
|
if (NS_FAILED(rv) && !mRequestFailed)
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
if (!aContentType.IsEmpty())
|
if (!aContentType.IsEmpty())
|
||||||
mContentType = aContentType;
|
mContentType = aContentType;
|
||||||
|
|
||||||
// set error status if stream failed so we notify the plugin
|
// set error status if stream failed so we notify the plugin
|
||||||
if (mRequestFailed)
|
if (mRequestFailed)
|
||||||
aStatus = NS_ERROR_FAILURE;
|
aStatus = NS_ERROR_FAILURE;
|
||||||
|
|
||||||
if (NS_FAILED(aStatus)) {
|
if (NS_FAILED(aStatus)) {
|
||||||
// on error status cleanup the stream
|
// on error status cleanup the stream
|
||||||
// and return w/o OnFileAvailable()
|
// and return w/o OnFileAvailable()
|
||||||
mPStreamListener->OnStopBinding(this, aStatus);
|
mPStreamListener->OnStopBinding(this, aStatus);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
|
// call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
|
||||||
if (mStreamType >= NP_ASFILE) {
|
if (mStreamType >= NP_ASFILE) {
|
||||||
nsCOMPtr<nsIFile> localFile;
|
nsCOMPtr<nsIFile> localFile;
|
||||||
|
@ -983,12 +958,12 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
fileChannel->GetFile(getter_AddRefs(localFile));
|
fileChannel->GetFile(getter_AddRefs(localFile));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localFile) {
|
if (localFile) {
|
||||||
OnFileAvailable(localFile);
|
OnFileAvailable(localFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStartBinding) {
|
if (mStartBinding) {
|
||||||
// On start binding has been called
|
// On start binding has been called
|
||||||
mPStreamListener->OnStopBinding(this, aStatus);
|
mPStreamListener->OnStopBinding(this, aStatus);
|
||||||
|
@ -997,11 +972,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
||||||
mPStreamListener->OnStartBinding(this);
|
mPStreamListener->OnStartBinding(this);
|
||||||
mPStreamListener->OnStopBinding(this, aStatus);
|
mPStreamListener->OnStopBinding(this, aStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(aStatus)) {
|
if (NS_SUCCEEDED(aStatus)) {
|
||||||
mStreamComplete = true;
|
mStreamComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1009,7 +984,7 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
||||||
nsIURI* aURL)
|
nsIURI* aURL)
|
||||||
{
|
{
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
|
||||||
// If we don't yet have a stream listener, we need to get
|
// If we don't yet have a stream listener, we need to get
|
||||||
// one from the plugin.
|
// one from the plugin.
|
||||||
// NOTE: this should only happen when a stream was NOT created
|
// NOTE: this should only happen when a stream was NOT created
|
||||||
|
@ -1033,11 +1008,11 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
||||||
mPStreamListener->SetStreamListenerPeer(this);
|
mPStreamListener->SetStreamListenerPeer(this);
|
||||||
|
|
||||||
bool useLocalCache = false;
|
bool useLocalCache = false;
|
||||||
|
|
||||||
// get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
|
// get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
|
||||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assumption
|
* Assumption
|
||||||
* By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
|
* By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
|
||||||
|
@ -1077,7 +1052,7 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
||||||
|
|
||||||
// Also provide all HTTP response headers to our listener.
|
// Also provide all HTTP response headers to our listener.
|
||||||
httpChannel->VisitResponseHeaders(this);
|
httpChannel->VisitResponseHeaders(this);
|
||||||
|
|
||||||
mSeekable = false;
|
mSeekable = false;
|
||||||
// first we look for a content-encoding header. If we find one, we tell the
|
// 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
|
// 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
|
// we require a content len
|
||||||
// get Last-Modified header for plugin info
|
// get Last-Modified header for plugin info
|
||||||
nsAutoCString lastModified;
|
nsAutoCString lastModified;
|
||||||
|
@ -1111,22 +1086,22 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
||||||
!lastModified.IsEmpty()) {
|
!lastModified.IsEmpty()) {
|
||||||
PRTime time64;
|
PRTime time64;
|
||||||
PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time
|
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
|
// Convert PRTime to unix-style time_t, i.e. seconds since the epoch
|
||||||
double fpTime = double(time64);
|
double fpTime = double(time64);
|
||||||
mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
|
mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = mPStreamListener->OnStartBinding(this);
|
rv = mPStreamListener->OnStartBinding(this);
|
||||||
|
|
||||||
mStartBinding = true;
|
mStartBinding = true;
|
||||||
|
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
|
||||||
mPStreamListener->GetStreamType(&mStreamType);
|
mPStreamListener->GetStreamType(&mStreamType);
|
||||||
|
|
||||||
if (!useLocalCache && mStreamType >= NP_ASFILE) {
|
if (!useLocalCache && mStreamType >= NP_ASFILE) {
|
||||||
// check it out if this is not a file channel.
|
// check it out if this is not a file channel.
|
||||||
nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
|
nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
|
||||||
|
@ -1134,11 +1109,11 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
||||||
useLocalCache = true;
|
useLocalCache = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useLocalCache) {
|
if (useLocalCache) {
|
||||||
SetupPluginCacheFile(channel);
|
SetupPluginCacheFile(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1148,16 +1123,16 @@ nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
if (!mPStreamListener)
|
if (!mPStreamListener)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
nsAutoCString path;
|
nsAutoCString path;
|
||||||
rv = aFile->GetNativePath(path);
|
rv = aFile->GetNativePath(path);
|
||||||
if (NS_FAILED(rv)) return rv;
|
if (NS_FAILED(rv)) return rv;
|
||||||
|
|
||||||
if (path.IsEmpty()) {
|
if (path.IsEmpty()) {
|
||||||
NS_WARNING("empty path");
|
NS_WARNING("empty path");
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = mPStreamListener->OnFileAvailable(this, path.get());
|
rv = mPStreamListener->OnFileAvailable(this, path.get());
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
@ -1247,7 +1222,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS1(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
|
NS_IMPL_ISUPPORTS1(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
|
||||||
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
|
nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
|
||||||
|
|
|
@ -13,7 +13,8 @@
|
||||||
#include "nsIHttpHeaderVisitor.h"
|
#include "nsIHttpHeaderVisitor.h"
|
||||||
#include "nsWeakReference.h"
|
#include "nsWeakReference.h"
|
||||||
#include "nsNPAPIPluginStreamListener.h"
|
#include "nsNPAPIPluginStreamListener.h"
|
||||||
#include "nsHashtable.h"
|
#include "nsDataHashtable.h"
|
||||||
|
#include "nsHashKeys.h"
|
||||||
#include "nsNPAPIPluginInstance.h"
|
#include "nsNPAPIPluginInstance.h"
|
||||||
#include "nsIInterfaceRequestor.h"
|
#include "nsIInterfaceRequestor.h"
|
||||||
#include "nsIChannelEventSink.h"
|
#include "nsIChannelEventSink.h"
|
||||||
|
@ -31,12 +32,12 @@ class CachedFileHolder
|
||||||
public:
|
public:
|
||||||
CachedFileHolder(nsIFile* cacheFile);
|
CachedFileHolder(nsIFile* cacheFile);
|
||||||
~CachedFileHolder();
|
~CachedFileHolder();
|
||||||
|
|
||||||
void AddRef();
|
void AddRef();
|
||||||
void Release();
|
void Release();
|
||||||
|
|
||||||
nsIFile* file() const { return mFile; }
|
nsIFile* file() const { return mFile; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsAutoRefCnt mRefCnt;
|
nsAutoRefCnt mRefCnt;
|
||||||
nsCOMPtr<nsIFile> mFile;
|
nsCOMPtr<nsIFile> mFile;
|
||||||
|
@ -52,7 +53,7 @@ public nsIChannelEventSink
|
||||||
public:
|
public:
|
||||||
nsPluginStreamListenerPeer();
|
nsPluginStreamListenerPeer();
|
||||||
virtual ~nsPluginStreamListenerPeer();
|
virtual ~nsPluginStreamListenerPeer();
|
||||||
|
|
||||||
NS_DECL_ISUPPORTS
|
NS_DECL_ISUPPORTS
|
||||||
NS_DECL_NSIPROGRESSEVENTSINK
|
NS_DECL_NSIPROGRESSEVENTSINK
|
||||||
NS_DECL_NSIREQUESTOBSERVER
|
NS_DECL_NSIREQUESTOBSERVER
|
||||||
|
@ -105,7 +106,7 @@ public:
|
||||||
mRequests.ReplaceObjectAt(newRequest, i);
|
mRequests.ReplaceObjectAt(newRequest, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CancelRequests(nsresult status)
|
void CancelRequests(nsresult status)
|
||||||
{
|
{
|
||||||
// Copy the array to avoid modification during the loop.
|
// 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)
|
// Set to true if we request failed (like with a HTTP response of 404)
|
||||||
bool mRequestFailed;
|
bool mRequestFailed;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
|
* Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
|
||||||
* been called. Checked in ::OnStopRequest so we can call the
|
* been called. Checked in ::OnStopRequest so we can call the
|
||||||
|
@ -149,20 +150,20 @@ private:
|
||||||
// these get passed to the plugin stream listener
|
// these get passed to the plugin stream listener
|
||||||
uint32_t mLength;
|
uint32_t mLength;
|
||||||
int32_t mStreamType;
|
int32_t mStreamType;
|
||||||
|
|
||||||
// local cached file, we save the content into local cache if browser cache is not available,
|
// 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
|
// or plugin asks stream as file and it expects file extension until bug 90558 got fixed
|
||||||
nsRefPtr<CachedFileHolder> mLocalCachedFileHolder;
|
nsRefPtr<CachedFileHolder> mLocalCachedFileHolder;
|
||||||
nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
|
nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
|
||||||
nsHashtable *mDataForwardToRequest;
|
nsDataHashtable<nsUint32HashKey, uint32_t>* mDataForwardToRequest;
|
||||||
|
|
||||||
nsCString mContentType;
|
nsCString mContentType;
|
||||||
bool mSeekable;
|
bool mSeekable;
|
||||||
uint32_t mModified;
|
uint32_t mModified;
|
||||||
nsRefPtr<nsNPAPIPluginInstance> mPluginInstance;
|
nsRefPtr<nsNPAPIPluginInstance> mPluginInstance;
|
||||||
int32_t mStreamOffset;
|
int32_t mStreamOffset;
|
||||||
bool mStreamComplete;
|
bool mStreamComplete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool mAbort;
|
bool mAbort;
|
||||||
int32_t mPendingRequests;
|
int32_t mPendingRequests;
|
||||||
|
|
|
@ -529,42 +529,7 @@ TelephonyProvider.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
|
this._getClient(aClientId).sendWorkerMessage("holdCall", { callIndex: aCallIndex });
|
||||||
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));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
resumeCall: function(aClientId, aCallIndex) {
|
resumeCall: function(aClientId, aCallIndex) {
|
||||||
|
@ -575,43 +540,7 @@ TelephonyProvider.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
|
this._getClient(aClientId).sendWorkerMessage("resumeCall", { callIndex: aCallIndex });
|
||||||
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));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
conferenceCall: function(aClientId) {
|
conferenceCall: function(aClientId) {
|
||||||
|
|
|
@ -20,8 +20,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=850517
|
||||||
sb.win = window;
|
sb.win = window;
|
||||||
sb.childA = ifrA.contentWindow;
|
sb.childA = ifrA.contentWindow;
|
||||||
sb.childB = ifrB.contentWindow;
|
sb.childB = ifrB.contentWindow;
|
||||||
sb.is = is;
|
sb.is = SpecialPowers.wrap(is);
|
||||||
sb.ok = ok;
|
sb.ok = SpecialPowers.wrap(ok);
|
||||||
is(window.theoneandonly.frameElement, ifrA, "Named child resolution works");
|
is(window.theoneandonly.frameElement, ifrA, "Named child resolution works");
|
||||||
SpecialPowers.Cu.evalInSandbox('is(win.theoneandonly, childA, "Named child resolution works via Xray");', sb);
|
SpecialPowers.Cu.evalInSandbox('is(win.theoneandonly, childA, "Named child resolution works via Xray");', sb);
|
||||||
ifrA.removeAttribute('name');
|
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");
|
is(evt.data, 'dosetup', "message from modal window is correct");
|
||||||
var win = SpecialPowers.wrap(evt.source);
|
var win = SpecialPowers.wrap(evt.source);
|
||||||
win.wrappedJSObject.SpecialPowers = SpecialPowers;
|
win.wrappedJSObject.SpecialPowers = SpecialPowers;
|
||||||
win.wrappedJSObject.is = is;
|
win.wrappedJSObject.is = SpecialPowers.wrap(is);
|
||||||
win.wrappedJSObject.ok = ok;
|
win.wrappedJSObject.ok = SpecialPowers.wrap(ok);
|
||||||
win.wrappedJSObject.go();
|
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
|
//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
|
||||||
partial interface Document {
|
partial interface Document {
|
||||||
[Throws, Pref="dom.webcomponents.enabled"]
|
[Throws, Func="nsDocument::IsRegisterElementEnabled"]
|
||||||
object registerElement(DOMString name, optional ElementRegistrationOptions options);
|
object registerElement(DOMString name, optional ElementRegistrationOptions options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ XBLFinalize(JSFreeOp *fop, JSObject *obj)
|
||||||
nsXBLDocumentInfo* docInfo =
|
nsXBLDocumentInfo* docInfo =
|
||||||
static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
|
static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
|
||||||
nsContentUtils::DeferredFinalize(docInfo);
|
nsContentUtils::DeferredFinalize(docInfo);
|
||||||
|
|
||||||
nsXBLJSClass* c = nsXBLJSClass::fromJSClass(::JS_GetClass(obj));
|
nsXBLJSClass* c = nsXBLJSClass::fromJSClass(::JS_GetClass(obj));
|
||||||
c->Drop();
|
c->Drop();
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,7 @@ nsXBLJSClass::Destroy()
|
||||||
"referenced nsXBLJSClass is on LRU list already!?");
|
"referenced nsXBLJSClass is on LRU list already!?");
|
||||||
|
|
||||||
if (nsXBLService::gClassTable) {
|
if (nsXBLService::gClassTable) {
|
||||||
nsCStringKey key(mKey);
|
nsXBLService::gClassTable->Remove(mKey);
|
||||||
(nsXBLService::gClassTable)->Remove(&key);
|
|
||||||
mKey.Truncate();
|
mKey.Truncate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,14 +140,7 @@ nsXBLJSClass::Destroy()
|
||||||
nsXBLJSClass*
|
nsXBLJSClass*
|
||||||
nsXBLService::getClass(const nsCString& k)
|
nsXBLService::getClass(const nsCString& k)
|
||||||
{
|
{
|
||||||
nsCStringKey key(k);
|
return nsXBLService::gClassTable->Get(k);
|
||||||
return getClass(&key);
|
|
||||||
}
|
|
||||||
|
|
||||||
nsXBLJSClass*
|
|
||||||
nsXBLService::getClass(nsCStringKey *k)
|
|
||||||
{
|
|
||||||
return static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(k));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation /////////////////////////////////////////////////////////////////
|
// Implementation /////////////////////////////////////////////////////////////////
|
||||||
|
@ -265,7 +257,7 @@ nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElem
|
||||||
// XXXbz This really shouldn't be a void method!
|
// XXXbz This really shouldn't be a void method!
|
||||||
child->UnbindFromTree();
|
child->UnbindFromTree();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
child->SetFlags(NODE_IS_ANONYMOUS_ROOT);
|
child->SetFlags(NODE_IS_ANONYMOUS_ROOT);
|
||||||
|
|
||||||
|
@ -360,7 +352,7 @@ nsXBLBinding::GenerateAnonymousContent()
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find out if we're really building kids or if we're just
|
// Find out if we're really building kids or if we're just
|
||||||
// using the attribute-setting shorthand hack.
|
// using the attribute-setting shorthand hack.
|
||||||
uint32_t contentCount = content->GetChildCount();
|
uint32_t contentCount = content->GetChildCount();
|
||||||
|
@ -478,7 +470,7 @@ nsXBLBinding::FindInsertionPointFor(nsIContent* aChild)
|
||||||
if (mContent) {
|
if (mContent) {
|
||||||
return FindInsertionPointForInternal(aChild);
|
return FindInsertionPointForInternal(aChild);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild)
|
return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
@ -492,7 +484,7 @@ nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild)
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mDefaultInsertionPoint;
|
return mDefaultInsertionPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +576,7 @@ nsXBLBinding::InstallEventHandlers()
|
||||||
nsAutoString type;
|
nsAutoString type;
|
||||||
handler->GetEventName(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.
|
// add it to the standard event group.
|
||||||
|
|
||||||
// Figure out if we're using capturing or not.
|
// Figure out if we're using capturing or not.
|
||||||
|
@ -684,7 +676,7 @@ nsXBLBinding::UnhookEventHandlers()
|
||||||
if (!manager) {
|
if (!manager) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isChromeBinding = mPrototypeBinding->IsChrome();
|
bool isChromeBinding = mPrototypeBinding->IsChrome();
|
||||||
nsXBLPrototypeHandler* curr;
|
nsXBLPrototypeHandler* curr;
|
||||||
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
|
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
|
||||||
|
@ -692,7 +684,7 @@ nsXBLBinding::UnhookEventHandlers()
|
||||||
if (!handler) {
|
if (!handler) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
|
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
|
||||||
if (!eventAtom ||
|
if (!eventAtom ||
|
||||||
eventAtom == nsGkAtoms::keyup ||
|
eventAtom == nsGkAtoms::keyup ||
|
||||||
|
@ -731,7 +723,7 @@ nsXBLBinding::UnhookEventHandlers()
|
||||||
EventListenerFlags flags;
|
EventListenerFlags flags;
|
||||||
flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
|
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.
|
// remove it from the standard event group.
|
||||||
|
|
||||||
if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) &&
|
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?
|
// XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
|
||||||
if (mContent)
|
if (mContent)
|
||||||
return mPrototypeBinding->InheritsStyle();
|
return mPrototypeBinding->InheritsStyle();
|
||||||
|
|
||||||
if (mNextBinding)
|
if (mNextBinding)
|
||||||
return mNextBinding->InheritsStyle();
|
return mNextBinding->InheritsStyle();
|
||||||
|
|
||||||
|
@ -995,9 +987,8 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
||||||
// We need to initialize the class.
|
// We need to initialize the class.
|
||||||
*aNew = true;
|
*aNew = true;
|
||||||
|
|
||||||
nsCStringKey key(xblKey);
|
|
||||||
if (!c) {
|
if (!c) {
|
||||||
c = nsXBLService::getClass(&key);
|
c = nsXBLService::getClass(xblKey);
|
||||||
}
|
}
|
||||||
if (c) {
|
if (c) {
|
||||||
// If c is on the LRU list, remove it now!
|
// If c is on the LRU list, remove it now!
|
||||||
|
@ -1015,8 +1006,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
||||||
nsXBLService::gClassLRUListLength--;
|
nsXBLService::gClassLRUListLength--;
|
||||||
|
|
||||||
// Remove any mapping from the old name to the class struct.
|
// Remove any mapping from the old name to the class struct.
|
||||||
nsCStringKey oldKey(c->Key());
|
nsXBLService::gClassTable->Remove(c->Key());
|
||||||
(nsXBLService::gClassTable)->Remove(&oldKey);
|
|
||||||
|
|
||||||
// Change the class name and we're done.
|
// Change the class name and we're done.
|
||||||
nsMemory::Free((void*) c->name);
|
nsMemory::Free((void*) c->name);
|
||||||
|
@ -1025,7 +1015,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add c to our table.
|
// 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.
|
// 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.
|
// Make a new object prototyped by parent_proto and parented by global.
|
||||||
proto = ::JS_InitClass(cx, // context
|
proto = ::JS_InitClass(cx, // context
|
||||||
global, // global object
|
global, // global object
|
||||||
parent_proto, // parent proto
|
parent_proto, // parent proto
|
||||||
c, // JSClass
|
c, // JSClass
|
||||||
nullptr, // JSNative ctor
|
nullptr, // JSNative ctor
|
||||||
0, // ctor args
|
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
|
// This will happen if we're OOM or if the security manager
|
||||||
// denies defining the new class...
|
// denies defining the new class...
|
||||||
|
|
||||||
(nsXBLService::gClassTable)->Remove(&key);
|
nsXBLService::gClassTable->Remove(xblKey);
|
||||||
|
|
||||||
c->Drop();
|
c->Drop();
|
||||||
|
|
||||||
|
|
|
@ -154,7 +154,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// nsXBLStreamListener, a helper class used for
|
// nsXBLStreamListener, a helper class used for
|
||||||
// asynchronous parsing of URLs
|
// asynchronous parsing of URLs
|
||||||
/* Header file */
|
/* Header file */
|
||||||
class nsXBLStreamListener MOZ_FINAL : public nsIStreamListener,
|
class nsXBLStreamListener MOZ_FINAL : public nsIStreamListener,
|
||||||
|
@ -177,7 +177,7 @@ public:
|
||||||
private:
|
private:
|
||||||
nsCOMPtr<nsIStreamListener> mInner;
|
nsCOMPtr<nsIStreamListener> mInner;
|
||||||
nsAutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
|
nsAutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
|
||||||
|
|
||||||
nsCOMPtr<nsIWeakReference> mBoundDocument;
|
nsCOMPtr<nsIWeakReference> mBoundDocument;
|
||||||
nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
|
nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
|
||||||
nsCOMPtr<nsIDocument> mBindingDocument; // 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);
|
return mInner->OnStartRequest(request, aCtxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
|
nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
|
||||||
{
|
{
|
||||||
nsresult rv = NS_OK;
|
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.
|
// 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
|
// 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
|
// 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
|
// We need to get the sink's notifications flushed and then make the binding
|
||||||
// ready.
|
// ready.
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
@ -347,7 +347,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
cache->PutXBLDocumentInfo(info);
|
cache->PutXBLDocumentInfo(info);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bindingManager->PutXBLDocumentInfo(info);
|
bindingManager->PutXBLDocumentInfo(info);
|
||||||
|
|
||||||
// Notify all pending requests that their bindings are
|
// Notify all pending requests that their bindings are
|
||||||
|
@ -368,7 +368,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
// Static member variable initialization
|
// Static member variable initialization
|
||||||
bool nsXBLService::gAllowDataURIs = false;
|
bool nsXBLService::gAllowDataURIs = false;
|
||||||
|
|
||||||
nsHashtable* nsXBLService::gClassTable = nullptr;
|
nsXBLService::ClassTable* nsXBLService::gClassTable = nullptr;
|
||||||
|
|
||||||
LinkedList<nsXBLJSClass>* nsXBLService::gClassLRUList = nullptr;
|
LinkedList<nsXBLJSClass>* nsXBLService::gClassLRUList = nullptr;
|
||||||
uint32_t nsXBLService::gClassLRUListLength = 0;
|
uint32_t nsXBLService::gClassLRUListLength = 0;
|
||||||
|
@ -393,7 +393,7 @@ nsXBLService::Init()
|
||||||
// Constructors/Destructors
|
// Constructors/Destructors
|
||||||
nsXBLService::nsXBLService(void)
|
nsXBLService::nsXBLService(void)
|
||||||
{
|
{
|
||||||
gClassTable = new nsHashtable();
|
gClassTable = new ClassTable();
|
||||||
gClassLRUList = new LinkedList<nsXBLJSClass>();
|
gClassLRUList = new LinkedList<nsXBLJSClass>();
|
||||||
|
|
||||||
Preferences::AddBoolVarCache(&gAllowDataURIs, "layout.debug.enable_data_xbl");
|
Preferences::AddBoolVarCache(&gAllowDataURIs, "layout.debug.enable_data_xbl");
|
||||||
|
@ -423,7 +423,7 @@ nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
|
||||||
{
|
{
|
||||||
bool isChrome = false;
|
bool isChrome = false;
|
||||||
bool isResource = false;
|
bool isResource = false;
|
||||||
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
|
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
|
||||||
NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
|
NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
|
||||||
return (isChrome || isResource);
|
return (isChrome || isResource);
|
||||||
return false;
|
return false;
|
||||||
|
@ -435,10 +435,10 @@ nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
|
||||||
nsresult
|
nsresult
|
||||||
nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
|
nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
|
||||||
nsIPrincipal* aOriginPrincipal,
|
nsIPrincipal* aOriginPrincipal,
|
||||||
nsXBLBinding** aBinding, bool* aResolveStyle)
|
nsXBLBinding** aBinding, bool* aResolveStyle)
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
|
NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
|
||||||
|
|
||||||
*aBinding = nullptr;
|
*aBinding = nullptr;
|
||||||
*aResolveStyle = false;
|
*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.
|
// Figure out if we have any scoped sheets. If so, we do a second resolve.
|
||||||
*aResolveStyle = newBinding->HasStyleSheets();
|
*aResolveStyle = newBinding->HasStyleSheets();
|
||||||
|
|
||||||
newBinding.swap(*aBinding);
|
newBinding.swap(*aBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
@ -537,7 +537,7 @@ nsXBLService::FlushStyleBindings(nsIContent* aContent)
|
||||||
|
|
||||||
aContent->SetXBLBinding(nullptr); // Flush old style bindings
|
aContent->SetXBLBinding(nullptr); // Flush old style bindings
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -585,6 +585,15 @@ nsXBLService::AttachGlobalKeyHandler(EventTarget* aTarget)
|
||||||
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
||||||
TrustedEventsAtSystemGroupBubble());
|
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)
|
if (contentNode)
|
||||||
return contentNode->SetProperty(nsGkAtoms::listener,
|
return contentNode->SetProperty(nsGkAtoms::listener,
|
||||||
handler.forget().take(),
|
handler.forget().take(),
|
||||||
|
@ -630,6 +639,13 @@ nsXBLService::DetachGlobalKeyHandler(EventTarget* aTarget)
|
||||||
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
||||||
TrustedEventsAtSystemGroupBubble());
|
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);
|
contentNode->DeleteProperty(nsGkAtoms::listener);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -659,7 +675,7 @@ nsXBLService::FlushMemory()
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsXBLService::BindingReady(nsIContent* aBoundElement,
|
nsXBLService::BindingReady(nsIContent* aBoundElement,
|
||||||
nsIURI* aURI,
|
nsIURI* aURI,
|
||||||
bool* aIsReady)
|
bool* aIsReady)
|
||||||
{
|
{
|
||||||
// Don't do a security check here; we know this binding is set to go.
|
// Don't do a security check here; we know this binding is set to go.
|
||||||
|
@ -667,7 +683,7 @@ nsXBLService::BindingReady(nsIContent* aBoundElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||||
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
||||||
bool* aIsReady, nsXBLBinding** aResult)
|
bool* aIsReady, nsXBLBinding** aResult)
|
||||||
{
|
{
|
||||||
|
@ -678,7 +694,7 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||||
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
||||||
bool* aIsReady, nsXBLBinding** aResult,
|
bool* aIsReady, nsXBLBinding** aResult,
|
||||||
nsTArray<nsIURI*>& aDontExtendURIs)
|
nsTArray<nsIURI*>& aDontExtendURIs)
|
||||||
|
@ -686,7 +702,7 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||||
NS_ASSERTION(aPeekOnly || aResult,
|
NS_ASSERTION(aPeekOnly || aResult,
|
||||||
"Must have non-null out param if not just peeking to see "
|
"Must have non-null out param if not just peeking to see "
|
||||||
"whether the binding is ready");
|
"whether the binding is ready");
|
||||||
|
|
||||||
if (aResult)
|
if (aResult)
|
||||||
*aResult = nullptr;
|
*aResult = nullptr;
|
||||||
|
|
||||||
|
@ -703,7 +719,7 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||||
aOriginPrincipal,
|
aOriginPrincipal,
|
||||||
false, getter_AddRefs(docInfo));
|
false, getter_AddRefs(docInfo));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (!docInfo)
|
if (!docInfo)
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
|
|
||||||
|
@ -821,11 +837,11 @@ IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
|
||||||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
nsCOMPtr<nsIURI> uri;
|
||||||
aPrincipal->GetURI(getter_AddRefs(uri));
|
aPrincipal->GetURI(getter_AddRefs(uri));
|
||||||
NS_ENSURE_TRUE(uri, false);
|
NS_ENSURE_TRUE(uri, false);
|
||||||
|
|
||||||
bool isChrome = false;
|
bool isChrome = false;
|
||||||
return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
|
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(aBindingURI, "Must have a binding URI");
|
||||||
NS_PRECONDITION(!aOriginPrincipal || aBoundDocument,
|
NS_PRECONDITION(!aOriginPrincipal || aBoundDocument,
|
||||||
"If we're doing a security check, we better have a document!");
|
"If we're doing a security check, we better have a document!");
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
if (aOriginPrincipal) {
|
if (aOriginPrincipal) {
|
||||||
// Security check - Enforce same-origin policy, except to chrome.
|
// 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
|
// 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
|
// 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
|
// any. Since we're not done loading this binding yet, that will reenter
|
||||||
|
@ -886,13 +902,13 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
||||||
#ifdef MOZ_XUL
|
#ifdef MOZ_XUL
|
||||||
// We've got a file. Check our XBL document cache.
|
// We've got a file. Check our XBL document cache.
|
||||||
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
||||||
bool useXULCache = cache && cache->IsEnabled();
|
bool useXULCache = cache && cache->IsEnabled();
|
||||||
|
|
||||||
if (useXULCache) {
|
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
|
// This cache crosses the entire product, so that any XBL bindings that are
|
||||||
// part of chrome will be reused across all XUL documents.
|
// part of chrome will be reused across all XUL documents.
|
||||||
info = cache->GetXBLDocumentInfo(documentURI);
|
info = cache->GetXBLDocumentInfo(documentURI);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -957,7 +973,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
||||||
if (!info) {
|
if (!info) {
|
||||||
// Finally, if all lines of defense fail, we go and fetch the binding
|
// Finally, if all lines of defense fail, we go and fetch the binding
|
||||||
// document.
|
// document.
|
||||||
|
|
||||||
// Always load chrome synchronously
|
// Always load chrome synchronously
|
||||||
bool chrome;
|
bool chrome;
|
||||||
if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
|
if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
|
||||||
|
@ -985,7 +1001,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
||||||
info->WritePrototypeBindings();
|
info->WritePrototypeBindings();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (bindingManager) {
|
if (bindingManager) {
|
||||||
// Also put it in our binding manager's document table.
|
// Also put it in our binding manager's document table.
|
||||||
bindingManager->PutXBLDocumentInfo(info);
|
bindingManager->PutXBLDocumentInfo(info);
|
||||||
|
@ -1001,7 +1017,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
||||||
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
||||||
bool aForceSyncLoad, nsIDocument** aResult)
|
bool aForceSyncLoad, nsIDocument** aResult)
|
||||||
{
|
{
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
|
@ -1009,7 +1025,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoun
|
||||||
*aResult = nullptr;
|
*aResult = nullptr;
|
||||||
|
|
||||||
// Now we have to synchronously load the binding file.
|
// 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;
|
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||||
if (aBoundDocument)
|
if (aBoundDocument)
|
||||||
loadGroup = aBoundDocument->GetDocumentLoadGroup();
|
loadGroup = aBoundDocument->GetDocumentLoadGroup();
|
||||||
|
@ -1092,4 +1108,3 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoun
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,9 @@
|
||||||
#include "nsWeakReference.h"
|
#include "nsWeakReference.h"
|
||||||
#include "js/Class.h" // nsXBLJSClass derives from JSClass
|
#include "js/Class.h" // nsXBLJSClass derives from JSClass
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
|
#include "nsDataHashtable.h"
|
||||||
|
#include "nsHashKeys.h"
|
||||||
|
|
||||||
class nsCStringKey;
|
|
||||||
class nsXBLBinding;
|
class nsXBLBinding;
|
||||||
class nsXBLDocumentInfo;
|
class nsXBLDocumentInfo;
|
||||||
class nsXBLJSClass;
|
class nsXBLJSClass;
|
||||||
|
@ -24,7 +25,6 @@ class nsIDocument;
|
||||||
class nsString;
|
class nsString;
|
||||||
class nsIURI;
|
class nsIURI;
|
||||||
class nsIPrincipal;
|
class nsIPrincipal;
|
||||||
class nsHashtable;
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
@ -84,10 +84,10 @@ protected:
|
||||||
|
|
||||||
// Release any memory that we can
|
// Release any memory that we can
|
||||||
nsresult FlushMemory();
|
nsresult FlushMemory();
|
||||||
|
|
||||||
// This method synchronously loads and parses an XBL file.
|
// This method synchronously loads and parses an XBL file.
|
||||||
nsresult FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
nsresult FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
||||||
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
||||||
bool aForceSyncLoad, nsIDocument** aResult);
|
bool aForceSyncLoad, nsIDocument** aResult);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +123,8 @@ protected:
|
||||||
public:
|
public:
|
||||||
static bool gDisableChromeCache;
|
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;
|
static mozilla::LinkedList<nsXBLJSClass>* gClassLRUList;
|
||||||
// LRU list of cached classes.
|
// LRU list of cached classes.
|
||||||
|
@ -135,7 +136,6 @@ public:
|
||||||
|
|
||||||
// Look up the class by key in gClassTable.
|
// Look up the class by key in gClassTable.
|
||||||
static nsXBLJSClass *getClass(const nsCString &key);
|
static nsXBLJSClass *getClass(const nsCString &key);
|
||||||
static nsXBLJSClass *getClass(nsCStringKey *key);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class nsXBLJSClass : public mozilla::LinkedListElement<nsXBLJSClass>
|
class nsXBLJSClass : public mozilla::LinkedListElement<nsXBLJSClass>
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/TextEvents.h"
|
#include "mozilla/TextEvents.h"
|
||||||
#include "mozilla/dom/Element.h"
|
#include "mozilla/dom/Element.h"
|
||||||
|
#include "mozilla/dom/Event.h"
|
||||||
#include "nsEventStateManager.h"
|
#include "nsEventStateManager.h"
|
||||||
#include "nsIEditor.h"
|
#include "nsIEditor.h"
|
||||||
#include "nsIHTMLEditor.h"
|
#include "nsIHTMLEditor.h"
|
||||||
|
@ -276,24 +277,23 @@ nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventTy
|
||||||
nsresult rv = EnsureHandlers();
|
nsresult rv = EnsureHandlers();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<Element> el = GetElement();
|
bool isDisabled;
|
||||||
|
nsCOMPtr<Element> el = GetElement(&isDisabled);
|
||||||
if (!el) {
|
if (!el) {
|
||||||
if (mUserHandler) {
|
if (mUserHandler) {
|
||||||
WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler);
|
WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler, true);
|
||||||
aKeyEvent->GetDefaultPrevented(&prevent);
|
aKeyEvent->GetDefaultPrevented(&prevent);
|
||||||
if (prevent)
|
if (prevent)
|
||||||
return NS_OK; // Handled by the user bindings. Our work here is done.
|
return NS_OK; // Handled by the user bindings. Our work here is done.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIContent> content = do_QueryInterface(el);
|
|
||||||
// skip keysets that are disabled
|
// skip keysets that are disabled
|
||||||
if (content && content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
|
if (el && isDisabled) {
|
||||||
nsGkAtoms::_true, eCaseMatters)) {
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
WalkHandlersInternal(aKeyEvent, aEventType, mHandler);
|
WalkHandlersInternal(aKeyEvent, aEventType, mHandler, true);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -304,20 +304,50 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
|
||||||
nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
|
nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
|
||||||
NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
|
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;
|
nsAutoString eventType;
|
||||||
aEvent->GetType(eventType);
|
aEvent->GetType(eventType);
|
||||||
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
|
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
|
||||||
NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
|
NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
if (!mWeakPtrForElement) {
|
return WalkHandlers(keyEvent, eventTypeAtom);
|
||||||
nsCOMPtr<mozilla::dom::Element> originalTarget =
|
}
|
||||||
do_QueryInterface(aEvent->GetInternalNSEvent()->originalTarget);
|
|
||||||
if (nsEventStateManager::IsRemoteTarget(originalTarget)) {
|
void
|
||||||
return NS_OK;
|
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
|
// WalkHandlersInternal and WalkHandlersAndExecute
|
||||||
//
|
//
|
||||||
// Given a particular DOM event and a pointer to the first handler in the list,
|
// 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
|
// scan through the list to find something to handle the event. If aExecute = true,
|
||||||
// so.
|
// 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,
|
nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
|
||||||
nsIAtom* aEventType,
|
nsIAtom* aEventType,
|
||||||
nsXBLPrototypeHandler* aHandler)
|
nsXBLPrototypeHandler* aHandler,
|
||||||
|
bool aExecute)
|
||||||
{
|
{
|
||||||
nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
|
nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
|
||||||
nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
|
nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
|
||||||
|
|
||||||
if (accessKeys.IsEmpty()) {
|
if (accessKeys.IsEmpty()) {
|
||||||
WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler, 0, false);
|
return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
|
||||||
return NS_OK;
|
0, false, aExecute);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
|
for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
|
||||||
nsShortcutCandidate &key = accessKeys[i];
|
nsShortcutCandidate &key = accessKeys[i];
|
||||||
if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
|
if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
|
||||||
key.mCharCode, key.mIgnoreShift))
|
key.mCharCode, key.mIgnoreShift, aExecute))
|
||||||
return NS_OK;
|
return true;
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -421,7 +453,8 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
|
||||||
nsIAtom* aEventType,
|
nsIAtom* aEventType,
|
||||||
nsXBLPrototypeHandler* aHandler,
|
nsXBLPrototypeHandler* aHandler,
|
||||||
uint32_t aCharCode,
|
uint32_t aCharCode,
|
||||||
bool aIgnoreShiftKey)
|
bool aIgnoreShiftKey,
|
||||||
|
bool aExecute)
|
||||||
{
|
{
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
|
@ -491,6 +524,10 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
|
||||||
piTarget = mTarget;
|
piTarget = mTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!aExecute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
rv = currHandler->ExecuteHandler(piTarget, aKeyEvent);
|
rv = currHandler->ExecuteHandler(piTarget, aKeyEvent);
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -500,10 +537,38 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
|
||||||
return false;
|
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>
|
already_AddRefed<Element>
|
||||||
nsXBLWindowKeyHandler::GetElement()
|
nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
|
||||||
{
|
{
|
||||||
nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
|
nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
|
||||||
|
if (element && aIsDisabled) {
|
||||||
|
*aIsDisabled = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
|
||||||
|
nsGkAtoms::_true, eCaseMatters);
|
||||||
|
}
|
||||||
return element.forget();
|
return element.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,14 +35,23 @@ protected:
|
||||||
nsresult WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType);
|
nsresult WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType);
|
||||||
|
|
||||||
// walk the handlers, looking for one to handle the event
|
// walk the handlers, looking for one to handle the event
|
||||||
nsresult WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
|
bool WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
|
||||||
nsIAtom* aEventType,
|
nsIAtom* aEventType,
|
||||||
nsXBLPrototypeHandler* aHandler);
|
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,
|
bool WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType,
|
||||||
nsXBLPrototypeHandler* aHandler,
|
nsXBLPrototypeHandler* aHandler,
|
||||||
uint32_t aCharCode, bool aIgnoreShiftKey);
|
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
|
// lazily load the handlers. Overridden to handle being attached
|
||||||
// to a particular element rather than the document
|
// to a particular element rather than the document
|
||||||
|
@ -57,8 +66,10 @@ protected:
|
||||||
bool IsHTMLEditableFieldFocused();
|
bool IsHTMLEditableFieldFocused();
|
||||||
|
|
||||||
// Returns the element which was passed as a parameter to the constructor,
|
// Returns the element which was passed as a parameter to the constructor,
|
||||||
// unless the element has been removed from the document.
|
// unless the element has been removed from the document. Optionally returns
|
||||||
already_AddRefed<mozilla::dom::Element> GetElement();
|
// 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.
|
// Using weak pointer to the DOM Element.
|
||||||
nsWeakPtr mWeakPtrForElement;
|
nsWeakPtr mWeakPtrForElement;
|
||||||
mozilla::dom::EventTarget* mTarget; // weak ref
|
mozilla::dom::EventTarget* mTarget; // weak ref
|
||||||
|
|
|
@ -68,5 +68,39 @@ Matrix::NudgeToIntegers()
|
||||||
NudgeToInteger(&_32);
|
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();
|
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
|
// Apply a scale to this matrix. This scale will be applied -before- the
|
||||||
// existing transformation of the matrix.
|
// existing transformation of the matrix.
|
||||||
Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
|
Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
#include "BasePoint.h"
|
#include "BasePoint.h"
|
||||||
#include "BasePoint3D.h"
|
#include "BasePoint3D.h"
|
||||||
|
#include "BasePoint4D.h"
|
||||||
#include "BaseSize.h"
|
#include "BaseSize.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -92,6 +93,27 @@ struct Point3DTyped :
|
||||||
};
|
};
|
||||||
typedef Point3DTyped<UnknownUnits> Point3D;
|
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>
|
template<class units>
|
||||||
struct IntSizeTyped :
|
struct IntSizeTyped :
|
||||||
public BaseSize< int32_t, IntSizeTyped<units> >,
|
public BaseSize< int32_t, IntSizeTyped<units> >,
|
||||||
|
|
|
@ -82,24 +82,14 @@ TextureClientX11::UpdateSurface(gfxASurface* aSurface)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsValid());
|
MOZ_ASSERT(IsValid());
|
||||||
|
|
||||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
if (!dt) {
|
||||||
if (!dt) {
|
return false;
|
||||||
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<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||||
|
dt->CopySurface(source, IntRect(IntPoint(), GetSize()), IntPoint());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,6 @@ using namespace mozilla::gfx;
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace layers {
|
namespace layers {
|
||||||
|
|
||||||
TextureClientPoolMember::TextureClientPoolMember(SurfaceFormat aFormat, TextureClientPool* aTexturePool)
|
|
||||||
: mFormat(aFormat)
|
|
||||||
, mTexturePool(aTexturePool)
|
|
||||||
{}
|
|
||||||
|
|
||||||
ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
|
ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
|
||||||
: mPhase(PHASE_NONE)
|
: mPhase(PHASE_NONE)
|
||||||
, mWidget(aWidget)
|
, mWidget(aWidget)
|
||||||
|
@ -61,8 +56,6 @@ ClientLayerManager::~ClientLayerManager()
|
||||||
mRoot = nullptr;
|
mRoot = nullptr;
|
||||||
|
|
||||||
MOZ_COUNT_DTOR(ClientLayerManager);
|
MOZ_COUNT_DTOR(ClientLayerManager);
|
||||||
|
|
||||||
mTexturePools.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t
|
int32_t
|
||||||
|
@ -233,9 +226,8 @@ ClientLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
|
||||||
MakeSnapshotIfRequired();
|
MakeSnapshotIfRequired();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const TextureClientPoolMember* item = mTexturePools.getFirst();
|
for (size_t i = 0; i < mTexturePools.Length(); i++) {
|
||||||
item; item = item->getNext()) {
|
mTexturePools[i]->ReturnDeferredClients();
|
||||||
item->mTexturePool->ReturnDeferredClients();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,21 +450,18 @@ ClientLayerManager::SetIsFirstPaint()
|
||||||
TextureClientPool*
|
TextureClientPool*
|
||||||
ClientLayerManager::GetTexturePool(SurfaceFormat aFormat)
|
ClientLayerManager::GetTexturePool(SurfaceFormat aFormat)
|
||||||
{
|
{
|
||||||
for (const TextureClientPoolMember* item = mTexturePools.getFirst();
|
for (size_t i = 0; i < mTexturePools.Length(); i++) {
|
||||||
item; item = item->getNext()) {
|
if (mTexturePools[i]->GetFormat() == aFormat) {
|
||||||
if (item->mFormat == aFormat) {
|
return mTexturePools[i];
|
||||||
return item->mTexturePool;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureClientPoolMember* texturePoolMember =
|
mTexturePools.AppendElement(
|
||||||
new TextureClientPoolMember(aFormat,
|
|
||||||
new TextureClientPool(aFormat, IntSize(TILEDLAYERBUFFER_TILE_SIZE,
|
new TextureClientPool(aFormat, IntSize(TILEDLAYERBUFFER_TILE_SIZE,
|
||||||
TILEDLAYERBUFFER_TILE_SIZE),
|
TILEDLAYERBUFFER_TILE_SIZE),
|
||||||
mForwarder));
|
mForwarder));
|
||||||
mTexturePools.insertBack(texturePoolMember);
|
|
||||||
|
|
||||||
return texturePoolMember->mTexturePool;
|
return mTexturePools.LastElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
SimpleTextureClientPool*
|
SimpleTextureClientPool*
|
||||||
|
@ -502,9 +491,8 @@ ClientLayerManager::ClearCachedResources(Layer* aSubtree)
|
||||||
} else if (mRoot) {
|
} else if (mRoot) {
|
||||||
ClearLayer(mRoot);
|
ClearLayer(mRoot);
|
||||||
}
|
}
|
||||||
for (const TextureClientPoolMember* item = mTexturePools.getFirst();
|
for (size_t i = 0; i < mTexturePools.Length(); i++) {
|
||||||
item; item = item->getNext()) {
|
mTexturePools[i]->Clear();
|
||||||
item->mTexturePool->Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,15 +36,6 @@ class PLayerChild;
|
||||||
class TextureClientPool;
|
class TextureClientPool;
|
||||||
class SimpleTextureClientPool;
|
class SimpleTextureClientPool;
|
||||||
|
|
||||||
class TextureClientPoolMember
|
|
||||||
: public LinkedListElement<TextureClientPoolMember> {
|
|
||||||
public:
|
|
||||||
TextureClientPoolMember(gfx::SurfaceFormat aFormat, TextureClientPool* aTexturePool);
|
|
||||||
|
|
||||||
gfx::SurfaceFormat mFormat;
|
|
||||||
RefPtr<TextureClientPool> mTexturePool;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ClientLayerManager : public LayerManager
|
class ClientLayerManager : public LayerManager
|
||||||
{
|
{
|
||||||
typedef nsTArray<nsRefPtr<Layer> > LayerRefArray;
|
typedef nsTArray<nsRefPtr<Layer> > LayerRefArray;
|
||||||
|
@ -229,7 +220,7 @@ private:
|
||||||
bool mNeedsComposite;
|
bool mNeedsComposite;
|
||||||
|
|
||||||
RefPtr<ShadowLayerForwarder> mForwarder;
|
RefPtr<ShadowLayerForwarder> mForwarder;
|
||||||
LinkedList<TextureClientPoolMember> mTexturePools;
|
nsAutoTArray<RefPtr<TextureClientPool>,2> mTexturePools;
|
||||||
|
|
||||||
// indexed by gfx::SurfaceFormat
|
// indexed by gfx::SurfaceFormat
|
||||||
nsTArray<RefPtr<SimpleTextureClientPool> > mSimpleTilePools;
|
nsTArray<RefPtr<SimpleTextureClientPool> > mSimpleTilePools;
|
||||||
|
|
|
@ -103,7 +103,6 @@ ContentClientBasic::CreateBuffer(ContentType aType,
|
||||||
RefPtr<gfx::DrawTarget>* aWhiteDT)
|
RefPtr<gfx::DrawTarget>* aWhiteDT)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
|
MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
|
||||||
MOZ_ASSERT(gfxPlatform::GetPlatform()->SupportsAzureContent());
|
|
||||||
gfxImageFormat format =
|
gfxImageFormat format =
|
||||||
gfxPlatform::GetPlatform()->OptimalFormatForContent(aType);
|
gfxPlatform::GetPlatform()->OptimalFormatForContent(aType);
|
||||||
|
|
||||||
|
@ -1285,7 +1284,6 @@ ContentClientIncremental::BorrowDrawTargetForPainting(ThebesLayer* aLayer,
|
||||||
"BeginUpdate should always modify the draw region in the same way!");
|
"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(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));
|
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> onBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onBlack, onBlack->GetSize().ToIntSize());
|
||||||
RefPtr<DrawTarget> onWhiteDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onWhite, onWhite->GetSize().ToIntSize());
|
RefPtr<DrawTarget> onWhiteDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onWhite, onWhite->GetSize().ToIntSize());
|
||||||
mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlackDT, onWhiteDT);
|
mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlackDT, onWhiteDT);
|
||||||
|
@ -1294,7 +1292,6 @@ ContentClientIncremental::BorrowDrawTargetForPainting(ThebesLayer* aLayer,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nsRefPtr<gfxASurface> surf = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw);
|
nsRefPtr<gfxASurface> surf = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw);
|
||||||
MOZ_ASSERT(gfxPlatform::GetPlatform()->SupportsAzureContent());
|
|
||||||
mLoanedDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(surf, surf->GetSize().ToIntSize());
|
mLoanedDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(surf, surf->GetSize().ToIntSize());
|
||||||
}
|
}
|
||||||
if (!mLoanedDrawTarget) {
|
if (!mLoanedDrawTarget) {
|
||||||
|
|
|
@ -248,29 +248,8 @@ DisableGralloc(SurfaceFormat aFormat)
|
||||||
if (aFormat == gfx::SurfaceFormat::A8) {
|
if (aFormat == gfx::SurfaceFormat::A8) {
|
||||||
return true;
|
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;
|
return false;
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -678,25 +657,13 @@ BufferTextureClient::UpdateSurface(gfxASurface* aSurface)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
|
||||||
|
|
||||||
dt->CopySurface(source, IntRect(IntPoint(), serializer.GetSize()), IntPoint());
|
dt->CopySurface(source, IntRect(IntPoint(), serializer.GetSize()), IntPoint());
|
||||||
// XXX - if the Moz2D backend is D2D, we would be much better off memcpying
|
// 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
|
// the content of the surface directly because with D2D, GetAsDrawTarget is
|
||||||
// very expensive.
|
// 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TextureRequiresLocking(mFlags) && !ImplementsLocking()) {
|
if (TextureRequiresLocking(mFlags) && !ImplementsLocking()) {
|
||||||
// We don't have support for proper locking yet, so we'll
|
// 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");
|
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextureClientPool::~TextureClientPool()
|
||||||
|
{
|
||||||
|
mTimer->Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
TemporaryRef<TextureClient>
|
TemporaryRef<TextureClient>
|
||||||
TextureClientPool::GetTextureClient()
|
TextureClientPool::GetTextureClient()
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,6 +24,7 @@ public:
|
||||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(TextureClientPool)
|
MOZ_DECLARE_REFCOUNTED_TYPENAME(TextureClientPool)
|
||||||
TextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
|
TextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
|
||||||
ISurfaceAllocator *aAllocator);
|
ISurfaceAllocator *aAllocator);
|
||||||
|
~TextureClientPool();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets an allocated TextureClient of size and format that are determined
|
* Gets an allocated TextureClient of size and format that are determined
|
||||||
|
@ -78,6 +79,8 @@ public:
|
||||||
*/
|
*/
|
||||||
void Clear();
|
void Clear();
|
||||||
|
|
||||||
|
gfx::SurfaceFormat GetFormat() { return mFormat; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The time in milliseconds before the pool will be shrunk to the minimum
|
// The time in milliseconds before the pool will be shrunk to the minimum
|
||||||
// size after returning a client.
|
// size after returning a client.
|
||||||
|
|
|
@ -318,9 +318,8 @@ TiledContentHost::RenderTile(const TileHost& aTile,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIntRect screenBounds = aScreenRegion.GetBounds();
|
nsIntRect screenBounds = aScreenRegion.GetBounds();
|
||||||
Matrix mat = aTransform.As2D();
|
|
||||||
Rect quad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
|
Rect quad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
|
||||||
quad = mat.TransformBounds(quad);
|
quad = aTransform.TransformBounds(quad);
|
||||||
|
|
||||||
if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(aClipRect))) {
|
if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(aClipRect))) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -408,14 +408,6 @@ ThebesLayerD3D10::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode)
|
||||||
|
|
||||||
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||||
FillTexturesBlackWhite(aRegion, visibleRect.TopLeft());
|
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 {
|
} else {
|
||||||
destinationSurface = mD2DSurface;
|
destinationSurface = mD2DSurface;
|
||||||
}
|
}
|
||||||
|
@ -465,18 +457,7 @@ ThebesLayerD3D10::CreateNewTextures(const gfx::IntSize &aSize, SurfaceMode aMode
|
||||||
NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10.");
|
NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
mDrawTarget = nullptr;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA && !mTextureOnWhite) {
|
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.");
|
NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
mDrawTarget = nullptr;
|
||||||
mD2DSurfaceOnWhite = new gfxD2DSurface(mTextureOnWhite, gfxContentType::COLOR);
|
|
||||||
|
|
||||||
if (!mD2DSurfaceOnWhite || mD2DSurfaceOnWhite->CairoStatus()) {
|
|
||||||
NS_WARNING("Failed to create surface for ThebesLayerD3D10.");
|
|
||||||
mD2DSurfaceOnWhite = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mDrawTarget = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent() && !mDrawTarget) {
|
if (!mDrawTarget) {
|
||||||
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||||
mDrawTarget = Factory::CreateDualDrawTargetForD3D10Textures(mTexture, mTextureOnWhite, SurfaceFormat::B8G8R8X8);
|
mDrawTarget = Factory::CreateDualDrawTargetForD3D10Textures(mTexture, mTextureOnWhite, SurfaceFormat::B8G8R8X8);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -47,12 +47,16 @@ IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
|
||||||
|
|
||||||
ISurfaceAllocator::~ISurfaceAllocator()
|
ISurfaceAllocator::~ISurfaceAllocator()
|
||||||
{
|
{
|
||||||
ShrinkShmemSectionHeap();
|
|
||||||
|
|
||||||
// Check if we're not leaking..
|
// Check if we're not leaking..
|
||||||
MOZ_ASSERT(mUsedShmems.empty());
|
MOZ_ASSERT(mUsedShmems.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ISurfaceAllocator::Finalize()
|
||||||
|
{
|
||||||
|
ShrinkShmemSectionHeap();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ISurfaceAllocator::AllocSharedImageSurface(const gfx::IntSize& aSize,
|
ISurfaceAllocator::AllocSharedImageSurface(const gfx::IntSize& aSize,
|
||||||
gfxContentType aContent,
|
gfxContentType aContent,
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "mozilla/layers/LayersMessages.h" // for ShmemSection
|
#include "mozilla/layers/LayersMessages.h" // for ShmemSection
|
||||||
#include "LayersTypes.h"
|
#include "LayersTypes.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FIXME [bjacob] *** PURE CRAZYNESS WARNING ***
|
* 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
|
* These methods should be only called in the ipdl implementor's thread, unless
|
||||||
* specified otherwise in the implementing class.
|
* specified otherwise in the implementing class.
|
||||||
*/
|
*/
|
||||||
class ISurfaceAllocator : public AtomicRefCounted<ISurfaceAllocator>
|
class ISurfaceAllocator : public AtomicRefCountedWithFinalize<ISurfaceAllocator>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
|
MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
|
||||||
ISurfaceAllocator() {}
|
ISurfaceAllocator() {}
|
||||||
|
|
||||||
|
void Finalize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the type of backend that is used off the main thread.
|
* 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
|
* 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.
|
// This is used to implement an extremely simple & naive heap allocator.
|
||||||
std::vector<mozilla::ipc::Shmem> mUsedShmems;
|
std::vector<mozilla::ipc::Shmem> mUsedShmems;
|
||||||
|
|
||||||
friend class detail::RefCounted<ISurfaceAllocator, detail::AtomicRefCount>;
|
friend class AtomicRefCountedWithFinalize<ISurfaceAllocator>;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GfxMemoryImageReporter MOZ_FINAL : public nsIMemoryReporter
|
class GfxMemoryImageReporter MOZ_FINAL : public nsIMemoryReporter
|
||||||
|
|
|
@ -423,29 +423,6 @@ ISurfaceAllocator::PlatformAllocSurfaceDescriptor(const gfx::IntSize& aSize,
|
||||||
SurfaceDescriptor* aBuffer)
|
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
|
// Some GL implementations fail to render gralloc textures with
|
||||||
// width < 64. There's not much point in gralloc'ing buffers that
|
// width < 64. There's not much point in gralloc'ing buffers that
|
||||||
// small anyway, so fall back on shared memory plus a texture
|
// small anyway, so fall back on shared memory plus a texture
|
||||||
|
|
|
@ -169,18 +169,10 @@ GrallocTextureClientOGL::UpdateSurface(gfxASurface* aSurface)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
|
||||||
|
|
||||||
dt->CopySurface(source, IntRect(IntPoint(), GetSize()), IntPoint());
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,17 +362,10 @@ gfxASurface::CopyToARGB32ImageSurface()
|
||||||
nsRefPtr<gfxImageSurface> imgSurface =
|
nsRefPtr<gfxImageSurface> imgSurface =
|
||||||
new gfxImageSurface(size, gfxImageFormat::ARGB32);
|
new gfxImageSurface(size, gfxImageFormat::ARGB32);
|
||||||
|
|
||||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
|
||||||
RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
|
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
|
||||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
|
|
||||||
|
|
||||||
dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
|
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
return imgSurface.forget();
|
return imgSurface.forget();
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,13 +392,11 @@ gfxPlatform::Init()
|
||||||
NS_RUNTIMEABORT("Could not initialize mScreenReferenceSurface");
|
NS_RUNTIMEABORT("Could not initialize mScreenReferenceSurface");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gPlatform->SupportsAzureContent()) {
|
gPlatform->mScreenReferenceDrawTarget =
|
||||||
gPlatform->mScreenReferenceDrawTarget =
|
gPlatform->CreateOffscreenContentDrawTarget(IntSize(1, 1),
|
||||||
gPlatform->CreateOffscreenContentDrawTarget(IntSize(1, 1),
|
SurfaceFormat::B8G8R8A8);
|
||||||
SurfaceFormat::B8G8R8A8);
|
if (!gPlatform->mScreenReferenceDrawTarget) {
|
||||||
if (!gPlatform->mScreenReferenceDrawTarget) {
|
NS_RUNTIMEABORT("Could not initialize mScreenReferenceDrawTarget");
|
||||||
NS_RUNTIMEABORT("Could not initialize mScreenReferenceDrawTarget");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = gfxFontCache::Init();
|
rv = gfxFontCache::Init();
|
||||||
|
|
|
@ -256,20 +256,6 @@ public:
|
||||||
CreateDrawTargetForData(unsigned char* aData, const mozilla::gfx::IntSize& aSize,
|
CreateDrawTargetForData(unsigned char* aData, const mozilla::gfx::IntSize& aSize,
|
||||||
int32_t aStride, mozilla::gfx::SurfaceFormat aFormat);
|
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
|
* 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
|
* 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;
|
mozilla::RefPtr<mozilla::gfx::DrawTarget> target;
|
||||||
nsRefPtr<gfxContext> ctx;
|
nsRefPtr<gfxContext> ctx;
|
||||||
|
|
||||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
target = gfxPlatform::GetPlatform()->
|
||||||
target = gfxPlatform::GetPlatform()->
|
CreateOffscreenContentDrawTarget(gfx::IntSize(mClip.width, mClip.height),
|
||||||
CreateOffscreenContentDrawTarget(gfx::IntSize(mClip.width, mClip.height),
|
gfx::SurfaceFormat::B8G8R8A8);
|
||||||
gfx::SurfaceFormat::B8G8R8A8);
|
ctx = new gfxContext(target);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create our callback.
|
// Create our callback.
|
||||||
nsRefPtr<gfxDrawingCallback> drawTileCallback =
|
nsRefPtr<gfxDrawingCallback> drawTileCallback =
|
||||||
|
|
|
@ -17,12 +17,6 @@
|
||||||
|
|
||||||
namespace base {
|
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()
|
SharedMemory::SharedMemory()
|
||||||
: mapped_file_(-1),
|
: mapped_file_(-1),
|
||||||
inode_(0),
|
inode_(0),
|
||||||
|
|
|
@ -302,20 +302,6 @@ TrimPositions TrimStringT(const STR& input,
|
||||||
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
|
((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 TrimWhitespace(const std::wstring& input,
|
||||||
TrimPositions positions,
|
TrimPositions positions,
|
||||||
std::wstring* output) {
|
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) {
|
static STR IntToString(INT value) {
|
||||||
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
|
// 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 '-'.
|
// 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.
|
// then return the substr of what we ended up using.
|
||||||
STR outbuf(kOutputBufSize, 0);
|
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
|
// Even though is_neg will never be true when INT is parameterized as
|
||||||
// unsigned, even the presence of the unary operation causes a warning.
|
// unsigned, even the presence of the unary operation causes a warning.
|
||||||
UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value);
|
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
|
// Specify the byte ordering explicitly, otherwise CFString will be confused
|
||||||
// when strings don't carry BOMs, as they typically won't.
|
// when strings don't carry BOMs, as they typically won't.
|
||||||
static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
|
static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
|
||||||
|
|
||||||
#ifdef __BIG_ENDIAN__
|
#ifdef __BIG_ENDIAN__
|
||||||
static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16BE;
|
|
||||||
static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE;
|
static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE;
|
||||||
#elif defined(__LITTLE_ENDIAN__)
|
#elif defined(__LITTLE_ENDIAN__)
|
||||||
static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16LE;
|
|
||||||
static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE;
|
static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE;
|
||||||
#endif // __LITTLE_ENDIAN__
|
#endif // __LITTLE_ENDIAN__
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ class JSAtom;
|
||||||
class JSFreeOp;
|
class JSFreeOp;
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
class StackFrame;
|
class InterpreterFrame;
|
||||||
class ScriptFrameIter;
|
class ScriptFrameIter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool s
|
||||||
# ifdef JS_DEBUG
|
# ifdef JS_DEBUG
|
||||||
JS_FRIEND_API(void) js_DumpValue(const JS::Value &val);
|
JS_FRIEND_API(void) js_DumpValue(const JS::Value &val);
|
||||||
JS_FRIEND_API(void) js_DumpId(jsid id);
|
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
|
# endif
|
||||||
|
|
||||||
JS_FRIEND_API(void)
|
JS_FRIEND_API(void)
|
||||||
|
|
|
@ -260,7 +260,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
||||||
unsigned staticLevel;
|
unsigned staticLevel;
|
||||||
RootedValue thisv(cx);
|
RootedValue thisv(cx);
|
||||||
if (evalType == DIRECT_EVAL) {
|
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;
|
staticLevel = caller.script()->staticLevel() + 1;
|
||||||
|
|
||||||
// Direct calls to eval are supposed to see the caller's |this|. If we
|
// 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;
|
return nullptr;
|
||||||
x4->initReservedSlot(JS_DESCR_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
|
x4->initReservedSlot(JS_DESCR_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
|
||||||
x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
|
x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
|
||||||
|
x4->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*proto));
|
||||||
|
|
||||||
// Link constructor to prototype and install properties.
|
// Link constructor to prototype and install properties.
|
||||||
|
|
||||||
|
|
|
@ -608,6 +608,8 @@ ArrayMetaTypeDescr::create(JSContext *cx,
|
||||||
if (!prototypeObj)
|
if (!prototypeObj)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
obj->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*prototypeObj));
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
|
if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
@ -979,6 +981,8 @@ StructMetaTypeDescr::create(JSContext *cx,
|
||||||
if (!prototypeObj)
|
if (!prototypeObj)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
descr->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*prototypeObj));
|
||||||
|
|
||||||
if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
|
if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
|
|
|
@ -135,6 +135,14 @@ static T ConvertScalar(double d)
|
||||||
class TypeDescr : public JSObject
|
class TypeDescr : public JSObject
|
||||||
{
|
{
|
||||||
public:
|
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 {
|
enum Kind {
|
||||||
Scalar = JS_TYPEREPR_SCALAR_KIND,
|
Scalar = JS_TYPEREPR_SCALAR_KIND,
|
||||||
Reference = JS_TYPEREPR_REFERENCE_KIND,
|
Reference = JS_TYPEREPR_REFERENCE_KIND,
|
||||||
|
@ -269,10 +277,22 @@ class ReferenceTypeDescr : public SimpleTypeDescr
|
||||||
macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \
|
macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \
|
||||||
macro_(ReferenceTypeDescr::TYPE_STRING, HeapPtrString, string)
|
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`
|
* Type descriptors `float32x4` and `int32x4`
|
||||||
*/
|
*/
|
||||||
class X4TypeDescr : public SizedTypeDescr
|
class X4TypeDescr : public ComplexTypeDescr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum Type {
|
enum Type {
|
||||||
|
@ -346,6 +366,11 @@ class ArrayMetaTypeDescr : public JSObject
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Type descriptor created by `new ArrayType(typeObj)`
|
* 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
|
class UnsizedArrayTypeDescr : public TypeDescr
|
||||||
{
|
{
|
||||||
|
@ -364,7 +389,7 @@ class UnsizedArrayTypeDescr : public TypeDescr
|
||||||
/*
|
/*
|
||||||
* Type descriptor created by `unsizedArrayTypeObj.dimension()`
|
* Type descriptor created by `unsizedArrayTypeObj.dimension()`
|
||||||
*/
|
*/
|
||||||
class SizedArrayTypeDescr : public SizedTypeDescr
|
class SizedArrayTypeDescr : public ComplexTypeDescr
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
@ -413,7 +438,8 @@ class StructMetaTypeDescr : public JSObject
|
||||||
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
||||||
};
|
};
|
||||||
|
|
||||||
class StructTypeDescr : public SizedTypeDescr {
|
class StructTypeDescr : public ComplexTypeDescr
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
|
@ -873,13 +899,19 @@ IsSimpleTypeDescrClass(const Class* clasp)
|
||||||
clasp == &ReferenceTypeDescr::class_;
|
clasp == &ReferenceTypeDescr::class_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
IsComplexTypeDescrClass(const Class* clasp)
|
||||||
|
{
|
||||||
|
return clasp == &StructTypeDescr::class_ ||
|
||||||
|
clasp == &SizedArrayTypeDescr::class_ ||
|
||||||
|
clasp == &X4TypeDescr::class_;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
IsSizedTypeDescrClass(const Class* clasp)
|
IsSizedTypeDescrClass(const Class* clasp)
|
||||||
{
|
{
|
||||||
return IsSimpleTypeDescrClass(clasp) ||
|
return IsSimpleTypeDescrClass(clasp) ||
|
||||||
clasp == &StructTypeDescr::class_ ||
|
IsComplexTypeDescrClass(clasp);
|
||||||
clasp == &SizedArrayTypeDescr::class_ ||
|
|
||||||
clasp == &X4TypeDescr::class_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
@ -908,6 +940,13 @@ JSObject::is<js::SizedTypeDescr>() const
|
||||||
return IsSizedTypeDescrClass(getClass());
|
return IsSizedTypeDescrClass(getClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline bool
|
||||||
|
JSObject::is<js::ComplexTypeDescr>() const
|
||||||
|
{
|
||||||
|
return IsComplexTypeDescrClass(getClass());
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline bool
|
inline bool
|
||||||
JSObject::is<js::TypeDescr>() const
|
JSObject::is<js::TypeDescr>() const
|
||||||
|
|
|
@ -945,18 +945,21 @@ function TypedArrayMap(a, b) {
|
||||||
|
|
||||||
// Warning: user exposed!
|
// Warning: user exposed!
|
||||||
function TypedArrayMapPar(a, b) {
|
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))
|
if (!IsObject(this) || !ObjectIsTypedObject(this))
|
||||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
return callFunction(TypedArrayMap, this, a, b);
|
||||||
var thisType = TYPEDOBJ_TYPE_DESCR(this);
|
var thisType = TYPEDOBJ_TYPE_DESCR(this);
|
||||||
if (!TypeDescrIsArrayType(thisType))
|
if (!TypeDescrIsArrayType(thisType))
|
||||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
return callFunction(TypedArrayMap, this, a, b);
|
||||||
|
|
||||||
// Arguments: [depth], func
|
if (typeof a === "number" && IsCallable(b))
|
||||||
if (typeof a === "number" && typeof b === "function")
|
|
||||||
return MapTypedParImpl(this, a, thisType, b);
|
return MapTypedParImpl(this, a, thisType, b);
|
||||||
else if (typeof a === "function")
|
else if (IsCallable(a))
|
||||||
return MapTypedParImpl(this, 1, thisType, a);
|
return MapTypedParImpl(this, 1, thisType, a);
|
||||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
return callFunction(TypedArrayMap, this, a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning: user exposed!
|
// Warning: user exposed!
|
||||||
|
@ -1017,6 +1020,20 @@ function TypedObjectArrayTypeBuildPar(a,b,c) {
|
||||||
|
|
||||||
// Warning: user exposed!
|
// Warning: user exposed!
|
||||||
function TypedObjectArrayTypeFromPar(a,b,c) {
|
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);
|
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.");
|
"Map/From called on non-object or untyped input array.");
|
||||||
assert(TypeDescrIsArrayType(outputType),
|
assert(TypeDescrIsArrayType(outputType),
|
||||||
"Map/From called on non array-type 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);
|
var inArrayType = TypeOfTypedObject(inArray);
|
||||||
|
|
||||||
|
@ -1516,6 +1537,12 @@ function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
|
||||||
outOffset += outGrainTypeSize;
|
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);
|
MARK_SLICE_DONE(slicesInfo, sliceId);
|
||||||
if (warmup)
|
if (warmup)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -21,23 +21,24 @@
|
||||||
#define JS_DESCR_SLOT_TYPE_REPR 0 // Associated Type Representation
|
#define JS_DESCR_SLOT_TYPE_REPR 0 // Associated Type Representation
|
||||||
#define JS_DESCR_SLOT_ALIGNMENT 1 // Alignment in bytes
|
#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_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
|
// 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
|
// 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
|
// 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
|
// Slots on struct type objects
|
||||||
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 3
|
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 4
|
||||||
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 4
|
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 5
|
||||||
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 5
|
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 6
|
||||||
|
|
||||||
// Maximum number of slots for any descriptor
|
// Maximum number of slots for any descriptor
|
||||||
#define JS_DESCR_SLOTS 6
|
#define JS_DESCR_SLOTS 7
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Slots for type representation objects
|
// Slots for type representation objects
|
||||||
|
|
|
@ -1136,7 +1136,8 @@ if test "$GNU_CC"; then
|
||||||
AC_MSG_CHECKING([for --build-id option to ld])
|
AC_MSG_CHECKING([for --build-id option to ld])
|
||||||
_SAVE_LDFLAGS=$LDFLAGS
|
_SAVE_LDFLAGS=$LDFLAGS
|
||||||
LDFLAGS="$LDFLAGS -Wl,--build-id"
|
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])
|
AC_MSG_RESULT([no])
|
||||||
LDFLAGS=$_SAVE_LDFLAGS)
|
LDFLAGS=$_SAVE_LDFLAGS)
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ class FreeOp;
|
||||||
namespace gc {
|
namespace gc {
|
||||||
|
|
||||||
struct Arena;
|
struct Arena;
|
||||||
|
struct ArenaList;
|
||||||
struct ArenaHeader;
|
struct ArenaHeader;
|
||||||
struct Chunk;
|
struct Chunk;
|
||||||
|
|
||||||
|
@ -579,6 +580,8 @@ struct Arena
|
||||||
return address() + ArenaSize;
|
return address() + ArenaSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAsFullyUnused(AllocKind thingKind);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize);
|
bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize);
|
||||||
};
|
};
|
||||||
|
@ -801,6 +804,7 @@ struct Chunk
|
||||||
ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
|
ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
|
||||||
|
|
||||||
void releaseArena(ArenaHeader *aheader);
|
void releaseArena(ArenaHeader *aheader);
|
||||||
|
void recycleArena(ArenaHeader *aheader, ArenaList &dest, AllocKind thingKind);
|
||||||
|
|
||||||
static Chunk *allocate(JSRuntime *rt);
|
static Chunk *allocate(JSRuntime *rt);
|
||||||
|
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче