Merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Razvan Maries 2019-03-14 23:41:17 +02:00
Родитель 323879348c 4c2b26578f
Коммит b52345353d
8 изменённых файлов: 143 добавлений и 59 удалений

Просмотреть файл

@ -16,7 +16,7 @@ import {
import { PROMISE } from "../utils/middleware/promise";
import {
getSymbols,
getFirstVisibleBreakpointPosition,
getFirstBreakpointPosition,
getBreakpointPositionsForSource,
getSourceFromId
} from "../../selectors";
@ -105,7 +105,7 @@ export function addBreakpoint(
const { sourceId, column } = location;
if (column === undefined) {
position = getFirstVisibleBreakpointPosition(getState(), location);
position = getFirstBreakpointPosition(getState(), location);
} else {
const positions = getBreakpointPositionsForSource(getState(), sourceId);
position = findPosition(positions, location);

Просмотреть файл

@ -18,7 +18,8 @@ import { getTextAtPosition } from "../../utils/source";
import { comparePosition } from "../../utils/location";
import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
import { getSource } from "../../selectors";
import { getSource, getBreakpointsList } from "../../selectors";
import { removeBreakpoint } from ".";
import type { ThunkArgs, Action } from "../types";
@ -87,6 +88,19 @@ function createSyncData(
return { breakpoint, previousLocation };
}
// Look for an existing breakpoint at the specified generated location.
function findExistingBreakpoint(state, generatedLocation) {
const breakpoints = getBreakpointsList(state);
return breakpoints.find(bp => {
return (
bp.generatedLocation.sourceUrl == generatedLocation.sourceUrl &&
bp.generatedLocation.line == generatedLocation.line &&
bp.generatedLocation.column == generatedLocation.column
);
});
}
// we have three forms of syncing: disabled syncing, existing server syncing
// and adding a new breakpoint
export async function syncBreakpointPromise(
@ -94,7 +108,7 @@ export async function syncBreakpointPromise(
sourceId: SourceId,
pendingBreakpoint: PendingBreakpoint
): Promise<?BreakpointSyncData> {
const { getState, client } = thunkArgs;
const { getState, client, dispatch } = thunkArgs;
assertPendingBreakpoint(pendingBreakpoint);
const source = getSource(getState(), sourceId);
@ -152,8 +166,11 @@ export async function syncBreakpointPromise(
);
}
// clear server breakpoints if they exist and we have moved
await client.removeBreakpoint(generatedLocation);
// Clear any breakpoint for the generated location.
const bp = findExistingBreakpoint(getState(), generatedLocation);
if (bp) {
await dispatch(removeBreakpoint(bp));
}
if (!newGeneratedLocation) {
return { previousLocation, breakpoint: null };

Просмотреть файл

@ -13,7 +13,7 @@ import { generatedToOriginalId } from "devtools-source-map";
import { flatten } from "lodash";
import { toggleBlackBox } from "./blackbox";
import { syncBreakpoint } from "../breakpoints";
import { syncBreakpoint, addBreakpoint } from "../breakpoints";
import { loadSourceText } from "./loadSourceText";
import { togglePrettyPrint } from "./prettyPrint";
import { selectLocation } from "../sources";
@ -186,8 +186,19 @@ function checkPendingBreakpoints(sourceId: string) {
// load the source text if there is a pending breakpoint for it
await dispatch(loadSourceText(source));
// Matching pending breakpoints could have either the same generated or the
// same original source. We expect the generated source to appear first and
// will add a breakpoint at that location initially. If the original source
// appears later then we use syncBreakpoint to see if the generated location
// changed and we need to remove the breakpoint we added earlier.
await Promise.all(
pendingBreakpoints.map(bp => dispatch(syncBreakpoint(sourceId, bp)))
pendingBreakpoints.map(bp => {
if (source.url == bp.location.sourceUrl) {
return dispatch(syncBreakpoint(sourceId, bp));
}
const { line, column } = bp.generatedLocation;
return dispatch(addBreakpoint({ sourceId, line, column }, bp.options));
})
);
};
}

Просмотреть файл

@ -161,9 +161,12 @@ export function getPendingBreakpointsForSource(
return [];
}
return getPendingBreakpointList(state).filter(
pendingBreakpoint => pendingBreakpoint.location.sourceUrl === source.url
);
return getPendingBreakpointList(state).filter(pendingBreakpoint => {
return (
pendingBreakpoint.location.sourceUrl === source.url ||
pendingBreakpoint.generatedLocation.sourceUrl == source.url
);
});
}
export default update;

Просмотреть файл

@ -173,19 +173,3 @@ export function getFirstBreakpointPosition(
position => getSelectedLocation(position, source).line == line
);
}
export function getFirstVisibleBreakpointPosition(
state: State,
{ line }: SourceLocation
) {
const positions = getVisibleBreakpointPositions(state);
const selectedSource = getSelectedSource(state);
if (!selectedSource || !positions) {
return;
}
return positions.find(
position => getSelectedLocation(position, selectedSource).line == line
);
}

Просмотреть файл

@ -6,31 +6,34 @@
"use strict";
const { Component, createFactory } = require("devtools/client/shared/vendor/react");
const { createFactory, createRef, PureComponent } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
const AutocompletePopup = createFactory(require("devtools/client/shared/components/AutoCompletePopup"));
loader.lazyGetter(this, "AutocompletePopup", function() {
return createFactory(require("devtools/client/shared/components/AutoCompletePopup"));
});
loader.lazyGetter(this, "MDNLink", function() {
return createFactory(require("./MdnLink"));
});
class SearchBox extends Component {
loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
class SearchBox extends PureComponent {
static get propTypes() {
return {
autocompleteProvider: PropTypes.func,
delay: PropTypes.number,
keyShortcut: PropTypes.string,
onChange: PropTypes.func,
onFocus: PropTypes.func,
onBlur: PropTypes.func,
onKeyDown: PropTypes.func,
placeholder: PropTypes.string,
plainStyle: PropTypes.bool,
type: PropTypes.string,
autocompleteProvider: PropTypes.func,
learnMoreUrl: PropTypes.string,
learnMoreTitle: PropTypes.string,
learnMoreUrl: PropTypes.string,
onBlur: PropTypes.func,
onChange: PropTypes.func.isRequired,
onFocus: PropTypes.func,
onKeyDown: PropTypes.func,
placeholder: PropTypes.string.isRequired,
plainStyle: PropTypes.bool,
type: PropTypes.string.isRequired,
};
}
@ -42,10 +45,13 @@ class SearchBox extends Component {
focused: false,
};
this.autocompleteRef = createRef();
this.inputRef = createRef();
this.onBlur = this.onBlur.bind(this);
this.onChange = this.onChange.bind(this);
this.onClearButtonClick = this.onClearButtonClick.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onBlur = this.onBlur.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
}
@ -59,7 +65,7 @@ class SearchBox extends Component {
});
this.shortcuts.on(this.props.keyShortcut, event => {
event.preventDefault();
this.refs.input.focus();
this.inputRef.current.focus();
});
}
@ -75,10 +81,10 @@ class SearchBox extends Component {
}
onChange() {
if (this.state.value !== this.refs.input.value) {
if (this.state.value !== this.inputRef.current.value) {
this.setState({
focused: true,
value: this.refs.input.value,
value: this.inputRef.current.value,
});
}
@ -101,7 +107,7 @@ class SearchBox extends Component {
}
onClearButtonClick() {
this.refs.input.value = "";
this.setState({ value: "" });
this.onChange();
}
@ -126,7 +132,7 @@ class SearchBox extends Component {
this.props.onKeyDown();
}
const { autocomplete } = this.refs;
const autocomplete = this.autocompleteRef.current;
if (!autocomplete || autocomplete.state.list.length <= 0) {
return;
}
@ -164,55 +170,54 @@ class SearchBox extends Component {
render() {
let {
type = "search",
placeholder,
autocompleteProvider,
plainStyle,
learnMoreUrl,
learnMoreTitle,
learnMoreUrl,
placeholder,
plainStyle,
type = "search",
} = this.props;
const { value } = this.state;
const divClassList = ["devtools-searchbox", "has-clear-btn"];
const showAutocomplete = autocompleteProvider && this.state.focused && value !== "";
const inputClassList = [`devtools-${type}input`];
if (plainStyle) {
inputClassList.push("devtools-plaininput");
}
const showAutocomplete = autocompleteProvider && this.state.focused && value !== "";
if (value !== "") {
inputClassList.push("filled");
learnMoreUrl = false;
}
return dom.div(
{ className: divClassList.join(" ") },
{ className: "devtools-searchbox has-clear-btn" },
dom.input({
className: inputClassList.join(" "),
onBlur: this.onBlur,
onChange: this.onChange,
onFocus: this.onFocus,
onBlur: this.onBlur,
onKeyDown: this.onKeyDown,
placeholder,
ref: "input",
ref: this.inputRef,
value,
}),
dom.button({
className: "devtools-searchinput-clear",
hidden: value == "",
hidden: value === "",
onClick: this.onClearButtonClick,
}),
learnMoreUrl && MDNLink({
url: learnMoreUrl,
title: learnMoreTitle,
url: learnMoreUrl,
}),
showAutocomplete && AutocompletePopup({
autocompleteProvider,
filter: value,
ref: "autocomplete",
onItemSelected: (itemValue) => {
this.setState({ value: itemValue });
this.onChange();
},
ref: this.autocompleteRef,
})
);
}

Просмотреть файл

@ -3128,6 +3128,12 @@ nsIContent* nsFocusManager::GetNextTabbableContentInScope(
break;
}
// We've been just trying to find some focusable element, and haven't, so
// bail out.
if (aIgnoreTabIndex) {
break;
}
// Continue looking for next highest priority tabindex
aCurrentTabIndex = GetNextTabIndex(aOwner, aCurrentTabIndex, aForward);
contentTraversal.Reset();

Просмотреть файл

@ -5,6 +5,17 @@
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script>
class TestNode extends HTMLElement {
constructor() {
super();
const styles = "<style>:focus{background-color:yellow;}</style>";
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML =
`${styles}<div tabindex='-1'>test node</div> <slot></slot>`;
}}
window.customElements.define('test-node', TestNode);
var lastFocusTarget;
function focusLogger(event) {
lastFocusTarget = event.target;
@ -774,6 +785,52 @@
input1.remove();
}
function testDeeplyNestedShadowTree() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var host1 = document.createElement("test-node");
var lastHost = host1;
for (var i = 0; i < 20; ++i) {
lastHost.appendChild(document.createElement("test-node"));
lastHost = lastHost.firstChild;
}
var input = document.createElement("input");
document.body.appendChild(host1);
document.body.appendChild(input);
document.body.offsetLeft;
// Test shadow tree which doesn't have anything tab-focusable.
host1.shadowRoot.getElementsByTagName("div")[0].focus();
synthesizeKey("KEY_Tab");
is(document.activeElement, input, "Should have focused input element.");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
// Same test but with focusable elements in the tree...
var input2 = document.createElement("input");
var host2 = host1.firstChild;
var host3 = host2.firstChild;
host2.insertBefore(input2, host3);
var input3 = document.createElement("input");
lastHost.appendChild(input3);
document.body.offsetLeft;
host3.shadowRoot.getElementsByTagName("div")[0].focus();
synthesizeKey("KEY_Tab");
is(document.activeElement, input3, "Should have focused input3 element.");
// ...and backwards
host3.shadowRoot.getElementsByTagName("div")[0].focus();
synthesizeKey("KEY_Tab", {shiftKey: true});
is(document.activeElement, input2, "Should have focused input2 element.");
// Remove elements added to body element.
host1.remove();
input.remove();
// Tests expect body.firstChild to have focus.
document.body.firstChild.focus();
}
function runTest() {
testTabbingThroughShadowDOMWithTabIndexes();
@ -787,6 +844,7 @@
testTabbingThroughSlotInLightDOM();
testTabbingThroughFocusableSlotInLightDOM();
testTabbingThroughScrollableShadowDOM();
testDeeplyNestedShadowTree();
opener.didRunTests();
window.close();