зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
1401934d7b
|
@ -4302,6 +4302,8 @@ function updateEditUIVisibility() {
|
|||
let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
|
||||
let placesContextMenuPopupState = document.getElementById("placesContext").state;
|
||||
|
||||
let oldVisible = gEditUIVisible;
|
||||
|
||||
// The UI is visible if the Edit menu is opening or open, if the context menu
|
||||
// is open, or if the toolbar has been customized to include the Cut, Copy,
|
||||
// or Paste toolbar buttons.
|
||||
|
@ -4310,18 +4312,35 @@ function updateEditUIVisibility() {
|
|||
contextMenuPopupState == "showing" ||
|
||||
contextMenuPopupState == "open" ||
|
||||
placesContextMenuPopupState == "showing" ||
|
||||
placesContextMenuPopupState == "open" ||
|
||||
document.getElementById("edit-controls") ? true : false;
|
||||
placesContextMenuPopupState == "open";
|
||||
if (!gEditUIVisible) {
|
||||
// Now check the edit-controls toolbar buttons.
|
||||
let placement = CustomizableUI.getPlacementOfWidget("edit-controls");
|
||||
let areaType = placement ? CustomizableUI.getAreaType(placement.area) : "";
|
||||
if (areaType == CustomizableUI.TYPE_MENU_PANEL) {
|
||||
let panelUIMenuPopupState = document.getElementById("PanelUI-popup").state;
|
||||
if (panelUIMenuPopupState == "showing" || panelUIMenuPopupState == "open") {
|
||||
gEditUIVisible = true;
|
||||
}
|
||||
} else if (areaType == CustomizableUI.TYPE_TOOLBAR) {
|
||||
// The edit controls are on a toolbar, so they are visible.
|
||||
gEditUIVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
// No need to update commands if the edit UI visibility has not changed.
|
||||
if (gEditUIVisible == oldVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If UI is visible, update the edit commands' enabled state to reflect
|
||||
// whether or not they are actually enabled for the current focus/selection.
|
||||
if (gEditUIVisible)
|
||||
if (gEditUIVisible) {
|
||||
goUpdateGlobalEditMenuItems();
|
||||
|
||||
// Otherwise, enable all commands, so that keyboard shortcuts still work,
|
||||
// then lazily determine their actual enabled state when the user presses
|
||||
// a keyboard shortcut.
|
||||
else {
|
||||
} else {
|
||||
// Otherwise, enable all commands, so that keyboard shortcuts still work,
|
||||
// then lazily determine their actual enabled state when the user presses
|
||||
// a keyboard shortcut.
|
||||
goSetCommandEnabled("cmd_undo", true);
|
||||
goSetCommandEnabled("cmd_redo", true);
|
||||
goSetCommandEnabled("cmd_cut", true);
|
||||
|
|
|
@ -1065,6 +1065,8 @@
|
|||
return;
|
||||
|
||||
if (!aForceUpdate) {
|
||||
document.commandDispatcher.lock();
|
||||
|
||||
TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
|
||||
if (!gMultiProcessBrowser) {
|
||||
// old way of measuring tab paint which is not valid with e10s.
|
||||
|
@ -1274,6 +1276,8 @@
|
|||
this.tabContainer._setPositionalAttributes();
|
||||
|
||||
if (!gMultiProcessBrowser) {
|
||||
document.commandDispatcher.unlock();
|
||||
|
||||
let event = new CustomEvent("TabSwitchDone", {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
|
@ -3981,6 +3985,8 @@
|
|||
fromBrowser.removeAttribute("primary");
|
||||
}
|
||||
|
||||
document.commandDispatcher.unlock();
|
||||
|
||||
let event = new CustomEvent("TabSwitchDone", {
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
|
|
|
@ -16,4 +16,5 @@ skip-if = os == 'mac'
|
|||
skip-if = !e10s # Pref and test only relevant for e10s.
|
||||
[browser_opened_file_tab_navigated_to_web.js]
|
||||
[browser_reload_deleted_file.js]
|
||||
[browser_tabswitch_updatecommands.js]
|
||||
[browser_viewsource_of_data_URI_in_file_process.js]
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// This test ensures that only one command update happens when switching tabs.
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function* () {
|
||||
const uri = "data:text/html,<body><input>";
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, uri);
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, uri);
|
||||
|
||||
let updates = 0;
|
||||
function countUpdates(event) { updates++; }
|
||||
let updater = document.getElementById("editMenuCommandSetAll");
|
||||
updater.addEventListener("commandupdate", countUpdates, true);
|
||||
yield BrowserTestUtils.switchTab(gBrowser, tab1);
|
||||
|
||||
is(updates, 1, "only one command update per tab switch");
|
||||
|
||||
updater.removeEventListener("commandupdate", countUpdates, true);
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
yield BrowserTestUtils.removeTab(tab2);
|
||||
});
|
|
@ -160,11 +160,6 @@ const PanelUI = {
|
|||
return;
|
||||
}
|
||||
|
||||
let editControlPlacement = CustomizableUI.getPlacementOfWidget("edit-controls");
|
||||
if (editControlPlacement && editControlPlacement.area == CustomizableUI.AREA_PANEL) {
|
||||
updateEditUIVisibility();
|
||||
}
|
||||
|
||||
let personalBookmarksPlacement = CustomizableUI.getPlacementOfWidget("personal-bookmarks");
|
||||
if (personalBookmarksPlacement &&
|
||||
personalBookmarksPlacement.area == CustomizableUI.AREA_PANEL) {
|
||||
|
@ -292,10 +287,14 @@ const PanelUI = {
|
|||
switch (aEvent.type) {
|
||||
case "popupshowing":
|
||||
this._adjustLabelsForAutoHyphens();
|
||||
updateEditUIVisibility();
|
||||
// Fall through
|
||||
case "popupshown":
|
||||
// Fall through
|
||||
case "popuphiding":
|
||||
if (aEvent.type == "popuphiding") {
|
||||
updateEditUIVisibility();
|
||||
}
|
||||
// Fall through
|
||||
case "popuphidden":
|
||||
this._updateNotifications();
|
||||
|
|
|
@ -155,3 +155,5 @@ skip-if = os == "mac"
|
|||
[browser_switch_to_customize_mode.js]
|
||||
[browser_synced_tabs_menu.js]
|
||||
[browser_check_tooltips_in_navbar.js]
|
||||
[browser_editcontrols_update.js]
|
||||
subsuite = clipboard
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
// This test checks that the edit command enabled state (cut/paste) is updated
|
||||
// properly when the edit controls are on the toolbar, popup and not present.
|
||||
// It also verifies that the performance optimiation implemented by
|
||||
// updateEditUIVisibility in browser.js is applied.
|
||||
|
||||
let isMac = navigator.platform.indexOf("Mac") == 0;
|
||||
|
||||
function checkState(allowCut, desc, testWindow = window) {
|
||||
is(testWindow.document.getElementById("cmd_cut").getAttribute("disabled") == "true", !allowCut, desc + " - cut");
|
||||
is(testWindow.document.getElementById("cmd_paste").getAttribute("disabled") == "true", false, desc + " - paste");
|
||||
}
|
||||
|
||||
// Add a special controller to the urlbar and browser to listen in on when
|
||||
// commands are being updated. Return a promise that resolves when 'count'
|
||||
// updates have occurred.
|
||||
function expectCommandUpdate(count, testWindow = window) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let overrideController = {
|
||||
supportsCommand(cmd) { return cmd == "cmd_delete"; },
|
||||
isCommandEnabled(cmd) {
|
||||
if (!count) {
|
||||
ok(false, "unexpected update");
|
||||
reject();
|
||||
}
|
||||
|
||||
if (!--count) {
|
||||
testWindow.gURLBar.controllers.removeControllerAt(0, overrideController);
|
||||
testWindow.gBrowser.selectedBrowser.controllers.removeControllerAt(0, overrideController);
|
||||
resolve(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!count) {
|
||||
SimpleTest.executeSoon(() => {
|
||||
testWindow.gURLBar.controllers.removeControllerAt(0, overrideController);
|
||||
testWindow.gBrowser.selectedBrowser.controllers.removeControllerAt(0, overrideController);
|
||||
resolve(false);
|
||||
});
|
||||
}
|
||||
|
||||
testWindow.gURLBar.controllers.insertControllerAt(0, overrideController);
|
||||
testWindow.gBrowser.selectedBrowser.controllers.insertControllerAt(0, overrideController);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test_init() {
|
||||
// Put something on the clipboard to verify that the paste button is properly enabled during the test.
|
||||
let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
yield new Promise(resolve => {
|
||||
SimpleTest.waitForClipboard("Sample", function() { clipboardHelper.copyString("Sample"); }, resolve);
|
||||
});
|
||||
|
||||
// Open and close the panel first so that it is fully initialized.
|
||||
yield PanelUI.show();
|
||||
let hiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield hiddenPromise;
|
||||
});
|
||||
|
||||
// Test updating when the panel is open with the edit-controls on the panel.
|
||||
// Updates should occur.
|
||||
add_task(function* test_panelui_opened() {
|
||||
gURLBar.focus();
|
||||
gURLBar.value = "test";
|
||||
|
||||
yield PanelUI.show();
|
||||
|
||||
checkState(false, "Update when edit-controls is on panel and visible");
|
||||
|
||||
let overridePromise = expectCommandUpdate(1);
|
||||
gURLBar.select();
|
||||
yield overridePromise;
|
||||
|
||||
checkState(true, "Update when edit-controls is on panel and selection changed");
|
||||
|
||||
overridePromise = expectCommandUpdate(0);
|
||||
let hiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield hiddenPromise;
|
||||
yield overridePromise;
|
||||
|
||||
// Check that updates do not occur after the panel has been closed.
|
||||
checkState(true, "Update when edit-controls is on panel and hidden");
|
||||
|
||||
// Mac will update the enabled st1ate even when the panel is closed so that
|
||||
// main menubar shortcuts will work properly.
|
||||
overridePromise = expectCommandUpdate(isMac ? 1 : 0);
|
||||
gURLBar.select();
|
||||
yield overridePromise;
|
||||
checkState(true, "Update when edit-controls is on panel, hidden and selection changed");
|
||||
});
|
||||
|
||||
// Test updating when the edit-controls are moved to the toolbar.
|
||||
add_task(function* test_panelui_customize_to_toolbar() {
|
||||
yield startCustomizing();
|
||||
let navbar = document.getElementById("nav-bar").customizationTarget;
|
||||
simulateItemDrag(document.getElementById("edit-controls"), navbar);
|
||||
yield endCustomizing();
|
||||
|
||||
// updateEditUIVisibility should be called when customization ends but isn't. See bug 1359790.
|
||||
updateEditUIVisibility();
|
||||
|
||||
let overridePromise = expectCommandUpdate(1);
|
||||
gURLBar.select();
|
||||
gURLBar.focus();
|
||||
gURLBar.value = "other";
|
||||
yield overridePromise;
|
||||
checkState(false, "Update when edit-controls on toolbar and focused");
|
||||
|
||||
overridePromise = expectCommandUpdate(1);
|
||||
gURLBar.select();
|
||||
yield overridePromise;
|
||||
checkState(true, "Update when edit-controls on toolbar and selection changed");
|
||||
});
|
||||
|
||||
// Test updating when the edit-controls are moved to the palette.
|
||||
add_task(function* test_panelui_customize_to_palette() {
|
||||
yield startCustomizing();
|
||||
let palette = document.getElementById("customization-palette");
|
||||
simulateItemDrag(document.getElementById("edit-controls"), palette);
|
||||
yield endCustomizing();
|
||||
|
||||
// updateEditUIVisibility should be called when customization ends but isn't. See bug 1359790.
|
||||
updateEditUIVisibility();
|
||||
|
||||
let overridePromise = expectCommandUpdate(isMac ? 1 : 0);
|
||||
gURLBar.focus();
|
||||
gURLBar.value = "other";
|
||||
gURLBar.select();
|
||||
yield overridePromise;
|
||||
|
||||
// If the UI isn't found, the command is set to be enabled.
|
||||
checkState(true, "Update when edit-controls is on palette, hidden and selection changed");
|
||||
});
|
||||
|
||||
add_task(function* finish() {
|
||||
yield resetCustomization();
|
||||
});
|
||||
|
||||
// Test updating in the initial state when the edit-controls are on the panel but
|
||||
// have not yet been created. This needs to be done in a new window to ensure that
|
||||
// other tests haven't opened the panel.
|
||||
add_task(function* test_initial_state() {
|
||||
let testWindow = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
yield SimpleTest.promiseFocus(testWindow);
|
||||
|
||||
let overridePromise = expectCommandUpdate(isMac, testWindow);
|
||||
|
||||
testWindow.gURLBar.focus();
|
||||
testWindow.gURLBar.value = "test";
|
||||
|
||||
yield overridePromise;
|
||||
|
||||
// Commands won't update when no edit UI is present. They default to being
|
||||
// enabled so that keyboard shortcuts will work. The real enabled state will
|
||||
// be checked when shortcut is pressed.
|
||||
checkState(!isMac, "No update when edit-controls is on panel and not visible", testWindow);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(testWindow);
|
||||
yield SimpleTest.promiseFocus(window);
|
||||
});
|
|
@ -12,5 +12,7 @@ STRIP_FLAGS="--strip-debug"
|
|||
|
||||
ac_add_options --with-branding=browser/branding/aurora
|
||||
|
||||
mk_add_options MOZ_PGO=1
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
|
|
@ -12,5 +12,7 @@ STRIP_FLAGS="--strip-debug"
|
|||
|
||||
ac_add_options --with-branding=browser/branding/aurora
|
||||
|
||||
mk_add_options MOZ_PGO=1
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
|
|
@ -8,5 +8,7 @@ ac_add_options --enable-verify-mar
|
|||
|
||||
ac_add_options --with-branding=browser/branding/aurora
|
||||
|
||||
mk_add_options MOZ_PGO=1
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
|
|
@ -9,5 +9,7 @@ ac_add_options --enable-verify-mar
|
|||
|
||||
ac_add_options --with-branding=browser/branding/aurora
|
||||
|
||||
mk_add_options MOZ_PGO=1
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
|
|
@ -13,6 +13,8 @@ function setConditionalBreakpoint(dbg, index, condition) {
|
|||
selectMenuItem(dbg, 2);
|
||||
yield waitForElement(dbg, ".conditional-breakpoint-panel input");
|
||||
findElementWithSelector(dbg, ".conditional-breakpoint-panel input").focus();
|
||||
// Position cursor reliably at the end of the text.
|
||||
pressKey(dbg, "End")
|
||||
type(dbg, condition);
|
||||
pressKey(dbg, "Enter");
|
||||
});
|
||||
|
|
|
@ -32,6 +32,8 @@ async function addExpression(dbg, input) {
|
|||
async function editExpression(dbg, input) {
|
||||
info("updating the expression");
|
||||
dblClickElement(dbg, "expressionNode", 1);
|
||||
// Position cursor reliably at the end of the text.
|
||||
pressKey(dbg, "End")
|
||||
type(dbg, input);
|
||||
pressKey(dbg, "Enter");
|
||||
await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
|
||||
|
|
|
@ -560,6 +560,10 @@ const keyMappings = {
|
|||
Enter: { code: "VK_RETURN" },
|
||||
Up: { code: "VK_UP" },
|
||||
Down: { code: "VK_DOWN" },
|
||||
Right: { code: "VK_RIGHT" },
|
||||
Left: { code: "VK_LEFT" },
|
||||
End: { code: "VK_RIGHT", modifiers: cmdOrCtrl },
|
||||
Start: { code: "VK_LEFT", modifiers: cmdOrCtrl },
|
||||
Tab: { code: "VK_TAB" },
|
||||
Escape: { code: "VK_ESCAPE" },
|
||||
pauseKey: { code: "VK_F8" },
|
||||
|
|
|
@ -297,6 +297,7 @@ bool nsContentUtils::sUseActivityCursor = false;
|
|||
bool nsContentUtils::sAnimationsAPICoreEnabled = false;
|
||||
bool nsContentUtils::sAnimationsAPIElementAnimateEnabled = false;
|
||||
bool nsContentUtils::sGetBoxQuadsEnabled = false;
|
||||
bool nsContentUtils::sSkipCursorMoveForSameValueSet = false;
|
||||
|
||||
int32_t nsContentUtils::sPrivacyMaxInnerWidth = 1000;
|
||||
int32_t nsContentUtils::sPrivacyMaxInnerHeight = 1000;
|
||||
|
@ -645,6 +646,10 @@ nsContentUtils::Init()
|
|||
Preferences::AddBoolVarCache(&sGetBoxQuadsEnabled,
|
||||
"layout.css.getBoxQuads.enabled", false);
|
||||
|
||||
Preferences::AddBoolVarCache(&sSkipCursorMoveForSameValueSet,
|
||||
"dom.input.skip_cursor_move_for_same_value_set",
|
||||
true);
|
||||
|
||||
Element::InitCCCallbacks();
|
||||
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
|
||||
|
|
|
@ -2888,6 +2888,13 @@ public:
|
|||
*/
|
||||
static uint64_t GenerateTabId();
|
||||
|
||||
/**
|
||||
* Check whether we should skip moving the cursor for a same-value .value set
|
||||
* on a text input or textarea.
|
||||
*/
|
||||
static bool
|
||||
SkipCursorMoveForSameValueSet() { return sSkipCursorMoveForSameValueSet; }
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
|
@ -3012,6 +3019,7 @@ private:
|
|||
static bool sAnimationsAPICoreEnabled;
|
||||
static bool sAnimationsAPIElementAnimateEnabled;
|
||||
static bool sGetBoxQuadsEnabled;
|
||||
static bool sSkipCursorMoveForSameValueSet;
|
||||
static uint32_t sCookiesLifetimePolicy;
|
||||
static uint32_t sCookiesBehavior;
|
||||
|
||||
|
|
|
@ -573,7 +573,10 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||
return False
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError("There are no extended attributes that are "
|
||||
"allowed on external interfaces",
|
||||
[attrs[0].location, self.location])
|
||||
|
||||
def resolve(self, parentScope):
|
||||
pass
|
||||
|
@ -1932,7 +1935,10 @@ class IDLDictionary(IDLObjectWithScope):
|
|||
[member.location] + locations)
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError("There are no extended attributes that are "
|
||||
"allowed on dictionaries",
|
||||
[attrs[0].location, self.location])
|
||||
|
||||
def _getDependentObjects(self):
|
||||
deps = set(self.members)
|
||||
|
@ -1966,7 +1972,10 @@ class IDLEnum(IDLObjectWithIdentifier):
|
|||
return True
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError("There are no extended attributes that are "
|
||||
"allowed on enums",
|
||||
[attrs[0].location, self.location])
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return set()
|
||||
|
@ -2139,7 +2148,11 @@ class IDLType(IDLObject):
|
|||
return self.nullable() and self.inner.callback._treatNonObjectAsNull
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError("There are no extended attributes that are "
|
||||
"allowed on types, for now (but this is "
|
||||
"changing; see bug 1359269)",
|
||||
[attrs[0].location, self.location])
|
||||
|
||||
def resolveType(self, parentScope):
|
||||
pass
|
||||
|
@ -2707,7 +2720,10 @@ class IDLTypedef(IDLObjectWithIdentifier):
|
|||
return True
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError("There are no extended attributes that are "
|
||||
"allowed on typedefs",
|
||||
[attrs[0].location, self.location])
|
||||
|
||||
def _getDependentObjects(self):
|
||||
return self.innerType._getDependentObjects()
|
||||
|
@ -5108,7 +5124,10 @@ class IDLImplementsStatement(IDLObject):
|
|||
pass
|
||||
|
||||
def addExtendedAttributes(self, attrs):
|
||||
assert len(attrs) == 0
|
||||
if len(attrs) != 0:
|
||||
raise WebIDLError("There are no extended attributes that are "
|
||||
"allowed on implements statements",
|
||||
[attrs[0].location, self.location])
|
||||
|
||||
|
||||
class IDLExtendedAttribute(IDLObject):
|
||||
|
|
|
@ -1974,9 +1974,10 @@ HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
|
|||
GetValue(currentValue, aCallerType);
|
||||
|
||||
nsresult rv =
|
||||
SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
|
||||
nsTextEditorState::eSetValue_Notify |
|
||||
nsTextEditorState::eSetValue_MoveCursorToEnd);
|
||||
SetValueInternal(aValue,
|
||||
nsTextEditorState::eSetValue_ByContent |
|
||||
nsTextEditorState::eSetValue_Notify |
|
||||
nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
|
@ -1987,9 +1988,10 @@ HTMLInputElement::SetValue(const nsAString& aValue, CallerType aCallerType,
|
|||
}
|
||||
} else {
|
||||
nsresult rv =
|
||||
SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
|
||||
nsTextEditorState::eSetValue_Notify |
|
||||
nsTextEditorState::eSetValue_MoveCursorToEnd);
|
||||
SetValueInternal(aValue,
|
||||
nsTextEditorState::eSetValue_ByContent |
|
||||
nsTextEditorState::eSetValue_Notify |
|
||||
nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv.Throw(rv);
|
||||
return;
|
||||
|
@ -2843,9 +2845,9 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
|
|||
} else {
|
||||
nsresult rv =
|
||||
SetValueInternal(aValue,
|
||||
nsTextEditorState::eSetValue_BySetUserInput |
|
||||
nsTextEditorState::eSetValue_Notify|
|
||||
nsTextEditorState::eSetValue_MoveCursorToEnd);
|
||||
nsTextEditorState::eSetValue_BySetUserInput |
|
||||
nsTextEditorState::eSetValue_Notify|
|
||||
nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
|
|
|
@ -404,9 +404,10 @@ HTMLTextAreaElement::SetValue(const nsAString& aValue)
|
|||
GetValueInternal(currentValue, true);
|
||||
|
||||
nsresult rv =
|
||||
SetValueInternal(aValue, nsTextEditorState::eSetValue_ByContent |
|
||||
nsTextEditorState::eSetValue_Notify |
|
||||
nsTextEditorState::eSetValue_MoveCursorToEnd);
|
||||
SetValueInternal(aValue,
|
||||
nsTextEditorState::eSetValue_ByContent |
|
||||
nsTextEditorState::eSetValue_Notify |
|
||||
nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mFocusedValue.Equals(currentValue)) {
|
||||
|
@ -420,9 +421,9 @@ NS_IMETHODIMP
|
|||
HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
|
||||
{
|
||||
return SetValueInternal(aValue,
|
||||
nsTextEditorState::eSetValue_BySetUserInput |
|
||||
nsTextEditorState::eSetValue_Notify|
|
||||
nsTextEditorState::eSetValue_MoveCursorToEnd);
|
||||
nsTextEditorState::eSetValue_BySetUserInput |
|
||||
nsTextEditorState::eSetValue_Notify|
|
||||
nsTextEditorState::eSetValue_MoveCursorToEndIfValueChanged);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -2503,6 +2503,12 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
|
|||
}
|
||||
}
|
||||
|
||||
// \r is an illegal character in the dom, but people use them,
|
||||
// so convert windows and mac platform linebreaks to \n:
|
||||
if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mEditor && mBoundFrame) {
|
||||
// The InsertText call below might flush pending notifications, which
|
||||
// could lead into a scheduled PrepareEditor to be called. That will
|
||||
|
@ -2528,14 +2534,6 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
|
|||
{
|
||||
ValueSetter valueSetter(mEditor);
|
||||
|
||||
// \r is an illegal character in the dom, but people use them,
|
||||
// so convert windows and mac platform linebreaks to \n:
|
||||
if (newValue.FindChar(char16_t('\r')) != -1) {
|
||||
if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
mEditor->GetDocument(getter_AddRefs(domDoc));
|
||||
if (!domDoc) {
|
||||
|
@ -2638,33 +2636,42 @@ nsTextEditorState::SetValue(const nsAString& aValue, uint32_t aFlags)
|
|||
if (!mValue) {
|
||||
mValue.emplace();
|
||||
}
|
||||
nsString value;
|
||||
if (!value.Assign(newValue, fallible)) {
|
||||
return false;
|
||||
}
|
||||
if (!nsContentUtils::PlatformToDOMLineBreaks(value, fallible)) {
|
||||
return false;
|
||||
}
|
||||
if (!mValue->Assign(value, fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since we have no editor we presumably have cached selection state.
|
||||
if (IsSelectionCached()) {
|
||||
SelectionProperties& props = GetSelectionProperties();
|
||||
if (aFlags & eSetValue_MoveCursorToEnd) {
|
||||
props.SetStart(value.Length());
|
||||
props.SetEnd(value.Length());
|
||||
} else {
|
||||
// Make sure our cached selection position is not outside the new value.
|
||||
props.SetStart(std::min(props.GetStart(), value.Length()));
|
||||
props.SetEnd(std::min(props.GetEnd(), value.Length()));
|
||||
// We can't just early-return here if mValue->Equals(newValue), because
|
||||
// ValueWasChanged and OnValueChanged below still need to be called.
|
||||
if (!mValue->Equals(newValue) ||
|
||||
!nsContentUtils::SkipCursorMoveForSameValueSet()) {
|
||||
if (!mValue->Assign(newValue, fallible)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the frame display if needed
|
||||
if (mBoundFrame) {
|
||||
mBoundFrame->UpdateValueDisplay(true);
|
||||
// Since we have no editor we presumably have cached selection state.
|
||||
if (IsSelectionCached()) {
|
||||
SelectionProperties& props = GetSelectionProperties();
|
||||
if (aFlags & eSetValue_MoveCursorToEndIfValueChanged) {
|
||||
props.SetStart(newValue.Length());
|
||||
props.SetEnd(newValue.Length());
|
||||
props.SetDirection(nsITextControlFrame::eForward);
|
||||
} else {
|
||||
// Make sure our cached selection position is not outside the new value.
|
||||
props.SetStart(std::min(props.GetStart(), newValue.Length()));
|
||||
props.SetEnd(std::min(props.GetEnd(), newValue.Length()));
|
||||
}
|
||||
}
|
||||
|
||||
// Update the frame display if needed
|
||||
if (mBoundFrame) {
|
||||
mBoundFrame->UpdateValueDisplay(true);
|
||||
}
|
||||
} else {
|
||||
// Even if our value is not actually changing, apparently we need to mark
|
||||
// our SelectionProperties dirty to make accessibility tests happy.
|
||||
// Probably because they depend on the SetSelectionRange() call we make on
|
||||
// our frame in RestoreSelectionState, but I have no idea why they do.
|
||||
if (IsSelectionCached()) {
|
||||
SelectionProperties& props = GetSelectionProperties();
|
||||
props.SetIsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -173,9 +173,11 @@ public:
|
|||
// Whether the value change should be notified to the frame/contet nor not.
|
||||
eSetValue_Notify = 1 << 2,
|
||||
// Whether to move the cursor to end of the value (in the case when we have
|
||||
// cached selection offsets). If this is not set, the cached selection
|
||||
// offsets will simply be clamped to be within the length of the new value.
|
||||
eSetValue_MoveCursorToEnd = 1 << 3,
|
||||
// cached selection offsets), in the case when the value has changed. If
|
||||
// this is not set, the cached selection offsets will simply be clamped to
|
||||
// be within the length of the new value. In either case, if the value has
|
||||
// not changed the cursor won't move.
|
||||
eSetValue_MoveCursorToEndIfValueChanged = 1 << 3,
|
||||
};
|
||||
MOZ_MUST_USE bool SetValue(const nsAString& aValue, uint32_t aFlags);
|
||||
void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
|
||||
|
@ -280,11 +282,16 @@ public:
|
|||
mIsDirty = true;
|
||||
mDirection = value;
|
||||
}
|
||||
// return true only if mStart, mEnd, or mDirection have been modified
|
||||
// return true only if mStart, mEnd, or mDirection have been modified,
|
||||
// or if SetIsDirty() was explicitly called.
|
||||
bool IsDirty() const
|
||||
{
|
||||
return mIsDirty;
|
||||
}
|
||||
void SetIsDirty()
|
||||
{
|
||||
mIsDirty = true;
|
||||
}
|
||||
private:
|
||||
uint32_t mStart, mEnd;
|
||||
bool mIsDirty = false;
|
||||
|
|
|
@ -31,4 +31,9 @@ interface nsIDOMXULCommandDispatcher : nsISupports
|
|||
void rewindFocus();
|
||||
void advanceFocusIntoSubtree(in nsIDOMElement elt);
|
||||
attribute boolean suppressFocusScroll;
|
||||
|
||||
// When locked, command updating is batched until unlocked. Always ensure that
|
||||
// lock and unlock is called in a pair.
|
||||
void lock();
|
||||
void unlock();
|
||||
};
|
||||
|
|
|
@ -5055,7 +5055,7 @@ ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch
|
|||
}
|
||||
|
||||
nsresult
|
||||
ContentParent::AboutToLoadDocumentForChild(nsIChannel* aChannel)
|
||||
ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild(nsIChannel* aChannel)
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
|
|
|
@ -656,7 +656,7 @@ public:
|
|||
// HTTP(S), FTP or wyciwyg channel for a content process. It is a useful
|
||||
// place to start to kick off work as early as possible in response to such
|
||||
// document loads.
|
||||
nsresult AboutToLoadDocumentForChild(nsIChannel* aChannel);
|
||||
nsresult AboutToLoadHttpFtpWyciwygDocumentForChild(nsIChannel* aChannel);
|
||||
|
||||
nsresult TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal);
|
||||
|
||||
|
|
|
@ -941,6 +941,10 @@ TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
|
|||
#ifdef XP_WIN
|
||||
a11y::WrapperFor(doc)->SetID(aMsaaID);
|
||||
MOZ_ASSERT(!aDocCOMProxy.IsNull());
|
||||
if (aDocCOMProxy.IsNull()) {
|
||||
return IPC_FAIL(this, "Constructing a top-level PDocAccessible with null COM proxy");
|
||||
}
|
||||
|
||||
RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
|
||||
doc->SetCOMInterface(proxy);
|
||||
doc->MaybeInitWindowEmulation();
|
||||
|
|
|
@ -42,7 +42,7 @@ static LazyLogModule gCommandLog("nsXULCommandDispatcher");
|
|||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
nsXULCommandDispatcher::nsXULCommandDispatcher(nsIDocument* aDocument)
|
||||
: mDocument(aDocument), mUpdaters(nullptr)
|
||||
: mDocument(aDocument), mUpdaters(nullptr), mLocked(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -350,6 +350,14 @@ nsXULCommandDispatcher::RemoveCommandUpdater(nsIDOMElement* aElement)
|
|||
NS_IMETHODIMP
|
||||
nsXULCommandDispatcher::UpdateCommands(const nsAString& aEventName)
|
||||
{
|
||||
if (mLocked) {
|
||||
if (!mPendingUpdates.Contains(aEventName)) {
|
||||
mPendingUpdates.AppendElement(aEventName);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString id;
|
||||
nsCOMPtr<nsIDOMElement> element;
|
||||
GetFocusedElement(getter_AddRefs(element));
|
||||
|
@ -457,3 +465,30 @@ nsXULCommandDispatcher::SetSuppressFocusScroll(bool aSuppressFocusScroll)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULCommandDispatcher::Lock()
|
||||
{
|
||||
// Since locking is used only as a performance optimization, we don't worry
|
||||
// about nested lock calls. If that does happen, it just means we will unlock
|
||||
// and process updates earlier.
|
||||
mLocked = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsXULCommandDispatcher::Unlock()
|
||||
{
|
||||
if (mLocked) {
|
||||
mLocked = false;
|
||||
|
||||
// Handle any pending updates one at a time. In the unlikely case where a
|
||||
// lock is added during the update, break out.
|
||||
while (!mLocked && mPendingUpdates.Length() > 0) {
|
||||
nsString name = mPendingUpdates.ElementAt(0);
|
||||
mPendingUpdates.RemoveElementAt(0);
|
||||
UpdateCommands(name);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,9 @@ protected:
|
|||
|
||||
bool Matches(const nsString& aList,
|
||||
const nsAString& aElement);
|
||||
|
||||
bool mLocked;
|
||||
nsTArray<nsString> mPendingUpdates;
|
||||
};
|
||||
|
||||
#endif // nsXULCommandDispatcher_h__
|
||||
|
|
|
@ -300,7 +300,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp)
|
|||
return false;
|
||||
}
|
||||
|
||||
#if !defined(SK_ARM_HAS_NEON)
|
||||
#if !defined(SK_ARM_HAS_NEON) || defined(SK_ARM_HAS_OPTIONAL_NEON)
|
||||
static const SampleProc32 gSkBitmapProcStateSample32[] = {
|
||||
S32_opaque_D32_nofilter_DXDY,
|
||||
S32_alpha_D32_nofilter_DXDY,
|
||||
|
|
|
@ -56,7 +56,7 @@ extern const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs_neon[];
|
|||
#endif // defined(SK_ARM_HAS_NEON)
|
||||
|
||||
// Compile non-neon code path if needed
|
||||
#if !defined(SK_ARM_HAS_NEON)
|
||||
#if !defined(SK_ARM_HAS_NEON) || defined(SK_ARM_HAS_OPTIONAL_NEON)
|
||||
#define MAKENAME(suffix) ClampX_ClampY ## suffix
|
||||
#define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max)
|
||||
#define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max)
|
||||
|
|
|
@ -1217,8 +1217,8 @@ gfxFT2FontList::FindFonts()
|
|||
FinalizeFamilyMemberList(key, family, /* aSortFaces */ false );
|
||||
}
|
||||
|
||||
LOG(("got font list from chrome process: %d faces in %d families "
|
||||
"and %d in hidden families",
|
||||
LOG(("got font list from chrome process: %" PRIdPTR " faces in %"
|
||||
PRIu32 " families and %" PRIu32 " in hidden families",
|
||||
fonts.Length(), mFontFamilies.Count(),
|
||||
mHiddenFontFamilies.Count()));
|
||||
return;
|
||||
|
|
|
@ -1552,13 +1552,19 @@ class MOZ_STACK_CLASS TryEmitter
|
|||
// stream and fixed-up later.
|
||||
//
|
||||
// If ShouldUseControl is DontUseControl, all that handling is skipped.
|
||||
// DontUseControl is used by yield*, that matches to the following:
|
||||
// * has only one catch block
|
||||
// * has no catch guard
|
||||
// * has JSOP_GOTO at the end of catch-block
|
||||
// * has no non-local-jump
|
||||
// * doesn't use finally block for normal completion of try-block and
|
||||
// DontUseControl is used by yield* and the internal try-catch around
|
||||
// IteratorClose. These internal uses must:
|
||||
// * have only one catch block
|
||||
// * have no catch guard
|
||||
// * have JSOP_GOTO at the end of catch-block
|
||||
// * have no non-local-jump
|
||||
// * don't use finally block for normal completion of try-block and
|
||||
// catch-block
|
||||
//
|
||||
// Additionally, a finally block may be emitted when ShouldUseControl is
|
||||
// DontUseControl, even if the kind is not TryCatchFinally or TryFinally,
|
||||
// because GOSUBs are not emitted. This internal use shares the
|
||||
// requirements as above.
|
||||
Maybe<TryFinallyControl> controlInfo_;
|
||||
|
||||
int depth_;
|
||||
|
@ -1726,7 +1732,17 @@ class MOZ_STACK_CLASS TryEmitter
|
|||
|
||||
public:
|
||||
bool emitFinally(const Maybe<uint32_t>& finallyPos = Nothing()) {
|
||||
MOZ_ASSERT(hasFinally());
|
||||
// If we are using controlInfo_ (i.e., emitting a syntactic try
|
||||
// blocks), we must have specified up front if there will be a finally
|
||||
// close. For internal try blocks, like those emitted for yield* and
|
||||
// IteratorClose inside for-of loops, we can emitFinally even without
|
||||
// specifying up front, since the internal try blocks emit no GOSUBs.
|
||||
if (!controlInfo_) {
|
||||
if (kind_ == TryCatch)
|
||||
kind_ = TryCatchFinally;
|
||||
} else {
|
||||
MOZ_ASSERT(hasFinally());
|
||||
}
|
||||
|
||||
if (state_ == Try) {
|
||||
if (!emitTryEnd())
|
||||
|
@ -2022,6 +2038,10 @@ class ForOfLoopControl : public LoopControl
|
|||
// }
|
||||
Maybe<TryEmitter> tryCatch_;
|
||||
|
||||
// Used to track if any yields were emitted between calls to to
|
||||
// emitBeginCodeNeedingIteratorClose and emitEndCodeNeedingIteratorClose.
|
||||
uint32_t numYieldsAtBeginCodeNeedingIterClose_;
|
||||
|
||||
bool allowSelfHosted_;
|
||||
|
||||
IteratorKind iterKind_;
|
||||
|
@ -2031,16 +2051,22 @@ class ForOfLoopControl : public LoopControl
|
|||
IteratorKind iterKind)
|
||||
: LoopControl(bce, StatementKind::ForOfLoop),
|
||||
iterDepth_(iterDepth),
|
||||
numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX),
|
||||
allowSelfHosted_(allowSelfHosted),
|
||||
iterKind_(iterKind)
|
||||
{
|
||||
}
|
||||
|
||||
bool emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
|
||||
tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal);
|
||||
tryCatch_.emplace(bce, TryEmitter::TryCatch, TryEmitter::DontUseRetVal,
|
||||
TryEmitter::DontUseControl);
|
||||
|
||||
if (!tryCatch_->emitTry())
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
|
||||
numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2078,10 +2104,33 @@ class ForOfLoopControl : public LoopControl
|
|||
if (!bce->emit1(JSOP_THROW)) // ITER ...
|
||||
return false;
|
||||
|
||||
// If any yields were emitted, then this for-of loop is inside a star
|
||||
// generator and must handle the case of Generator.return. Like in
|
||||
// yield*, it is handled with a finally block.
|
||||
uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields;
|
||||
if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) {
|
||||
if (!tryCatch_->emitFinally())
|
||||
return false;
|
||||
|
||||
IfThenElseEmitter ifGeneratorClosing(bce);
|
||||
if (!bce->emit1(JSOP_ISGENCLOSING)) // ITER ... FTYPE FVALUE CLOSING
|
||||
return false;
|
||||
if (!ifGeneratorClosing.emitIf()) // ITER ... FTYPE FVALUE
|
||||
return false;
|
||||
if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER
|
||||
return false;
|
||||
if (!emitIteratorClose(bce, CompletionKind::Normal)) // ITER ... FTYPE FVALUE
|
||||
return false;
|
||||
if (!ifGeneratorClosing.emitEnd()) // ITER ... FTYPE FVALUE
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tryCatch_->emitEnd())
|
||||
return false;
|
||||
|
||||
tryCatch_.reset();
|
||||
numYieldsAtBeginCodeNeedingIterClose_ = UINT32_MAX;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4829,6 +4878,11 @@ BytecodeEmitter::emitYieldOp(JSOp op)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (op == JSOP_AWAIT)
|
||||
yieldAndAwaitOffsetList.numAwaits++;
|
||||
else
|
||||
yieldAndAwaitOffsetList.numYields++;
|
||||
|
||||
SET_UINT24(code(off), yieldAndAwaitIndex);
|
||||
|
||||
if (!yieldAndAwaitOffsetList.append(offset()))
|
||||
|
|
|
@ -101,7 +101,9 @@ struct CGScopeNoteList {
|
|||
|
||||
struct CGYieldAndAwaitOffsetList {
|
||||
Vector<uint32_t> list;
|
||||
explicit CGYieldAndAwaitOffsetList(JSContext* cx) : list(cx) {}
|
||||
uint32_t numYields;
|
||||
uint32_t numAwaits;
|
||||
explicit CGYieldAndAwaitOffsetList(JSContext* cx) : list(cx), numYields(0), numAwaits(0) {}
|
||||
|
||||
MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
|
||||
size_t length() const { return list.length(); }
|
||||
|
|
|
@ -1277,12 +1277,12 @@ CacheIRCompiler::emitGuardIsInt32Index()
|
|||
return true;
|
||||
}
|
||||
|
||||
ValueOperand input = allocator.useValueRegister(masm, inputId);
|
||||
|
||||
FailurePath* failure;
|
||||
if (!addFailurePath(&failure))
|
||||
return false;
|
||||
|
||||
ValueOperand input = allocator.useValueRegister(masm, inputId);
|
||||
|
||||
Label notInt32, done;
|
||||
masm.branchTestInt32(Assembler::NotEqual, input, ¬Int32);
|
||||
masm.unboxInt32(input, output);
|
||||
|
|
|
@ -315,7 +315,7 @@ mapMemoryAt(void* desired, size_t length)
|
|||
|
||||
#if defined(__ia64__) || defined(__aarch64__) || \
|
||||
(defined(__sparc__) && defined(__arch64__) && (defined(__NetBSD__) || defined(__linux__)))
|
||||
MOZ_RELEASE_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0);
|
||||
MOZ_RELEASE_ASSERT((0xffff800000000000ULL & (uintptr_t(desired) + length - 1)) == 0);
|
||||
#endif
|
||||
void* region = mmap(desired, length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
|
||||
if (region == MAP_FAILED)
|
||||
|
|
|
@ -66,6 +66,8 @@
|
|||
# define GETRANDOM_NR 318
|
||||
# elif defined(__i386__)
|
||||
# define GETRANDOM_NR 355
|
||||
# elif defined(__aarch64__)
|
||||
# define GETRANDOM_NR 278
|
||||
# elif defined(__arm__)
|
||||
# define GETRANDOM_NR 384
|
||||
// Added other architectures:
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
# include <pthread_np.h>
|
||||
# endif
|
||||
|
||||
# if defined(ANDROID)
|
||||
# if defined(ANDROID) && !defined(__aarch64__)
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
# endif
|
||||
|
@ -120,11 +120,11 @@ js::GetNativeStackBaseImpl()
|
|||
rc = pthread_stackseg_np(pthread_self(), &ss);
|
||||
stackBase = (void*)((size_t) ss.ss_sp - ss.ss_size);
|
||||
stackSize = ss.ss_size;
|
||||
# elif defined(ANDROID)
|
||||
# elif defined(ANDROID) && !defined(__aarch64__)
|
||||
if (gettid() == getpid()) {
|
||||
// bionic's pthread_attr_getstack doesn't tell the truth for the main
|
||||
// thread (see bug 846670). So we scan /proc/self/maps to find the
|
||||
// segment which contains the stack.
|
||||
// bionic's pthread_attr_getstack prior to API 21 doesn't tell the truth
|
||||
// for the main thread (see bug 846670). So we scan /proc/self/maps to
|
||||
// find the segment which contains the stack.
|
||||
rc = -1;
|
||||
|
||||
// Put the string on the stack, otherwise there is the danger that it
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// Test that IteratorClose is called when a Generator is abruptly completed by
|
||||
// Generator.return.
|
||||
|
||||
var returnCalled = 0;
|
||||
function* wrapNoThrow() {
|
||||
let iter = {
|
||||
[Symbol.iterator]() {
|
||||
return this;
|
||||
},
|
||||
next() {
|
||||
return { value: 10, done: false };
|
||||
},
|
||||
return() {
|
||||
returnCalled++;
|
||||
return {};
|
||||
}
|
||||
};
|
||||
for (const i of iter) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
// Breaking calls Generator.return, which causes the yield above to resume with
|
||||
// an abrupt completion of kind "return", which then calls
|
||||
// iter.return.
|
||||
for (const i of wrapNoThrow()) {
|
||||
break;
|
||||
}
|
||||
assertEq(returnCalled, 1);
|
||||
|
||||
function* wrapThrow() {
|
||||
let iter = {
|
||||
[Symbol.iterator]() {
|
||||
return this;
|
||||
},
|
||||
next() {
|
||||
return { value: 10, done: false };
|
||||
},
|
||||
return() {
|
||||
throw 42;
|
||||
}
|
||||
};
|
||||
for (const i of iter) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
// Breaking calls Generator.return, which, like above, calls iter.return. If
|
||||
// iter.return throws, since the yield is resuming with an abrupt completion of
|
||||
// kind "return", the newly thrown value takes precedence over returning.
|
||||
assertThrowsValue(() => {
|
||||
for (const i of wrapThrow()) {
|
||||
break;
|
||||
}
|
||||
}, 42);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
|
@ -61,7 +61,7 @@ rdtsc(void)
|
|||
return result;
|
||||
|
||||
}
|
||||
#elif defined(__arm__)
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
|
|
|
@ -1374,6 +1374,7 @@ ProcessHasSignalHandlers()
|
|||
sTriedInstallSignalHandlers = true;
|
||||
|
||||
#if defined(ANDROID)
|
||||
# if !defined(__aarch64__)
|
||||
// Before Android 4.4 (SDK version 19), there is a bug
|
||||
// https://android-review.googlesource.com/#/c/52333
|
||||
// in Bionic's pthread_join which causes pthread_join to return early when
|
||||
|
@ -1385,6 +1386,7 @@ ProcessHasSignalHandlers()
|
|||
if (atol(version_string) < 19)
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
# if defined(MOZ_LINKER)
|
||||
// Signal handling is broken on some android systems.
|
||||
if (IsSignalHandlingBroken())
|
||||
|
|
|
@ -503,7 +503,7 @@ bool OmxDecoder::Init()
|
|||
int64_t durationUs;
|
||||
if (videoTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
|
||||
if (durationUs < 0)
|
||||
LOG("video duration %lld should be nonnegative", durationUs);
|
||||
LOG("video duration %" PRId64 " should be nonnegative", durationUs);
|
||||
if (durationUs > totalDurationUs)
|
||||
totalDurationUs = durationUs;
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ bool OmxDecoder::Init()
|
|||
int64_t durationUs;
|
||||
if (audioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
|
||||
if (durationUs < 0)
|
||||
LOG("audio duration %lld should be nonnegative", durationUs);
|
||||
LOG("audio duration %" PRId64 " should be nonnegative", durationUs);
|
||||
if (durationUs > totalDurationUs)
|
||||
totalDurationUs = durationUs;
|
||||
}
|
||||
|
@ -913,7 +913,7 @@ bool OmxDecoder::ReadVideo(VideoFrame *aFrame, int64_t aSeekTimeUs,
|
|||
}
|
||||
|
||||
if (timeUs < 0) {
|
||||
LOG("frame time %lld must be nonnegative", timeUs);
|
||||
LOG("frame time %" PRId64 " must be nonnegative", timeUs);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -977,7 +977,7 @@ bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs)
|
|||
}
|
||||
|
||||
if (timeUs < 0) {
|
||||
LOG("frame time %lld must be nonnegative", timeUs);
|
||||
LOG("frame time %" PRId64 " must be nonnegative", timeUs);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1155,10 +1155,10 @@ int32_t WebrtcMediaCodecVP8VideoDecoder::Decode(
|
|||
const webrtc::CodecSpecificInfo* codecSpecificInfo,
|
||||
int64_t renderTimeMs) {
|
||||
|
||||
CSFLogDebug(logTag, "%s, renderTimeMs = %lld ", __FUNCTION__, renderTimeMs);
|
||||
CSFLogDebug(logTag, "%s, renderTimeMs = %" PRId64, __FUNCTION__, renderTimeMs);
|
||||
|
||||
if (inputImage._length== 0 || !inputImage._buffer) {
|
||||
CSFLogDebug(logTag, "%s, input Image invalid. length = %d", __FUNCTION__, inputImage._length);
|
||||
CSFLogDebug(logTag, "%s, input Image invalid. length = %" PRIdPTR, __FUNCTION__, inputImage._length);
|
||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,4 +49,36 @@
|
|||
# define PRIXPTR "X" /* uintptr_t */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fix up Android's broken macros for [u]int_fastN_t. On ARM64, Android's
|
||||
* PRI*FAST16/32 macros are defined as "d", but the types themselves are defined
|
||||
* as long and unsigned long.
|
||||
*/
|
||||
#if defined(ANDROID) && defined(__LP64__)
|
||||
# undef PRIdFAST16 /* int_fast16_t */
|
||||
# define PRIdFAST16 PRId64 /* int_fast16_t */
|
||||
# undef PRIiFAST16 /* int_fast16_t */
|
||||
# define PRIiFAST16 PRIi64 /* int_fast16_t */
|
||||
# undef PRIoFAST16 /* uint_fast16_t */
|
||||
# define PRIoFAST16 PRIo64 /* uint_fast16_t */
|
||||
# undef PRIuFAST16 /* uint_fast16_t */
|
||||
# define PRIuFAST16 PRIu64 /* uint_fast16_t */
|
||||
# undef PRIxFAST16 /* uint_fast16_t */
|
||||
# define PRIxFAST16 PRIx64 /* uint_fast16_t */
|
||||
# undef PRIXFAST16 /* uint_fast16_t */
|
||||
# define PRIXFAST16 PRIX64 /* uint_fast16_t */
|
||||
# undef PRIdFAST32 /* int_fast32_t */
|
||||
# define PRIdFAST32 PRId64 /* int_fast32_t */
|
||||
# undef PRIiFAST32 /* int_fast32_t */
|
||||
# define PRIiFAST32 PRIi64 /* int_fast32_t */
|
||||
# undef PRIoFAST32 /* uint_fast32_t */
|
||||
# define PRIoFAST32 PRIo64 /* uint_fast32_t */
|
||||
# undef PRIuFAST32 /* uint_fast32_t */
|
||||
# define PRIuFAST32 PRIu64 /* uint_fast32_t */
|
||||
# undef PRIxFAST32 /* uint_fast32_t */
|
||||
# define PRIxFAST32 PRIx64 /* uint_fast32_t */
|
||||
# undef PRIXFAST32 /* uint_fast32_t */
|
||||
# define PRIXFAST32 PRIX64 /* uint_fast32_t */
|
||||
#endif
|
||||
|
||||
#endif /* mozilla_IntegerPrintfMacros_h_ */
|
||||
|
|
|
@ -921,3 +921,7 @@ pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https:
|
|||
pref("media.openUnsupportedTypeWithExternalApp", true);
|
||||
|
||||
pref("dom.keyboardevent.dispatch_during_composition", true);
|
||||
|
||||
#if CPU_ARCH == aarch64
|
||||
pref("javascript.options.native_regexp", false);
|
||||
#endif
|
||||
|
|
|
@ -34,7 +34,7 @@ with Files('src/test/**'):
|
|||
for var in ('APP_NAME', 'APP_VERSION'):
|
||||
DEFINES[var] = CONFIG['MOZ_%s' % var]
|
||||
|
||||
for var in ('MOZ_UPDATER', 'MOZ_APP_UA_NAME', 'ANDROID_PACKAGE_NAME'):
|
||||
for var in ('MOZ_UPDATER', 'MOZ_APP_UA_NAME', 'ANDROID_PACKAGE_NAME', 'CPU_ARCH'):
|
||||
DEFINES[var] = CONFIG[var]
|
||||
|
||||
for var in ('MOZ_ANDROID_GCM', ):
|
||||
|
|
|
@ -1251,6 +1251,10 @@ pref("dom.select_popup_in_parent.enabled", false);
|
|||
// Enable Directory API. By default, disabled.
|
||||
pref("dom.input.dirpicker", false);
|
||||
|
||||
// Enable not moving the cursor to end when a text input or textarea has .value
|
||||
// set to the value it already has. By default, enabled.
|
||||
pref("dom.input.skip_cursor_move_for_same_value_set", false);
|
||||
|
||||
// Enables system messages and activities
|
||||
pref("dom.sysmsg.enabled", false);
|
||||
|
||||
|
|
|
@ -248,6 +248,17 @@ PrintError(const char* aPrefix)
|
|||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
|
||||
static void
|
||||
InitializeDbgHelpCriticalSection()
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
::InitializeCriticalSection(&gDbgHelpCS);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
static unsigned int WINAPI WalkStackThread(void* aData);
|
||||
|
||||
static bool
|
||||
|
@ -300,8 +311,7 @@ EnsureWalkThreadReady()
|
|||
stackWalkThread = nullptr;
|
||||
readyEvent = nullptr;
|
||||
|
||||
|
||||
::InitializeCriticalSection(&gDbgHelpCS);
|
||||
InitializeDbgHelpCriticalSection();
|
||||
|
||||
return walkThreadReady = true;
|
||||
}
|
||||
|
@ -851,9 +861,7 @@ EnsureSymInitialized()
|
|||
return gInitialized;
|
||||
}
|
||||
|
||||
if (!EnsureWalkThreadReady()) {
|
||||
return false;
|
||||
}
|
||||
InitializeDbgHelpCriticalSection();
|
||||
|
||||
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
||||
retStat = SymInitialize(GetCurrentProcess(), nullptr, TRUE);
|
||||
|
|
|
@ -462,7 +462,7 @@ FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
|
|||
// performing a document load.
|
||||
PContentParent* pcp = Manager()->Manager();
|
||||
DebugOnly<nsresult> rv =
|
||||
static_cast<ContentParent*>(pcp)->AboutToLoadDocumentForChild(chan);
|
||||
static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpWyciwygDocumentForChild(chan);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
int64_t contentLength;
|
||||
|
|
|
@ -1148,7 +1148,7 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
|||
PContentParent* pcp = Manager()->Manager();
|
||||
MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
|
||||
DebugOnly<nsresult> rv =
|
||||
static_cast<ContentParent*>(pcp)->AboutToLoadDocumentForChild(chan);
|
||||
static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpWyciwygDocumentForChild(chan);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
|
||||
|
|
|
@ -326,7 +326,7 @@ WyciwygChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext
|
|||
// Send down any permissions which are relevant to this URL if we are
|
||||
// performing a document load.
|
||||
PContentParent* pcp = Manager()->Manager();
|
||||
rv = static_cast<ContentParent*>(pcp)->AboutToLoadDocumentForChild(chan);
|
||||
rv = static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpWyciwygDocumentForChild(chan);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
nsresult status;
|
||||
|
|
|
@ -77,7 +77,7 @@ linux64-devedition/opt:
|
|||
job-name: linux64-opt
|
||||
treeherder:
|
||||
platform: linux64-devedition/opt
|
||||
symbol: tc(DE)
|
||||
symbol: tc(B)
|
||||
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
|
||||
worker:
|
||||
implementation: docker-worker
|
||||
|
@ -176,7 +176,7 @@ linux-devedition/opt:
|
|||
job-name: linux-opt
|
||||
treeherder:
|
||||
platform: linux32-devedition/opt
|
||||
symbol: tc(DE)
|
||||
symbol: tc(B)
|
||||
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
|
||||
worker:
|
||||
implementation: docker-worker
|
||||
|
|
|
@ -6,7 +6,7 @@ macosx64/debug:
|
|||
treeherder:
|
||||
platform: osx-10-7/debug
|
||||
symbol: tc(B)
|
||||
tier: 2
|
||||
tier: 1
|
||||
worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
|
||||
worker:
|
||||
implementation: docker-worker
|
||||
|
|
|
@ -238,9 +238,9 @@ def target_tasks_mozilla_beta(full_task_graph, parameters):
|
|||
platform = task.attributes.get('build_platform')
|
||||
if platform in ('linux64-pgo', 'linux-pgo', 'win32-pgo', 'win64-pgo',
|
||||
'android-api-15-nightly', 'android-x86-nightly',
|
||||
'win32', 'win64', 'macosx64'):
|
||||
'win32', 'win64'):
|
||||
return False
|
||||
if platform in ('linux64', 'linux'):
|
||||
if platform in ('linux64', 'linux', 'macosx64'):
|
||||
if task.attributes['build_type'] == 'opt':
|
||||
return False
|
||||
# skip l10n, beetmover, balrog
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
config = {
|
||||
'src_mozconfig': 'browser/config/mozconfigs/macosx64/devedition',
|
||||
'force_clobber': True,
|
||||
'stage_platform': 'macosx64-devedition',
|
||||
'stage_product': 'devedition',
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
config = {
|
||||
'src_mozconfig': 'browser/config/mozconfigs/win32/devedition',
|
||||
'force_clobber': True,
|
||||
'stage_platform': 'win32-devedition',
|
||||
'stage_product': 'devedition',
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
config = {
|
||||
'src_mozconfig': 'browser/config/mozconfigs/win64/devedition',
|
||||
'force_clobber': True,
|
||||
'stage_platform': 'win64-devedition',
|
||||
'stage_product': 'devedition',
|
||||
}
|
||||
|
|
|
@ -2044,9 +2044,8 @@ CreateJSHangHistogram(JSContext* cx, const Telemetry::HangHistogram& hang)
|
|||
}
|
||||
|
||||
if (!hang.GetNativeStack().empty()) {
|
||||
const Telemetry::HangStack& stack = hang.GetNativeStack();
|
||||
const std::vector<uintptr_t>& frames = stack.GetNativeFrames();
|
||||
Telemetry::ProcessedStack processed = Telemetry::GetStackAndModules(frames);
|
||||
const Telemetry::NativeHangStack& stack = hang.GetNativeStack();
|
||||
Telemetry::ProcessedStack processed = Telemetry::GetStackAndModules(stack);
|
||||
|
||||
CombinedStacks singleStack;
|
||||
singleStack.AddStack(processed);
|
||||
|
|
|
@ -45,13 +45,19 @@ public:
|
|||
void Add(PRIntervalTime aTime);
|
||||
};
|
||||
|
||||
/* A native stack is a simple list of pointers, so rather than building a
|
||||
wrapper type, we typdef the type here. */
|
||||
typedef std::vector<uintptr_t> NativeHangStack;
|
||||
|
||||
/* HangStack stores an array of const char pointers,
|
||||
with optional internal storage for strings. */
|
||||
class HangStack
|
||||
{
|
||||
public:
|
||||
static const size_t sMaxInlineStorage = 8;
|
||||
|
||||
// The maximum depth for the native stack frames that we might collect.
|
||||
// XXX: Consider moving this to a different object?
|
||||
static const size_t sMaxNativeFrames = 25;
|
||||
|
||||
private:
|
||||
|
@ -61,11 +67,6 @@ private:
|
|||
// Stack entries can either be a static const char*
|
||||
// or a pointer to within this buffer.
|
||||
mozilla::Vector<char, 0> mBuffer;
|
||||
// When a native stack is gathered, this vector holds the raw program counter
|
||||
// values that MozStackWalk will return to us after it walks the stack.
|
||||
// When gathering the Telemetry payload, Telemetry will take care of mapping
|
||||
// these program counters to proper addresses within modules.
|
||||
std::vector<uintptr_t> mNativeFrames;
|
||||
|
||||
public:
|
||||
HangStack() {}
|
||||
|
@ -76,6 +77,12 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
HangStack& operator=(HangStack&& aOther) {
|
||||
mImpl = mozilla::Move(aOther.mImpl);
|
||||
mBuffer = mozilla::Move(aOther.mBuffer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const HangStack& aOther) const {
|
||||
for (size_t i = 0; i < length(); i++) {
|
||||
if (!IsSameAsEntry(operator[](i), aOther[i])) {
|
||||
|
@ -118,7 +125,6 @@ public:
|
|||
void clear() {
|
||||
mImpl.clear();
|
||||
mBuffer.clear();
|
||||
mNativeFrames.clear();
|
||||
}
|
||||
|
||||
bool IsInBuffer(const char* aEntry) const {
|
||||
|
@ -144,21 +150,6 @@ public:
|
|||
|
||||
const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
|
||||
const char* AppendViaBuffer(const char* aText, size_t aLength);
|
||||
|
||||
void EnsureNativeFrameCapacity(size_t aCapacity) {
|
||||
mNativeFrames.reserve(aCapacity);
|
||||
}
|
||||
|
||||
void AppendNativeFrame(uintptr_t aPc) {
|
||||
MOZ_ASSERT(mNativeFrames.size() <= sMaxNativeFrames);
|
||||
if (mNativeFrames.size() < sMaxNativeFrames) {
|
||||
mNativeFrames.push_back(aPc);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<uintptr_t>& GetNativeFrames() const {
|
||||
return mNativeFrames;
|
||||
}
|
||||
};
|
||||
|
||||
/* A hang histogram consists of a stack associated with the
|
||||
|
@ -170,7 +161,7 @@ private:
|
|||
|
||||
HangStack mStack;
|
||||
// Native stack that corresponds to the pseudostack in mStack
|
||||
HangStack mNativeStack;
|
||||
NativeHangStack mNativeStack;
|
||||
// Use a hash to speed comparisons
|
||||
const uint32_t mHash;
|
||||
// Annotations attributed to this stack
|
||||
|
@ -182,6 +173,15 @@ public:
|
|||
, mHash(GetHash(mStack))
|
||||
{
|
||||
}
|
||||
|
||||
explicit HangHistogram(HangStack&& aStack,
|
||||
NativeHangStack&& aNativeStack)
|
||||
: mStack(mozilla::Move(aStack))
|
||||
, mNativeStack(mozilla::Move(aNativeStack))
|
||||
, mHash(GetHash(mStack))
|
||||
{
|
||||
}
|
||||
|
||||
HangHistogram(HangHistogram&& aOther)
|
||||
: TimeHistogram(mozilla::Move(aOther))
|
||||
, mStack(mozilla::Move(aOther.mStack))
|
||||
|
@ -198,10 +198,10 @@ public:
|
|||
const HangStack& GetStack() const {
|
||||
return mStack;
|
||||
}
|
||||
HangStack& GetNativeStack() {
|
||||
NativeHangStack& GetNativeStack() {
|
||||
return mNativeStack;
|
||||
}
|
||||
const HangStack& GetNativeStack() const {
|
||||
const NativeHangStack& GetNativeStack() const {
|
||||
return mNativeStack;
|
||||
}
|
||||
const HangMonitor::HangAnnotationsVector& GetAnnotations() const {
|
||||
|
@ -231,16 +231,20 @@ private:
|
|||
public:
|
||||
TimeHistogram mActivity;
|
||||
mozilla::Vector<HangHistogram, 4> mHangs;
|
||||
uint32_t mNativeStackCnt;
|
||||
|
||||
explicit ThreadHangStats(const char* aName)
|
||||
: mName(aName)
|
||||
, mNativeStackCnt(0)
|
||||
{
|
||||
}
|
||||
ThreadHangStats(ThreadHangStats&& aOther)
|
||||
: mName(mozilla::Move(aOther.mName))
|
||||
, mActivity(mozilla::Move(aOther.mActivity))
|
||||
, mHangs(mozilla::Move(aOther.mHangs))
|
||||
, mNativeStackCnt(aOther.mNativeStackCnt)
|
||||
{
|
||||
aOther.mNativeStackCnt = 0;
|
||||
}
|
||||
const char* GetName() const {
|
||||
return mName.get();
|
||||
|
|
|
@ -84,8 +84,8 @@ function run_test() {
|
|||
notEqual(endHangs.hangs[0].stack.length, 0);
|
||||
equal(typeof endHangs.hangs[0].stack[0], "string");
|
||||
|
||||
// Native stack gathering is only enabled on Windows.
|
||||
if (mozinfo.os == "win") {
|
||||
// Native stack gathering is only enabled on Windows x86.
|
||||
if (mozinfo.os == "win" && mozinfo.bits == 32) {
|
||||
// Make sure one of the hangs is a permanent
|
||||
// hang containing a native stack.
|
||||
ok(endHangs.hangs.some((hang) => (
|
||||
|
|
|
@ -701,9 +701,17 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir, int appArgc,
|
|||
}
|
||||
#elif defined(XP_MACOSX)
|
||||
UpdateDriverSetupMacCommandLine(argc, argv, restart);
|
||||
// LaunchChildMac uses posix_spawnp and prefers the current
|
||||
// architecture when launching. It doesn't require a
|
||||
// null-terminated string but it doesn't matter if we pass one.
|
||||
// We need to detect whether elevation is required for this update. This can
|
||||
// occur when an admin user installs the application, but another admin
|
||||
// user attempts to update (see bug 394984).
|
||||
if (restart && !IsRecursivelyWritable(installDirPath.get())) {
|
||||
if (!LaunchElevatedUpdate(argc, argv, outpid)) {
|
||||
LOG(("Failed to launch elevated update!"));
|
||||
exit(1);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (isStaged) {
|
||||
// Launch the updater to replace the installation with the staged updated.
|
||||
LaunchChildMac(argc, argv);
|
||||
|
@ -711,6 +719,9 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir, int appArgc,
|
|||
// Launch the updater to either stage or apply an update.
|
||||
LaunchChildMac(argc, argv, outpid);
|
||||
}
|
||||
if (restart) {
|
||||
exit(0);
|
||||
}
|
||||
#else
|
||||
if (isStaged) {
|
||||
// Launch the updater to replace the installation with the staged updated.
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "PseudoStack.h"
|
||||
#include "ThreadInfo.h"
|
||||
|
@ -675,7 +674,7 @@ AddDynamicCodeLocationTag(ProfileBuffer* aBuffer, const char* aStr)
|
|||
}
|
||||
}
|
||||
|
||||
static const int SAMPLER_MAX_STRING_LENGTH = 128;
|
||||
static const int SAMPLER_MAX_STRING_LENGTH = 512;
|
||||
|
||||
static void
|
||||
AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
|
||||
|
@ -700,9 +699,17 @@ AddPseudoEntry(PSLockRef aLock, ProfileBuffer* aBuffer,
|
|||
|
||||
if (entry.isCopyLabel() || dynamicString) {
|
||||
if (dynamicString) {
|
||||
int bytesWritten =
|
||||
SprintfLiteral(combinedStringBuffer, "%s %s", sampleLabel, dynamicString);
|
||||
if (bytesWritten > 0) {
|
||||
// Create a string that is sampleLabel + ' ' + annotationString.
|
||||
// Avoid sprintf because it can take a lock on Windows, and this
|
||||
// code runs during the profiler's "critical section" as defined
|
||||
// in SamplerThread::SuspendAndSampleAndResumeThread.
|
||||
size_t labelLength = strlen(sampleLabel);
|
||||
size_t dynamicLength = strlen(dynamicString);
|
||||
if (labelLength + 1 + dynamicLength < ArrayLength(combinedStringBuffer)) {
|
||||
PodCopy(combinedStringBuffer, sampleLabel, labelLength);
|
||||
combinedStringBuffer[labelLength] = ' ';
|
||||
PodCopy(&combinedStringBuffer[labelLength + 1], dynamicString, dynamicLength);
|
||||
combinedStringBuffer[labelLength + 1 + dynamicLength] = '\0';
|
||||
sampleLabel = combinedStringBuffer;
|
||||
}
|
||||
}
|
||||
|
@ -3069,5 +3076,16 @@ profiler_call_exit(void* aHandle)
|
|||
pseudoStack->pop();
|
||||
}
|
||||
|
||||
void*
|
||||
profiler_get_stack_top()
|
||||
{
|
||||
PSAutoLock lock(gPSMutex);
|
||||
ThreadInfo* threadInfo = FindLiveThreadInfo(lock);
|
||||
if (threadInfo) {
|
||||
return threadInfo->StackTop();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// END externally visible functions
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -315,6 +315,12 @@ PROFILER_FUNC(double profiler_time(), 0)
|
|||
|
||||
PROFILER_FUNC_VOID(profiler_log(const char *str))
|
||||
|
||||
// Gets the stack top of the current thread.
|
||||
//
|
||||
// The thread must have been previously registered with the profiler, otherwise
|
||||
// this method will return nullptr.
|
||||
PROFILER_FUNC(void* profiler_get_stack_top(), nullptr)
|
||||
|
||||
// End of the functions defined whether the profiler is enabled or not.
|
||||
|
||||
#if defined(MOZ_GECKO_PROFILER)
|
||||
|
|
|
@ -3418,7 +3418,7 @@ static const NSString* kStateCollectionBehavior = @"collectionBehavior";
|
|||
|
||||
- (void)restoreBackgroundColor
|
||||
{
|
||||
[super setBackgroundColor:mColor];
|
||||
[super setBackgroundColor:mBackgroundColor];
|
||||
}
|
||||
|
||||
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect
|
||||
|
|
|
@ -32,6 +32,16 @@
|
|||
// It can be scaled back again in the future
|
||||
#define BHR_BETA_MOD 1;
|
||||
|
||||
// This variable controls the maximum number of native hang stacks which may be
|
||||
// attached to a ping. This is due to how large native stacks can be. We want to
|
||||
// reduce the chance of a ping being discarded due to it exceeding the maximum
|
||||
// ping size.
|
||||
//
|
||||
// NOTE: 300 native hang stacks would, by a rough estimation based on stacks
|
||||
// collected from nightly on March 21, 2017, take up approximately 168kb of
|
||||
// space.
|
||||
static const uint32_t kMaximumNativeHangStacks = 300;
|
||||
|
||||
// Maximum depth of the call stack in the reported thread hangs. This value represents
|
||||
// the 99.9th percentile of the thread hangs stack depths reported by Telemetry.
|
||||
static const size_t kMaxThreadHangStackDepth = 30;
|
||||
|
@ -180,6 +190,8 @@ public:
|
|||
ThreadStackHelper mStackHelper;
|
||||
// Stack of current hang
|
||||
Telemetry::HangStack mHangStack;
|
||||
// Native stack of current hang
|
||||
Telemetry::NativeHangStack mNativeHangStack;
|
||||
// Statistics for telemetry
|
||||
Telemetry::ThreadHangStats mStats;
|
||||
// Annotations for the current hang
|
||||
|
@ -332,7 +344,19 @@ BackgroundHangManager::RunMonitorThread()
|
|||
if (MOZ_LIKELY(!currentThread->mHanging)) {
|
||||
if (MOZ_UNLIKELY(hangTime >= currentThread->mTimeout)) {
|
||||
// A hang started
|
||||
currentThread->mStackHelper.GetStack(currentThread->mHangStack);
|
||||
#ifdef NIGHTLY_BUILD
|
||||
if (currentThread->mStats.mNativeStackCnt < kMaximumNativeHangStacks) {
|
||||
// NOTE: In nightly builds of firefox we want to collect native stacks
|
||||
// for all hangs, not just permahangs.
|
||||
currentThread->mStats.mNativeStackCnt += 1;
|
||||
currentThread->mStackHelper.GetPseudoAndNativeStack(
|
||||
currentThread->mHangStack, currentThread->mNativeHangStack);
|
||||
} else {
|
||||
currentThread->mStackHelper.GetPseudoStack(currentThread->mHangStack);
|
||||
}
|
||||
#else
|
||||
currentThread->mStackHelper.GetPseudoStack(currentThread->mHangStack);
|
||||
#endif
|
||||
currentThread->mHangStart = interval;
|
||||
currentThread->mHanging = true;
|
||||
currentThread->mAnnotations =
|
||||
|
@ -451,7 +475,7 @@ BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
|
|||
mHangStack.erase(mHangStack.begin() + 1, mHangStack.begin() + elementsToRemove);
|
||||
}
|
||||
|
||||
Telemetry::HangHistogram newHistogram(Move(mHangStack));
|
||||
Telemetry::HangHistogram newHistogram(Move(mHangStack), Move(mNativeHangStack));
|
||||
for (Telemetry::HangHistogram* oldHistogram = mStats.mHangs.begin();
|
||||
oldHistogram != mStats.mHangs.end(); oldHistogram++) {
|
||||
if (newHistogram == *oldHistogram) {
|
||||
|
@ -475,9 +499,12 @@ BackgroundHangThread::ReportPermaHang()
|
|||
// mManager->mLock IS locked
|
||||
|
||||
Telemetry::HangHistogram& hang = ReportHang(mMaxTimeout);
|
||||
Telemetry::HangStack& stack = hang.GetNativeStack();
|
||||
Telemetry::NativeHangStack& stack = hang.GetNativeStack();
|
||||
if (stack.empty()) {
|
||||
mStackHelper.GetNativeStack(stack);
|
||||
mStats.mNativeStackCnt += 1;
|
||||
if (mStats.mNativeStackCnt <= kMaximumNativeHangStacks) {
|
||||
mStackHelper.GetNativeStack(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,7 @@ ThreadStackHelper::ThreadStackHelper()
|
|||
| THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION
|
||||
#endif
|
||||
, FALSE, 0);
|
||||
mStackTop = profiler_get_stack_top();
|
||||
MOZ_ASSERT(mInitialized);
|
||||
#elif defined(XP_MACOSX)
|
||||
mThreadID = mach_thread_self();
|
||||
|
@ -164,40 +165,42 @@ public:
|
|||
} // namespace
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStack(Stack& aStack)
|
||||
ThreadStackHelper::GetPseudoStack(Stack& aStack)
|
||||
{
|
||||
GetStackInternal(aStack, /* aAppendNativeStack */ false);
|
||||
GetStacksInternal(&aStack, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
|
||||
ThreadStackHelper::GetStacksInternal(Stack* aStack, NativeStack* aNativeStack)
|
||||
{
|
||||
// Always run PrepareStackBuffer first to clear aStack
|
||||
if (!PrepareStackBuffer(aStack)) {
|
||||
if (aStack && !PrepareStackBuffer(*aStack)) {
|
||||
// Skip and return empty aStack
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedSetPtr<Stack> stackPtr(mStackToFill, &aStack);
|
||||
ScopedSetPtr<Stack> stackPtr(mStackToFill, aStack);
|
||||
|
||||
#if defined(XP_LINUX)
|
||||
if (!sInitialized) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
siginfo_t uinfo = {};
|
||||
uinfo.si_signo = sFillStackSignum;
|
||||
uinfo.si_code = SI_QUEUE;
|
||||
uinfo.si_pid = getpid();
|
||||
uinfo.si_uid = getuid();
|
||||
uinfo.si_value.sival_ptr = this;
|
||||
if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid,
|
||||
mThreadID, sFillStackSignum, &uinfo)) {
|
||||
// rt_tgsigqueueinfo was added in Linux 2.6.31.
|
||||
// Could have failed because the syscall did not exist.
|
||||
return;
|
||||
if (aStack) {
|
||||
siginfo_t uinfo = {};
|
||||
uinfo.si_signo = sFillStackSignum;
|
||||
uinfo.si_code = SI_QUEUE;
|
||||
uinfo.si_pid = getpid();
|
||||
uinfo.si_uid = getuid();
|
||||
uinfo.si_value.sival_ptr = this;
|
||||
if (::syscall(SYS_rt_tgsigqueueinfo, uinfo.si_pid,
|
||||
mThreadID, sFillStackSignum, &uinfo)) {
|
||||
// rt_tgsigqueueinfo was added in Linux 2.6.31.
|
||||
// Could have failed because the syscall did not exist.
|
||||
return;
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(!::sem_wait(&mSem));
|
||||
}
|
||||
MOZ_ALWAYS_TRUE(!::sem_wait(&mSem));
|
||||
|
||||
#elif defined(XP_WIN)
|
||||
if (!mInitialized) {
|
||||
|
@ -205,9 +208,14 @@ ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
|
|||
return;
|
||||
}
|
||||
|
||||
if (aAppendNativeStack) {
|
||||
aStack.EnsureNativeFrameCapacity(Telemetry::HangStack::sMaxNativeFrames);
|
||||
// NOTE: We can only perform frame pointer stack walking on non win64
|
||||
// platforms, because Win64 always omits frame pointers. We don't want to use
|
||||
// MozStackWalk here, so we just skip collecting stacks entirely.
|
||||
#ifndef MOZ_THREADSTACKHELPER_X64
|
||||
if (aNativeStack) {
|
||||
aNativeStack->reserve(Telemetry::HangStack::sMaxNativeFrames);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (::SuspendThread(mThreadID) == DWORD(-1)) {
|
||||
MOZ_ASSERT(false);
|
||||
|
@ -218,21 +226,49 @@ ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
|
|||
// GetThreadContext to ensure it's really suspended.
|
||||
// See https://blogs.msdn.microsoft.com/oldnewthing/20150205-00/?p=44743.
|
||||
CONTEXT context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
context.ContextFlags = CONTEXT_CONTROL;
|
||||
if (::GetThreadContext(mThreadID, &context)) {
|
||||
FillStackBuffer();
|
||||
}
|
||||
if (aStack) {
|
||||
FillStackBuffer();
|
||||
}
|
||||
|
||||
if (aAppendNativeStack) {
|
||||
auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
|
||||
Stack* stack = static_cast<Stack*>(aClosure);
|
||||
stack->AppendNativeFrame(reinterpret_cast<uintptr_t>(aPC));
|
||||
};
|
||||
#ifndef MOZ_THREADSTACKHELPER_X64
|
||||
if (aNativeStack) {
|
||||
auto callback = [](uint32_t, void* aPC, void*, void* aClosure) {
|
||||
NativeStack* stack = static_cast<NativeStack*>(aClosure);
|
||||
stack->push_back(reinterpret_cast<uintptr_t>(aPC));
|
||||
};
|
||||
|
||||
MozStackWalk(callback, /* skipFrames */ 0,
|
||||
/* maxFrames */ Telemetry::HangStack::sMaxNativeFrames,
|
||||
reinterpret_cast<void*>(&aStack),
|
||||
reinterpret_cast<uintptr_t>(mThreadID), nullptr);
|
||||
// Now we need to get our frame pointer, our stack pointer, and our stack
|
||||
// top. Rather than registering and storing the stack tops ourselves, we use
|
||||
// the gecko profiler to look it up.
|
||||
void** framePointer = reinterpret_cast<void**>(context.Ebp);
|
||||
void** stackPointer = reinterpret_cast<void**>(context.Esp);
|
||||
|
||||
MOZ_ASSERT(mStackTop, "The thread should be registered by the profiler");
|
||||
|
||||
// Double check that the values we pulled for the thread make sense before
|
||||
// walking the stack.
|
||||
if (mStackTop && framePointer >= stackPointer && framePointer < mStackTop) {
|
||||
// NOTE: In bug 1346415 this was changed to use FramePointerStackWalk.
|
||||
// This was done because lowering the background hang timer threshold
|
||||
// would cause it to fire on infra early during the boot process, causing
|
||||
// a deadlock in MozStackWalk when the target thread was holding the
|
||||
// windows-internal lock on the function table, as it would be suspended
|
||||
// before we tried to grab the lock to walk its stack.
|
||||
//
|
||||
// FramePointerStackWalk is implemented entirely in userspace and thus
|
||||
// doesn't have the same issues with deadlocking. Unfortunately as 64-bit
|
||||
// windows is not guaranteed to have frame pointers, the stack walking
|
||||
// code is only enabled on 32-bit windows builds (bug 1357829).
|
||||
FramePointerStackWalk(callback, /* skipFrames */ 0,
|
||||
/* maxFrames */ Telemetry::HangStack::sMaxNativeFrames,
|
||||
reinterpret_cast<void*>(aNativeStack), framePointer,
|
||||
mStackTop);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(::ResumeThread(mThreadID) != DWORD(-1));
|
||||
|
@ -246,26 +282,34 @@ ThreadStackHelper::GetStackInternal(Stack& aStack, bool aAppendNativeStack)
|
|||
}
|
||||
# endif
|
||||
|
||||
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
if (aStack) {
|
||||
if (::thread_suspend(mThreadID) != KERN_SUCCESS) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
FillStackBuffer();
|
||||
|
||||
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
FillStackBuffer();
|
||||
|
||||
MOZ_ALWAYS_TRUE(::thread_resume(mThreadID) == KERN_SUCCESS);
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetNativeStack(Stack& aStack)
|
||||
ThreadStackHelper::GetNativeStack(NativeStack& aNativeStack)
|
||||
{
|
||||
#ifdef MOZ_THREADSTACKHELPER_NATIVE
|
||||
GetStackInternal(aStack, /* aAppendNativeStack */ true);
|
||||
GetStacksInternal(nullptr, &aNativeStack);
|
||||
#endif // MOZ_THREADSTACKHELPER_NATIVE
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::GetPseudoAndNativeStack(Stack& aStack, NativeStack& aNativeStack)
|
||||
{
|
||||
GetStacksInternal(&aStack, &aNativeStack);
|
||||
}
|
||||
|
||||
#ifdef XP_LINUX
|
||||
|
||||
int ThreadStackHelper::sInitialized;
|
||||
|
|
|
@ -60,6 +60,12 @@ class ThreadStackHelper
|
|||
public:
|
||||
typedef Telemetry::HangStack Stack;
|
||||
|
||||
// When a native stack is gathered, this vector holds the raw program counter
|
||||
// values that FramePointerStackWalk will return to us after it walks the
|
||||
// stack. When gathering the Telemetry payload, Telemetry will take care of
|
||||
// mapping these program counters to proper addresses within modules.
|
||||
typedef Telemetry::NativeHangStack NativeStack;
|
||||
|
||||
private:
|
||||
Stack* mStackToFill;
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
|
@ -99,18 +105,31 @@ public:
|
|||
*
|
||||
* @param aStack Stack instance to be filled.
|
||||
*/
|
||||
void GetStack(Stack& aStack);
|
||||
void GetPseudoStack(Stack& aStack);
|
||||
|
||||
/**
|
||||
* Retrieve the current native stack of the thread associated
|
||||
* with this ThreadStackHelper.
|
||||
*
|
||||
* @param aNativeStack Stack instance to be filled.
|
||||
* @param aNativeStack NativeStack instance to be filled.
|
||||
*/
|
||||
void GetNativeStack(Stack& aStack);
|
||||
void GetNativeStack(NativeStack& aNativeStack);
|
||||
|
||||
/**
|
||||
* Retrieve the current pseudostack and native stack of the thread associated
|
||||
* with this ThreadStackHelper. This method only pauses the target thread once
|
||||
* to get both stacks.
|
||||
*
|
||||
* @param aStack Stack instance to be filled with the pseudostack.
|
||||
* @param aNativeStack NativeStack instance to be filled with the native stack.
|
||||
*/
|
||||
void GetPseudoAndNativeStack(Stack& aStack, NativeStack& aNativeStack);
|
||||
|
||||
private:
|
||||
void GetStackInternal(Stack& aStack, bool aAppendNativeStack);
|
||||
// Fill in the passed aStack and aNativeStack datastructures with backtraces.
|
||||
// If only aStack needs to be collected, nullptr may be passed for
|
||||
// aNativeStack, and vice versa.
|
||||
void GetStacksInternal(Stack* aStack, NativeStack* aNativeStack);
|
||||
#if defined(XP_LINUX)
|
||||
private:
|
||||
static int sInitialized;
|
||||
|
@ -125,6 +144,7 @@ private:
|
|||
private:
|
||||
bool mInitialized;
|
||||
HANDLE mThreadID;
|
||||
void* mStackTop;
|
||||
|
||||
#elif defined(XP_MACOSX)
|
||||
private:
|
||||
|
|
Загрузка…
Ссылка в новой задаче