зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
e471ad3958
|
@ -44,7 +44,7 @@ ARIAGridAccessible::NativeRole()
|
|||
// Table
|
||||
|
||||
uint32_t
|
||||
ARIAGridAccessible::ColCount()
|
||||
ARIAGridAccessible::ColCount() const
|
||||
{
|
||||
AccIterator rowIter(this, filters::GetRow);
|
||||
Accessible* row = rowIter.Next();
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
virtual TableAccessible* AsTable() override { return this; }
|
||||
|
||||
// TableAccessible
|
||||
virtual uint32_t ColCount() override;
|
||||
virtual uint32_t ColCount() const override;
|
||||
virtual uint32_t RowCount() override;
|
||||
virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override;
|
||||
virtual bool IsColSelected(uint32_t aColIdx) override;
|
||||
|
|
|
@ -1842,12 +1842,12 @@ Accessible::GetNativeInterface(void** aNativeAccessible)
|
|||
}
|
||||
|
||||
void
|
||||
Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
|
||||
Accessible::DoCommand(nsIContent* aContent, uint32_t aActionIndex) const
|
||||
{
|
||||
class Runnable final : public mozilla::Runnable
|
||||
{
|
||||
public:
|
||||
Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx)
|
||||
Runnable(const Accessible* aAcc, nsIContent* aContent, uint32_t aIdx)
|
||||
: mozilla::Runnable("Runnable")
|
||||
, mAcc(aAcc)
|
||||
, mContent(aContent)
|
||||
|
@ -1870,7 +1870,7 @@ Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
|
|||
}
|
||||
|
||||
private:
|
||||
RefPtr<Accessible> mAcc;
|
||||
RefPtr<const Accessible> mAcc;
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
uint32_t mIdx;
|
||||
};
|
||||
|
@ -1881,7 +1881,7 @@ Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
|
|||
}
|
||||
|
||||
void
|
||||
Accessible::DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex)
|
||||
Accessible::DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex) const
|
||||
{
|
||||
if (IsDefunct())
|
||||
return;
|
||||
|
|
|
@ -1096,12 +1096,14 @@ protected:
|
|||
* @param aContent [in, optional] element to click
|
||||
* @param aActionIndex [in, optional] index of accessible action
|
||||
*/
|
||||
void DoCommand(nsIContent *aContent = nullptr, uint32_t aActionIndex = 0);
|
||||
void DoCommand(nsIContent* aContent = nullptr,
|
||||
uint32_t aActionIndex = 0) const;
|
||||
|
||||
/**
|
||||
* Dispatch click event.
|
||||
*/
|
||||
virtual void DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex);
|
||||
virtual void DispatchClickEvent(nsIContent *aContent,
|
||||
uint32_t aActionIndex) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
|
|
@ -35,7 +35,7 @@ public:
|
|||
/**
|
||||
* Return the number of columns in the table.
|
||||
*/
|
||||
virtual uint32_t ColCount() { return 0; }
|
||||
virtual uint32_t ColCount() const { return 0; }
|
||||
|
||||
/**
|
||||
* Return the number of rows in the table.
|
||||
|
|
|
@ -484,7 +484,7 @@ HTMLTableAccessible::Summary(nsString& aSummary)
|
|||
}
|
||||
|
||||
uint32_t
|
||||
HTMLTableAccessible::ColCount()
|
||||
HTMLTableAccessible::ColCount() const
|
||||
{
|
||||
nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
|
||||
return tableFrame ? tableFrame->GetColCount() : 0;
|
||||
|
|
|
@ -132,7 +132,7 @@ public:
|
|||
// TableAccessible
|
||||
virtual Accessible* Caption() const override;
|
||||
virtual void Summary(nsString& aSummary) override;
|
||||
virtual uint32_t ColCount() override;
|
||||
virtual uint32_t ColCount() const override;
|
||||
virtual uint32_t RowCount() override;
|
||||
virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override;
|
||||
virtual int32_t CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) override;
|
||||
|
|
|
@ -156,9 +156,6 @@ var AccessFu = {
|
|||
case "AccessFu:Present":
|
||||
this._output(aMessage.json, aMessage.target);
|
||||
break;
|
||||
case "AccessFu:Input":
|
||||
this.Input.setEditState(aMessage.json);
|
||||
break;
|
||||
case "AccessFu:DoScroll":
|
||||
this.Input.doScroll(aMessage.json);
|
||||
break;
|
||||
|
@ -207,14 +204,12 @@ var AccessFu = {
|
|||
|
||||
_addMessageListeners: function _addMessageListeners(aMessageManager) {
|
||||
aMessageManager.addMessageListener("AccessFu:Present", this);
|
||||
aMessageManager.addMessageListener("AccessFu:Input", this);
|
||||
aMessageManager.addMessageListener("AccessFu:Ready", this);
|
||||
aMessageManager.addMessageListener("AccessFu:DoScroll", this);
|
||||
},
|
||||
|
||||
_removeMessageListeners: function _removeMessageListeners(aMessageManager) {
|
||||
aMessageManager.removeMessageListener("AccessFu:Present", this);
|
||||
aMessageManager.removeMessageListener("AccessFu:Input", this);
|
||||
aMessageManager.removeMessageListener("AccessFu:Ready", this);
|
||||
aMessageManager.removeMessageListener("AccessFu:DoScroll", this);
|
||||
},
|
||||
|
@ -364,8 +359,6 @@ var AccessFu = {
|
|||
};
|
||||
|
||||
var Input = {
|
||||
editState: {},
|
||||
|
||||
moveToPoint: function moveToPoint(aRule, aX, aY) {
|
||||
// XXX: Bug 1013408 - There is no alignment between the chrome window's
|
||||
// viewport size and the content viewport size in Android. This makes
|
||||
|
@ -396,23 +389,8 @@ var Input = {
|
|||
},
|
||||
|
||||
moveByGranularity: function moveByGranularity(aDetails) {
|
||||
const GRANULARITY_PARAGRAPH = 8;
|
||||
const GRANULARITY_LINE = 4;
|
||||
|
||||
if (!this.editState.editing) {
|
||||
if (aDetails.granularity & (GRANULARITY_PARAGRAPH | GRANULARITY_LINE)) {
|
||||
this.moveCursor("move" + aDetails.direction, "Simple", "gesture");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
aDetails.atStart = this.editState.atStart;
|
||||
aDetails.atEnd = this.editState.atEnd;
|
||||
}
|
||||
|
||||
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
|
||||
let type = this.editState.editing ? "AccessFu:MoveCaret" :
|
||||
"AccessFu:MoveByGranularity";
|
||||
mm.sendAsyncMessage(type, aDetails);
|
||||
mm.sendAsyncMessage("AccessFu:MoveByGranularity", aDetails);
|
||||
},
|
||||
|
||||
activateCurrent: function activateCurrent(aData, aActivateIfKey = false) {
|
||||
|
@ -423,11 +401,6 @@ var Input = {
|
|||
{offset, activateIfKey: aActivateIfKey});
|
||||
},
|
||||
|
||||
setEditState: function setEditState(aEditState) {
|
||||
Logger.debug(() => { return ["setEditState", JSON.stringify(aEditState)]; });
|
||||
this.editState = aEditState;
|
||||
},
|
||||
|
||||
// XXX: This is here for backwards compatability with screen reader simulator
|
||||
// it should be removed when the extension is updated on amo.
|
||||
scroll: function scroll(aPage, aHorizontal) {
|
||||
|
|
|
@ -9,6 +9,8 @@ ChromeUtils.defineModuleGetter(this, "Logger",
|
|||
"resource://gre/modules/accessibility/Utils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "Roles",
|
||||
"resource://gre/modules/accessibility/Constants.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "States",
|
||||
"resource://gre/modules/accessibility/Constants.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "TraversalRules",
|
||||
"resource://gre/modules/accessibility/Traversal.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "TraversalHelper",
|
||||
|
@ -33,7 +35,6 @@ this.ContentControl.prototype = {
|
|||
"AccessFu:MoveToPoint",
|
||||
"AccessFu:AutoMove",
|
||||
"AccessFu:Activate",
|
||||
"AccessFu:MoveCaret",
|
||||
"AccessFu:MoveByGranularity",
|
||||
"AccessFu:AndroidScroll"],
|
||||
|
||||
|
@ -300,25 +301,29 @@ this.ContentControl.prototype = {
|
|||
},
|
||||
|
||||
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;
|
||||
let { direction, granularity } = aMessage.json;
|
||||
let focusedAcc = Utils.AccService.getAccessibleFor(this.document.activeElement);
|
||||
if (focusedAcc && Utils.getState(focusedAcc).contains(States.EDITABLE)) {
|
||||
this.moveCaret(focusedAcc, direction, granularity);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aMessage.json.granularity) {
|
||||
let pivotGranularity;
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
granularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
|
||||
pivotGranularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
granularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
|
||||
pivotGranularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === "Previous") {
|
||||
this.vc.movePreviousByText(granularity);
|
||||
this.vc.movePreviousByText(pivotGranularity);
|
||||
} else if (direction === "Next") {
|
||||
this.vc.moveNextByText(granularity);
|
||||
this.vc.moveNextByText(pivotGranularity);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -331,16 +336,13 @@ this.ContentControl.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
handleMoveCaret: function cc_handleMoveCaret(aMessage) {
|
||||
let direction = aMessage.json.direction;
|
||||
let granularity = aMessage.json.granularity;
|
||||
let accessible = this.vc.position;
|
||||
moveCaret: function cc_moveCaret(accessible, direction, granularity) {
|
||||
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) {
|
||||
if (direction === "Previous" && oldOffset > 0) {
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
accText.caretOffset--;
|
||||
|
@ -356,7 +358,7 @@ this.ContentControl.prototype = {
|
|||
accText.caretOffset = startOfParagraph !== -1 ? startOfParagraph : 0;
|
||||
break;
|
||||
}
|
||||
} else if (direction === "Next" && !aMessage.json.atEnd) {
|
||||
} else if (direction === "Next" && oldOffset < accText.characterCount) {
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
accText.caretOffset++;
|
||||
|
|
|
@ -37,8 +37,6 @@ function EventManager(aContentScope, aContentControl) {
|
|||
}
|
||||
|
||||
this.EventManager.prototype = {
|
||||
editState: { editing: false },
|
||||
|
||||
start: function start() {
|
||||
try {
|
||||
if (!this._started) {
|
||||
|
@ -144,8 +142,7 @@ this.EventManager.prototype = {
|
|||
let reason = event.reason;
|
||||
let oldAccessible = event.oldAccessible;
|
||||
|
||||
if (this.editState.editing &&
|
||||
!Utils.getState(position).contains(States.FOCUSED)) {
|
||||
if (!Utils.getState(position).contains(States.FOCUSED)) {
|
||||
aEvent.accessibleDocument.takeFocus();
|
||||
}
|
||||
this.present(
|
||||
|
@ -208,12 +205,9 @@ this.EventManager.prototype = {
|
|||
// it doesn't mean we are not on any editable accessible. just not
|
||||
// on this one..
|
||||
let state = Utils.getState(acc);
|
||||
if (state.contains(States.FOCUSED)) {
|
||||
this._setEditingMode(aEvent, caretOffset);
|
||||
if (state.contains(States.EDITABLE)) {
|
||||
this.present(Presentation.textSelectionChanged(acc.getText(0, -1),
|
||||
caretOffset, caretOffset, 0, 0, aEvent.isFromUserInput));
|
||||
}
|
||||
if (state.contains(States.FOCUSED) && state.contains(States.EDITABLE)) {
|
||||
this.present(Presentation.textSelectionChanged(acc.getText(0, -1),
|
||||
caretOffset, caretOffset, 0, 0, aEvent.isFromUserInput));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -259,12 +253,13 @@ this.EventManager.prototype = {
|
|||
{
|
||||
// Put vc where the focus is at
|
||||
let acc = aEvent.accessible;
|
||||
this._setEditingMode(aEvent);
|
||||
if (![Roles.CHROME_WINDOW,
|
||||
Roles.DOCUMENT,
|
||||
Roles.APPLICATION].includes(acc.role)) {
|
||||
this.contentControl.autoMove(acc);
|
||||
}
|
||||
}
|
||||
|
||||
this.present(Presentation.focused(acc));
|
||||
|
||||
if (this.inTest) {
|
||||
this.sendMsgFunc("AccessFu:Focused");
|
||||
|
@ -288,8 +283,10 @@ this.EventManager.prototype = {
|
|||
this.contentControl.autoMove(aEvent.accessible, { delay: 500 });
|
||||
break;
|
||||
}
|
||||
case Events.VALUE_CHANGE:
|
||||
case Events.TEXT_VALUE_CHANGE:
|
||||
// We handle this events in TEXT_INSERTED/TEXT_REMOVED.
|
||||
break;
|
||||
case Events.VALUE_CHANGE:
|
||||
{
|
||||
let position = this.contentControl.vc.position;
|
||||
let target = aEvent.accessible;
|
||||
|
@ -307,54 +304,6 @@ this.EventManager.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_setEditingMode: function _setEditingMode(aEvent, aCaretOffset) {
|
||||
let acc = aEvent.accessible;
|
||||
let accText, characterCount;
|
||||
let caretOffset = aCaretOffset;
|
||||
|
||||
try {
|
||||
accText = acc.QueryInterface(Ci.nsIAccessibleText);
|
||||
} catch (e) {
|
||||
// No text interface on this accessible.
|
||||
}
|
||||
|
||||
if (accText) {
|
||||
characterCount = accText.characterCount;
|
||||
if (caretOffset === undefined) {
|
||||
caretOffset = accText.caretOffset;
|
||||
}
|
||||
}
|
||||
|
||||
// Update editing state, both for presenter and other things
|
||||
let state = Utils.getState(acc);
|
||||
|
||||
let editState = {
|
||||
editing: state.contains(States.EDITABLE) &&
|
||||
state.contains(States.FOCUSED),
|
||||
multiline: state.contains(States.MULTI_LINE),
|
||||
atStart: caretOffset === 0,
|
||||
atEnd: caretOffset === characterCount
|
||||
};
|
||||
|
||||
// Not interesting
|
||||
if (!editState.editing && editState.editing === this.editState.editing) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (editState.editing !== this.editState.editing) {
|
||||
this.present(Presentation.editingModeChanged(editState.editing));
|
||||
}
|
||||
|
||||
if (editState.editing !== this.editState.editing ||
|
||||
editState.multiline !== this.editState.multiline ||
|
||||
editState.atEnd !== this.editState.atEnd ||
|
||||
editState.atStart !== this.editState.atStart) {
|
||||
this.sendMsgFunc("AccessFu:Input", editState);
|
||||
}
|
||||
|
||||
this.editState = editState;
|
||||
},
|
||||
|
||||
_handleShow: function _handleShow(aEvent) {
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
["additions", "all"]);
|
||||
|
|
|
@ -65,15 +65,10 @@ class AndroidPresentor {
|
|||
});
|
||||
}
|
||||
} else {
|
||||
let state = Utils.getState(context.accessible);
|
||||
androidEvents.push({eventType: (isExploreByTouch) ?
|
||||
AndroidEvents.VIEW_HOVER_ENTER : focusEventType,
|
||||
text: Utils.localize(UtteranceGenerator.genForContext(
|
||||
context)),
|
||||
bounds: context.bounds,
|
||||
clickable: context.accessible.actionCount > 0,
|
||||
checkable: state.contains(States.CHECKABLE),
|
||||
checked: state.contains(States.CHECKED)});
|
||||
let info = this._infoFromContext(context);
|
||||
let eventType = isExploreByTouch ?
|
||||
AndroidEvents.VIEW_HOVER_ENTER : focusEventType;
|
||||
androidEvents.push({...info, eventType});
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -88,6 +83,12 @@ class AndroidPresentor {
|
|||
return androidEvents;
|
||||
}
|
||||
|
||||
focused(aObject) {
|
||||
let info = this._infoFromContext(
|
||||
new PivotContext(aObject, null, -1, -1, true, false));
|
||||
return [{ eventType: AndroidEvents.VIEW_FOCUSED, ...info }];
|
||||
}
|
||||
|
||||
/**
|
||||
* An object's action has been invoked.
|
||||
* @param {nsIAccessible} aObject the object that has been invoked.
|
||||
|
@ -250,13 +251,6 @@ class AndroidPresentor {
|
|||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have entered or left text editing mode.
|
||||
*/
|
||||
editingModeChanged(aIsEditing) {
|
||||
return this.announce(UtteranceGenerator.genForEditingMode(aIsEditing));
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce something. Typically an app state change.
|
||||
*/
|
||||
|
@ -299,6 +293,20 @@ class AndroidPresentor {
|
|||
return this.announce(
|
||||
UtteranceGenerator.genForLiveRegion(context, aIsHide, aModifiedText));
|
||||
}
|
||||
|
||||
_infoFromContext(aContext) {
|
||||
let state = Utils.getState(aContext.accessible);
|
||||
return {
|
||||
text: Utils.localize(UtteranceGenerator.genForContext(aContext)),
|
||||
bounds: aContext.bounds,
|
||||
focusable: state.contains(States.FOCUSABLE),
|
||||
focused: state.contains(States.FOCUSED),
|
||||
clickable: aContext.accessible.actionCount > 0,
|
||||
checkable: state.contains(States.CHECKABLE),
|
||||
checked: state.contains(States.CHECKED),
|
||||
editable: state.contains(States.EDITABLE),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const Presentation = new AndroidPresentor();
|
||||
|
|
|
@ -408,26 +408,4 @@ class AccessFuContentTestRunner {
|
|||
movePreviousByGranularity(aGranularity, ...aExpectedEvents) {
|
||||
return this.moveByGranularity("Previous", aGranularity, ...aExpectedEvents);
|
||||
}
|
||||
|
||||
// XXX: moveCaret* methods will go away soon as we fold it into the
|
||||
// granularity messages above.
|
||||
moveCaret(aDirection, aGranularity, ...aExpectedEvents) {
|
||||
return this.expectAndroidEvents(() => {
|
||||
this.sendMessage({
|
||||
name: "AccessFu:MoveCaret",
|
||||
data: {
|
||||
direction: aDirection,
|
||||
granularity: aGranularity
|
||||
}
|
||||
});
|
||||
}, ...aExpectedEvents);
|
||||
}
|
||||
|
||||
moveCaretNext(aGranularity, ...aExpectedEvents) {
|
||||
return this.moveCaret("Next", aGranularity, ...aExpectedEvents);
|
||||
}
|
||||
|
||||
moveCaretPrevious(aGranularity, ...aExpectedEvents) {
|
||||
return this.moveCaret("Previous", aGranularity, ...aExpectedEvents);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
evt = await runner.moveNext("Simple",
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
|
||||
runner.eventTextMatches(evt, ["Traversal Rule test document", "Phone status bar"]);
|
||||
runner.isFocused("body");
|
||||
runner.isFocused("html");
|
||||
|
||||
evt = await runner.movePrevious("Simple",
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
|
||||
|
|
|
@ -36,15 +36,15 @@
|
|||
let evt;
|
||||
|
||||
evt = await runner.focusSelector("textarea",
|
||||
AndroidEvents.ANNOUNCEMENT,
|
||||
AndroidEvents.VIEW_FOCUSED,
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
// XXX: Get rid of announcements, and send focus events instead
|
||||
runner.eventTextMatches(evt[0], ["editing"]);
|
||||
is(evt[0].editable, true, "focused item is editable");
|
||||
runner.eventTextMatches(evt[1],
|
||||
["Text content test document",
|
||||
"Please refrain from Mayoneggs during this salmonella scare.",
|
||||
"text area"]);
|
||||
is(evt[1].focused, true, "a11y focused item is focused");
|
||||
is(evt[2].fromIndex, 0, "Correct fromIndex");
|
||||
is(evt[2].toIndex, 0, "Correct toIndex");
|
||||
|
||||
|
@ -58,38 +58,38 @@
|
|||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 10, 20);
|
||||
|
||||
evt = await runner.moveCaretNext(MovementGranularity.WORD,
|
||||
evt = await runner.moveNextByGranularity(MovementGranularity.WORD,
|
||||
AndroidEvents.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 20, 29);
|
||||
|
||||
evt = await runner.moveCaretNext(MovementGranularity.WORD,
|
||||
evt = await runner.moveNextByGranularity(MovementGranularity.WORD,
|
||||
AndroidEvents.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 29, 36);
|
||||
|
||||
evt = await runner.moveCaretNext(MovementGranularity.CHARACTER,
|
||||
evt = await runner.moveNextByGranularity(MovementGranularity.CHARACTER,
|
||||
AndroidEvents.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 36, 37);
|
||||
|
||||
evt = await runner.moveCaretNext(MovementGranularity.CHARACTER,
|
||||
evt = await runner.moveNextByGranularity(MovementGranularity.CHARACTER,
|
||||
AndroidEvents.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 37, 38);
|
||||
|
||||
evt = await runner.moveCaretNext(MovementGranularity.PARAGRAPH,
|
||||
evt = await runner.moveNextByGranularity(MovementGranularity.PARAGRAPH,
|
||||
AndroidEvents.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 38, 59);
|
||||
|
||||
evt = await runner.moveCaretPrevious(MovementGranularity.WORD,
|
||||
evt = await runner.movePreviousByGranularity(MovementGranularity.WORD,
|
||||
AndroidEvents.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkMoveCaret(...evt, 59, 53);
|
||||
|
||||
evt = await runner.blur(AndroidEvents.ANNOUNCEMENT);
|
||||
runner.eventTextMatches(evt, ["navigating"]);
|
||||
evt = await runner.blur(AndroidEvents.VIEW_FOCUSED);
|
||||
is(evt.editable, false, "Focused out of editable");
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
|
|
|
@ -37,77 +37,67 @@
|
|||
let evt;
|
||||
|
||||
evt = await runner.focusSelector("input",
|
||||
AndroidEvents.ANNOUNCEMENT,
|
||||
AndroidEvents.VIEW_FOCUSED,
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
// XXX: Get rid of announcements, and send focus events instead
|
||||
runner.eventTextMatches(evt[0], ["editing"]);
|
||||
is(evt[0].editable, true, "focused item is editable");
|
||||
runner.eventTextMatches(evt[1], ["Text content test document", "entry"]);
|
||||
is(evt[1].focused, true, "a11y focused item is focused");
|
||||
is(evt[2].fromIndex, 0, "Caret at start (fromIndex)");
|
||||
is(evt[2].toIndex, 0, "Caret at start (toIndex)");
|
||||
|
||||
evt = await runner.typeKey("B",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["B"], 0, 1);
|
||||
|
||||
evt = await runner.typeKey("o",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bo"], 1, 1);
|
||||
|
||||
evt = await runner.typeKey("b",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob"], 2, 1);
|
||||
|
||||
evt = await runner.typeKey(" ",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob "], 3, 1);
|
||||
|
||||
evt = await runner.typeKey("L",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob L"], 4, 1);
|
||||
|
||||
evt = await runner.typeKey("o",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob Lo"], 5, 1);
|
||||
|
||||
evt = await runner.typeKey("b",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob Lob"], 6, 1);
|
||||
|
||||
evt = await runner.typeKey("l",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob Lobl"], 7, 1);
|
||||
|
||||
evt = await runner.typeKey("a",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob Lobla"], 8, 1);
|
||||
|
||||
evt = await runner.typeKey("w",
|
||||
AndroidEvents.VIEW_TEXT_CHANGED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED,
|
||||
"todo.value-changed");
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
checkInsert(evt[0], evt[1], ["Bob Loblaw"], 9, 1);
|
||||
|
||||
evt = await runner.blur(AndroidEvents.ANNOUNCEMENT);
|
||||
runner.eventTextMatches(evt, ["navigating"]);
|
||||
evt = await runner.blur(AndroidEvents.VIEW_FOCUSED);
|
||||
is(evt.editable, false, "Focused out of editable");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -28,20 +28,20 @@
|
|||
let evt;
|
||||
|
||||
evt = await runner.focusSelector("textarea",
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED,
|
||||
AndroidEvents.ANNOUNCEMENT);
|
||||
// XXX: Get rid of announcements, and send focus events instead
|
||||
runner.eventTextMatches(evt[0],
|
||||
AndroidEvents.VIEW_FOCUSED,
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
|
||||
is(evt[0].editable, true, "focused item is editable");
|
||||
is(evt[1].focused, true, "a11y focused item is focused");
|
||||
runner.eventTextMatches(evt[1],
|
||||
["Text content test document",
|
||||
"Please refrain from Mayoneggs during this salmonella scare.",
|
||||
"text area"]);
|
||||
runner.eventTextMatches(evt[1], ["editing"]);
|
||||
|
||||
evt = await runner.moveNext("Simple",
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED,
|
||||
AndroidEvents.ANNOUNCEMENT);
|
||||
runner.eventTextMatches(evt[0], ["So we don't get dessert?", "label"]);
|
||||
runner.eventTextMatches(evt[1], ["navigating"]);
|
||||
AndroidEvents.VIEW_FOCUSED,
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
|
||||
is(evt[0].editable, false, "focused out of editable");
|
||||
runner.eventTextMatches(evt[1], ["So we don't get dessert?", "label"]);
|
||||
runner.isFocused("html");
|
||||
|
||||
evt = await runner.moveNext("Simple",
|
||||
|
@ -51,17 +51,17 @@
|
|||
|
||||
evt = await runner.activateCurrent(0,
|
||||
AndroidEvents.VIEW_CLICKED,
|
||||
AndroidEvents.ANNOUNCEMENT,
|
||||
AndroidEvents.VIEW_FOCUSED,
|
||||
AndroidEvents.VIEW_TEXT_SELECTION_CHANGED);
|
||||
runner.eventTextMatches(evt[1], ["editing"]);
|
||||
is(evt[1].editable, true, "focused item is editable");
|
||||
is(evt[2].fromIndex, 0, "Cursor at start");
|
||||
runner.isFocused("input[type=text]");
|
||||
|
||||
evt = await runner.movePrevious("Simple",
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED,
|
||||
AndroidEvents.ANNOUNCEMENT);
|
||||
runner.eventTextMatches(evt[0], ["So we don't get dessert?", "label"]);
|
||||
runner.eventTextMatches(evt[1], ["navigating"]);
|
||||
AndroidEvents.VIEW_FOCUSED,
|
||||
AndroidEvents.VIEW_ACCESSIBILITY_FOCUSED);
|
||||
is(evt[0].editable, false, "focused out of editable");
|
||||
runner.eventTextMatches(evt[1], ["So we don't get dessert?", "label"]);
|
||||
runner.isFocused("html");
|
||||
|
||||
evt = await runner.moveNext("Simple",
|
||||
|
@ -72,12 +72,12 @@
|
|||
// XXX: TEXT_SELECTION_CHANGED should be fired here
|
||||
evt = await runner.activateCurrent(0,
|
||||
AndroidEvents.VIEW_CLICKED,
|
||||
AndroidEvents.ANNOUNCEMENT);
|
||||
runner.eventTextMatches(evt[1], ["editing"]);
|
||||
AndroidEvents.VIEW_FOCUSED);
|
||||
is(evt[1].editable, true, "focused item is editable");
|
||||
runner.isFocused("input[type=text]");
|
||||
|
||||
evt = await runner.blur(AndroidEvents.ANNOUNCEMENT);
|
||||
runner.eventTextMatches(evt, ["navigating"]);
|
||||
evt = await runner.blur(AndroidEvents.VIEW_FOCUSED);
|
||||
is(evt.editable, false, "Focused out of editable");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -166,7 +166,7 @@ XULListboxAccessible::NativeRole()
|
|||
// XULListboxAccessible: Table
|
||||
|
||||
uint32_t
|
||||
XULListboxAccessible::ColCount()
|
||||
XULListboxAccessible::ColCount() const
|
||||
{
|
||||
nsIContent* headContent = nullptr;
|
||||
for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
|
||||
|
|
|
@ -62,7 +62,7 @@ public:
|
|||
XULListboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
|
||||
|
||||
// TableAccessible
|
||||
virtual uint32_t ColCount() override;
|
||||
virtual uint32_t ColCount() const override;
|
||||
virtual uint32_t RowCount() override;
|
||||
virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override;
|
||||
virtual bool IsColSelected(uint32_t aColIdx) override;
|
||||
|
@ -95,7 +95,7 @@ public:
|
|||
protected:
|
||||
virtual ~XULListboxAccessible() {}
|
||||
|
||||
bool IsMulticolumn() { return ColCount() > 1; }
|
||||
bool IsMulticolumn() const { return ColCount() > 1; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -982,7 +982,7 @@ XULTreeItemAccessibleBase::ContainerWidget() const
|
|||
|
||||
void
|
||||
XULTreeItemAccessibleBase::DispatchClickEvent(nsIContent* aContent,
|
||||
uint32_t aActionIndex)
|
||||
uint32_t aActionIndex) const
|
||||
{
|
||||
if (IsDefunct())
|
||||
return;
|
||||
|
|
|
@ -191,7 +191,8 @@ protected:
|
|||
enum { eAction_Click = 0, eAction_Expand = 1 };
|
||||
|
||||
// Accessible
|
||||
virtual void DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex) override;
|
||||
virtual void DispatchClickEvent(nsIContent *aContent,
|
||||
uint32_t aActionIndex) const override;
|
||||
virtual Accessible* GetSiblingAtOffset(int32_t aOffset,
|
||||
nsresult *aError = nullptr) const override;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ XULTreeGridAccessible::~XULTreeGridAccessible()
|
|||
// XULTreeGridAccessible: Table
|
||||
|
||||
uint32_t
|
||||
XULTreeGridAccessible::ColCount()
|
||||
XULTreeGridAccessible::ColCount() const
|
||||
{
|
||||
return nsCoreUtils::GetSensibleColumnCount(mTree);
|
||||
}
|
||||
|
@ -805,7 +805,7 @@ XULTreeGridCellAccessible::GetSiblingAtOffset(int32_t aOffset,
|
|||
|
||||
void
|
||||
XULTreeGridCellAccessible::DispatchClickEvent(nsIContent* aContent,
|
||||
uint32_t aActionIndex)
|
||||
uint32_t aActionIndex) const
|
||||
{
|
||||
if (IsDefunct())
|
||||
return;
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
{ mGenericTypes |= eTable; }
|
||||
|
||||
// TableAccessible
|
||||
virtual uint32_t ColCount() override;
|
||||
virtual uint32_t ColCount() const override;
|
||||
virtual uint32_t RowCount() override;
|
||||
virtual Accessible* CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) override;
|
||||
virtual void ColDescription(uint32_t aColIdx, nsString& aDescription) override;
|
||||
|
@ -163,7 +163,8 @@ protected:
|
|||
// Accessible
|
||||
virtual Accessible* GetSiblingAtOffset(int32_t aOffset,
|
||||
nsresult* aError = nullptr) const override;
|
||||
virtual void DispatchClickEvent(nsIContent* aContent, uint32_t aActionIndex) override;
|
||||
virtual void DispatchClickEvent(nsIContent* aContent,
|
||||
uint32_t aActionIndex) const override;
|
||||
|
||||
// XULTreeGridCellAccessible
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* 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/. */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
"cxx": "cl.exe",
|
||||
"ml": "ml64.exe",
|
||||
"patches": [
|
||||
"r332092.patch",
|
||||
"loosen-msvc-detection.patch"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
Bug 1458325: Fix copy/paste error in AddrIsInHighShadow that breaks Win64 jit-tests
|
||||
|
||||
https://reviews.llvm.org/D46291
|
||||
https://github.com/llvm-mirror/compiler-rt/commit/d55f7bdbe7079a3d331d8ac7d0e82352eaf26af1
|
||||
|
||||
--- a/compiler-rt/lib/asan/asan_mapping.h
|
||||
+++ b/compiler-rt/lib/asan/asan_mapping.h
|
||||
@@ -319,12 +319,12 @@
|
||||
|
||||
static inline bool AddrIsInHighShadow(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
- return a >= kHighShadowBeg && a <= kHighMemEnd;
|
||||
+ return a >= kHighShadowBeg && a <= kHighShadowEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInMidShadow(uptr a) {
|
||||
PROFILE_ASAN_MAPPING();
|
||||
- return kMidMemBeg && a >= kMidShadowBeg && a <= kMidMemEnd;
|
||||
+ return kMidMemBeg && a >= kMidShadowBeg && a <= kMidShadowEnd;
|
||||
}
|
||||
|
||||
static inline bool AddrIsInShadow(uptr a) {
|
|
@ -925,8 +925,9 @@ ifndef MOZ_TSAN
|
|||
# Cargo needs the same linker flags as the C/C++ compiler,
|
||||
# but not the final libraries. Filter those out because they
|
||||
# cause problems on macOS 10.7; see bug 1365993 for details.
|
||||
# Also, we don't want to pass PGO flags until cargo supports them.
|
||||
target_cargo_env_vars := \
|
||||
MOZ_CARGO_WRAP_LDFLAGS="$(filter-out -framework Cocoa -lobjc AudioToolbox ExceptionHandling,$(LDFLAGS))" \
|
||||
MOZ_CARGO_WRAP_LDFLAGS="$(filter-out -framework Cocoa -lobjc AudioToolbox ExceptionHandling -fprofile-%,$(LDFLAGS))" \
|
||||
MOZ_CARGO_WRAP_LD="$(CC)" \
|
||||
$(cargo_linker_env_var)=$(topsrcdir)/build/cargo-linker
|
||||
endif # MOZ_TSAN
|
||||
|
|
|
@ -1551,7 +1551,7 @@ FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
|
|||
static bool
|
||||
IsCertainlyAliveNode(nsINode* aNode, nsIDocument* aDoc)
|
||||
{
|
||||
MOZ_ASSERT(aNode->GetUncomposedDoc() == aDoc);
|
||||
MOZ_ASSERT(aNode->GetComposedDoc() == aDoc);
|
||||
|
||||
// Marked to be in-CC-generation or if the document is an svg image that's
|
||||
// being kept alive by the image cache. (Note that an svg image's internal
|
||||
|
@ -1573,9 +1573,7 @@ FragmentOrElement::CanSkipInCC(nsINode* aNode)
|
|||
return false;
|
||||
}
|
||||
|
||||
//XXXsmaug Need to figure out in which cases Shadow DOM can be optimized out
|
||||
// from the CC graph.
|
||||
nsIDocument* currentDoc = aNode->GetUncomposedDoc();
|
||||
nsIDocument* currentDoc = aNode->GetComposedDoc();
|
||||
if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
|
||||
return !NeedsScriptTraverse(aNode);
|
||||
}
|
||||
|
@ -1752,7 +1750,7 @@ FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
|
|||
}
|
||||
|
||||
bool unoptimizable = aNode->UnoptimizableCCNode();
|
||||
nsIDocument* currentDoc = aNode->GetUncomposedDoc();
|
||||
nsIDocument* currentDoc = aNode->GetComposedDoc();
|
||||
if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
|
||||
(!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
|
||||
OwnedByBindingManager(currentDoc, aNode))) {
|
||||
|
@ -1880,7 +1878,7 @@ FragmentOrElement::CanSkipThis(nsINode* aNode)
|
|||
if (aNode->HasKnownLiveWrapper()) {
|
||||
return true;
|
||||
}
|
||||
nsIDocument* c = aNode->GetUncomposedDoc();
|
||||
nsIDocument* c = aNode->GetComposedDoc();
|
||||
return
|
||||
((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
|
||||
!NeedsScriptTraverse(aNode);
|
||||
|
|
|
@ -10496,19 +10496,6 @@ nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent)
|
|||
return false;
|
||||
}
|
||||
|
||||
/* static */ Element*
|
||||
nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
MOZ_ASSERT(aElement->IsNativeAnonymous());
|
||||
|
||||
Element* e = aElement;
|
||||
while (e && e->IsNativeAnonymous()) {
|
||||
e = e->GetParentElement();
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given type is a supported document type for
|
||||
* loading within the nsObjectLoadingContent specified by aContent.
|
||||
|
|
|
@ -3120,12 +3120,6 @@ public:
|
|||
*/
|
||||
static bool ShouldBlockReservedKeys(mozilla::WidgetKeyboardEvent* aKeyEvent);
|
||||
|
||||
/**
|
||||
* Walks up the tree from aElement until it finds an element that is
|
||||
* not native anonymous content. aElement must be NAC itself.
|
||||
*/
|
||||
static Element* GetClosestNonNativeAnonymousAncestor(Element* aElement);
|
||||
|
||||
/**
|
||||
* Returns the nsIPluginTag for the plugin we should try to use for a given
|
||||
* MIME type.
|
||||
|
|
|
@ -1155,8 +1155,7 @@ nsINode::UnoptimizableCCNode() const
|
|||
const uintptr_t problematicFlags = (NODE_IS_ANONYMOUS_ROOT |
|
||||
NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
|
||||
NODE_IS_NATIVE_ANONYMOUS_ROOT |
|
||||
NODE_MAY_BE_IN_BINDING_MNGR |
|
||||
NODE_IS_IN_SHADOW_TREE);
|
||||
NODE_MAY_BE_IN_BINDING_MNGR);
|
||||
return HasFlag(problematicFlags) ||
|
||||
NodeType() == ATTRIBUTE_NODE ||
|
||||
// For strange cases like xbl:content/xbl:children
|
||||
|
@ -1169,7 +1168,7 @@ bool
|
|||
nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
if (MOZ_LIKELY(!cb.WantAllTraces())) {
|
||||
nsIDocument *currentDoc = tmp->GetUncomposedDoc();
|
||||
nsIDocument* currentDoc = tmp->GetComposedDoc();
|
||||
if (currentDoc &&
|
||||
nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
|
||||
return false;
|
||||
|
|
|
@ -194,6 +194,10 @@ struct PropertyInfo {
|
|||
uint32_t prefIndex: NUM_BITS_PROPERTY_INFO_PREF_INDEX;
|
||||
// The index to the corresponding spec in Duo.mPrefables[prefIndex].specs[].
|
||||
uint32_t specIndex: NUM_BITS_PROPERTY_INFO_SPEC_INDEX;
|
||||
|
||||
// Note: the default constructor is not constexpr because of the bit fields,
|
||||
// so we need this one.
|
||||
constexpr PropertyInfo() : id(), type(0), prefIndex(0), specIndex(0) {}
|
||||
};
|
||||
|
||||
static_assert(ePropertyTypeCount <= 1ull << NUM_BITS_PROPERTY_INFO_TYPE,
|
||||
|
|
|
@ -1519,6 +1519,10 @@ ContentParent::OnChannelConnected(int32_t pid)
|
|||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
#ifndef ASYNC_CONTENTPROC_LAUNCH
|
||||
SetOtherProcessId(pid);
|
||||
#endif
|
||||
|
||||
#if defined(ANDROID) || defined(LINUX)
|
||||
// Check nice preference
|
||||
int32_t nice = Preferences::GetInt("dom.ipc.content.nice", 0);
|
||||
|
@ -1543,7 +1547,7 @@ ContentParent::OnChannelConnected(int32_t pid)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_CODE_COVERAGE
|
||||
#if defined(MOZ_CODE_COVERAGE) && defined(ASYNC_CONTENTPROC_LAUNCH)
|
||||
Unused << SendShareCodeCoverageMutex(
|
||||
CodeCoverageHandler::Get()->GetMutexHandle(pid));
|
||||
#endif
|
||||
|
@ -2059,13 +2063,27 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR
|
|||
extraArgs.push_back(parentBuildID.get());
|
||||
|
||||
SetOtherProcessId(kInvalidProcessId, ProcessIdState::ePending);
|
||||
#ifdef ASYNC_CONTENTPROC_LAUNCH
|
||||
if (!mSubprocess->Launch(extraArgs)) {
|
||||
#else
|
||||
if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
|
||||
#endif
|
||||
NS_ERROR("failed to launch child in the parent");
|
||||
MarkAsDead();
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ASYNC_CONTENTPROC_LAUNCH
|
||||
OpenWithAsyncPid(mSubprocess->GetChannel());
|
||||
#else
|
||||
base::ProcessId procId =
|
||||
base::GetProcId(mSubprocess->GetChildProcessHandle());
|
||||
Open(mSubprocess->GetChannel(), procId);
|
||||
#ifdef MOZ_CODE_COVERAGE
|
||||
Unused << SendShareCodeCoverageMutex(
|
||||
CodeCoverageHandler::Get()->GetMutexHandle(procId));
|
||||
#endif
|
||||
#endif // ASYNC_CONTENTPROC_LAUNCH
|
||||
|
||||
InitInternal(aInitialPriority);
|
||||
|
||||
|
@ -2076,8 +2094,9 @@ ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PR
|
|||
// Set a reply timeout for CPOWs.
|
||||
SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));
|
||||
|
||||
// TODO: If OtherPid() is not called between mSubprocess->Launch() and this,
|
||||
// then we're not really measuring how long it took to spawn the process.
|
||||
// TODO: In ASYNC_CONTENTPROC_LAUNCH, if OtherPid() is not called between
|
||||
// mSubprocess->Launch() and this, then we're not really measuring how long it
|
||||
// took to spawn the process.
|
||||
Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_TIME_MS,
|
||||
static_cast<uint32_t>((TimeStamp::Now() - mLaunchTS)
|
||||
.ToMilliseconds()));
|
||||
|
|
|
@ -88,7 +88,10 @@ interface mozISpellCheckingEngine : nsISupports {
|
|||
void removeDirectory(in nsIFile dir);
|
||||
|
||||
/**
|
||||
* Add a dictionary with the given language code and file URI.
|
||||
* Add a dictionary with the given language code and source URI. The URI
|
||||
* must point to an affix file, with the ".aff" extension. The word list
|
||||
* file must be in the same directory, with the same base name, and the
|
||||
* ".dic" extension.
|
||||
*/
|
||||
void addDictionary(in AString lang, in nsIURI file);
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@ GPUProcessImpl::Init(int aArgc, char* aArgv[])
|
|||
mozilla::SandboxTarget::Instance()->StartSandbox();
|
||||
#endif
|
||||
char* parentBuildID = nullptr;
|
||||
for (int idx = aArgc; idx > 0; idx--) {
|
||||
if (!strcmp(aArgv[idx], "-parentBuildID")) {
|
||||
parentBuildID = aArgv[idx + 1];
|
||||
for (int i = 1; i < aArgc; i++) {
|
||||
if (strcmp(aArgv[i], "-parentBuildID") == 0) {
|
||||
parentBuildID = aArgv[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3364,7 +3364,7 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
|
|||
const char16_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
uint8_t aMatchType,
|
||||
gfxTextRange::MatchType aMatchType,
|
||||
gfx::ShapedTextFlags aOrientation,
|
||||
Script aScript,
|
||||
bool aSyntheticLower,
|
||||
|
@ -3539,7 +3539,7 @@ gfxFont::InitFakeSmallCapsRun(DrawTarget *aDrawTarget,
|
|||
const uint8_t *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
uint8_t aMatchType,
|
||||
gfxTextRange::MatchType aMatchType,
|
||||
gfx::ShapedTextFlags aOrientation,
|
||||
Script aScript,
|
||||
bool aSyntheticLower,
|
||||
|
|
|
@ -631,14 +631,15 @@ protected:
|
|||
};
|
||||
|
||||
struct gfxTextRange {
|
||||
enum {
|
||||
// flags for recording the kind of font-matching that was used
|
||||
kFontGroup = 0x0001,
|
||||
kPrefsFallback = 0x0002,
|
||||
kSystemFallback = 0x0004
|
||||
enum class MatchType : uint8_t {
|
||||
// Flags for recording the kind of font-matching that was used.
|
||||
// Note that multiple flags may be set on a single range.
|
||||
kFontGroup = 0x01,
|
||||
kPrefsFallback = 0x02,
|
||||
kSystemFallback = 0x04
|
||||
};
|
||||
gfxTextRange(uint32_t aStart, uint32_t aEnd,
|
||||
gfxFont* aFont, uint8_t aMatchType,
|
||||
gfxFont* aFont, MatchType aMatchType,
|
||||
mozilla::gfx::ShapedTextFlags aOrientation)
|
||||
: start(aStart),
|
||||
end(aEnd),
|
||||
|
@ -649,10 +650,12 @@ struct gfxTextRange {
|
|||
uint32_t Length() const { return end - start; }
|
||||
uint32_t start, end;
|
||||
RefPtr<gfxFont> font;
|
||||
uint8_t matchType;
|
||||
MatchType matchType;
|
||||
mozilla::gfx::ShapedTextFlags orientation;
|
||||
};
|
||||
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(gfxTextRange::MatchType)
|
||||
|
||||
/**
|
||||
* gfxFontShaper
|
||||
*
|
||||
|
@ -1865,7 +1868,7 @@ public:
|
|||
const T *aText,
|
||||
uint32_t aOffset,
|
||||
uint32_t aLength,
|
||||
uint8_t aMatchType,
|
||||
gfxTextRange::MatchType aMatchType,
|
||||
mozilla::gfx::ShapedTextFlags aOrientation,
|
||||
Script aScript,
|
||||
bool aSyntheticLower,
|
||||
|
|
|
@ -1276,7 +1276,7 @@ gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const
|
|||
}
|
||||
|
||||
nsresult
|
||||
gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
|
||||
gfxTextRun::AddGlyphRun(gfxFont *aFont, gfxTextRange::MatchType aMatchType,
|
||||
uint32_t aUTF16Offset, bool aForceNewRun,
|
||||
gfx::ShapedTextFlags aOrientation)
|
||||
{
|
||||
|
@ -1608,8 +1608,8 @@ gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
|
|||
roundingFlags,
|
||||
nullptr);
|
||||
if (sw) {
|
||||
AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
|
||||
aOrientation);
|
||||
AddGlyphRun(aFont, gfxTextRange::MatchType::kFontGroup, aCharIndex,
|
||||
false, aOrientation);
|
||||
CopyGlyphDataFrom(sw, aCharIndex);
|
||||
}
|
||||
}
|
||||
|
@ -1634,7 +1634,7 @@ gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
|
|||
return false;
|
||||
}
|
||||
|
||||
AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
|
||||
AddGlyphRun(aFont, gfxTextRange::MatchType::kFontGroup, aCharIndex, false,
|
||||
aOrientation);
|
||||
CompressedGlyph g =
|
||||
CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
|
||||
|
@ -2186,8 +2186,8 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams,
|
|||
// Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
|
||||
// them, and always create at least size 1 fonts, i.e. they still
|
||||
// render something for size 0 fonts.
|
||||
textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false,
|
||||
orientation);
|
||||
textRun->AddGlyphRun(font, gfxTextRange::MatchType::kFontGroup, 0,
|
||||
false, orientation);
|
||||
}
|
||||
else {
|
||||
if (font->GetSpaceGlyph()) {
|
||||
|
@ -2197,7 +2197,7 @@ gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams,
|
|||
} else {
|
||||
// In case the primary font doesn't have <space> (bug 970891),
|
||||
// find one that does.
|
||||
uint8_t matchType;
|
||||
gfxTextRange::MatchType matchType;
|
||||
gfxFont* spaceFont =
|
||||
FindFontForChar(' ', 0, 0, Script::LATIN, nullptr,
|
||||
&matchType);
|
||||
|
@ -2230,7 +2230,8 @@ gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
|
|||
if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
|
||||
orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
|
||||
}
|
||||
textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
|
||||
textRun->AddGlyphRun(GetFirstValidFont(),
|
||||
gfxTextRange::MatchType::kFontGroup, 0, false,
|
||||
orientation);
|
||||
return textRun.forget();
|
||||
}
|
||||
|
@ -2664,7 +2665,8 @@ gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
|
||||
aTextRun->AddGlyphRun(mainFont,
|
||||
gfxTextRange::MatchType::kFontGroup,
|
||||
aOffset + runStart, (matchedLength > 0),
|
||||
range.orientation);
|
||||
}
|
||||
|
@ -2846,7 +2848,7 @@ gfxFontGroup::GetUnderlineOffset()
|
|||
gfxFont*
|
||||
gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
||||
Script aRunScript, gfxFont *aPrevMatchedFont,
|
||||
uint8_t *aMatchType)
|
||||
gfxTextRange::MatchType* aMatchType)
|
||||
{
|
||||
// If the char is a cluster extender, we want to use the same font as the
|
||||
// preceding character if possible. This is preferable to using the font
|
||||
|
@ -2887,7 +2889,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
gfxFont* firstFont = GetFontAt(0, aCh);
|
||||
if (firstFont) {
|
||||
if (firstFont->HasCharacter(aCh)) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return firstFont;
|
||||
}
|
||||
|
||||
|
@ -2902,7 +2904,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
|
||||
}
|
||||
if (font) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
@ -2980,7 +2982,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
if (pfe && pfe->HasCharacter(aCh)) {
|
||||
font = GetFontAt(i, aCh);
|
||||
if (font) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
@ -2989,7 +2991,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
// build the font via GetFontAt
|
||||
font = GetFontAt(i, aCh);
|
||||
if (font) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
@ -3002,7 +3004,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
"should only do fallback once per font family");
|
||||
font = FindFallbackFaceForChar(ff.Family(), aCh);
|
||||
if (font) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return font;
|
||||
}
|
||||
} else {
|
||||
|
@ -3013,7 +3015,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
if (!fe->mIsUserFontContainer && !fe->IsUserFont()) {
|
||||
font = FindFallbackFaceForChar(ff.Family(), aCh);
|
||||
if (font) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
@ -3023,7 +3025,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
if (fontListLength == 0) {
|
||||
gfxFont* defaultFont = GetDefaultFont();
|
||||
if (defaultFont->HasCharacter(aCh)) {
|
||||
*aMatchType = gfxTextRange::kFontGroup;
|
||||
*aMatchType = gfxTextRange::MatchType::kFontGroup;
|
||||
return defaultFont;
|
||||
}
|
||||
}
|
||||
|
@ -3035,14 +3037,14 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
// 2. search pref fonts
|
||||
gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh);
|
||||
if (font) {
|
||||
*aMatchType = gfxTextRange::kPrefsFallback;
|
||||
*aMatchType = gfxTextRange::MatchType::kPrefsFallback;
|
||||
return font;
|
||||
}
|
||||
|
||||
// 3. use fallback fonts
|
||||
// -- before searching for something else check the font used for the previous character
|
||||
if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
|
||||
*aMatchType = gfxTextRange::kSystemFallback;
|
||||
*aMatchType = gfxTextRange::MatchType::kSystemFallback;
|
||||
return aPrevMatchedFont;
|
||||
}
|
||||
|
||||
|
@ -3056,7 +3058,7 @@ gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
|
|||
}
|
||||
|
||||
// -- otherwise look for other stuff
|
||||
*aMatchType = gfxTextRange::kSystemFallback;
|
||||
*aMatchType = gfxTextRange::MatchType::kSystemFallback;
|
||||
return WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
|
||||
}
|
||||
|
||||
|
@ -3086,7 +3088,7 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
|
||||
// if we use the initial value of prevFont, we treat this as a match from
|
||||
// the font group; fixes bug 978313
|
||||
uint8_t matchType = gfxTextRange::kFontGroup;
|
||||
gfxTextRange::MatchType matchType = gfxTextRange::MatchType::kFontGroup;
|
||||
|
||||
for (uint32_t i = 0; i < aLength; i++) {
|
||||
|
||||
|
@ -3136,7 +3138,7 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
&& !gfxFontUtils::IsJoinControl(ch)
|
||||
&& !gfxFontUtils::IsJoinCauser(prevCh)
|
||||
&& !gfxFontUtils::IsVarSelector(ch)))) {
|
||||
matchType = gfxTextRange::kFontGroup;
|
||||
matchType = gfxTextRange::MatchType::kFontGroup;
|
||||
} else {
|
||||
font = FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont,
|
||||
&matchType);
|
||||
|
@ -3144,9 +3146,9 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
|
||||
#ifndef RELEASE_OR_BETA
|
||||
if (MOZ_UNLIKELY(mTextPerf)) {
|
||||
if (matchType == gfxTextRange::kPrefsFallback) {
|
||||
if (matchType == gfxTextRange::MatchType::kPrefsFallback) {
|
||||
mTextPerf->current.fallbackPrefs++;
|
||||
} else if (matchType == gfxTextRange::kSystemFallback) {
|
||||
} else if (matchType == gfxTextRange::MatchType::kSystemFallback) {
|
||||
mTextPerf->current.fallbackSystem++;
|
||||
}
|
||||
}
|
||||
|
@ -3194,7 +3196,7 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
// if font or orientation has changed, make a new range...
|
||||
// unless ch is a variation selector (bug 1248248)
|
||||
gfxTextRange& prevRange = aRanges[lastRangeIndex];
|
||||
if (prevRange.font != font || prevRange.matchType != matchType ||
|
||||
if (prevRange.font != font ||
|
||||
(prevRange.orientation != orient && !IsClusterExtender(ch))) {
|
||||
// close out the previous range
|
||||
prevRange.end = origI;
|
||||
|
@ -3210,6 +3212,8 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
{
|
||||
prevFont = font;
|
||||
}
|
||||
} else {
|
||||
prevRange.matchType |= matchType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3231,13 +3235,26 @@ void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
|
|||
nsAutoCString fontMatches;
|
||||
for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
|
||||
const gfxTextRange& r = aRanges[i];
|
||||
nsAutoCString matchTypes;
|
||||
if (r.matchType & gfxTextRange::MatchType::kFontGroup) {
|
||||
matchTypes.AppendLiteral("list");
|
||||
}
|
||||
if (r.matchType & gfxTextRange::MatchType::kPrefsFallback) {
|
||||
if (!matchTypes.IsEmpty()) {
|
||||
matchTypes.AppendLiteral(",");
|
||||
}
|
||||
matchTypes.AppendLiteral("prefs");
|
||||
}
|
||||
if (r.matchType & gfxTextRange::MatchType::kPrefsFallback) {
|
||||
if (!matchTypes.IsEmpty()) {
|
||||
matchTypes.AppendLiteral(",");
|
||||
}
|
||||
matchTypes.AppendLiteral("sys");
|
||||
}
|
||||
fontMatches.AppendPrintf(" [%u:%u] %.200s (%s)", r.start, r.end,
|
||||
(r.font.get() ?
|
||||
NS_ConvertUTF16toUTF8(r.font->GetName()).get() : "<null>"),
|
||||
(r.matchType == gfxTextRange::kFontGroup ?
|
||||
"list" :
|
||||
(r.matchType == gfxTextRange::kPrefsFallback) ?
|
||||
"prefs" : "sys"));
|
||||
matchTypes.get());
|
||||
}
|
||||
MOZ_LOG(log, LogLevel::Debug,\
|
||||
("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
|
||||
|
|
|
@ -469,7 +469,7 @@ public:
|
|||
RefPtr<gfxFont> mFont; // never null in a valid GlyphRun
|
||||
uint32_t mCharacterOffset; // into original UTF16 string
|
||||
mozilla::gfx::ShapedTextFlags mOrientation; // gfxTextRunFactory::TEXT_ORIENT_* value
|
||||
uint8_t mMatchType;
|
||||
gfxTextRange::MatchType mMatchType;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS GlyphRunIterator {
|
||||
|
@ -527,7 +527,7 @@ public:
|
|||
* are added before any further operations are performed with this
|
||||
* TextRun.
|
||||
*/
|
||||
nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
|
||||
nsresult AddGlyphRun(gfxFont *aFont, gfxTextRange::MatchType aMatchType,
|
||||
uint32_t aStartCharIndex, bool aForceNewRun,
|
||||
mozilla::gfx::ShapedTextFlags aOrientation);
|
||||
void ResetGlyphRuns()
|
||||
|
@ -957,7 +957,7 @@ public:
|
|||
|
||||
gfxFont* FindFontForChar(uint32_t ch, uint32_t prevCh, uint32_t aNextCh,
|
||||
Script aRunScript, gfxFont *aPrevMatchedFont,
|
||||
uint8_t *aMatchType);
|
||||
gfxTextRange::MatchType *aMatchType);
|
||||
|
||||
gfxUserFontSet* GetUserFontSet();
|
||||
|
||||
|
|
|
@ -555,13 +555,17 @@ GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts
|
|||
MonitorAutoLock lock(mMonitor);
|
||||
mProcessState = PROCESS_ERROR;
|
||||
lock.Notify();
|
||||
#ifdef ASYNC_CONTENTPROC_LAUNCH
|
||||
OnProcessLaunchError();
|
||||
#endif
|
||||
CHROMIUM_LOG(ERROR) << "Failed to launch " <<
|
||||
XRE_ChildProcessTypeToString(mProcessType) << " subprocess";
|
||||
Telemetry::Accumulate(Telemetry::SUBPROCESS_LAUNCH_FAILURE,
|
||||
nsDependentCString(XRE_ChildProcessTypeToString(mProcessType)));
|
||||
#ifdef ASYNC_CONTENTPROC_LAUNCH
|
||||
} else {
|
||||
OnProcessHandleReady(mChildProcessHandle);
|
||||
#endif
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -27,20 +27,29 @@
|
|||
#include "js/TypeDecls.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
struct jsid
|
||||
{
|
||||
size_t asBits;
|
||||
bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
|
||||
bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
|
||||
} JS_HAZ_GC_POINTER;
|
||||
#define JSID_BITS(id) (id.asBits)
|
||||
|
||||
#define JSID_TYPE_STRING 0x0
|
||||
#define JSID_TYPE_INT 0x1
|
||||
#define JSID_TYPE_VOID 0x2
|
||||
#define JSID_TYPE_SYMBOL 0x4
|
||||
#define JSID_TYPE_MASK 0x7
|
||||
|
||||
struct jsid
|
||||
{
|
||||
size_t asBits;
|
||||
|
||||
constexpr jsid() : asBits(JSID_TYPE_VOID) {}
|
||||
|
||||
static constexpr jsid fromRawBits(size_t bits) {
|
||||
jsid id;
|
||||
id.asBits = bits;
|
||||
return id;
|
||||
}
|
||||
|
||||
bool operator==(const jsid& rhs) const { return asBits == rhs.asBits; }
|
||||
bool operator!=(const jsid& rhs) const { return asBits != rhs.asBits; }
|
||||
} JS_HAZ_GC_POINTER;
|
||||
#define JSID_BITS(id) (id.asBits)
|
||||
|
||||
// Avoid using canonical 'id' for jsid parameters since this is a magic word in
|
||||
// Objective-C++ which, apparently, wants to be able to #include jsapi.h.
|
||||
#define id iden
|
||||
|
@ -156,7 +165,7 @@ JSID_IS_EMPTY(const jsid id)
|
|||
return (size_t)JSID_BITS(id) == JSID_TYPE_SYMBOL;
|
||||
}
|
||||
|
||||
constexpr const jsid JSID_VOID = { size_t(JSID_TYPE_VOID) };
|
||||
constexpr const jsid JSID_VOID;
|
||||
extern JS_PUBLIC_DATA(const jsid) JSID_EMPTY;
|
||||
|
||||
extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
/* JS reflection package. */
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
@ -3471,10 +3472,16 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
|
|||
UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
|
||||
mozilla::Nothing()));
|
||||
if (!sourceObject)
|
||||
return false;
|
||||
|
||||
Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options,
|
||||
chars.begin().get(), chars.length(),
|
||||
/* foldConstants = */ false, usedNames, nullptr,
|
||||
nullptr);
|
||||
nullptr, sourceObject);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -223,14 +223,15 @@ BytecodeCompiler::createParser()
|
|||
|
||||
if (canLazilyParse()) {
|
||||
syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
|
||||
/* foldConstants = */ false, *usedNames, nullptr, nullptr);
|
||||
|
||||
/* foldConstants = */ false, *usedNames, nullptr, nullptr,
|
||||
sourceObject);
|
||||
if (!syntaxParser->checkOptions())
|
||||
return false;
|
||||
}
|
||||
|
||||
parser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
|
||||
/* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr);
|
||||
/* foldConstants = */ true, *usedNames, syntaxParser.ptrOr(nullptr), nullptr,
|
||||
sourceObject);
|
||||
parser->ss = scriptSource;
|
||||
return parser->checkOptions();
|
||||
}
|
||||
|
@ -741,6 +742,8 @@ bool
|
|||
frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
|
||||
{
|
||||
MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
|
||||
// We can't be running this script unless we've run its parent.
|
||||
MOZ_ASSERT(!lazy->isEnclosingScriptLazy());
|
||||
|
||||
AutoAssertReportedException assertException(cx);
|
||||
|
||||
|
@ -773,9 +776,11 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
|
||||
Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
|
||||
/* foldConstants = */ true, usedNames, nullptr,
|
||||
lazy);
|
||||
lazy, sourceObject);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
@ -786,9 +791,6 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
|
|||
if (!pn)
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, lazy->sourceObject());
|
||||
MOZ_ASSERT(sourceObject);
|
||||
|
||||
Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
|
||||
lazy->sourceStart(), lazy->sourceEnd(),
|
||||
lazy->toStringStart(), lazy->toStringEnd()));
|
||||
|
|
|
@ -7943,8 +7943,7 @@ BytecodeEmitter::emitFunction(ParseNode* pn, bool needsProto)
|
|||
// fact. If we attempt to compile the outer script again, the
|
||||
// static scope chain will be newly allocated and will mismatch
|
||||
// the previously compiled LazyScript's.
|
||||
ScriptSourceObject* source = &script->sourceObject()->as<ScriptSourceObject>();
|
||||
fun->lazyScript()->setEnclosingScopeAndSource(innermostScope(), source);
|
||||
fun->lazyScript()->setEnclosingScope(innermostScope());
|
||||
if (emittingRunOnceLambda)
|
||||
fun->lazyScript()->setTreatAsRunOnce();
|
||||
} else {
|
||||
|
|
|
@ -797,7 +797,8 @@ ParserBase::errorNoOffset(unsigned errorNumber, ...)
|
|||
ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
|
||||
const ReadOnlyCompileOptions& options,
|
||||
bool foldConstants,
|
||||
UsedNameTracker& usedNames)
|
||||
UsedNameTracker& usedNames,
|
||||
ScriptSourceObject* sourceObject)
|
||||
: AutoGCRooter(cx, PARSER),
|
||||
context(cx),
|
||||
alloc(alloc),
|
||||
|
@ -806,6 +807,7 @@ ParserBase::ParserBase(JSContext* cx, LifoAlloc& alloc,
|
|||
pc(nullptr),
|
||||
usedNames(usedNames),
|
||||
ss(nullptr),
|
||||
sourceObject(cx, sourceObject),
|
||||
keepAtoms(cx),
|
||||
foldConstants(foldConstants),
|
||||
#ifdef DEBUG
|
||||
|
@ -848,8 +850,9 @@ template <class ParseHandler>
|
|||
PerHandlerParser<ParseHandler>::PerHandlerParser(JSContext* cx, LifoAlloc& alloc,
|
||||
const ReadOnlyCompileOptions& options,
|
||||
bool foldConstants, UsedNameTracker& usedNames,
|
||||
LazyScript* lazyOuterFunction)
|
||||
: ParserBase(cx, alloc, options, foldConstants, usedNames),
|
||||
LazyScript* lazyOuterFunction,
|
||||
ScriptSourceObject* sourceObject)
|
||||
: ParserBase(cx, alloc, options, foldConstants, usedNames, sourceObject),
|
||||
handler(cx, alloc, lazyOuterFunction)
|
||||
{
|
||||
|
||||
|
@ -862,8 +865,9 @@ GeneralParser<ParseHandler, CharT>::GeneralParser(JSContext* cx, LifoAlloc& allo
|
|||
bool foldConstants,
|
||||
UsedNameTracker& usedNames,
|
||||
SyntaxParser* syntaxParser,
|
||||
LazyScript* lazyOuterFunction)
|
||||
: Base(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction),
|
||||
LazyScript* lazyOuterFunction,
|
||||
ScriptSourceObject* sourceObject)
|
||||
: Base(cx, alloc, options, foldConstants, usedNames, lazyOuterFunction, sourceObject),
|
||||
tokenStream(cx, options, chars, length)
|
||||
{
|
||||
// The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
|
||||
|
@ -2534,7 +2538,8 @@ PerHandlerParser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /
|
|||
|
||||
FunctionBox* funbox = pc->functionBox();
|
||||
RootedFunction fun(context, funbox->function());
|
||||
LazyScript* lazy = LazyScript::Create(context, fun, pc->closedOverBindingsForLazy(),
|
||||
LazyScript* lazy = LazyScript::Create(context, fun, sourceObject,
|
||||
pc->closedOverBindingsForLazy(),
|
||||
pc->innerFunctionsForLazy,
|
||||
funbox->bufStart, funbox->bufEnd,
|
||||
funbox->toStringStart,
|
||||
|
|
|
@ -247,7 +247,7 @@ enum AwaitHandling : uint8_t { AwaitIsName, AwaitIsKeyword, AwaitIsModuleKeyword
|
|||
template <class ParseHandler, typename CharT>
|
||||
class AutoAwaitIsKeyword;
|
||||
|
||||
class ParserBase
|
||||
class MOZ_STACK_CLASS ParserBase
|
||||
: public StrictModeGetter,
|
||||
private JS::AutoGCRooter
|
||||
{
|
||||
|
@ -276,6 +276,8 @@ class ParserBase
|
|||
|
||||
ScriptSource* ss;
|
||||
|
||||
RootedScriptSourceObject sourceObject;
|
||||
|
||||
/* Root atoms and objects allocated for the parsed tree. */
|
||||
AutoKeepAtoms keepAtoms;
|
||||
|
||||
|
@ -301,7 +303,8 @@ class ParserBase
|
|||
template<class, typename> friend class AutoAwaitIsKeyword;
|
||||
|
||||
ParserBase(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
||||
bool foldConstants, UsedNameTracker& usedNames);
|
||||
bool foldConstants, UsedNameTracker& usedNames,
|
||||
ScriptSourceObject* sourceObject);
|
||||
~ParserBase();
|
||||
|
||||
bool checkOptions();
|
||||
|
@ -433,7 +436,7 @@ enum FunctionCallBehavior {
|
|||
};
|
||||
|
||||
template <class ParseHandler>
|
||||
class PerHandlerParser
|
||||
class MOZ_STACK_CLASS PerHandlerParser
|
||||
: public ParserBase
|
||||
{
|
||||
private:
|
||||
|
@ -465,7 +468,8 @@ class PerHandlerParser
|
|||
protected:
|
||||
PerHandlerParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
||||
bool foldConstants, UsedNameTracker& usedNames,
|
||||
LazyScript* lazyOuterFunction);
|
||||
LazyScript* lazyOuterFunction,
|
||||
ScriptSourceObject* sourceObject);
|
||||
|
||||
static Node null() { return ParseHandler::null(); }
|
||||
|
||||
|
@ -636,7 +640,7 @@ template <class ParseHandler, typename CharT>
|
|||
class Parser;
|
||||
|
||||
template <class ParseHandler, typename CharT>
|
||||
class GeneralParser
|
||||
class MOZ_STACK_CLASS GeneralParser
|
||||
: public PerHandlerParser<ParseHandler>
|
||||
{
|
||||
public:
|
||||
|
@ -867,7 +871,8 @@ class GeneralParser
|
|||
GeneralParser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
|
||||
const CharT* chars, size_t length, bool foldConstants,
|
||||
UsedNameTracker& usedNames, SyntaxParser* syntaxParser,
|
||||
LazyScript* lazyOuterFunction);
|
||||
LazyScript* lazyOuterFunction,
|
||||
ScriptSourceObject* sourceObject);
|
||||
|
||||
inline void setAwaitHandling(AwaitHandling awaitHandling);
|
||||
|
||||
|
@ -1248,7 +1253,7 @@ class GeneralParser
|
|||
};
|
||||
|
||||
template <typename CharT>
|
||||
class Parser<SyntaxParseHandler, CharT> final
|
||||
class MOZ_STACK_CLASS Parser<SyntaxParseHandler, CharT> final
|
||||
: public GeneralParser<SyntaxParseHandler, CharT>
|
||||
{
|
||||
using Base = GeneralParser<SyntaxParseHandler, CharT>;
|
||||
|
@ -1358,7 +1363,7 @@ class Parser<SyntaxParseHandler, CharT> final
|
|||
};
|
||||
|
||||
template <typename CharT>
|
||||
class Parser<FullParseHandler, CharT> final
|
||||
class MOZ_STACK_CLASS Parser<FullParseHandler, CharT> final
|
||||
: public GeneralParser<FullParseHandler, CharT>
|
||||
{
|
||||
using Base = GeneralParser<FullParseHandler, CharT>;
|
||||
|
|
|
@ -96,9 +96,9 @@ const I64RemUCode = 0x82;
|
|||
|
||||
const FirstInvalidOpcode = wasmThreadsSupported() ? 0xc5 : 0xc0;
|
||||
const LastInvalidOpcode = 0xfb;
|
||||
const NumericPrefix = 0xfc;
|
||||
const MiscPrefix = 0xfc;
|
||||
const SimdPrefix = 0xfd;
|
||||
const AtomicPrefix = 0xfe;
|
||||
const ThreadPrefix = 0xfe;
|
||||
const MozPrefix = 0xff;
|
||||
|
||||
// DefinitionKind
|
||||
|
|
|
@ -480,7 +480,7 @@ function checkIllegalPrefixed(prefix, opcode) {
|
|||
assertEq(WebAssembly.validate(binary), false);
|
||||
}
|
||||
|
||||
// Illegal AtomicPrefix opcodes
|
||||
// Illegal ThreadPrefix opcodes
|
||||
//
|
||||
// June 2017 threads draft:
|
||||
//
|
||||
|
@ -488,19 +488,25 @@ function checkIllegalPrefixed(prefix, opcode) {
|
|||
// 0x10 .. 0x4f are primitive atomic ops
|
||||
|
||||
for (let i = 3; i < 0x10; i++)
|
||||
checkIllegalPrefixed(AtomicPrefix, i);
|
||||
checkIllegalPrefixed(ThreadPrefix, i);
|
||||
|
||||
for (let i = 0x4f; i < 0x100; i++)
|
||||
checkIllegalPrefixed(AtomicPrefix, i);
|
||||
checkIllegalPrefixed(ThreadPrefix, i);
|
||||
|
||||
// Illegal Numeric opcodes
|
||||
//
|
||||
// Feb 2018 numeric draft:
|
||||
//
|
||||
// 0x00 .. 0x07 are saturating truncation ops
|
||||
// 0x00 .. 0x07 are saturating truncation ops. 0x40 and 0x41 are
|
||||
// from the bulk memory proposal. 0x40/0x41 are unofficial values,
|
||||
// until such time as there is an official assignment for memory.copy/fill
|
||||
// subopcodes.
|
||||
|
||||
for (let i = 0x08; i < 256; i++)
|
||||
checkIllegalPrefixed(NumericPrefix, i);
|
||||
for (let i = 0; i < 256; i++) {
|
||||
if (i <= 0x07 || i == 0x40 || i == 0x41)
|
||||
continue;
|
||||
checkIllegalPrefixed(MiscPrefix, i);
|
||||
}
|
||||
|
||||
// Illegal SIMD opcodes (all of them, for now)
|
||||
for (let i = 0; i < 256; i++)
|
||||
|
@ -510,7 +516,7 @@ for (let i = 0; i < 256; i++)
|
|||
for (let i = 0; i < 256; i++)
|
||||
checkIllegalPrefixed(MozPrefix, i);
|
||||
|
||||
for (let prefix of [AtomicPrefix, NumericPrefix, SimdPrefix, MozPrefix]) {
|
||||
for (let prefix of [ThreadPrefix, MiscPrefix, SimdPrefix, MozPrefix]) {
|
||||
// Prefix without a subsequent opcode
|
||||
let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[prefix]})])]);
|
||||
assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
|
||||
|
|
|
@ -40,7 +40,7 @@ class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
|
|||
ICStubEngine engine_;
|
||||
#endif
|
||||
|
||||
uint32_t stubDataOffset_;
|
||||
|
||||
bool inStubFrame_;
|
||||
bool makesGCCalls_;
|
||||
|
||||
|
@ -57,11 +57,10 @@ class MOZ_RAII BaselineCacheIRCompiler : public CacheIRCompiler
|
|||
|
||||
BaselineCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, ICStubEngine engine,
|
||||
uint32_t stubDataOffset)
|
||||
: CacheIRCompiler(cx, writer, Mode::Baseline),
|
||||
: CacheIRCompiler(cx, writer, stubDataOffset, Mode::Baseline, StubFieldPolicy::Address),
|
||||
#ifdef DEBUG
|
||||
engine_(engine),
|
||||
#endif
|
||||
stubDataOffset_(stubDataOffset),
|
||||
inStubFrame_(false),
|
||||
makesGCCalls_(false)
|
||||
{}
|
||||
|
|
|
@ -924,7 +924,7 @@ CacheIRWriter::copyStubData(uint8_t* dest) const
|
|||
InitGCPtr<JSString*>(destWords, field.asWord());
|
||||
break;
|
||||
case StubField::Type::Id:
|
||||
InitGCPtr<jsid>(destWords, field.asWord());
|
||||
AsGCPtr<jsid>(destWords)->init(jsid::fromRawBits(field.asWord()));
|
||||
break;
|
||||
case StubField::Type::RawInt64:
|
||||
case StubField::Type::DOMExpandoGeneration:
|
||||
|
@ -2934,6 +2934,40 @@ CacheIRCompiler::emitCallObjectHasSparseElementResult()
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Move a constant value into register dest.
|
||||
*/
|
||||
void CacheIRCompiler::EmitLoadStubFieldConstant(StubFieldOffset val, Register dest) {
|
||||
MOZ_ASSERT(mode_ == Mode::Ion);
|
||||
switch (val.getStubFieldType()) {
|
||||
case StubField::Type::Shape:
|
||||
masm.movePtr(ImmGCPtr(shapeStubField(val.getOffset())),dest);
|
||||
break;
|
||||
case StubField::Type::String:
|
||||
masm.movePtr(ImmGCPtr(stringStubField(val.getOffset())), dest);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("Unhandled stub field constant type");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* After this is done executing, dest contains the value; either through a constant load
|
||||
* or through the load from the stub data.
|
||||
*
|
||||
* The current policy is that Baseline will use loads from the stub data (to allow IC
|
||||
* sharing), where as Ion doesn't share ICs, and so we can safely use constants in the
|
||||
* IC.
|
||||
*/
|
||||
void CacheIRCompiler::EmitLoadStubField(StubFieldOffset val, Register dest) {
|
||||
if (stubFieldPolicy_ == StubFieldPolicy::Constant) {
|
||||
EmitLoadStubFieldConstant(val, dest);
|
||||
} else {
|
||||
Address load(ICStubReg, stubDataOffset_ + val.getOffset());
|
||||
masm.loadPtr(load, dest);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CacheIRCompiler::emitLoadInstanceOfObjectResult()
|
||||
{
|
||||
|
|
|
@ -531,6 +531,24 @@ class FailurePath
|
|||
bool canShareFailurePath(const FailurePath& other) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrap an offset so that a call can decide to embed a constant
|
||||
* or load from the stub data.
|
||||
*/
|
||||
class StubFieldOffset {
|
||||
private:
|
||||
uint32_t offset_;
|
||||
StubField::Type type_;
|
||||
public:
|
||||
StubFieldOffset(uint32_t offset, StubField::Type type)
|
||||
: offset_(offset),
|
||||
type_(type)
|
||||
{ }
|
||||
|
||||
uint32_t getOffset() { return offset_; }
|
||||
StubField::Type getStubFieldType() { return type_; }
|
||||
};
|
||||
|
||||
class AutoOutputRegister;
|
||||
|
||||
// Base class for BaselineCacheIRCompiler and IonCacheIRCompiler.
|
||||
|
@ -561,13 +579,29 @@ class MOZ_RAII CacheIRCompiler
|
|||
// Whether this IC may read double values from uint32 arrays.
|
||||
mozilla::Maybe<bool> allowDoubleResult_;
|
||||
|
||||
CacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, Mode mode)
|
||||
// Distance from the IC to the stub data; mostly will be
|
||||
// sizeof(stubType)
|
||||
uint32_t stubDataOffset_;
|
||||
|
||||
uint32_t nextStubField_;
|
||||
|
||||
enum class StubFieldPolicy {
|
||||
Address,
|
||||
Constant
|
||||
};
|
||||
|
||||
StubFieldPolicy stubFieldPolicy_;
|
||||
|
||||
CacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, uint32_t stubDataOffset, Mode mode, StubFieldPolicy policy)
|
||||
: cx_(cx),
|
||||
reader(writer),
|
||||
writer_(writer),
|
||||
allocator(writer_),
|
||||
liveFloatRegs_(FloatRegisterSet::All()),
|
||||
mode_(mode)
|
||||
mode_(mode),
|
||||
stubDataOffset_(stubDataOffset),
|
||||
nextStubField_(0),
|
||||
stubFieldPolicy_(policy)
|
||||
{
|
||||
MOZ_ASSERT(!writer.failed());
|
||||
}
|
||||
|
@ -624,6 +658,63 @@ class MOZ_RAII CacheIRCompiler
|
|||
#define DEFINE_SHARED_OP(op) MOZ_MUST_USE bool emit##op();
|
||||
CACHE_IR_SHARED_OPS(DEFINE_SHARED_OP)
|
||||
#undef DEFINE_SHARED_OP
|
||||
|
||||
void EmitLoadStubField(StubFieldOffset val, Register dest);
|
||||
void EmitLoadStubFieldConstant(StubFieldOffset val, Register dest);
|
||||
|
||||
uintptr_t readStubWord(uint32_t offset, StubField::Type type) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
||||
// We use nextStubField_ to access the data as it's stored in an as-of-yet
|
||||
// unpacked vector, and so using the offset can be incorrect where the index
|
||||
// would change as a result of packing.
|
||||
return writer_.readStubFieldForIon(nextStubField_++, type).asWord();
|
||||
}
|
||||
uint64_t readStubInt64(uint32_t offset, StubField::Type type) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
||||
return writer_.readStubFieldForIon(nextStubField_++, type).asInt64();
|
||||
}
|
||||
int32_t int32StubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
Shape* shapeStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (Shape*)readStubWord(offset, StubField::Type::Shape);
|
||||
}
|
||||
JSObject* objectStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (JSObject*)readStubWord(offset, StubField::Type::JSObject);
|
||||
}
|
||||
JSString* stringStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (JSString*)readStubWord(offset, StubField::Type::String);
|
||||
}
|
||||
JS::Symbol* symbolStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (JS::Symbol*)readStubWord(offset, StubField::Type::Symbol);
|
||||
}
|
||||
ObjectGroup* groupStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (ObjectGroup*)readStubWord(offset, StubField::Type::ObjectGroup);
|
||||
}
|
||||
JSCompartment* compartmentStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (JSCompartment*)readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
const Class* classStubField(uintptr_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (const Class*)readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
const void* proxyHandlerStubField(uintptr_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return (const void*)readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
jsid idStubField(uint32_t offset) {
|
||||
MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
|
||||
return jsid::fromRawBits(readStubWord(offset, StubField::Type::Id));
|
||||
}
|
||||
};
|
||||
|
||||
// Ensures the IC's output register is available for writing.
|
||||
|
|
|
@ -36,14 +36,13 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler
|
|||
friend class AutoSaveLiveRegisters;
|
||||
|
||||
IonCacheIRCompiler(JSContext* cx, const CacheIRWriter& writer, IonIC* ic, IonScript* ionScript,
|
||||
IonICStub* stub, const PropertyTypeCheckInfo* typeCheckInfo)
|
||||
: CacheIRCompiler(cx, writer, Mode::Ion),
|
||||
IonICStub* stub, const PropertyTypeCheckInfo* typeCheckInfo, uint32_t stubDataOffset)
|
||||
: CacheIRCompiler(cx, writer, stubDataOffset, Mode::Ion, StubFieldPolicy::Constant),
|
||||
writer_(writer),
|
||||
ic_(ic),
|
||||
ionScript_(ionScript),
|
||||
stub_(stub),
|
||||
typeCheckInfo_(typeCheckInfo),
|
||||
nextStubField_(0),
|
||||
#ifdef DEBUG
|
||||
calledPrepareVMCall_(false),
|
||||
#endif
|
||||
|
@ -72,51 +71,13 @@ class MOZ_RAII IonCacheIRCompiler : public CacheIRCompiler
|
|||
Vector<CodeOffset, 4, SystemAllocPolicy> nextCodeOffsets_;
|
||||
Maybe<LiveRegisterSet> liveRegs_;
|
||||
Maybe<CodeOffset> stubJitCodeOffset_;
|
||||
uint32_t nextStubField_;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool calledPrepareVMCall_;
|
||||
#endif
|
||||
bool savedLiveRegs_;
|
||||
|
||||
uintptr_t readStubWord(uint32_t offset, StubField::Type type) {
|
||||
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
||||
return writer_.readStubFieldForIon(nextStubField_++, type).asWord();
|
||||
}
|
||||
uint64_t readStubInt64(uint32_t offset, StubField::Type type) {
|
||||
MOZ_ASSERT((offset % sizeof(uintptr_t)) == 0);
|
||||
return writer_.readStubFieldForIon(nextStubField_++, type).asInt64();
|
||||
}
|
||||
int32_t int32StubField(uint32_t offset) {
|
||||
return readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
Shape* shapeStubField(uint32_t offset) {
|
||||
return (Shape*)readStubWord(offset, StubField::Type::Shape);
|
||||
}
|
||||
JSObject* objectStubField(uint32_t offset) {
|
||||
return (JSObject*)readStubWord(offset, StubField::Type::JSObject);
|
||||
}
|
||||
JSString* stringStubField(uint32_t offset) {
|
||||
return (JSString*)readStubWord(offset, StubField::Type::String);
|
||||
}
|
||||
JS::Symbol* symbolStubField(uint32_t offset) {
|
||||
return (JS::Symbol*)readStubWord(offset, StubField::Type::Symbol);
|
||||
}
|
||||
ObjectGroup* groupStubField(uint32_t offset) {
|
||||
return (ObjectGroup*)readStubWord(offset, StubField::Type::ObjectGroup);
|
||||
}
|
||||
JSCompartment* compartmentStubField(uint32_t offset) {
|
||||
return (JSCompartment*)readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
const Class* classStubField(uintptr_t offset) {
|
||||
return (const Class*)readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
const void* proxyHandlerStubField(uintptr_t offset) {
|
||||
return (const void*)readStubWord(offset, StubField::Type::RawWord);
|
||||
}
|
||||
jsid idStubField(uint32_t offset) {
|
||||
return mozilla::BitwiseCast<jsid>(readStubWord(offset, StubField::Type::Id));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T rawWordStubField(uint32_t offset) {
|
||||
static_assert(sizeof(T) == sizeof(uintptr_t), "T must have word size");
|
||||
|
@ -599,7 +560,7 @@ IonCacheIRCompiler::compile()
|
|||
allocator.nextOp();
|
||||
} while (reader.more());
|
||||
|
||||
MOZ_ASSERT(nextStubField_ == writer_.numStubFields());
|
||||
MOZ_RELEASE_ASSERT(nextStubField_ == writer_.numStubFields());
|
||||
|
||||
masm.assumeUnreachable("Should have returned from IC");
|
||||
|
||||
|
@ -2559,7 +2520,7 @@ IonIC::attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind k
|
|||
writer.copyStubData(newStub->stubDataStart());
|
||||
|
||||
JitContext jctx(cx, nullptr);
|
||||
IonCacheIRCompiler compiler(cx, writer, this, ionScript, newStub, typeCheckInfo);
|
||||
IonCacheIRCompiler compiler(cx, writer, this, ionScript, newStub, typeCheckInfo, stubDataOffset);
|
||||
if (!compiler.init())
|
||||
return;
|
||||
|
||||
|
|
|
@ -19,8 +19,9 @@
|
|||
|
||||
#endif
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "frontend/BinSource.h"
|
||||
#include "frontend/FullParseHandler.h"
|
||||
|
@ -167,10 +168,16 @@ runTestFromPath(JSContext* cx, const char* path)
|
|||
UsedNameTracker txtUsedNames(cx);
|
||||
if (!txtUsedNames.init())
|
||||
MOZ_CRASH("Couldn't initialize used names");
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(
|
||||
cx, txtOptions, mozilla::Nothing()));
|
||||
if (!sourceObject)
|
||||
MOZ_CRASH("Couldn't initialize ScriptSourceObject");
|
||||
|
||||
js::frontend::Parser<js::frontend::FullParseHandler, char16_t> txtParser(
|
||||
cx, allocScope.alloc(), txtOptions, txtSource.begin(), txtSource.length(),
|
||||
/* foldConstants = */ false, txtUsedNames, nullptr,
|
||||
nullptr);
|
||||
nullptr, sourceObject);
|
||||
if (!txtParser.checkOptions())
|
||||
MOZ_CRASH("Bad options");
|
||||
|
||||
|
|
|
@ -4462,10 +4462,17 @@ JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, siz
|
|||
frontend::UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
|
||||
mozilla::Nothing()));
|
||||
if (!sourceObject)
|
||||
return false;
|
||||
|
||||
frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
|
||||
options, chars, length,
|
||||
/* foldConstants = */ true,
|
||||
usedNames, nullptr, nullptr);
|
||||
usedNames, nullptr, nullptr,
|
||||
sourceObject);
|
||||
JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
|
||||
if (!parser.checkOptions() || !parser.parse()) {
|
||||
// We ran into an error. If it was because we ran out of source, we
|
||||
|
|
|
@ -2688,12 +2688,16 @@ static size_t
|
|||
FormatTime(char* buf, int buflen, const char* fmt, double utcTime, double localTime)
|
||||
{
|
||||
PRMJTime prtm = ToPRMJTime(localTime, utcTime);
|
||||
int eqivalentYear = IsRepresentableAsTime32(utcTime)
|
||||
? prtm.tm_year
|
||||
: EquivalentYearForDST(prtm.tm_year);
|
||||
|
||||
// If an equivalent year was used to compute the date/time components, use
|
||||
// the same equivalent year to determine the time zone name and offset in
|
||||
// PRMJ_FormatTime(...).
|
||||
int timeZoneYear = IsRepresentableAsTime32(utcTime)
|
||||
? prtm.tm_year
|
||||
: EquivalentYearForDST(prtm.tm_year);
|
||||
int offsetInSeconds = (int) floor((localTime - utcTime) / msPerSecond);
|
||||
|
||||
return PRMJ_FormatTime(buf, buflen, fmt, &prtm, eqivalentYear, offsetInSeconds);
|
||||
return PRMJ_FormatTime(buf, buflen, fmt, &prtm, timeZoneYear, offsetInSeconds);
|
||||
}
|
||||
|
||||
enum class FormatSpec {
|
||||
|
|
|
@ -4486,9 +4486,15 @@ Parse(JSContext* cx, unsigned argc, Value* vp)
|
|||
UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
|
||||
Nothing()));
|
||||
if (!sourceObject)
|
||||
return false;
|
||||
|
||||
Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
|
||||
/* foldConstants = */ false, usedNames, nullptr,
|
||||
nullptr);
|
||||
nullptr, sourceObject);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
@ -4537,9 +4543,16 @@ SyntaxParse(JSContext* cx, unsigned argc, Value* vp)
|
|||
UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
|
||||
Nothing()));
|
||||
if (!sourceObject)
|
||||
return false;
|
||||
|
||||
Parser<frontend::SyntaxParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
|
||||
options, chars, length, false,
|
||||
usedNames, nullptr, nullptr);
|
||||
usedNames, nullptr, nullptr,
|
||||
sourceObject);
|
||||
if (!parser.checkOptions())
|
||||
return false;
|
||||
|
||||
|
|
|
@ -5050,11 +5050,18 @@ Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
|
|||
frontend::UsedNameTracker usedNames(cx);
|
||||
if (!usedNames.init())
|
||||
return false;
|
||||
|
||||
RootedScriptSourceObject sourceObject(cx, frontend::CreateScriptSourceObject(cx, options,
|
||||
Nothing()));
|
||||
if (!sourceObject)
|
||||
return false;
|
||||
|
||||
frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
|
||||
options, chars.twoByteChars(),
|
||||
length,
|
||||
/* foldConstants = */ true,
|
||||
usedNames, nullptr, nullptr);
|
||||
usedNames, nullptr, nullptr,
|
||||
sourceObject);
|
||||
JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
|
||||
if (!parser.checkOptions() || !parser.parse()) {
|
||||
// We ran into an error. If it was because we ran out of memory we report
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
#include "js/Id.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
const jsid JSID_EMPTY = { size_t(JSID_TYPE_SYMBOL) };
|
||||
const jsid JSID_EMPTY = jsid::fromRawBits(size_t(JSID_TYPE_SYMBOL));
|
||||
|
||||
static const jsid voidIdValue = JSID_VOID;
|
||||
static const jsid emptyIdValue = JSID_EMPTY;
|
||||
const JS::HandleId JSID_VOIDHANDLE = JS::HandleId::fromMarkedLocation(&voidIdValue);
|
||||
const JS::HandleId JSID_EMPTYHANDLE = JS::HandleId::fromMarkedLocation(&emptyIdValue);
|
||||
|
||||
|
|
|
@ -1090,10 +1090,10 @@ static bool
|
|||
AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, AllocKind kind)
|
||||
{
|
||||
// Find all live root lazy functions in the compartment: those which have a
|
||||
// source object, indicating that they have a parent, and which do not have
|
||||
// an uncompiled enclosing script. The last condition is so that we don't
|
||||
// compile lazy scripts whose enclosing scripts failed to compile,
|
||||
// indicating that the lazy script did not escape the script.
|
||||
// non-lazy enclosing script, and which do not have an uncompiled enclosing
|
||||
// script. The last condition is so that we don't compile lazy scripts
|
||||
// whose enclosing scripts failed to compile, indicating that the lazy
|
||||
// script did not escape the script.
|
||||
//
|
||||
// Some LazyScripts have a non-null |JSScript* script| pointer. We still
|
||||
// want to delazify in that case: this pointer is weak so the JSScript
|
||||
|
@ -1113,7 +1113,7 @@ AddLazyFunctionsForCompartment(JSContext* cx, AutoObjectVector& lazyFunctions, A
|
|||
|
||||
if (fun->isInterpretedLazy()) {
|
||||
LazyScript* lazy = fun->lazyScriptOrNull();
|
||||
if (lazy && lazy->sourceObject() && !lazy->hasUncompiledEnclosingScript()) {
|
||||
if (lazy && !lazy->isEnclosingScriptLazy() && !lazy->hasUncompletedEnclosingScript()) {
|
||||
if (!lazyFunctions.append(fun))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -792,7 +792,7 @@ JSFunction::trace(JSTracer* trc)
|
|||
// Functions can be be marked as interpreted despite having no script
|
||||
// yet at some points when parsing, and can be lazy with no lazy script
|
||||
// for self-hosted code.
|
||||
if (hasScript() && !hasUncompiledScript())
|
||||
if (hasScript() && !hasUncompletedScript())
|
||||
TraceManuallyBarrieredEdge(trc, &u.scripted.s.script_, "script");
|
||||
else if (isInterpretedLazy() && u.scripted.s.lazy_)
|
||||
TraceManuallyBarrieredEdge(trc, &u.scripted.s.lazy_, "lazyScript");
|
||||
|
@ -1655,7 +1655,7 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
|
|||
|
||||
// XDR the newly delazified function.
|
||||
if (script->scriptSource()->hasEncoder()) {
|
||||
RootedScriptSourceObject sourceObject(cx, lazy->sourceObject());
|
||||
RootedScriptSourceObject sourceObject(cx, &lazy->sourceObject());
|
||||
if (!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -564,13 +564,13 @@ class JSFunction : public js::NativeObject
|
|||
// The state of a JSFunction whose script errored out during bytecode
|
||||
// compilation. Such JSFunctions are only reachable via GC iteration and
|
||||
// not from script.
|
||||
bool hasUncompiledScript() const {
|
||||
bool hasUncompletedScript() const {
|
||||
MOZ_ASSERT(hasScript());
|
||||
return !u.scripted.s.script_;
|
||||
}
|
||||
|
||||
JSScript* nonLazyScript() const {
|
||||
MOZ_ASSERT(!hasUncompiledScript());
|
||||
MOZ_ASSERT(!hasUncompletedScript());
|
||||
return u.scripted.s.script_;
|
||||
}
|
||||
|
||||
|
|
|
@ -909,6 +909,8 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
HandleScriptSourceObject sourceObject, HandleFunction fun,
|
||||
MutableHandle<LazyScript*> lazy)
|
||||
{
|
||||
MOZ_ASSERT_IF(mode == XDR_DECODE, sourceObject);
|
||||
|
||||
JSContext* cx = xdr->cx();
|
||||
|
||||
{
|
||||
|
@ -967,7 +969,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
|
|||
if (mode == XDR_ENCODE)
|
||||
func = innerFunctions[i];
|
||||
|
||||
MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, nullptr, &func));
|
||||
MOZ_TRY(XDRInterpretedFunction(xdr, nullptr, sourceObject, &func));
|
||||
|
||||
if (mode == XDR_DECODE)
|
||||
innerFunctions[i] = func;
|
||||
|
@ -3655,7 +3657,7 @@ js::CloneScriptIntoFunction(JSContext* cx, HandleScope enclosingScope, HandleFun
|
|||
HandleScript src)
|
||||
{
|
||||
MOZ_ASSERT(fun->isInterpreted());
|
||||
MOZ_ASSERT(!fun->hasScript() || fun->hasUncompiledScript());
|
||||
MOZ_ASSERT(!fun->hasScript() || fun->hasUncompletedScript());
|
||||
|
||||
RootedScript dst(cx, CreateEmptyScriptForClone(cx, src));
|
||||
if (!dst)
|
||||
|
@ -4175,13 +4177,14 @@ JSScript::formalLivesInArgumentsObject(unsigned argSlot)
|
|||
return argsObjAliasesFormals() && !formalIsAliased(argSlot);
|
||||
}
|
||||
|
||||
LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
|
||||
LazyScript::LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
|
||||
void* table, uint64_t packedFields,
|
||||
uint32_t sourceStart, uint32_t sourceEnd,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column)
|
||||
: script_(nullptr),
|
||||
function_(fun),
|
||||
enclosingScope_(nullptr),
|
||||
sourceObject_(nullptr),
|
||||
sourceObject_(&sourceObject),
|
||||
table_(table),
|
||||
packedFields_(packedFields),
|
||||
sourceStart_(sourceStart),
|
||||
|
@ -4191,6 +4194,9 @@ LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
|
|||
lineno_(lineno),
|
||||
column_(column)
|
||||
{
|
||||
MOZ_ASSERT(function_);
|
||||
MOZ_ASSERT(sourceObject_);
|
||||
MOZ_ASSERT(function_->compartment() == sourceObject_->compartment());
|
||||
MOZ_ASSERT(sourceStart <= sourceEnd);
|
||||
MOZ_ASSERT(toStringStart <= sourceStart);
|
||||
}
|
||||
|
@ -4211,36 +4217,33 @@ LazyScript::resetScript()
|
|||
}
|
||||
|
||||
void
|
||||
LazyScript::setEnclosingScopeAndSource(Scope* enclosingScope, ScriptSourceObject* sourceObject)
|
||||
LazyScript::setEnclosingScope(Scope* enclosingScope)
|
||||
{
|
||||
MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
|
||||
// This method may be called to update the enclosing scope. See comment
|
||||
// above the callsite in BytecodeEmitter::emitFunction.
|
||||
MOZ_ASSERT_IF(sourceObject_, sourceObject_ == sourceObject && enclosingScope_);
|
||||
MOZ_ASSERT_IF(!sourceObject_, !enclosingScope_);
|
||||
|
||||
enclosingScope_ = enclosingScope;
|
||||
sourceObject_ = sourceObject;
|
||||
}
|
||||
|
||||
ScriptSourceObject*
|
||||
ScriptSourceObject&
|
||||
LazyScript::sourceObject() const
|
||||
{
|
||||
return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr;
|
||||
return sourceObject_->as<ScriptSourceObject>();
|
||||
}
|
||||
|
||||
ScriptSource*
|
||||
LazyScript::maybeForwardedScriptSource() const
|
||||
{
|
||||
JSObject* source = MaybeForwarded(sourceObject());
|
||||
JSObject* source = MaybeForwarded(&sourceObject());
|
||||
return UncheckedUnwrapWithoutExpose(source)->as<ScriptSourceObject>().source();
|
||||
}
|
||||
|
||||
/* static */ LazyScript*
|
||||
LazyScript::CreateRaw(JSContext* cx, HandleFunction fun,
|
||||
HandleScriptSourceObject sourceObject,
|
||||
uint64_t packedFields, uint32_t sourceStart, uint32_t sourceEnd,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column)
|
||||
{
|
||||
MOZ_ASSERT(sourceObject);
|
||||
union {
|
||||
PackedView p;
|
||||
uint64_t packed;
|
||||
|
@ -4267,12 +4270,13 @@ LazyScript::CreateRaw(JSContext* cx, HandleFunction fun,
|
|||
|
||||
cx->compartment()->scheduleDelazificationForDebugger();
|
||||
|
||||
return new (res) LazyScript(fun, table.forget(), packed, sourceStart, sourceEnd,
|
||||
return new (res) LazyScript(fun, *sourceObject, table.forget(), packed, sourceStart, sourceEnd,
|
||||
toStringStart, lineno, column);
|
||||
}
|
||||
|
||||
/* static */ LazyScript*
|
||||
LazyScript::Create(JSContext* cx, HandleFunction fun,
|
||||
HandleScriptSourceObject sourceObject,
|
||||
const frontend::AtomVector& closedOverBindings,
|
||||
Handle<GCVector<JSFunction*, 8>> innerFunctions,
|
||||
uint32_t sourceStart, uint32_t sourceEnd,
|
||||
|
@ -4298,7 +4302,8 @@ LazyScript::Create(JSContext* cx, HandleFunction fun,
|
|||
p.isDerivedClassConstructor = false;
|
||||
p.needsHomeObject = false;
|
||||
|
||||
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, sourceStart, sourceEnd,
|
||||
LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
|
||||
sourceStart, sourceEnd,
|
||||
toStringStart, lineno, column);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
@ -4328,7 +4333,8 @@ LazyScript::Create(JSContext* cx, HandleFunction fun,
|
|||
// holding this lazy script.
|
||||
HandleFunction dummyFun = fun;
|
||||
|
||||
LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, sourceStart, sourceEnd,
|
||||
LazyScript* res = LazyScript::CreateRaw(cx, fun, sourceObject, packedFields,
|
||||
sourceStart, sourceEnd,
|
||||
toStringStart, lineno, column);
|
||||
if (!res)
|
||||
return nullptr;
|
||||
|
@ -4344,15 +4350,13 @@ LazyScript::Create(JSContext* cx, HandleFunction fun,
|
|||
for (i = 0, num = res->numInnerFunctions(); i < num; i++)
|
||||
functions[i].init(dummyFun);
|
||||
|
||||
// Set the enclosing scope and source object of the lazy function. These
|
||||
// values should only be non-null if we have a non-lazy enclosing script.
|
||||
// AddLazyFunctionsForCompartment relies on the source object being null
|
||||
// if we're nested inside another lazy function.
|
||||
MOZ_ASSERT(!!sourceObject == !!enclosingScope);
|
||||
MOZ_ASSERT(!res->sourceObject());
|
||||
// Set the enclosing scope of the lazy function. This value should only be
|
||||
// non-null if we have a non-lazy enclosing script.
|
||||
// LazyScript::isEnclosingScriptLazy relies on the enclosing scope being
|
||||
// null if we're nested inside another lazy function.
|
||||
MOZ_ASSERT(!res->enclosingScope());
|
||||
if (sourceObject)
|
||||
res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
|
||||
if (enclosingScope)
|
||||
res->setEnclosingScope(enclosingScope);
|
||||
|
||||
MOZ_ASSERT(!res->hasScript());
|
||||
if (script)
|
||||
|
@ -4375,7 +4379,7 @@ LazyScript::initRuntimeFields(uint64_t packedFields)
|
|||
}
|
||||
|
||||
bool
|
||||
LazyScript::hasUncompiledEnclosingScript() const
|
||||
LazyScript::hasUncompletedEnclosingScript() const
|
||||
{
|
||||
// It can happen that we created lazy scripts while compiling an enclosing
|
||||
// script, but we errored out while compiling that script. When we iterate
|
||||
|
@ -4389,7 +4393,7 @@ LazyScript::hasUncompiledEnclosingScript() const
|
|||
return false;
|
||||
|
||||
JSFunction* fun = enclosingScope()->as<FunctionScope>().canonicalFunction();
|
||||
return !fun->hasScript() || fun->hasUncompiledScript() || !fun->nonLazyScript()->code();
|
||||
return !fun->hasScript() || fun->hasUncompletedScript() || !fun->nonLazyScript()->code();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -4502,11 +4506,7 @@ JS::ubi::Concrete<js::LazyScript>::size(mozilla::MallocSizeOf mallocSizeOf) cons
|
|||
const char*
|
||||
JS::ubi::Concrete<js::LazyScript>::scriptFilename() const
|
||||
{
|
||||
auto sourceObject = get().sourceObject();
|
||||
if (!sourceObject)
|
||||
return nullptr;
|
||||
|
||||
auto source = sourceObject->source();
|
||||
auto source = get().sourceObject().source();
|
||||
if (!source)
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -2159,7 +2159,8 @@ class LazyScript : public gc::TenuredCell
|
|||
uint32_t lineno_;
|
||||
uint32_t column_;
|
||||
|
||||
LazyScript(JSFunction* fun, void* table, uint64_t packedFields,
|
||||
LazyScript(JSFunction* fun, ScriptSourceObject& sourceObject,
|
||||
void* table, uint64_t packedFields,
|
||||
uint32_t begin, uint32_t end, uint32_t toStringStart,
|
||||
uint32_t lineno, uint32_t column);
|
||||
|
||||
|
@ -2167,6 +2168,7 @@ class LazyScript : public gc::TenuredCell
|
|||
// innerFunctions. To be GC-safe, the caller must initialize both vectors
|
||||
// with valid atoms and functions.
|
||||
static LazyScript* CreateRaw(JSContext* cx, HandleFunction fun,
|
||||
HandleScriptSourceObject sourceObject,
|
||||
uint64_t packedData, uint32_t begin, uint32_t end,
|
||||
uint32_t toStringStart, uint32_t lineno, uint32_t column);
|
||||
|
||||
|
@ -2177,6 +2179,7 @@ class LazyScript : public gc::TenuredCell
|
|||
// Create a LazyScript and initialize closedOverBindings and innerFunctions
|
||||
// with the provided vectors.
|
||||
static LazyScript* Create(JSContext* cx, HandleFunction fun,
|
||||
HandleScriptSourceObject sourceObject,
|
||||
const frontend::AtomVector& closedOverBindings,
|
||||
Handle<GCVector<JSFunction*, 8>> innerFunctions,
|
||||
uint32_t begin, uint32_t end,
|
||||
|
@ -2221,16 +2224,16 @@ class LazyScript : public gc::TenuredCell
|
|||
return enclosingScope_;
|
||||
}
|
||||
|
||||
ScriptSourceObject* sourceObject() const;
|
||||
ScriptSourceObject& sourceObject() const;
|
||||
ScriptSource* scriptSource() const {
|
||||
return sourceObject()->source();
|
||||
return sourceObject().source();
|
||||
}
|
||||
ScriptSource* maybeForwardedScriptSource() const;
|
||||
bool mutedErrors() const {
|
||||
return scriptSource()->mutedErrors();
|
||||
}
|
||||
|
||||
void setEnclosingScopeAndSource(Scope* enclosingScope, ScriptSourceObject* sourceObject);
|
||||
void setEnclosingScope(Scope* enclosingScope);
|
||||
|
||||
uint32_t numClosedOverBindings() const {
|
||||
return p_.numClosedOverBindings;
|
||||
|
@ -2382,7 +2385,14 @@ class LazyScript : public gc::TenuredCell
|
|||
toStringEnd_ = toStringEnd;
|
||||
}
|
||||
|
||||
bool hasUncompiledEnclosingScript() const;
|
||||
// Returns true if the enclosing script failed to compile.
|
||||
// See the comment in the definition for more details.
|
||||
bool hasUncompletedEnclosingScript() const;
|
||||
|
||||
// Returns true if the enclosing script is also lazy.
|
||||
bool isEnclosingScriptLazy() const {
|
||||
return !enclosingScope_;
|
||||
}
|
||||
|
||||
friend class GCMarker;
|
||||
void traceChildren(JSTracer* trc);
|
||||
|
|
|
@ -262,7 +262,7 @@ PRMJ_InvalidParameterHandler(const wchar_t* expression,
|
|||
/* Format a time value into a buffer. Same semantics as strftime() */
|
||||
size_t
|
||||
PRMJ_FormatTime(char* buf, int buflen, const char* fmt, const PRMJTime* prtm,
|
||||
int equivalentYear, int offsetInSeconds)
|
||||
int timeZoneYear, int offsetInSeconds)
|
||||
{
|
||||
size_t result = 0;
|
||||
#if defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
@ -295,7 +295,8 @@ PRMJ_FormatTime(char* buf, int buflen, const char* fmt, const PRMJTime* prtm,
|
|||
* Fill out |td| to the time represented by |prtm|, leaving the
|
||||
* timezone fields zeroed out. localtime_r will then fill in the
|
||||
* timezone fields for that local time according to the system's
|
||||
* timezone parameters.
|
||||
* timezone parameters. Use |timeZoneYear| for the year to ensure the
|
||||
* time zone name matches the time zone offset used by the caller.
|
||||
*/
|
||||
struct tm td;
|
||||
memset(&td, 0, sizeof(td));
|
||||
|
@ -305,19 +306,12 @@ PRMJ_FormatTime(char* buf, int buflen, const char* fmt, const PRMJTime* prtm,
|
|||
td.tm_mday = prtm->tm_mday;
|
||||
td.tm_mon = prtm->tm_mon;
|
||||
td.tm_wday = prtm->tm_wday;
|
||||
td.tm_year = prtm->tm_year - 1900;
|
||||
td.tm_year = timeZoneYear - 1900;
|
||||
td.tm_yday = prtm->tm_yday;
|
||||
td.tm_isdst = prtm->tm_isdst;
|
||||
|
||||
time_t t = mktime(&td);
|
||||
|
||||
// If |prtm| cannot be represented in |time_t| the year is probably
|
||||
// out of range, try again with the DST equivalent year.
|
||||
if (t == static_cast<time_t>(-1)) {
|
||||
td.tm_year = equivalentYear - 1900;
|
||||
t = mktime(&td);
|
||||
}
|
||||
|
||||
// If either mktime or localtime_r failed, fill in the fallback time
|
||||
// zone offset |offsetInSeconds| and set the time zone identifier to
|
||||
// the empty string.
|
||||
|
|
|
@ -55,7 +55,7 @@ PRMJ_NowShutdown() {}
|
|||
/* Format a time value into a buffer. Same semantics as strftime() */
|
||||
extern size_t
|
||||
PRMJ_FormatTime(char* buf, int buflen, const char* fmt, const PRMJTime* tm,
|
||||
int equivalentYear, int offsetInSeconds);
|
||||
int timeZoneYear, int offsetInSeconds);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1192,20 +1192,20 @@ class AstConversionOperator final : public AstExpr
|
|||
};
|
||||
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
// Like AstConversionOperator, but for opcodes encoded with the Numeric prefix.
|
||||
// Like AstConversionOperator, but for opcodes encoded with the Misc prefix.
|
||||
class AstExtraConversionOperator final : public AstExpr
|
||||
{
|
||||
NumericOp op_;
|
||||
MiscOp op_;
|
||||
AstExpr* operand_;
|
||||
|
||||
public:
|
||||
static const AstExprKind Kind = AstExprKind::ExtraConversionOperator;
|
||||
explicit AstExtraConversionOperator(NumericOp op, AstExpr* operand)
|
||||
explicit AstExtraConversionOperator(MiscOp op, AstExpr* operand)
|
||||
: AstExpr(Kind, ExprType::Limit),
|
||||
op_(op), operand_(operand)
|
||||
{}
|
||||
|
||||
NumericOp op() const { return op_; }
|
||||
MiscOp op() const { return op_; }
|
||||
AstExpr* operand() const { return operand_; }
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -9803,21 +9803,6 @@ BaseCompiler::emitBody()
|
|||
case uint16_t(Op::CurrentMemory):
|
||||
CHECK_NEXT(emitCurrentMemory());
|
||||
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
// Bulk memory operations
|
||||
case uint16_t(Op::CopyOrFillPrefix): {
|
||||
switch (op.b1) {
|
||||
case uint16_t(CopyOrFillOp::Copy):
|
||||
CHECK_NEXT(emitMemCopy());
|
||||
case uint16_t(CopyOrFillOp::Fill):
|
||||
CHECK_NEXT(emitMemFill());
|
||||
default:
|
||||
return iter_.unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_WASM_GC
|
||||
case uint16_t(Op::RefNull):
|
||||
if (env_.gcTypesEnabled == HasGcTypes::False)
|
||||
|
@ -9831,23 +9816,23 @@ BaseCompiler::emitBody()
|
|||
break;
|
||||
#endif
|
||||
|
||||
// Numeric operations
|
||||
case uint16_t(Op::NumericPrefix): {
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
// "Miscellaneous" operations
|
||||
case uint16_t(Op::MiscPrefix): {
|
||||
switch (op.b1) {
|
||||
case uint16_t(NumericOp::I32TruncSSatF32):
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
case uint16_t(MiscOp::I32TruncSSatF32):
|
||||
CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<TRUNC_SATURATING>,
|
||||
ValType::F32, ValType::I32));
|
||||
case uint16_t(NumericOp::I32TruncUSatF32):
|
||||
case uint16_t(MiscOp::I32TruncUSatF32):
|
||||
CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>,
|
||||
ValType::F32, ValType::I32));
|
||||
case uint16_t(NumericOp::I32TruncSSatF64):
|
||||
case uint16_t(MiscOp::I32TruncSSatF64):
|
||||
CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<TRUNC_SATURATING>,
|
||||
ValType::F64, ValType::I32));
|
||||
case uint16_t(NumericOp::I32TruncUSatF64):
|
||||
case uint16_t(MiscOp::I32TruncUSatF64):
|
||||
CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI32<TRUNC_UNSIGNED | TRUNC_SATURATING>,
|
||||
ValType::F64, ValType::I32));
|
||||
case uint16_t(NumericOp::I64TruncSSatF32):
|
||||
case uint16_t(MiscOp::I64TruncSSatF32):
|
||||
#ifdef RABALDR_FLOAT_TO_I64_CALLOUT
|
||||
CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
|
||||
SymbolicAddress::SaturatingTruncateDoubleToInt64,
|
||||
|
@ -9856,7 +9841,7 @@ BaseCompiler::emitBody()
|
|||
CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<TRUNC_SATURATING>,
|
||||
ValType::F32, ValType::I64));
|
||||
#endif
|
||||
case uint16_t(NumericOp::I64TruncUSatF32):
|
||||
case uint16_t(MiscOp::I64TruncUSatF32):
|
||||
#ifdef RABALDR_FLOAT_TO_I64_CALLOUT
|
||||
CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
|
||||
SymbolicAddress::SaturatingTruncateDoubleToUint64,
|
||||
|
@ -9865,7 +9850,7 @@ BaseCompiler::emitBody()
|
|||
CHECK_NEXT(emitConversionOOM(emitTruncateF32ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>,
|
||||
ValType::F32, ValType::I64));
|
||||
#endif
|
||||
case uint16_t(NumericOp::I64TruncSSatF64):
|
||||
case uint16_t(MiscOp::I64TruncSSatF64):
|
||||
#ifdef RABALDR_FLOAT_TO_I64_CALLOUT
|
||||
CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
|
||||
SymbolicAddress::SaturatingTruncateDoubleToInt64,
|
||||
|
@ -9874,7 +9859,7 @@ BaseCompiler::emitBody()
|
|||
CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<TRUNC_SATURATING>,
|
||||
ValType::F64, ValType::I64));
|
||||
#endif
|
||||
case uint16_t(NumericOp::I64TruncUSatF64):
|
||||
case uint16_t(MiscOp::I64TruncUSatF64):
|
||||
#ifdef RABALDR_FLOAT_TO_I64_CALLOUT
|
||||
CHECK_NEXT(emitCalloutConversionOOM(emitConvertFloatingToInt64Callout,
|
||||
SymbolicAddress::SaturatingTruncateDoubleToUint64,
|
||||
|
@ -9883,13 +9868,17 @@ BaseCompiler::emitBody()
|
|||
CHECK_NEXT(emitConversionOOM(emitTruncateF64ToI64<TRUNC_UNSIGNED | TRUNC_SATURATING>,
|
||||
ValType::F64, ValType::I64));
|
||||
#endif
|
||||
#endif // ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
case uint16_t(MiscOp::MemCopy):
|
||||
CHECK_NEXT(emitMemCopy());
|
||||
case uint16_t(MiscOp::MemFill):
|
||||
CHECK_NEXT(emitMemFill());
|
||||
#endif // ENABLE_WASM_BULKMEM_OPS
|
||||
default:
|
||||
return iter_.unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
break;
|
||||
} // switch (op.b1)
|
||||
return iter_.unrecognizedOpcode(&op);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Thread operations
|
||||
|
|
|
@ -336,9 +336,8 @@ enum class Op
|
|||
RefNull = 0xd0,
|
||||
RefIsNull = 0xd1,
|
||||
|
||||
FirstPrefix = 0xfb,
|
||||
CopyOrFillPrefix = 0xfb,
|
||||
NumericPrefix = 0xfc,
|
||||
FirstPrefix = 0xfc,
|
||||
MiscPrefix = 0xfc,
|
||||
ThreadPrefix = 0xfe,
|
||||
MozPrefix = 0xff,
|
||||
|
||||
|
@ -351,8 +350,8 @@ IsPrefixByte(uint8_t b)
|
|||
return b >= uint8_t(Op::FirstPrefix);
|
||||
}
|
||||
|
||||
// Opcodes in the "numeric" opcode space.
|
||||
enum class NumericOp
|
||||
// Opcodes in the "miscellaneous" opcode space.
|
||||
enum class MiscOp
|
||||
{
|
||||
// Saturating float-to-int conversions
|
||||
I32TruncSSatF32 = 0x00,
|
||||
|
@ -364,6 +363,10 @@ enum class NumericOp
|
|||
I64TruncSSatF64 = 0x06,
|
||||
I64TruncUSatF64 = 0x07,
|
||||
|
||||
// Bulk memory operations. Note, these are unofficial.
|
||||
MemCopy = 0x40,
|
||||
MemFill = 0x41,
|
||||
|
||||
Limit
|
||||
};
|
||||
|
||||
|
|
|
@ -714,7 +714,7 @@ AstDecodeConversion(AstDecodeContext& c, ValType fromType, ValType toType, Op op
|
|||
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
static bool
|
||||
AstDecodeExtraConversion(AstDecodeContext& c, ValType fromType, ValType toType, NumericOp op)
|
||||
AstDecodeExtraConversion(AstDecodeContext& c, ValType fromType, ValType toType, MiscOp op)
|
||||
{
|
||||
if (!c.iter().readConversion(fromType, toType, nullptr))
|
||||
return false;
|
||||
|
@ -1712,50 +1712,44 @@ AstDecodeExpr(AstDecodeContext& c)
|
|||
if (!c.push(AstDecodeStackItem(tmp)))
|
||||
return false;
|
||||
break;
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
case uint16_t(Op::CopyOrFillPrefix):
|
||||
case uint16_t(Op::MiscPrefix):
|
||||
switch (op.b1) {
|
||||
case uint16_t(CopyOrFillOp::Copy):
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
case uint16_t(MiscOp::I32TruncSSatF32):
|
||||
case uint16_t(MiscOp::I32TruncUSatF32):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I32, MiscOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(MiscOp::I32TruncSSatF64):
|
||||
case uint16_t(MiscOp::I32TruncUSatF64):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I32, MiscOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(MiscOp::I64TruncSSatF32):
|
||||
case uint16_t(MiscOp::I64TruncUSatF32):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I64, MiscOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(MiscOp::I64TruncSSatF64):
|
||||
case uint16_t(MiscOp::I64TruncUSatF64):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I64, MiscOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
case uint16_t(MiscOp::MemCopy):
|
||||
if (!AstDecodeMemCopy(c))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(CopyOrFillOp::Fill):
|
||||
case uint16_t(MiscOp::MemFill):
|
||||
if (!AstDecodeMemFill(c))
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return c.iter().unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
case uint16_t(Op::NumericPrefix):
|
||||
switch (op.b1) {
|
||||
case uint16_t(NumericOp::I32TruncSSatF32):
|
||||
case uint16_t(NumericOp::I32TruncUSatF32):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I32, NumericOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(NumericOp::I32TruncSSatF64):
|
||||
case uint16_t(NumericOp::I32TruncUSatF64):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I32, NumericOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(NumericOp::I64TruncSSatF32):
|
||||
case uint16_t(NumericOp::I64TruncUSatF32):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F32, ValType::I64, NumericOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
case uint16_t(NumericOp::I64TruncSSatF64):
|
||||
case uint16_t(NumericOp::I64TruncUSatF64):
|
||||
if (!AstDecodeExtraConversion(c, ValType::F64, ValType::I64, NumericOp(op.b1)))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return c.iter().unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case uint16_t(Op::ThreadPrefix):
|
||||
switch (op.b1) {
|
||||
case uint16_t(ThreadOp::Wake):
|
||||
|
|
|
@ -762,14 +762,14 @@ RenderExtraConversionOperator(WasmRenderContext& c, AstExtraConversionOperator&
|
|||
MAP_AST_EXPR(c, conv);
|
||||
const char* opStr;
|
||||
switch (conv.op()) {
|
||||
case NumericOp::I32TruncSSatF32: opStr = "i32.trunc_s:sat/f32"; break;
|
||||
case NumericOp::I32TruncUSatF32: opStr = "i32.trunc_u:sat/f32"; break;
|
||||
case NumericOp::I32TruncSSatF64: opStr = "i32.trunc_s:sat/f64"; break;
|
||||
case NumericOp::I32TruncUSatF64: opStr = "i32.trunc_u:sat/f64"; break;
|
||||
case NumericOp::I64TruncSSatF32: opStr = "i64.trunc_s:sat/f32"; break;
|
||||
case NumericOp::I64TruncUSatF32: opStr = "i64.trunc_u:sat/f32"; break;
|
||||
case NumericOp::I64TruncSSatF64: opStr = "i64.trunc_s:sat/f64"; break;
|
||||
case NumericOp::I64TruncUSatF64: opStr = "i64.trunc_u:sat/f64"; break;
|
||||
case MiscOp::I32TruncSSatF32: opStr = "i32.trunc_s:sat/f32"; break;
|
||||
case MiscOp::I32TruncUSatF32: opStr = "i32.trunc_u:sat/f32"; break;
|
||||
case MiscOp::I32TruncSSatF64: opStr = "i32.trunc_s:sat/f64"; break;
|
||||
case MiscOp::I32TruncUSatF64: opStr = "i32.trunc_u:sat/f64"; break;
|
||||
case MiscOp::I64TruncSSatF32: opStr = "i64.trunc_s:sat/f32"; break;
|
||||
case MiscOp::I64TruncUSatF32: opStr = "i64.trunc_u:sat/f32"; break;
|
||||
case MiscOp::I64TruncSSatF64: opStr = "i64.trunc_s:sat/f64"; break;
|
||||
case MiscOp::I64TruncUSatF64: opStr = "i64.trunc_u:sat/f64"; break;
|
||||
default: return Fail(c, "unexpected extra conversion operator");
|
||||
}
|
||||
return c.buffer.append(opStr, strlen(opStr));
|
||||
|
|
|
@ -4077,48 +4077,37 @@ EmitBodyExprs(FunctionCompiler& f)
|
|||
CHECK(EmitSignExtend(f, 4, 8));
|
||||
#endif
|
||||
|
||||
// Bulk memory operations
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
case uint16_t(Op::CopyOrFillPrefix): {
|
||||
// Miscellaneous operations
|
||||
case uint16_t(Op::MiscPrefix): {
|
||||
switch (op.b1) {
|
||||
case uint16_t(CopyOrFillOp::Copy):
|
||||
CHECK(EmitMemCopy(f));
|
||||
case uint16_t(CopyOrFillOp::Fill):
|
||||
CHECK(EmitMemFill(f));
|
||||
default:
|
||||
return f.iter().unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Numeric operations
|
||||
case uint16_t(Op::NumericPrefix): {
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
switch (op.b1) {
|
||||
case uint16_t(NumericOp::I32TruncSSatF32):
|
||||
case uint16_t(NumericOp::I32TruncUSatF32):
|
||||
case uint16_t(MiscOp::I32TruncSSatF32):
|
||||
case uint16_t(MiscOp::I32TruncUSatF32):
|
||||
CHECK(EmitTruncate(f, ValType::F32, ValType::I32,
|
||||
NumericOp(op.b1) == NumericOp::I32TruncUSatF32, true));
|
||||
case uint16_t(NumericOp::I32TruncSSatF64):
|
||||
case uint16_t(NumericOp::I32TruncUSatF64):
|
||||
MiscOp(op.b1) == MiscOp::I32TruncUSatF32, true));
|
||||
case uint16_t(MiscOp::I32TruncSSatF64):
|
||||
case uint16_t(MiscOp::I32TruncUSatF64):
|
||||
CHECK(EmitTruncate(f, ValType::F64, ValType::I32,
|
||||
NumericOp(op.b1) == NumericOp::I32TruncUSatF64, true));
|
||||
case uint16_t(NumericOp::I64TruncSSatF32):
|
||||
case uint16_t(NumericOp::I64TruncUSatF32):
|
||||
MiscOp(op.b1) == MiscOp::I32TruncUSatF64, true));
|
||||
case uint16_t(MiscOp::I64TruncSSatF32):
|
||||
case uint16_t(MiscOp::I64TruncUSatF32):
|
||||
CHECK(EmitTruncate(f, ValType::F32, ValType::I64,
|
||||
NumericOp(op.b1) == NumericOp::I64TruncUSatF32, true));
|
||||
case uint16_t(NumericOp::I64TruncSSatF64):
|
||||
case uint16_t(NumericOp::I64TruncUSatF64):
|
||||
MiscOp(op.b1) == MiscOp::I64TruncUSatF32, true));
|
||||
case uint16_t(MiscOp::I64TruncSSatF64):
|
||||
case uint16_t(MiscOp::I64TruncUSatF64):
|
||||
CHECK(EmitTruncate(f, ValType::F64, ValType::I64,
|
||||
NumericOp(op.b1) == NumericOp::I64TruncUSatF64, true));
|
||||
MiscOp(op.b1) == MiscOp::I64TruncUSatF64, true));
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
case uint16_t(MiscOp::MemCopy):
|
||||
CHECK(EmitMemCopy(f));
|
||||
case uint16_t(MiscOp::MemFill):
|
||||
CHECK(EmitMemFill(f));
|
||||
#endif
|
||||
default:
|
||||
return f.iter().unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
return f.iter().unrecognizedOpcode(&op);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Thread operations
|
||||
|
|
|
@ -241,37 +241,30 @@ wasm::Classify(OpBytes op)
|
|||
return OpKind::CurrentMemory;
|
||||
case Op::GrowMemory:
|
||||
return OpKind::GrowMemory;
|
||||
case Op::CopyOrFillPrefix: {
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
switch (CopyOrFillOp(op.b1)) {
|
||||
case CopyOrFillOp::Copy:
|
||||
return OpKind::MemCopy;
|
||||
case CopyOrFillOp::Fill:
|
||||
return OpKind::MemFill;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Op::RefNull:
|
||||
return OpKind::RefNull;
|
||||
case Op::NumericPrefix: {
|
||||
case Op::MiscPrefix: {
|
||||
switch (MiscOp(op.b1)) {
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
switch (NumericOp(op.b1)) {
|
||||
case NumericOp::I32TruncSSatF32:
|
||||
case NumericOp::I32TruncUSatF32:
|
||||
case NumericOp::I32TruncSSatF64:
|
||||
case NumericOp::I32TruncUSatF64:
|
||||
case NumericOp::I64TruncSSatF32:
|
||||
case NumericOp::I64TruncUSatF32:
|
||||
case NumericOp::I64TruncSSatF64:
|
||||
case NumericOp::I64TruncUSatF64:
|
||||
case MiscOp::I32TruncSSatF32:
|
||||
case MiscOp::I32TruncUSatF32:
|
||||
case MiscOp::I32TruncSSatF64:
|
||||
case MiscOp::I32TruncUSatF64:
|
||||
case MiscOp::I64TruncSSatF32:
|
||||
case MiscOp::I64TruncUSatF32:
|
||||
case MiscOp::I64TruncSSatF64:
|
||||
case MiscOp::I64TruncUSatF64:
|
||||
return OpKind::Conversion;
|
||||
#endif
|
||||
#ifdef ENABLE_WASM_BULKMEM_OPS
|
||||
case MiscOp::MemCopy:
|
||||
return OpKind::MemCopy;
|
||||
case MiscOp::MemFill:
|
||||
return OpKind::MemFill;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case Op::ThreadPrefix: {
|
||||
|
|
|
@ -153,7 +153,7 @@ class WasmToken
|
|||
FloatLiteralKind floatLiteralKind_;
|
||||
ValType valueType_;
|
||||
Op op_;
|
||||
NumericOp numericOp_;
|
||||
MiscOp miscOp_;
|
||||
ThreadOp threadOp_;
|
||||
} u;
|
||||
public:
|
||||
|
@ -226,14 +226,14 @@ class WasmToken
|
|||
u.op_ = op;
|
||||
}
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
explicit WasmToken(Kind kind, NumericOp op, const char16_t* begin, const char16_t* end)
|
||||
explicit WasmToken(Kind kind, MiscOp op, const char16_t* begin, const char16_t* end)
|
||||
: kind_(kind),
|
||||
begin_(begin),
|
||||
end_(end)
|
||||
{
|
||||
MOZ_ASSERT(begin != end);
|
||||
MOZ_ASSERT(kind_ == ExtraConversionOpcode);
|
||||
u.numericOp_ = op;
|
||||
u.miscOp_ = op;
|
||||
}
|
||||
#endif
|
||||
explicit WasmToken(Kind kind, ThreadOp op, const char16_t* begin, const char16_t* end)
|
||||
|
@ -298,9 +298,9 @@ class WasmToken
|
|||
return u.op_;
|
||||
}
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
NumericOp numericOp() const {
|
||||
MiscOp miscOp() const {
|
||||
MOZ_ASSERT(kind_ == ExtraConversionOpcode);
|
||||
return u.numericOp_;
|
||||
return u.miscOp_;
|
||||
}
|
||||
#endif
|
||||
ThreadOp threadOp() const {
|
||||
|
@ -1359,16 +1359,16 @@ WasmTokenStream::next()
|
|||
begin, cur_);
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
if (consume(u"trunc_s:sat/f32"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I32TruncSSatF32,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I32TruncSSatF32,
|
||||
begin, cur_);
|
||||
if (consume(u"trunc_s:sat/f64"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I32TruncSSatF64,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I32TruncSSatF64,
|
||||
begin, cur_);
|
||||
if (consume(u"trunc_u:sat/f32"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I32TruncUSatF32,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I32TruncUSatF32,
|
||||
begin, cur_);
|
||||
if (consume(u"trunc_u:sat/f64"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I32TruncUSatF64,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I32TruncUSatF64,
|
||||
begin, cur_);
|
||||
#endif
|
||||
break;
|
||||
|
@ -1609,16 +1609,16 @@ WasmTokenStream::next()
|
|||
begin, cur_);
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
if (consume(u"trunc_s:sat/f32"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I64TruncSSatF32,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I64TruncSSatF32,
|
||||
begin, cur_);
|
||||
if (consume(u"trunc_s:sat/f64"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I64TruncSSatF64,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I64TruncSSatF64,
|
||||
begin, cur_);
|
||||
if (consume(u"trunc_u:sat/f32"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I64TruncUSatF32,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I64TruncUSatF32,
|
||||
begin, cur_);
|
||||
if (consume(u"trunc_u:sat/f64"))
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, NumericOp::I64TruncUSatF64,
|
||||
return WasmToken(WasmToken::ExtraConversionOpcode, MiscOp::I64TruncUSatF64,
|
||||
begin, cur_);
|
||||
#endif
|
||||
break;
|
||||
|
@ -2454,7 +2454,7 @@ ParseConversionOperator(WasmParseContext& c, Op op, bool inParens)
|
|||
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
static AstExtraConversionOperator*
|
||||
ParseExtraConversionOperator(WasmParseContext& c, NumericOp op, bool inParens)
|
||||
ParseExtraConversionOperator(WasmParseContext& c, MiscOp op, bool inParens)
|
||||
{
|
||||
AstExpr* operand = ParseExpr(c, inParens);
|
||||
if (!operand)
|
||||
|
@ -3056,7 +3056,7 @@ ParseExprBody(WasmParseContext& c, WasmToken token, bool inParens)
|
|||
return ParseConversionOperator(c, token.op(), inParens);
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
case WasmToken::ExtraConversionOpcode:
|
||||
return ParseExtraConversionOperator(c, token.numericOp(), inParens);
|
||||
return ParseExtraConversionOperator(c, token.miscOp(), inParens);
|
||||
#endif
|
||||
case WasmToken::Drop:
|
||||
return ParseDrop(c, inParens);
|
||||
|
@ -5051,7 +5051,7 @@ EncodeMemCopy(Encoder& e, AstMemCopy& s)
|
|||
return EncodeExpr(e, s.dest()) &&
|
||||
EncodeExpr(e, s.src()) &&
|
||||
EncodeExpr(e, s.len()) &&
|
||||
e.writeOp(CopyOrFillOp::Copy);
|
||||
e.writeOp(MiscOp::MemCopy);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -5060,7 +5060,7 @@ EncodeMemFill(Encoder& e, AstMemFill& s)
|
|||
return EncodeExpr(e, s.start()) &&
|
||||
EncodeExpr(e, s.val()) &&
|
||||
EncodeExpr(e, s.len()) &&
|
||||
e.writeOp(CopyOrFillOp::Fill);
|
||||
e.writeOp(MiscOp::MemFill);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -801,28 +801,26 @@ DecodeFunctionBodyExprs(const ModuleEnvironment& env, const Sig& sig, const ValT
|
|||
CHECK(iter.readReturn(¬hing));
|
||||
case uint16_t(Op::Unreachable):
|
||||
CHECK(iter.readUnreachable());
|
||||
case uint16_t(Op::NumericPrefix): {
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
case uint16_t(Op::MiscPrefix): {
|
||||
switch (op.b1) {
|
||||
case uint16_t(NumericOp::I32TruncSSatF32):
|
||||
case uint16_t(NumericOp::I32TruncUSatF32):
|
||||
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
|
||||
case uint16_t(MiscOp::I32TruncSSatF32):
|
||||
case uint16_t(MiscOp::I32TruncUSatF32):
|
||||
CHECK(iter.readConversion(ValType::F32, ValType::I32, ¬hing));
|
||||
case uint16_t(NumericOp::I32TruncSSatF64):
|
||||
case uint16_t(NumericOp::I32TruncUSatF64):
|
||||
case uint16_t(MiscOp::I32TruncSSatF64):
|
||||
case uint16_t(MiscOp::I32TruncUSatF64):
|
||||
CHECK(iter.readConversion(ValType::F64, ValType::I32, ¬hing));
|
||||
case uint16_t(NumericOp::I64TruncSSatF32):
|
||||
case uint16_t(NumericOp::I64TruncUSatF32):
|
||||
case uint16_t(MiscOp::I64TruncSSatF32):
|
||||
case uint16_t(MiscOp::I64TruncUSatF32):
|
||||
CHECK(iter.readConversion(ValType::F32, ValType::I64, ¬hing));
|
||||
case uint16_t(NumericOp::I64TruncSSatF64):
|
||||
case uint16_t(NumericOp::I64TruncUSatF64):
|
||||
case uint16_t(MiscOp::I64TruncSSatF64):
|
||||
case uint16_t(MiscOp::I64TruncUSatF64):
|
||||
CHECK(iter.readConversion(ValType::F64, ValType::I64, ¬hing));
|
||||
#endif
|
||||
default:
|
||||
return iter.unrecognizedOpcode(&op);
|
||||
}
|
||||
break;
|
||||
#else
|
||||
return iter.unrecognizedOpcode(&op);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_WASM_GC
|
||||
case uint16_t(Op::RefNull): {
|
||||
|
|
|
@ -282,22 +282,10 @@ class Encoder
|
|||
MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
|
||||
return writeFixedU8(uint8_t(op));
|
||||
}
|
||||
MOZ_MUST_USE bool writeOp(MozOp op) {
|
||||
static_assert(size_t(MozOp::Limit) <= 256, "fits");
|
||||
MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
|
||||
return writeFixedU8(uint8_t(Op::MozPrefix)) &&
|
||||
writeFixedU8(uint8_t(op));
|
||||
}
|
||||
MOZ_MUST_USE bool writeOp(CopyOrFillOp op) {
|
||||
static_assert(size_t(CopyOrFillOp::Limit) <= 256, "fits");
|
||||
MOZ_ASSERT(size_t(op) < size_t(CopyOrFillOp::Limit));
|
||||
return writeFixedU8(uint8_t(Op::CopyOrFillPrefix)) &&
|
||||
writeFixedU8(uint8_t(op));
|
||||
}
|
||||
MOZ_MUST_USE bool writeOp(NumericOp op) {
|
||||
static_assert(size_t(NumericOp::Limit) <= 256, "fits");
|
||||
MOZ_ASSERT(size_t(op) < size_t(NumericOp::Limit));
|
||||
return writeFixedU8(uint8_t(Op::NumericPrefix)) &&
|
||||
MOZ_MUST_USE bool writeOp(MiscOp op) {
|
||||
static_assert(size_t(MiscOp::Limit) <= 256, "fits");
|
||||
MOZ_ASSERT(size_t(op) < size_t(MiscOp::Limit));
|
||||
return writeFixedU8(uint8_t(Op::MiscPrefix)) &&
|
||||
writeFixedU8(uint8_t(op));
|
||||
}
|
||||
MOZ_MUST_USE bool writeOp(ThreadOp op) {
|
||||
|
@ -306,6 +294,12 @@ class Encoder
|
|||
return writeFixedU8(uint8_t(Op::ThreadPrefix)) &&
|
||||
writeFixedU8(uint8_t(op));
|
||||
}
|
||||
MOZ_MUST_USE bool writeOp(MozOp op) {
|
||||
static_assert(size_t(MozOp::Limit) <= 256, "fits");
|
||||
MOZ_ASSERT(size_t(op) < size_t(MozOp::Limit));
|
||||
return writeFixedU8(uint8_t(Op::MozPrefix)) &&
|
||||
writeFixedU8(uint8_t(op));
|
||||
}
|
||||
|
||||
// Fixed-length encodings that allow back-patching.
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ skip-if = toolkit == 'android' # Bug 1355815
|
|||
[test_bug386575.xhtml]
|
||||
[test_bug388019.html]
|
||||
[test_bug394057.html]
|
||||
skip-if = toolkit == 'android' # Bug 1355817
|
||||
skip-if = toolkit == 'android' || (os == 'win' && asan) # Bug 1355817, bug 1460993
|
||||
[test_bug399284.html]
|
||||
[test_bug399951.html]
|
||||
[test_bug404209.xhtml]
|
||||
|
|
|
@ -670,7 +670,7 @@ MathMLTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
|
|||
// Bug 930504. Some platforms do not have fonts for Mathematical
|
||||
// Alphanumeric Symbols. Hence we check whether the transformed
|
||||
// character is actually available.
|
||||
uint8_t matchType;
|
||||
gfxTextRange::MatchType matchType;
|
||||
RefPtr<gfxFont> mathFont = fontGroup->
|
||||
FindFontForChar(ch2, 0, 0, unicode::Script::COMMON, nullptr, &matchType);
|
||||
if (mathFont) {
|
||||
|
|
|
@ -22,19 +22,19 @@ namespace dom {
|
|||
bool
|
||||
InspectorFontFace::FromFontGroup()
|
||||
{
|
||||
return mMatchType & gfxTextRange::kFontGroup;
|
||||
return bool(mMatchType & gfxTextRange::MatchType::kFontGroup);
|
||||
}
|
||||
|
||||
bool
|
||||
InspectorFontFace::FromLanguagePrefs()
|
||||
{
|
||||
return mMatchType & gfxTextRange::kPrefsFallback;
|
||||
return bool(mMatchType & gfxTextRange::MatchType::kPrefsFallback);
|
||||
}
|
||||
|
||||
bool
|
||||
InspectorFontFace::FromSystemFallback()
|
||||
{
|
||||
return mMatchType & gfxTextRange::kSystemFallback;
|
||||
return bool(mMatchType & gfxTextRange::MatchType::kSystemFallback);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
#include "mozilla/dom/InspectorUtilsBinding.h"
|
||||
#include "mozilla/dom/NonRefcountedDOMObject.h"
|
||||
#include "nsRange.h"
|
||||
#include "gfxFont.h"
|
||||
|
||||
class gfxFontEntry;
|
||||
class gfxFontGroup;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -27,7 +27,7 @@ class InspectorFontFace final : public NonRefcountedDOMObject
|
|||
public:
|
||||
InspectorFontFace(gfxFontEntry* aFontEntry,
|
||||
gfxFontGroup* aFontGroup,
|
||||
uint8_t aMatchType)
|
||||
gfxTextRange::MatchType aMatchType)
|
||||
: mFontEntry(aFontEntry)
|
||||
, mFontGroup(aFontGroup)
|
||||
, mMatchType(aMatchType)
|
||||
|
@ -41,7 +41,9 @@ public:
|
|||
}
|
||||
|
||||
gfxFontEntry* GetFontEntry() const { return mFontEntry; }
|
||||
void AddMatchType(uint8_t aMatchType) { mMatchType |= aMatchType; }
|
||||
void AddMatchType(gfxTextRange::MatchType aMatchType) {
|
||||
mMatchType |= aMatchType;
|
||||
}
|
||||
|
||||
void AddRange(nsRange* aRange);
|
||||
size_t RangeCount() const {
|
||||
|
@ -81,7 +83,7 @@ protected:
|
|||
RefPtr<gfxFontEntry> mFontEntry;
|
||||
RefPtr<gfxFontGroup> mFontGroup;
|
||||
RefPtr<ServoFontFaceRule> mRule;
|
||||
uint8_t mMatchType;
|
||||
gfxTextRange::MatchType mMatchType;
|
||||
|
||||
nsTArray<RefPtr<nsRange>> mRanges;
|
||||
};
|
||||
|
|
|
@ -578,7 +578,7 @@ nsOpenTypeTable::MakeTextRun(DrawTarget* aDrawTarget,
|
|||
gfxTextRun::Create(¶ms, 1, aFontGroup,
|
||||
gfx::ShapedTextFlags(), nsTextFrameUtils::Flags());
|
||||
textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(),
|
||||
gfxTextRange::kFontGroup, 0,
|
||||
gfxTextRange::MatchType::kFontGroup, 0,
|
||||
false, gfx::ShapedTextFlags::TEXT_ORIENT_HORIZONTAL);
|
||||
// We don't care about CSS writing mode here;
|
||||
// math runs are assumed to be horizontal.
|
||||
|
|
|
@ -356,6 +356,9 @@ public class SessionAccessibility {
|
|||
node.setCheckable(message.getBoolean("checkable"));
|
||||
node.setChecked(message.getBoolean("checked"));
|
||||
node.setPassword(message.getBoolean("password"));
|
||||
node.setFocusable(message.getBoolean("focusable"));
|
||||
node.setFocused(message.getBoolean("focused"));
|
||||
node.setEditable(message.getBoolean("editable"));
|
||||
|
||||
final String[] textArray = message.getStringArray("text");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -418,7 +421,10 @@ public class SessionAccessibility {
|
|||
}
|
||||
}
|
||||
|
||||
if (eventSource != View.NO_ID) {
|
||||
if (eventSource != View.NO_ID &&
|
||||
(eventType == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED ||
|
||||
eventType == AccessibilityEvent.TYPE_VIEW_FOCUSED ||
|
||||
eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)) {
|
||||
// In Jelly Bean we populate an AccessibilityNodeInfo with the minimal amount of data to have
|
||||
// it work with TalkBack.
|
||||
if (mVirtualContentNode == null) {
|
||||
|
|
|
@ -1453,12 +1453,3 @@ nsNSSCertificateDB::OpenSignedAppFileAsync(
|
|||
aCallback));
|
||||
return task->Dispatch("SignedJAR");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSCertificateDB::VerifySignedDirectoryAsync(AppTrustedRoot, nsIFile*,
|
||||
nsIVerifySignedDirectoryCallback* aCallback)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aCallback);
|
||||
return aCallback->VerifySignedDirectoryFinished(
|
||||
NS_ERROR_SIGNED_JAR_NOT_SIGNED, nullptr);
|
||||
}
|
||||
|
|
|
@ -28,15 +28,6 @@ interface nsIOpenSignedAppFileCallback : nsISupports
|
|||
in nsIX509Cert aSignerCert);
|
||||
};
|
||||
|
||||
// Only relevant while we transition away from legacy add-ons. rv will always be
|
||||
// NS_ERROR_SIGNED_JAR_NOT_SIGNED. aSignerCert will always be null.
|
||||
[scriptable, function, uuid(d5f97827-622a-488f-be08-d850432ac8ec)]
|
||||
interface nsIVerifySignedDirectoryCallback : nsISupports
|
||||
{
|
||||
void verifySignedDirectoryFinished(in nsresult rv,
|
||||
in nsIX509Cert aSignerCert);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback type for use with asyncVerifyCertAtTime.
|
||||
* If aPRErrorCode is PRErrorCodeSuccess (i.e. 0), aVerifiedChain represents the
|
||||
|
@ -258,16 +249,6 @@ interface nsIX509CertDB : nsISupports {
|
|||
in nsIFile aJarFile,
|
||||
in nsIOpenSignedAppFileCallback callback);
|
||||
|
||||
/**
|
||||
* Vestigial implementation of verifying signed unpacked add-ons. trustedRoot
|
||||
* and aUnpackedDir are ignored. The callback is always called with
|
||||
* NS_ERROR_SIGNED_JAR_NOT_SIGNED and a null signer cert.
|
||||
*/
|
||||
[must_use]
|
||||
void verifySignedDirectoryAsync(in AppTrustedRoot trustedRoot,
|
||||
in nsIFile aUnpackedDir,
|
||||
in nsIVerifySignedDirectoryCallback callback);
|
||||
|
||||
/*
|
||||
* Add a cert to a cert DB from a binary string.
|
||||
*
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
"use strict";
|
||||
|
||||
// Tests that signed extensions extracted/unpacked into a directory do not pass
|
||||
// signature verification, because that's no longer supported.
|
||||
|
||||
const { ZipUtils } = ChromeUtils.import("resource://gre/modules/ZipUtils.jsm", {});
|
||||
|
||||
do_get_profile(); // must be called before getting nsIX509CertDB
|
||||
const certdb = Cc["@mozilla.org/security/x509certdb;1"]
|
||||
.getService(Ci.nsIX509CertDB);
|
||||
|
||||
/**
|
||||
* Signed test extension. This is any arbitrary Mozilla signed XPI that
|
||||
* preferably has recently been signed (but note that it actually doesn't
|
||||
* matter, since we ignore expired certificates when checking signing).
|
||||
* @type nsIFile
|
||||
*/
|
||||
var gSignedXPI =
|
||||
do_get_file("test_signed_dir/lightbeam_for_firefox-1.3.1-fx.xpi", false);
|
||||
/**
|
||||
* The directory that the test extension will be extracted to.
|
||||
* @type nsIFile
|
||||
*/
|
||||
var gTarget = FileUtils.getDir("TmpD", ["test_signed_dir"]);
|
||||
gTarget.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
/**
|
||||
* Extracts the signed XPI into a directory, and tampers the files in that
|
||||
* directory if instructed.
|
||||
*
|
||||
* @returns {nsIFile}
|
||||
* The directory where the XPI was extracted to.
|
||||
*/
|
||||
function prepare() {
|
||||
ZipUtils.extractFiles(gSignedXPI, gTarget);
|
||||
return gTarget;
|
||||
}
|
||||
|
||||
function checkResult(expectedRv, dir, resolve) {
|
||||
return function verifySignedDirCallback(rv, aSignerCert) {
|
||||
equal(rv, expectedRv, "Actual and expected return value should match");
|
||||
equal(aSignerCert != null, Components.isSuccessCode(expectedRv),
|
||||
"expecting certificate:");
|
||||
dir.remove(true);
|
||||
resolve();
|
||||
};
|
||||
}
|
||||
|
||||
function verifyDirAsync(expectedRv) {
|
||||
let targetDir = prepare();
|
||||
return new Promise((resolve, reject) => {
|
||||
certdb.verifySignedDirectoryAsync(
|
||||
Ci.nsIX509CertDB.AddonsPublicRoot, targetDir,
|
||||
checkResult(expectedRv, targetDir, resolve));
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function testAPIFails() {
|
||||
await verifyDirAsync(Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED);
|
||||
});
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
if (gTarget.exists()) {
|
||||
gTarget.remove(true);
|
||||
}
|
||||
});
|
Двоичный файл не отображается.
|
@ -31,7 +31,6 @@ support-files =
|
|||
test_sdr_preexisting/**
|
||||
test_sdr_preexisting_with_password/**
|
||||
test_signed_apps/**
|
||||
test_signed_dir/**
|
||||
test_startcom_wosign/**
|
||||
test_symantec_apple_google/**
|
||||
test_validity/**
|
||||
|
@ -150,8 +149,6 @@ skip-if = toolkit == 'android'
|
|||
[test_session_resumption.js]
|
||||
run-sequentially = hardcoded ports
|
||||
[test_signed_apps.js]
|
||||
[test_signed_dir.js]
|
||||
tags = addons psm
|
||||
[test_ssl_status.js]
|
||||
[test_sss_enumerate.js]
|
||||
[test_sss_eviction.js]
|
||||
|
|
|
@ -22,6 +22,17 @@ awsy:
|
|||
.*-devedition/.*: [] # don't run on devedition
|
||||
default: built-projects
|
||||
|
||||
awsy-base:
|
||||
description: "Are we slim yet - about:blank base case"
|
||||
treeherder-symbol: SY(ab)
|
||||
run-on-projects:
|
||||
by-test-platform:
|
||||
.*-devedition/.*: [] # don't run on devedition
|
||||
default: built-projects
|
||||
mozharness:
|
||||
extra-options:
|
||||
- --base
|
||||
|
||||
awsy-stylo-sequential:
|
||||
description: "Are we slim yet for Stylo sequential"
|
||||
treeherder-symbol: SYss(sy)
|
||||
|
|
|
@ -82,6 +82,7 @@ talos:
|
|||
|
||||
awsy:
|
||||
- awsy
|
||||
- awsy-base
|
||||
|
||||
awsy-stylo-sequential:
|
||||
- awsy-stylo-sequential
|
||||
|
|
|
@ -0,0 +1,373 @@
|
|||
# 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/.
|
||||
|
||||
import fnmatch
|
||||
import glob
|
||||
import gzip
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from marionette_harness import MarionetteTestCase
|
||||
from marionette_driver import Actions
|
||||
from marionette_driver.errors import JavascriptException, ScriptTimeoutException
|
||||
import mozlog.structured
|
||||
from marionette_driver.keys import Keys
|
||||
|
||||
AWSY_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
if AWSY_PATH not in sys.path:
|
||||
sys.path.append(AWSY_PATH)
|
||||
|
||||
from awsy import ITERATIONS, PER_TAB_PAUSE, SETTLE_WAIT_TIME, MAX_TABS
|
||||
from awsy import process_perf_data
|
||||
|
||||
class AwsyTestCase(MarionetteTestCase):
|
||||
"""
|
||||
Base test case for AWSY tests.
|
||||
"""
|
||||
|
||||
def urls(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def perf_suites(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def perf_checkpoints(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def iterations(self):
|
||||
return self._iterations
|
||||
|
||||
def pages_to_load(self):
|
||||
return self._pages_to_load if self._pages_to_load else len(self.urls())
|
||||
|
||||
def settle(self):
|
||||
"""
|
||||
Pauses for the settle time.
|
||||
"""
|
||||
time.sleep(self._settleWaitTime)
|
||||
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
|
||||
self.logger = mozlog.structured.structuredlog.get_default_logger()
|
||||
self.marionette.set_context('chrome')
|
||||
self._resultsDir = self.testvars["resultsDir"]
|
||||
|
||||
# Cleanup our files from previous runs.
|
||||
for patt in ('memory-report-*.json.gz',
|
||||
'perfherder_data.json',
|
||||
'dmd-*.json.gz'):
|
||||
for f in glob.glob(os.path.join(self._resultsDir, patt)):
|
||||
os.unlink(f)
|
||||
|
||||
# Optional testvars.
|
||||
self._pages_to_load = self.testvars.get("entities", 0)
|
||||
self._iterations = self.testvars.get("iterations", ITERATIONS)
|
||||
self._perTabPause = self.testvars.get("perTabPause", PER_TAB_PAUSE)
|
||||
self._settleWaitTime = self.testvars.get("settleWaitTime", SETTLE_WAIT_TIME)
|
||||
self._maxTabs = self.testvars.get("maxTabs", MAX_TABS)
|
||||
self._dmd = self.testvars.get("dmd", False)
|
||||
|
||||
self.logger.info("areweslimyet run by %d pages, %d iterations, %d perTabPause, %d settleWaitTime"
|
||||
% (self._pages_to_load, self._iterations, self._perTabPause, self._settleWaitTime))
|
||||
self.reset_state()
|
||||
|
||||
def tearDown(self):
|
||||
MarionetteTestCase.tearDown(self)
|
||||
|
||||
self.logger.info("processing data in %s!" % self._resultsDir)
|
||||
perf_blob = process_perf_data.create_perf_data(
|
||||
self._resultsDir, self.perf_suites(),
|
||||
self.perf_checkpoints())
|
||||
self.logger.info("PERFHERDER_DATA: %s" % json.dumps(perf_blob))
|
||||
|
||||
perf_file = os.path.join(self._resultsDir, "perfherder_data.json")
|
||||
with open(perf_file, 'w') as fp:
|
||||
json.dump(perf_blob, fp, indent=2)
|
||||
self.logger.info("Perfherder data written to %s" % perf_file)
|
||||
|
||||
if self._dmd:
|
||||
self.cleanup_dmd()
|
||||
|
||||
# copy it to moz upload dir if set
|
||||
if 'MOZ_UPLOAD_DIR' in os.environ:
|
||||
for file in os.listdir(self._resultsDir):
|
||||
file = os.path.join(self._resultsDir, file)
|
||||
if os.path.isfile(file):
|
||||
shutil.copy2(file, os.environ["MOZ_UPLOAD_DIR"])
|
||||
|
||||
def cleanup_dmd(self):
|
||||
"""
|
||||
Handles moving DMD reports from the temp dir to our resultsDir.
|
||||
"""
|
||||
from dmd import fixStackTraces
|
||||
|
||||
# Move DMD files from temp dir to resultsDir.
|
||||
tmpdir = tempfile.gettempdir()
|
||||
tmp_files = os.listdir(tmpdir)
|
||||
for f in fnmatch.filter(tmp_files, "dmd-*.json.gz"):
|
||||
f = os.path.join(tmpdir, f)
|
||||
self.logger.info("Fixing stacks for %s, this may take a while" % f)
|
||||
isZipped = True
|
||||
fixStackTraces(f, isZipped, gzip.open)
|
||||
shutil.move(f, self._resultsDir)
|
||||
|
||||
# Also attempt to cleanup the unified memory reports.
|
||||
for f in fnmatch.filter(tmp_files, "unified-memory-report-*.json.gz"):
|
||||
try:
|
||||
os.remove(f)
|
||||
except OSError:
|
||||
self.logger.info("Unable to remove %s" % f)
|
||||
|
||||
def reset_state(self):
|
||||
self._pages_loaded = 0
|
||||
|
||||
# Close all tabs except one
|
||||
for x in self.marionette.window_handles[1:]:
|
||||
self.logger.info("closing window: %s" % x)
|
||||
self.marionette.switch_to_window(x)
|
||||
self.marionette.close()
|
||||
|
||||
self._tabs = self.marionette.window_handles
|
||||
self.marionette.switch_to_window(self._tabs[0])
|
||||
|
||||
def do_full_gc(self):
|
||||
"""Performs a full garbage collection cycle and returns when it is finished.
|
||||
|
||||
Returns True on success and False on failure.
|
||||
"""
|
||||
# NB: we could do this w/ a signal or the fifo queue too
|
||||
self.logger.info("starting gc...")
|
||||
gc_script = """
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Services.obs.notifyObservers(null, "child-mmu-request", null);
|
||||
|
||||
let memMgrSvc = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
|
||||
memMgrSvc.minimizeMemoryUsage(() => marionetteScriptFinished("gc done!"));
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = self.marionette.execute_async_script(
|
||||
gc_script, script_timeout=180000)
|
||||
except JavascriptException, e:
|
||||
self.logger.error("GC JavaScript error: %s" % e)
|
||||
except ScriptTimeoutException:
|
||||
self.logger.error("GC timed out")
|
||||
except:
|
||||
self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
|
||||
else:
|
||||
self.logger.info(result)
|
||||
|
||||
return result is not None
|
||||
|
||||
def do_memory_report(self, checkpointName, iteration):
|
||||
"""Creates a memory report for all processes and and returns the
|
||||
checkpoint.
|
||||
|
||||
This will block until all reports are retrieved or a timeout occurs.
|
||||
Returns the checkpoint or None on error.
|
||||
|
||||
:param checkpointName: The name of the checkpoint.
|
||||
"""
|
||||
self.logger.info("starting checkpoint %s..." % checkpointName)
|
||||
|
||||
checkpoint_file = "memory-report-%s-%d.json.gz" % (checkpointName, iteration)
|
||||
checkpoint_path = os.path.join(self._resultsDir, checkpoint_file)
|
||||
# On Windows, replace / with the Windows directory
|
||||
# separator \ and escape it to prevent it from being
|
||||
# interpreted as an escape character.
|
||||
if sys.platform.startswith('win'):
|
||||
checkpoint_path = (checkpoint_path.
|
||||
replace('\\', '\\\\').
|
||||
replace('/', '\\\\'))
|
||||
|
||||
checkpoint_script = r"""
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
|
||||
dumper.dumpMemoryReportsToNamedFile(
|
||||
"%s",
|
||||
() => marionetteScriptFinished("memory report done!"),
|
||||
null,
|
||||
/* anonymize */ false);
|
||||
""" % checkpoint_path
|
||||
|
||||
checkpoint = None
|
||||
try:
|
||||
finished = self.marionette.execute_async_script(
|
||||
checkpoint_script, script_timeout=60000)
|
||||
if finished:
|
||||
checkpoint = checkpoint_path
|
||||
except JavascriptException, e:
|
||||
self.logger.error("Checkpoint JavaScript error: %s" % e)
|
||||
except ScriptTimeoutException:
|
||||
self.logger.error("Memory report timed out")
|
||||
except:
|
||||
self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
|
||||
else:
|
||||
self.logger.info("checkpoint created, stored in %s" % checkpoint_path)
|
||||
|
||||
# Now trigger a DMD report if requested.
|
||||
if self._dmd:
|
||||
self.do_dmd(checkpointName, iteration)
|
||||
|
||||
return checkpoint
|
||||
|
||||
def do_dmd(self, checkpointName, iteration):
|
||||
"""
|
||||
Triggers DMD reports that are used to help identify sources of
|
||||
'heap-unclassified'.
|
||||
|
||||
NB: This will dump DMD reports to the temp dir. Unfortunately it also
|
||||
dumps memory reports, but that's all we have to work with right now.
|
||||
"""
|
||||
self.logger.info("Starting %s DMD reports..." % checkpointName)
|
||||
|
||||
ident = "%s-%d" % (checkpointName, iteration)
|
||||
|
||||
# TODO(ER): This actually takes a minimize argument. We could use that
|
||||
# rather than have a separate `do_gc` function. Also it generates a
|
||||
# memory report so we could combine this with `do_checkpoint`. The main
|
||||
# issue would be moving everything out of the temp dir.
|
||||
#
|
||||
# Generated files have the form:
|
||||
# dmd-<checkpoint>-<iteration>-pid.json.gz, ie:
|
||||
# dmd-TabsOpenForceGC-0-10885.json.gz
|
||||
#
|
||||
# and for the memory report:
|
||||
# unified-memory-report-<checkpoint>-<iteration>.json.gz
|
||||
dmd_script = r"""
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
|
||||
dumper.dumpMemoryInfoToTempDir(
|
||||
"%s",
|
||||
/* anonymize = */ false,
|
||||
/* minimize = */ false);
|
||||
""" % ident
|
||||
|
||||
try:
|
||||
# This is async and there's no callback so we use the existence
|
||||
# of an incomplete memory report to check if it hasn't finished yet.
|
||||
self.marionette.execute_script(dmd_script, script_timeout=60000)
|
||||
tmpdir = tempfile.gettempdir()
|
||||
prefix = "incomplete-unified-memory-report-%s-%d-*" % (checkpointName, iteration)
|
||||
max_wait = 60
|
||||
elapsed = 0
|
||||
while fnmatch.filter(os.listdir(tmpdir), prefix) and elapsed < max_wait:
|
||||
self.logger.info("Waiting for memory report to finish")
|
||||
time.sleep(1)
|
||||
elapsed += 1
|
||||
|
||||
incomplete = fnmatch.filter(os.listdir(tmpdir), prefix)
|
||||
if incomplete:
|
||||
# The memory reports never finished.
|
||||
self.logger.error("Incomplete memory reports leftover.")
|
||||
for f in incomplete:
|
||||
os.remove(os.path.join(tmpdir, f))
|
||||
|
||||
except JavascriptException, e:
|
||||
self.logger.error("DMD JavaScript error: %s" % e)
|
||||
except ScriptTimeoutException:
|
||||
self.logger.error("DMD timed out")
|
||||
except:
|
||||
self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
|
||||
else:
|
||||
self.logger.info("DMD started, prefixed with %s" % ident)
|
||||
|
||||
def open_and_focus(self):
|
||||
"""Opens the next URL in the list and focuses on the tab it is opened in.
|
||||
|
||||
A new tab will be opened if |_maxTabs| has not been exceeded, otherwise
|
||||
the URL will be loaded in the next tab.
|
||||
"""
|
||||
page_to_load = self.urls()[self._pages_loaded % len(self.urls())]
|
||||
tabs_loaded = len(self._tabs)
|
||||
is_new_tab = False
|
||||
|
||||
if tabs_loaded < self._maxTabs and tabs_loaded <= self._pages_loaded:
|
||||
full_tab_list = self.marionette.window_handles
|
||||
|
||||
# Trigger opening a new tab by finding the new tab button and
|
||||
# clicking it
|
||||
newtab_button = (self.marionette.find_element('id', 'tabbrowser-tabs')
|
||||
.find_element('anon attribute',
|
||||
{'anonid': 'tabs-newtab-button'}))
|
||||
newtab_button.click()
|
||||
|
||||
self.wait_for_condition(lambda mn: len(
|
||||
mn.window_handles) == tabs_loaded + 1)
|
||||
|
||||
# NB: The tab list isn't sorted, so we do a set diff to determine
|
||||
# which is the new tab
|
||||
new_tab_list = self.marionette.window_handles
|
||||
new_tabs = list(set(new_tab_list) - set(full_tab_list))
|
||||
|
||||
self._tabs.append(new_tabs[0])
|
||||
tabs_loaded += 1
|
||||
|
||||
is_new_tab = True
|
||||
|
||||
tab_idx = self._pages_loaded % self._maxTabs
|
||||
|
||||
tab = self._tabs[tab_idx]
|
||||
|
||||
# Tell marionette which tab we're on
|
||||
# NB: As a work-around for an e10s marionette bug, only select the tab
|
||||
# if we're really switching tabs.
|
||||
if tabs_loaded > 1:
|
||||
self.logger.info("switching to tab")
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.logger.info("switched to tab")
|
||||
|
||||
with self.marionette.using_context('content'):
|
||||
self.logger.info("loading %s" % page_to_load)
|
||||
self.marionette.navigate(page_to_load)
|
||||
self.logger.info("loaded!")
|
||||
|
||||
# On e10s the tab handle can change after actually loading content
|
||||
if is_new_tab:
|
||||
# First build a set up w/o the current tab
|
||||
old_tabs = set(self._tabs)
|
||||
old_tabs.remove(tab)
|
||||
# Perform a set diff to get the (possibly) new handle
|
||||
[new_tab] = set(self.marionette.window_handles) - old_tabs
|
||||
# Update the tab list at the current index to preserve the tab
|
||||
# ordering
|
||||
self._tabs[tab_idx] = new_tab
|
||||
|
||||
# give the page time to settle
|
||||
time.sleep(self._perTabPause)
|
||||
|
||||
self._pages_loaded += 1
|
||||
|
||||
def signal_user_active(self):
|
||||
"""Signal to the browser that the user is active.
|
||||
|
||||
Normally when being driven by marionette the browser thinks the
|
||||
user is inactive the whole time because user activity is
|
||||
detected by looking at key and mouse events.
|
||||
|
||||
This would be a problem for this test because user inactivity is
|
||||
used to schedule some GCs (in particular shrinking GCs), so it
|
||||
would make this unrepresentative of real use.
|
||||
|
||||
Instead we manually cause some inconsequential activity (a press
|
||||
and release of the shift key) to make the browser think the user
|
||||
is active. Then when we sleep to allow things to settle the
|
||||
browser will see the user as becoming inactive and trigger
|
||||
appropriate GCs, as would have happened in real use.
|
||||
"""
|
||||
action = Actions(self.marionette)
|
||||
action.key_down(Keys.SHIFT)
|
||||
action.key_up(Keys.SHIFT)
|
||||
action.perform()
|
||||
|
||||
def open_pages(self):
|
||||
"""
|
||||
Opens all pages with our given configuration.
|
||||
"""
|
||||
for _ in range(self.pages_to_load()):
|
||||
self.open_and_focus()
|
||||
self.signal_user_active()
|
|
@ -32,10 +32,11 @@ PERF_SUITES = [
|
|||
{ 'name': "Images", 'node': "explicit/images/" }
|
||||
]
|
||||
|
||||
def update_checkpoint_paths(checkpoint_files):
|
||||
def update_checkpoint_paths(checkpoint_files, checkpoints):
|
||||
"""
|
||||
Updates CHECKPOINTS with memory report file fetched in data_path
|
||||
Updates checkpoints with memory report file fetched in data_path
|
||||
:param checkpoint_files: list of files in data_path
|
||||
:param checkpoints: The checkpoints to update the path of.
|
||||
"""
|
||||
target_path = [['Start-', 0],
|
||||
['StartSettled-', 0],
|
||||
|
@ -49,9 +50,14 @@ def update_checkpoint_paths(checkpoint_files):
|
|||
for i in range(len(target_path)):
|
||||
(name, idx) = target_path[i]
|
||||
paths = sorted([x for x in checkpoint_files if name in x])
|
||||
CHECKPOINTS[i]['path'] = paths[idx]
|
||||
if paths:
|
||||
indices = [i for i,x in enumerate(checkpoints) if name in x['path']]
|
||||
if indices:
|
||||
checkpoints[indices[0]]['path'] = paths[idx]
|
||||
else:
|
||||
print "found files but couldn't find %s" % name
|
||||
|
||||
def create_suite(name, node, data_path):
|
||||
def create_suite(name, node, data_path, checkpoints=CHECKPOINTS):
|
||||
"""
|
||||
Creates a suite suitable for adding to a perfherder blob. Calculates the
|
||||
geometric mean of the checkpoint values and adds that to the suite as
|
||||
|
@ -60,6 +66,7 @@ def create_suite(name, node, data_path):
|
|||
:param name: The name of the suite.
|
||||
:param node: The path of the data node to extract data from.
|
||||
:param data_path: The directory to retrieve data from.
|
||||
:param checkpoints: Which checkpoints to include.
|
||||
"""
|
||||
suite = {
|
||||
'name': name,
|
||||
|
@ -71,16 +78,22 @@ def create_suite(name, node, data_path):
|
|||
suite['extraOptions'] = ["stylo"]
|
||||
if 'STYLO_THREADS' in os.environ and os.environ['STYLO_THREADS'] == '1':
|
||||
suite['extraOptions'] = ["stylo-sequential"]
|
||||
update_checkpoint_paths(glob.glob(os.path.join(data_path, "memory-report*")))
|
||||
update_checkpoint_paths(glob.glob(os.path.join(data_path, "memory-report*")), checkpoints)
|
||||
|
||||
total = 0
|
||||
for checkpoint in CHECKPOINTS:
|
||||
for checkpoint in checkpoints:
|
||||
memory_report_path = os.path.join(data_path, checkpoint['path'])
|
||||
|
||||
name_filter = checkpoint.get('name_filter', None)
|
||||
count = checkpoint.get('count', 0)
|
||||
|
||||
if node != "resident":
|
||||
totals = parse_about_memory.calculate_memory_report_values(
|
||||
memory_report_path, node)
|
||||
value = sum(totals.values())
|
||||
memory_report_path, node, name_filter)
|
||||
if count:
|
||||
value = sum(totals.values()[:count])
|
||||
else:
|
||||
value = sum(totals.values())
|
||||
else:
|
||||
# For "resident" we really want RSS of the chrome ("Main") process
|
||||
# and USS of the child processes. We'll still call it resident
|
||||
|
@ -104,12 +117,12 @@ def create_suite(name, node, data_path):
|
|||
|
||||
# Add the geometric mean. For more details on the calculation see:
|
||||
# https://en.wikipedia.org/wiki/Geometric_mean#Relationship_with_arithmetic_mean_of_logarithms
|
||||
suite['value'] = math.exp(total / len(CHECKPOINTS))
|
||||
suite['value'] = math.exp(total / len(checkpoints))
|
||||
|
||||
return suite
|
||||
|
||||
|
||||
def create_perf_data(data_path):
|
||||
def create_perf_data(data_path, perf_suites=PERF_SUITES, checkpoints=CHECKPOINTS):
|
||||
"""
|
||||
Builds up a performance data blob suitable for submitting to perfherder.
|
||||
"""
|
||||
|
@ -122,8 +135,8 @@ def create_perf_data(data_path):
|
|||
'suites': []
|
||||
}
|
||||
|
||||
for suite in PERF_SUITES:
|
||||
perf_blob['suites'].append(create_suite(suite['name'], suite['node'], data_path))
|
||||
for suite in perf_suites:
|
||||
perf_blob['suites'].append(create_suite(suite['name'], suite['node'], data_path, checkpoints))
|
||||
|
||||
return perf_blob
|
||||
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
# 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/.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
AWSY_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
if AWSY_PATH not in sys.path:
|
||||
sys.path.append(AWSY_PATH)
|
||||
|
||||
from awsy.awsy_test_case import AwsyTestCase
|
||||
|
||||
# A description of each checkpoint and the root path to it.
|
||||
CHECKPOINTS = [
|
||||
{
|
||||
'name': "After tabs open [+30s, forced GC]",
|
||||
'path': "memory-report-TabsOpenForceGC-4.json.gz",
|
||||
'name_filter': 'Web Content', # We only want the content process
|
||||
'count': 1 # We only care about the first one
|
||||
},
|
||||
]
|
||||
|
||||
# A description of each perfherder suite and the path to its values.
|
||||
PERF_SUITES = [
|
||||
{ 'name': "Base Content Resident Unique Memory", 'node': "resident-unique" },
|
||||
{ 'name': "Base Content Heap Unclassified", 'node': "explicit/heap-unclassified" },
|
||||
{ 'name': "Base Content JS", 'node': "js-main-runtime/" },
|
||||
]
|
||||
|
||||
class TestMemoryUsage(AwsyTestCase):
|
||||
"""
|
||||
Provides a base case test that just loads about:memory and reports the
|
||||
memory usage of a single content process.
|
||||
"""
|
||||
|
||||
def urls(self):
|
||||
return self._urls
|
||||
|
||||
def perf_suites(self):
|
||||
return PERF_SUITES
|
||||
|
||||
def perf_checkpoints(self):
|
||||
return CHECKPOINTS
|
||||
|
||||
def setUp(self):
|
||||
AwsyTestCase.setUp(self)
|
||||
self.logger.info("setting up!")
|
||||
|
||||
# Override AwsyTestCase value, this is always going to be 1 iteration.
|
||||
self._iterations = 1
|
||||
|
||||
self._urls = ['about:blank'] * 4
|
||||
|
||||
self.logger.info("areweslimyet run by %d pages, %d iterations, %d perTabPause, %d settleWaitTime"
|
||||
% (self._pages_to_load, self._iterations, self._perTabPause, self._settleWaitTime))
|
||||
self.logger.info("done setting up!")
|
||||
|
||||
def tearDown(self):
|
||||
self.logger.info("tearing down!")
|
||||
AwsyTestCase.tearDown(self)
|
||||
self.logger.info("done tearing down!")
|
||||
|
||||
def test_open_tabs(self):
|
||||
"""Marionette test entry that returns an array of checkoint arrays.
|
||||
|
||||
This will generate a set of checkpoints for each iteration requested.
|
||||
Upon succesful completion the results will be stored in
|
||||
|self.testvars["results"]| and accessible to the test runner via the
|
||||
|testvars| object it passed in.
|
||||
"""
|
||||
# setup the results array
|
||||
results = [[] for _ in range(self.iterations())]
|
||||
|
||||
def create_checkpoint(name, iteration):
|
||||
checkpoint = self.do_memory_report(name, iteration)
|
||||
self.assertIsNotNone(checkpoint, "Checkpoint was recorded")
|
||||
results[iteration].append(checkpoint)
|
||||
|
||||
for itr in range(self.iterations()):
|
||||
self.open_pages()
|
||||
self.settle()
|
||||
self.settle()
|
||||
self.assertTrue(self.do_full_gc())
|
||||
self.settle()
|
||||
create_checkpoint("TabsOpenForceGC", itr)
|
||||
|
||||
# TODO(ER): Temporary hack until bug 1121139 lands
|
||||
self.logger.info("setting results")
|
||||
self.testvars["results"] = results
|
|
@ -2,31 +2,19 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import fnmatch
|
||||
import glob
|
||||
import gzip
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from marionette_harness import MarionetteTestCase
|
||||
from marionette_driver import Actions
|
||||
from marionette_driver.errors import JavascriptException, ScriptTimeoutException
|
||||
import mozlog.structured
|
||||
from marionette_driver.keys import Keys
|
||||
|
||||
AWSY_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
if AWSY_PATH not in sys.path:
|
||||
sys.path.append(AWSY_PATH)
|
||||
|
||||
from awsy import ITERATIONS, PER_TAB_PAUSE, SETTLE_WAIT_TIME, MAX_TABS
|
||||
from awsy import process_perf_data, webservers
|
||||
from awsy.awsy_test_case import AwsyTestCase
|
||||
|
||||
|
||||
class TestMemoryUsage(MarionetteTestCase):
|
||||
class TestMemoryUsage(AwsyTestCase):
|
||||
"""Provides a test that collects memory usage at various checkpoints:
|
||||
- "Start" - Just after startup
|
||||
- "StartSettled" - After an additional wait time
|
||||
|
@ -38,23 +26,19 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
- "TabsClosedForceGC" - After forcibly invoking garbage collection
|
||||
"""
|
||||
|
||||
def urls(self):
|
||||
return self._urls
|
||||
|
||||
def perf_suites(self):
|
||||
return process_perf_data.PERF_SUITES
|
||||
|
||||
def perf_checkpoints(self):
|
||||
return process_perf_data.CHECKPOINTS
|
||||
|
||||
def setUp(self):
|
||||
MarionetteTestCase.setUp(self)
|
||||
self.logger = mozlog.structured.structuredlog.get_default_logger()
|
||||
self.logger.info("setting up!")
|
||||
|
||||
self.marionette.set_context('chrome')
|
||||
|
||||
AwsyTestCase.setUp(self)
|
||||
self.logger.info("setting up")
|
||||
self._webroot_dir = self.testvars["webRootDir"]
|
||||
self._resultsDir = self.testvars["resultsDir"]
|
||||
# Be conservative in what we delete automatically.
|
||||
for f in glob.glob(os.path.join(self._resultsDir, 'memory-report-*.json.gz')):
|
||||
os.unlink(f)
|
||||
for f in glob.glob(os.path.join(self._resultsDir, 'perfherder_data.json')):
|
||||
os.unlink(f)
|
||||
for f in glob.glob(os.path.join(self._resultsDir, 'dmd-*.json.gz')):
|
||||
os.unlink(f)
|
||||
|
||||
self._urls = []
|
||||
|
||||
urls = None
|
||||
|
@ -65,89 +49,33 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
urls = fp.readlines()
|
||||
urls = map(lambda x:x.replace('localhost', 'localhost:{}'), urls)
|
||||
|
||||
# Optional testvars.
|
||||
self._pages_to_load = self.testvars.get("entities", len(urls))
|
||||
self._iterations = self.testvars.get("iterations", ITERATIONS)
|
||||
self._perTabPause = self.testvars.get("perTabPause", PER_TAB_PAUSE)
|
||||
self._settleWaitTime = self.testvars.get("settleWaitTime", SETTLE_WAIT_TIME)
|
||||
self._maxTabs = self.testvars.get("maxTabs", MAX_TABS)
|
||||
self._dmd = self.testvars.get("dmd", False)
|
||||
|
||||
# We haven't set self._urls yet, so this value might be zero if
|
||||
# 'entities' wasn't specified.
|
||||
to_load = self.pages_to_load()
|
||||
if not to_load:
|
||||
to_load = len(urls)
|
||||
self._webservers = webservers.WebServers("localhost",
|
||||
8001,
|
||||
self._webroot_dir,
|
||||
self._pages_to_load)
|
||||
to_load)
|
||||
self._webservers.start()
|
||||
for url, server in zip(urls, self._webservers.servers):
|
||||
self._urls.append(url.strip().format(server.port))
|
||||
|
||||
self.logger.info("areweslimyet run by %d pages, %d iterations, %d perTabPause, %d settleWaitTime"
|
||||
% (self._pages_to_load, self._iterations, self._perTabPause, self._settleWaitTime))
|
||||
self.reset_state()
|
||||
self.logger.info("done setting up!")
|
||||
|
||||
def tearDown(self):
|
||||
self.logger.info("tearing down!")
|
||||
MarionetteTestCase.tearDown(self)
|
||||
|
||||
self.logger.info("tearing down webservers!")
|
||||
self._webservers.stop()
|
||||
|
||||
self.logger.info("processing data in %s!" % self._resultsDir)
|
||||
perf_blob = process_perf_data.create_perf_data(self._resultsDir)
|
||||
self.logger.info("PERFHERDER_DATA: %s" % json.dumps(perf_blob))
|
||||
|
||||
perf_file = os.path.join(self._resultsDir, "perfherder_data.json")
|
||||
with open(perf_file, 'w') as fp:
|
||||
json.dump(perf_blob, fp, indent=2)
|
||||
self.logger.info("Perfherder data written to %s" % perf_file)
|
||||
|
||||
if self._dmd:
|
||||
self.cleanup_dmd()
|
||||
|
||||
# copy it to moz upload dir if set
|
||||
if 'MOZ_UPLOAD_DIR' in os.environ:
|
||||
for file in os.listdir(self._resultsDir):
|
||||
file = os.path.join(self._resultsDir, file)
|
||||
if os.path.isfile(file):
|
||||
shutil.copy2(file, os.environ["MOZ_UPLOAD_DIR"])
|
||||
AwsyTestCase.tearDown(self)
|
||||
|
||||
self.logger.info("done tearing down!")
|
||||
|
||||
def cleanup_dmd(self):
|
||||
"""
|
||||
Handles moving DMD reports from the temp dir to our resultsDir.
|
||||
"""
|
||||
from dmd import fixStackTraces
|
||||
|
||||
# Move DMD files from temp dir to resultsDir.
|
||||
tmpdir = tempfile.gettempdir()
|
||||
tmp_files = os.listdir(tmpdir)
|
||||
for f in fnmatch.filter(tmp_files, "dmd-*.json.gz"):
|
||||
f = os.path.join(tmpdir, f)
|
||||
self.logger.info("Fixing stacks for %s, this may take a while" % f)
|
||||
isZipped = True
|
||||
fixStackTraces(f, isZipped, gzip.open)
|
||||
shutil.move(f, self._resultsDir)
|
||||
|
||||
# Also attempt to cleanup the unified memory reports.
|
||||
for f in fnmatch.filter(tmp_files, "unified-memory-report-*.json.gz"):
|
||||
try:
|
||||
os.remove(f)
|
||||
except OSError:
|
||||
self.logger.info("Unable to remove %s" % f)
|
||||
|
||||
def reset_state(self):
|
||||
self._pages_loaded = 0
|
||||
|
||||
# Close all tabs except one
|
||||
for x in self.marionette.window_handles[1:]:
|
||||
self.logger.info("closing window: %s" % x)
|
||||
self.marionette.switch_to_window(x)
|
||||
self.marionette.close()
|
||||
|
||||
self._tabs = self.marionette.window_handles
|
||||
self.marionette.switch_to_window(self._tabs[0])
|
||||
|
||||
def clear_preloaded_browser(self):
|
||||
"""
|
||||
Clears out the preloaded browser.
|
||||
|
@ -176,234 +104,6 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
if result:
|
||||
self.logger.info(result)
|
||||
|
||||
def do_full_gc(self):
|
||||
"""Performs a full garbage collection cycle and returns when it is finished.
|
||||
|
||||
Returns True on success and False on failure.
|
||||
"""
|
||||
# NB: we could do this w/ a signal or the fifo queue too
|
||||
self.logger.info("starting gc...")
|
||||
gc_script = """
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Services.obs.notifyObservers(null, "child-mmu-request", null);
|
||||
|
||||
let memMgrSvc = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
|
||||
memMgrSvc.minimizeMemoryUsage(() => marionetteScriptFinished("gc done!"));
|
||||
"""
|
||||
result = None
|
||||
try:
|
||||
result = self.marionette.execute_async_script(
|
||||
gc_script, script_timeout=180000)
|
||||
except JavascriptException, e:
|
||||
self.logger.error("GC JavaScript error: %s" % e)
|
||||
except ScriptTimeoutException:
|
||||
self.logger.error("GC timed out")
|
||||
except:
|
||||
self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
|
||||
else:
|
||||
self.logger.info(result)
|
||||
|
||||
return result is not None
|
||||
|
||||
def do_memory_report(self, checkpointName, iteration):
|
||||
"""Creates a memory report for all processes and and returns the
|
||||
checkpoint.
|
||||
|
||||
This will block until all reports are retrieved or a timeout occurs.
|
||||
Returns the checkpoint or None on error.
|
||||
|
||||
:param checkpointName: The name of the checkpoint.
|
||||
"""
|
||||
self.logger.info("starting checkpoint %s..." % checkpointName)
|
||||
|
||||
checkpoint_file = "memory-report-%s-%d.json.gz" % (checkpointName, iteration)
|
||||
checkpoint_path = os.path.join(self._resultsDir, checkpoint_file)
|
||||
# On Windows, replace / with the Windows directory
|
||||
# separator \ and escape it to prevent it from being
|
||||
# interpreted as an escape character.
|
||||
if sys.platform.startswith('win'):
|
||||
checkpoint_path = (checkpoint_path.
|
||||
replace('\\', '\\\\').
|
||||
replace('/', '\\\\'))
|
||||
|
||||
checkpoint_script = r"""
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
|
||||
dumper.dumpMemoryReportsToNamedFile(
|
||||
"%s",
|
||||
() => marionetteScriptFinished("memory report done!"),
|
||||
null,
|
||||
/* anonymize */ false);
|
||||
""" % checkpoint_path
|
||||
|
||||
checkpoint = None
|
||||
try:
|
||||
finished = self.marionette.execute_async_script(
|
||||
checkpoint_script, script_timeout=60000)
|
||||
if finished:
|
||||
checkpoint = checkpoint_path
|
||||
except JavascriptException, e:
|
||||
self.logger.error("Checkpoint JavaScript error: %s" % e)
|
||||
except ScriptTimeoutException:
|
||||
self.logger.error("Memory report timed out")
|
||||
except:
|
||||
self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
|
||||
else:
|
||||
self.logger.info("checkpoint created, stored in %s" % checkpoint_path)
|
||||
|
||||
# Now trigger a DMD report if requested.
|
||||
if self._dmd:
|
||||
self.do_dmd(checkpointName, iteration)
|
||||
|
||||
return checkpoint
|
||||
|
||||
def do_dmd(self, checkpointName, iteration):
|
||||
"""
|
||||
Triggers DMD reports that are used to help identify sources of
|
||||
'heap-unclassified'.
|
||||
|
||||
NB: This will dump DMD reports to the temp dir. Unfortunately it also
|
||||
dumps memory reports, but that's all we have to work with right now.
|
||||
"""
|
||||
self.logger.info("Starting %s DMD reports..." % checkpointName)
|
||||
|
||||
ident = "%s-%d" % (checkpointName, iteration)
|
||||
|
||||
# TODO(ER): This actually takes a minimize argument. We could use that
|
||||
# rather than have a separate `do_gc` function. Also it generates a
|
||||
# memory report so we could combine this with `do_checkpoint`. The main
|
||||
# issue would be moving everything out of the temp dir.
|
||||
#
|
||||
# Generated files have the form:
|
||||
# dmd-<checkpoint>-<iteration>-pid.json.gz, ie:
|
||||
# dmd-TabsOpenForceGC-0-10885.json.gz
|
||||
#
|
||||
# and for the memory report:
|
||||
# unified-memory-report-<checkpoint>-<iteration>.json.gz
|
||||
dmd_script = r"""
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].getService(Ci.nsIMemoryInfoDumper);
|
||||
dumper.dumpMemoryInfoToTempDir(
|
||||
"%s",
|
||||
/* anonymize = */ false,
|
||||
/* minimize = */ false);
|
||||
""" % ident
|
||||
|
||||
try:
|
||||
# This is async and there's no callback so we use the existence
|
||||
# of an incomplete memory report to check if it hasn't finished yet.
|
||||
self.marionette.execute_script(dmd_script, script_timeout=60000)
|
||||
tmpdir = tempfile.gettempdir()
|
||||
prefix = "incomplete-unified-memory-report-%s-%d-*" % (checkpointName, iteration)
|
||||
max_wait = 60
|
||||
elapsed = 0
|
||||
while fnmatch.filter(os.listdir(tmpdir), prefix) and elapsed < max_wait:
|
||||
self.logger.info("Waiting for memory report to finish")
|
||||
time.sleep(1)
|
||||
elapsed += 1
|
||||
|
||||
incomplete = fnmatch.filter(os.listdir(tmpdir), prefix)
|
||||
if incomplete:
|
||||
# The memory reports never finished.
|
||||
self.logger.error("Incomplete memory reports leftover.")
|
||||
for f in incomplete:
|
||||
os.remove(os.path.join(tmpdir, f))
|
||||
|
||||
except JavascriptException, e:
|
||||
self.logger.error("DMD JavaScript error: %s" % e)
|
||||
except ScriptTimeoutException:
|
||||
self.logger.error("DMD timed out")
|
||||
except:
|
||||
self.logger.error("Unexpected error: %s" % sys.exc_info()[0])
|
||||
else:
|
||||
self.logger.info("DMD started, prefixed with %s" % ident)
|
||||
|
||||
def open_and_focus(self):
|
||||
"""Opens the next URL in the list and focuses on the tab it is opened in.
|
||||
|
||||
A new tab will be opened if |_maxTabs| has not been exceeded, otherwise
|
||||
the URL will be loaded in the next tab.
|
||||
"""
|
||||
page_to_load = self._urls[self._pages_loaded % len(self._urls)]
|
||||
tabs_loaded = len(self._tabs)
|
||||
is_new_tab = False
|
||||
|
||||
if tabs_loaded < self._maxTabs and tabs_loaded <= self._pages_loaded:
|
||||
full_tab_list = self.marionette.window_handles
|
||||
|
||||
# Trigger opening a new tab by finding the new tab button and
|
||||
# clicking it
|
||||
newtab_button = (self.marionette.find_element('id', 'tabbrowser-tabs')
|
||||
.find_element('anon attribute',
|
||||
{'anonid': 'tabs-newtab-button'}))
|
||||
newtab_button.click()
|
||||
|
||||
self.wait_for_condition(lambda mn: len(
|
||||
mn.window_handles) == tabs_loaded + 1)
|
||||
|
||||
# NB: The tab list isn't sorted, so we do a set diff to determine
|
||||
# which is the new tab
|
||||
new_tab_list = self.marionette.window_handles
|
||||
new_tabs = list(set(new_tab_list) - set(full_tab_list))
|
||||
|
||||
self._tabs.append(new_tabs[0])
|
||||
tabs_loaded += 1
|
||||
|
||||
is_new_tab = True
|
||||
|
||||
tab_idx = self._pages_loaded % self._maxTabs
|
||||
|
||||
tab = self._tabs[tab_idx]
|
||||
|
||||
# Tell marionette which tab we're on
|
||||
# NB: As a work-around for an e10s marionette bug, only select the tab
|
||||
# if we're really switching tabs.
|
||||
if tabs_loaded > 1:
|
||||
self.logger.info("switching to tab")
|
||||
self.marionette.switch_to_window(tab)
|
||||
self.logger.info("switched to tab")
|
||||
|
||||
with self.marionette.using_context('content'):
|
||||
self.logger.info("loading %s" % page_to_load)
|
||||
self.marionette.navigate(page_to_load)
|
||||
self.logger.info("loaded!")
|
||||
|
||||
# On e10s the tab handle can change after actually loading content
|
||||
if is_new_tab:
|
||||
# First build a set up w/o the current tab
|
||||
old_tabs = set(self._tabs)
|
||||
old_tabs.remove(tab)
|
||||
# Perform a set diff to get the (possibly) new handle
|
||||
[new_tab] = set(self.marionette.window_handles) - old_tabs
|
||||
# Update the tab list at the current index to preserve the tab
|
||||
# ordering
|
||||
self._tabs[tab_idx] = new_tab
|
||||
|
||||
# give the page time to settle
|
||||
time.sleep(self._perTabPause)
|
||||
|
||||
self._pages_loaded += 1
|
||||
|
||||
def signal_user_active(self):
|
||||
"""Signal to the browser that the user is active.
|
||||
|
||||
Normally when being driven by marionette the browser thinks the
|
||||
user is inactive the whole time because user activity is
|
||||
detected by looking at key and mouse events.
|
||||
|
||||
This would be a problem for this test because user inactivity is
|
||||
used to schedule some GCs (in particular shrinking GCs), so it
|
||||
would make this unrepresentative of real use.
|
||||
|
||||
Instead we manually cause some inconsequential activity (a press
|
||||
and release of the shift key) to make the browser think the user
|
||||
is active. Then when we sleep to allow things to settle the
|
||||
browser will see the user as becoming inactive and trigger
|
||||
appropriate GCs, as would have happened in real use.
|
||||
"""
|
||||
action = Actions(self.marionette)
|
||||
action.key_down(Keys.SHIFT)
|
||||
action.key_up(Keys.SHIFT)
|
||||
action.perform()
|
||||
|
||||
def test_open_tabs(self):
|
||||
"""Marionette test entry that returns an array of checkoint arrays.
|
||||
|
||||
|
@ -413,7 +113,7 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
|testvars| object it passed in.
|
||||
"""
|
||||
# setup the results array
|
||||
results = [[] for _ in range(self._iterations)]
|
||||
results = [[] for _ in range(self.iterations())]
|
||||
|
||||
def create_checkpoint(name, iteration):
|
||||
checkpoint = self.do_memory_report(name, iteration)
|
||||
|
@ -423,16 +123,14 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
# The first iteration gets Start and StartSettled entries before
|
||||
# opening tabs
|
||||
create_checkpoint("Start", 0)
|
||||
time.sleep(self._settleWaitTime)
|
||||
self.settle()
|
||||
create_checkpoint("StartSettled", 0)
|
||||
|
||||
for itr in range(self._iterations):
|
||||
for _ in range(self._pages_to_load):
|
||||
self.open_and_focus()
|
||||
self.signal_user_active()
|
||||
for itr in range(self.iterations()):
|
||||
self.open_pages()
|
||||
|
||||
create_checkpoint("TabsOpen", itr)
|
||||
time.sleep(self._settleWaitTime)
|
||||
self.settle()
|
||||
create_checkpoint("TabsOpenSettled", itr)
|
||||
self.assertTrue(self.do_full_gc())
|
||||
create_checkpoint("TabsOpenForceGC", itr)
|
||||
|
@ -440,9 +138,6 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
# Close all tabs
|
||||
self.reset_state()
|
||||
|
||||
self.logger.info("switching to first window")
|
||||
self.marionette.switch_to_window(self._tabs[0])
|
||||
self.logger.info("switched to first window")
|
||||
with self.marionette.using_context('content'):
|
||||
self.logger.info("navigating to about:blank")
|
||||
self.marionette.navigate("about:blank")
|
||||
|
@ -457,7 +152,7 @@ class TestMemoryUsage(MarionetteTestCase):
|
|||
self.clear_preloaded_browser()
|
||||
|
||||
create_checkpoint("TabsClosed", itr)
|
||||
time.sleep(self._settleWaitTime)
|
||||
self.settle()
|
||||
create_checkpoint("TabsClosedSettled", itr)
|
||||
self.assertTrue(self.do_full_gc(), "GC ran")
|
||||
create_checkpoint("TabsClosedForceGC", itr)
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"browser.urlbar.userMadeSearchSuggestionsChoice": true,
|
||||
"javascript.options.asyncstack": false,
|
||||
"image.mem.surfacecache.min_expiration_ms": 10000,
|
||||
"network.proxy.socks": "localhost",
|
||||
"network.proxy.socks_port": 90000,
|
||||
"network.proxy.socks_remote_dns": true,
|
||||
"network.proxy.type": 1,
|
||||
"plugin.disable": true,
|
||||
"startup.homepage_override_url": "",
|
||||
"startup.homepage_welcome_url": "",
|
||||
"browser.startup.homepage": "about:blank",
|
||||
"browser.newtabpage.enabled": false
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"entities": 4,
|
||||
"iterations": 1,
|
||||
"perTabPause": 10,
|
||||
"settleWaitTime": 60
|
||||
}
|
|
@ -76,7 +76,7 @@ class MachCommands(MachCommandBase):
|
|||
runtime_testvars = {}
|
||||
for arg in ('webRootDir', 'pageManifest', 'resultsDir', 'entities', 'iterations',
|
||||
'perTabPause', 'settleWaitTime', 'maxTabs', 'dmd'):
|
||||
if kwargs[arg]:
|
||||
if arg in kwargs and kwargs[arg] is not None:
|
||||
runtime_testvars[arg] = kwargs[arg]
|
||||
|
||||
if 'webRootDir' not in runtime_testvars:
|
||||
|
|
|
@ -816,7 +816,8 @@ class AndroidEmulatorTest(TestingMixin, BaseScript, MozbaseMixin, CodeCoverageMi
|
|||
log_obj=self.log_obj,
|
||||
error_list=[])
|
||||
self.run_command(final_cmd, cwd=cwd, env=env, output_parser=parser)
|
||||
tbpl_status, log_level, summary = parser.evaluate_parser(0, summary)
|
||||
tbpl_status, log_level, summary = parser.evaluate_parser(0,
|
||||
previous_summary=summary)
|
||||
parser.append_tinderboxprint_line(self.test_suite)
|
||||
|
||||
self.info("##### %s log ends" % self.test_suite)
|
||||
|
|
|
@ -49,6 +49,12 @@ class AWSY(TestingMixin, MercurialScript, BlobUploadMixin, TooltoolMixin, CodeCo
|
|||
"dest": "enable_webrender",
|
||||
"default": False,
|
||||
"help": "Tries to enable the WebRender compositor.",
|
||||
}],
|
||||
[["--base"],
|
||||
{"action": "store_true",
|
||||
"dest": "test_about_blank",
|
||||
"default": False,
|
||||
"help": "Runs the about:blank base case memory test.",
|
||||
}]
|
||||
] + testing_config_options + copy.deepcopy(blobupload_config_options) \
|
||||
+ copy.deepcopy(code_coverage_config_options)
|
||||
|
@ -151,8 +157,13 @@ class AWSY(TestingMixin, MercurialScript, BlobUploadMixin, TooltoolMixin, CodeCo
|
|||
runtime_testvars_file.close()
|
||||
|
||||
cmd = ['marionette']
|
||||
cmd.append("--preferences=%s" % os.path.join(self.awsy_path, "conf", "prefs.json"))
|
||||
cmd.append("--testvars=%s" % os.path.join(self.awsy_path, "conf", "testvars.json"))
|
||||
|
||||
if self.config['test_about_blank']:
|
||||
cmd.append("--testvars=%s" % os.path.join(self.awsy_path, "conf",
|
||||
"base-testvars.json"))
|
||||
else:
|
||||
cmd.append("--testvars=%s" % os.path.join(self.awsy_path, "conf", "testvars.json"))
|
||||
|
||||
cmd.append("--testvars=%s" % runtime_testvars_path)
|
||||
cmd.append("--log-raw=-")
|
||||
cmd.append("--log-errorsummary=%s" % error_summary_file)
|
||||
|
@ -166,7 +177,14 @@ class AWSY(TestingMixin, MercurialScript, BlobUploadMixin, TooltoolMixin, CodeCo
|
|||
# self.symbols_path
|
||||
cmd.append('--symbols-path=%s' % self.symbols_path)
|
||||
|
||||
test_file = os.path.join(self.awsy_libdir, 'test_memory_usage.py')
|
||||
if self.config['test_about_blank']:
|
||||
test_file = os.path.join(self.awsy_libdir, 'test_base_memory_usage.py')
|
||||
prefs_file = "base-prefs.json"
|
||||
else:
|
||||
test_file = os.path.join(self.awsy_libdir, 'test_memory_usage.py')
|
||||
prefs_file = "prefs.json"
|
||||
|
||||
cmd.append("--preferences=%s" % os.path.join(self.awsy_path, "conf", prefs_file))
|
||||
cmd.append(test_file)
|
||||
|
||||
if self.config['single_stylo_traversal']:
|
||||
|
|
|
@ -1887,7 +1887,7 @@ class Dictionary extends ExtensionData {
|
|||
async startup(reason) {
|
||||
this.dictionaries = {};
|
||||
for (let [lang, path] of Object.entries(this.startupData.dictionaries)) {
|
||||
let uri = Services.io.newURI(path, null, this.rootURI);
|
||||
let uri = Services.io.newURI(path.slice(0, -4) + ".aff", null, this.rootURI);
|
||||
this.dictionaries[lang] = uri;
|
||||
|
||||
spellCheck.addDictionary(lang, uri);
|
||||
|
|
|
@ -121,7 +121,7 @@ skip-if = os == 'android' && debug # bug 1397615
|
|||
skip-if = os == 'android'
|
||||
[test_ext_webrequest_background_events.html]
|
||||
[test_ext_webrequest_basic.html]
|
||||
skip-if = os == 'android' && debug || (os == 'linux' && !asan && bits == 64) # bug 1397615, bug 1455405
|
||||
skip-if = os == 'android' && debug || (os == 'linux' && !asan) # bug 1397615, bug 1455405
|
||||
[test_ext_webrequest_errors.html]
|
||||
[test_ext_webrequest_filter.html]
|
||||
[test_ext_webrequest_frameId.html]
|
||||
|
|
|
@ -2056,19 +2056,6 @@ var AddonManagerInternal = {
|
|||
.installTemporaryAddon(aFile);
|
||||
},
|
||||
|
||||
installAddonFromSources(aFile) {
|
||||
if (!gStarted)
|
||||
throw Components.Exception("AddonManager is not initialized",
|
||||
Cr.NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
if (!(aFile instanceof Ci.nsIFile))
|
||||
throw Components.Exception("aFile must be a nsIFile",
|
||||
Cr.NS_ERROR_INVALID_ARG);
|
||||
|
||||
return AddonManagerInternal._getProviderByName("XPIProvider")
|
||||
.installAddonFromSources(aFile);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an Addon corresponding to an instance ID.
|
||||
* @param aInstanceID
|
||||
|
@ -3441,10 +3428,6 @@ var AddonManager = {
|
|||
return AddonManagerInternal.installTemporaryAddon(aDirectory);
|
||||
},
|
||||
|
||||
installAddonFromSources(aDirectory) {
|
||||
return AddonManagerInternal.installAddonFromSources(aDirectory);
|
||||
},
|
||||
|
||||
getAddonByInstanceID(aInstanceID) {
|
||||
return AddonManagerInternal.getAddonByInstanceID(aInstanceID);
|
||||
},
|
||||
|
|
|
@ -675,16 +675,6 @@ var AddonTestUtils = {
|
|||
});
|
||||
},
|
||||
|
||||
verifySignedDirectoryAsync(root, dir, callback) {
|
||||
// First try calling the real cert DB
|
||||
this._genuine.verifySignedDirectoryAsync(root, dir, (result, cert) => {
|
||||
verifyCert(dir.clone(), result, cert, callback)
|
||||
.then(([callback, result, cert]) => {
|
||||
callback.verifySignedDirectoryFinished(result, cert);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIX509CertDB]),
|
||||
};
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче