Bug 1531350 - [release 128] [Breakpoints] use the selected location for column breakpoints.

Differential Revision: https://phabricator.services.mozilla.com/D21843

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jason Laster 2019-03-05 21:29:48 +00:00
Родитель 3688c50ea4
Коммит f81988d9c0
62 изменённых файлов: 820 добавлений и 530 удалений

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

@ -35,6 +35,17 @@ const { clearWasmXScopes } = require("./utils/wasmXScopes");
import type { SourceLocation, Source, SourceId } from "debugger-html";
type Range = {
start: {
line: number,
column: number
},
end: {
line: number,
column: number
}
};
async function getOriginalURLs(
generatedSource: Source
): Promise<SourceMapConsumer> {
@ -343,21 +354,12 @@ async function getGeneratedRangesForOriginal(
sourceId: SourceId,
url: string,
mergeUnmappedRegions: boolean = false
): Promise<
Array<{
start: {
line: number,
column: number
},
end: {
line: number,
column: number
}
}>
> {
): Promise<Range[]> {
assert(isOriginalId(sourceId), "Source is not an original source");
const map = await getSourceMap(originalToGeneratedId(sourceId));
// NOTE: this is only needed for Flow
if (!map) {
return [];
}
@ -367,9 +369,18 @@ async function getGeneratedRangesForOriginal(
map.computeColumnSpans();
}
const cachedGeneratedMappingsForOriginal = GENERATED_MAPPINGS.get(map);
if (cachedGeneratedMappingsForOriginal) {
return cachedGeneratedMappingsForOriginal;
if (!GENERATED_MAPPINGS.has(map)) {
GENERATED_MAPPINGS.set(map, new Map());
}
const generatedRangesMap = GENERATED_MAPPINGS.get(map);
if (!generatedRangesMap) {
return [];
}
if (generatedRangesMap.has(sourceId)) {
// NOTE we need to coerce the result to an array for Flow
return generatedRangesMap.get(sourceId) || [];
}
// Gather groups of mappings on the generated file, with new groups created
@ -445,7 +456,7 @@ async function getGeneratedRangesForOriginal(
}
}
GENERATED_MAPPINGS.set(map, generatedMappingsForOriginal);
generatedRangesMap.set(sourceId, generatedMappingsForOriginal);
return generatedMappingsForOriginal;
}

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

@ -81,6 +81,10 @@ export function setOutOfScopeLocations() {
const source = getSourceFromId(getState(), location.sourceId);
if (!isLoaded(source)) {
return;
}
let locations = null;
if (location.line && source && !source.isWasm && isPaused(getState())) {
locations = await parser.findOutOfScopeLocations(

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

@ -9,21 +9,20 @@ import {
assertBreakpoint,
createBreakpoint,
getASTLocation,
assertLocation,
makeBreakpointId,
makeBreakpointLocation
makeBreakpointLocation,
findPosition
} from "../../utils/breakpoint";
import { PROMISE } from "../utils/middleware/promise";
import {
getSource,
getSymbols,
getFirstVisibleBreakpointPosition
getFirstVisibleBreakpointPosition,
getBreakpointPositionsForSource,
getSourceFromId
} from "../../selectors";
import { getGeneratedLocation } from "../../utils/source-maps";
import { getTextAtPosition } from "../../utils/source";
import { recordEvent } from "../../utils/telemetry";
import { features } from "../../utils/prefs";
import { setBreakpointPositions } from "./breakpointPositions";
import type {
BreakpointOptions,
@ -34,35 +33,9 @@ import type { ThunkArgs } from "../types";
async function addBreakpointPromise(getState, client, sourceMaps, breakpoint) {
const state = getState();
const source = getSource(state, breakpoint.location.sourceId);
if (!source) {
throw new Error(`Unable to find source: ${breakpoint.location.sourceId}`);
}
const location = {
...breakpoint.location,
sourceId: source.id,
sourceUrl: source.url
};
const generatedLocation = await getGeneratedLocation(
state,
source,
location,
sourceMaps
);
const generatedSource = getSource(state, generatedLocation.sourceId);
if (!generatedSource) {
throw new Error(
`Unable to find generated source: ${generatedLocation.sourceId}`
);
}
assertLocation(location);
assertLocation(generatedLocation);
const { location, generatedLocation } = breakpoint;
const source = getSourceFromId(state, location.sourceId);
const generatedSource = getSourceFromId(state, generatedLocation.sourceId);
if (breakpointExists(state, location)) {
const newBreakpoint = { ...breakpoint, location, generatedLocation };
@ -128,20 +101,21 @@ export function addBreakpoint(
) {
return async ({ dispatch, getState, sourceMaps, client }: ThunkArgs) => {
recordEvent("add_breakpoint");
let breakpointPosition = location;
if (features.columnBreakpoints && location.column === undefined) {
await dispatch(setBreakpointPositions(location.sourceId));
breakpointPosition = getFirstVisibleBreakpointPosition(
getState(),
location
);
let position;
const { sourceId, column } = location;
if (column === undefined) {
position = getFirstVisibleBreakpointPosition(getState(), location);
} else {
const positions = getBreakpointPositionsForSource(getState(), sourceId);
position = findPosition(positions, location);
}
if (!breakpointPosition) {
if (!position) {
return;
}
const breakpoint = createBreakpoint(breakpointPosition, { options });
const breakpoint = createBreakpoint(position, { options });
return dispatch({
type: "ADD_BREAKPOINT",

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

@ -5,55 +5,74 @@
// @flow
import { isOriginalId, originalToGeneratedId } from "devtools-source-map";
import { uniqBy } from "lodash";
import { getSourceFromId, hasBreakpointPositions } from "../../selectors";
import {
getSource,
getSourceFromId,
hasBreakpointPositions,
getBreakpointPositionsForSource
} from "../../selectors";
import type { MappedLocation, SourceLocation } from "../../types";
import type { ThunkArgs } from "../../actions/types";
import { getOriginalLocation } from "../../utils/source-maps";
import { makeBreakpointId } from "../../utils/breakpoint";
import typeof SourceMaps from "../../../packages/devtools-source-map/src";
const requests = new Map();
async function mapLocations(generatedLocations, state, source, sourceMaps) {
async function mapLocations(
generatedLocations: SourceLocation[],
{ sourceMaps }: { sourceMaps: SourceMaps }
) {
return Promise.all(
generatedLocations.map(async generatedLocation => {
const location = await getOriginalLocation(
generatedLocation,
source,
sourceMaps
);
(generatedLocations: any).map(async (generatedLocation: SourceLocation) => {
const location = await getOriginalLocation(generatedLocation, sourceMaps);
return { location, generatedLocation };
})
);
}
function convertToList(results, sourceId) {
function filterByUniqLocation(positions: MappedLocation[]) {
return uniqBy(positions, ({ location }) => makeBreakpointId(location));
}
function convertToList(results, source) {
const { id, url } = source;
const positions = [];
for (const line in results) {
for (const column of results[line]) {
positions.push({ line: Number(line), column: column, sourceId });
positions.push({
line: Number(line),
column: column,
sourceId: id,
sourceUrl: url
});
}
}
return positions;
}
async function getBreakpointPositions(
sourceId,
{ client, dispatch, getState, sourceMaps }
) {
let source = getSourceFromId(getState(), sourceId);
async function _setBreakpointPositions(sourceId, thunkArgs) {
const { client, dispatch, getState, sourceMaps } = thunkArgs;
let generatedSource = getSource(getState(), sourceId);
if (!generatedSource) {
return;
}
let results = {};
if (isOriginalId(sourceId)) {
const ranges = await sourceMaps.getGeneratedRangesForOriginal(
sourceId,
source.url,
generatedSource.url,
true
);
sourceId = originalToGeneratedId(sourceId);
source = getSourceFromId(getState(), sourceId);
const generatedSourceId = originalToGeneratedId(sourceId);
generatedSource = getSourceFromId(getState(), generatedSourceId);
// Note: While looping here may not look ideal, in the vast majority of
// cases, the number of ranges here should be very small, and is quite
@ -67,25 +86,26 @@ async function getBreakpointPositions(
range.end.column = 0;
}
const bps = await client.getBreakpointPositions(source.actors[0], range);
const bps = await client.getBreakpointPositions(generatedSource, range);
for (const line in bps) {
results[line] = (results[line] || []).concat(bps[line]);
}
}
} else {
results = await client.getBreakpointPositions(source.actors[0]);
results = await client.getBreakpointPositions(generatedSource);
}
const positions = convertToList(results, sourceId);
return mapLocations(positions, getState(), source, sourceMaps);
let positions = convertToList(results, generatedSource);
positions = await mapLocations(positions, thunkArgs);
positions = filterByUniqLocation(positions);
dispatch({ type: "ADD_BREAKPOINT_POSITIONS", sourceId, positions });
}
export function setBreakpointPositions(sourceId: string) {
return async (thunkArgs: ThunkArgs) => {
const { dispatch, getState } = thunkArgs;
const { getState } = thunkArgs;
if (hasBreakpointPositions(getState(), sourceId)) {
return;
return getBreakpointPositionsForSource(getState(), sourceId);
}
if (!requests.has(sourceId)) {
@ -93,11 +113,7 @@ export function setBreakpointPositions(sourceId: string) {
sourceId,
(async () => {
try {
dispatch({
type: "ADD_BREAKPOINT_POSITIONS",
sourceId,
positions: await getBreakpointPositions(sourceId, thunkArgs)
});
await _setBreakpointPositions(sourceId, thunkArgs);
} finally {
requests.delete(sourceId);
}
@ -106,5 +122,6 @@ export function setBreakpointPositions(sourceId: string) {
}
await requests.get(sourceId);
return getBreakpointPositionsForSource(getState(), sourceId);
};
}

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

@ -6,19 +6,19 @@
import { setBreakpointPositions } from "./breakpointPositions";
import {
locationMoved,
createBreakpoint,
assertBreakpoint,
assertPendingBreakpoint,
findScopeByName,
findFunctionByName,
findPosition,
makeBreakpointLocation
} from "../../utils/breakpoint";
import { getGeneratedLocation } from "../../utils/source-maps";
import { getTextAtPosition } from "../../utils/source";
import { comparePosition } from "../../utils/location";
import { originalToGeneratedId, isOriginalId } from "devtools-source-map";
import { getSource, getBreakpointPositionsForSource } from "../../selectors";
import { features } from "../../utils/prefs";
import { getSource } from "../../selectors";
import type { ThunkArgs, Action } from "../types";
@ -35,29 +35,28 @@ type BreakpointSyncData = {
breakpoint: ?Breakpoint
};
async function isPossiblePosition(state, location, dispatch) {
if (!features.columnBreakpoints || location.column != undefined) {
return true;
}
await dispatch(setBreakpointPositions(location.sourceId));
const positions = getBreakpointPositionsForSource(state, location.sourceId);
return (
positions &&
positions.some(({ generatedLocation }) => generatedLocation.column)
);
async function findBreakpointPosition(
{ getState, dispatch },
location: SourceLocation
) {
const positions = await dispatch(setBreakpointPositions(location.sourceId));
const position = findPosition(positions, location);
return position && position.generatedLocation;
}
async function makeScopedLocation(
async function findNewLocation(
{ name, offset, index }: ASTLocation,
location: SourceLocation,
source
) {
const scope = await findScopeByName(source, name, index);
// fallback onto the location line, if the scope is not found
// note: we may at some point want to delete the breakpoint if the scope
// disappears
const line = scope ? scope.location.start.line + offset.line : location.line;
const func = await findFunctionByName(source, name, index);
// Fallback onto the location line, if we do not find a function is not found
let line = location.line;
if (func) {
line = func.location.start.line + offset.line;
}
return {
line,
column: location.column,
@ -76,11 +75,13 @@ function createSyncData(
): BreakpointSyncData {
const overrides = {
...pendingBreakpoint,
generatedLocation,
text,
originalText
};
const breakpoint = createBreakpoint(location, overrides);
const breakpoint = createBreakpoint(
{ generatedLocation, location },
overrides
);
assertBreakpoint(breakpoint);
return { breakpoint, previousLocation };
@ -89,13 +90,11 @@ function createSyncData(
// we have three forms of syncing: disabled syncing, existing server syncing
// and adding a new breakpoint
export async function syncBreakpointPromise(
getState: Function,
client: Object,
sourceMaps: Object,
dispatch: Function,
thunkArgs: ThunkArgs,
sourceId: SourceId,
pendingBreakpoint: PendingBreakpoint
): Promise<BreakpointSyncData | null> {
): Promise<BreakpointSyncData> {
const { getState, client } = thunkArgs;
assertPendingBreakpoint(pendingBreakpoint);
const source = getSource(getState(), sourceId);
@ -106,70 +105,47 @@ export async function syncBreakpointPromise(
const generatedSource = getSource(getState(), generatedSourceId);
if (!source) {
return null;
if (!source || !generatedSource) {
return;
}
const { location, astLocation } = pendingBreakpoint;
const { location, generatedLocation, astLocation } = pendingBreakpoint;
const previousLocation = { ...location, sourceId };
const scopedLocation = await makeScopedLocation(
const newLocation = await findNewLocation(
astLocation,
previousLocation,
source
);
const scopedGeneratedLocation = await getGeneratedLocation(
getState(),
source,
scopedLocation,
sourceMaps
const newGeneratedLocation = await findBreakpointPosition(
thunkArgs,
newLocation
);
// this is the generatedLocation of the pending breakpoint, with
// the source id updated to reflect the new connection
const generatedLocation = {
...pendingBreakpoint.generatedLocation,
sourceId: generatedSourceId
};
const isSameLocation = !locationMoved(
const isSameLocation = comparePosition(
generatedLocation,
scopedGeneratedLocation
);
// makeBreakpointLocation requires the source to still exist, which might not
// be the case if we navigated.
if (!getSource(getState(), generatedSourceId)) {
return null;
}
const breakpointLocation = makeBreakpointLocation(
getState(),
generatedLocation
);
const possiblePosition = await isPossiblePosition(
getState(),
generatedLocation,
dispatch
newGeneratedLocation
);
/** ******* CASE 1: No server change ***********/
// early return if breakpoint is disabled or we are in the sameLocation
if (possiblePosition && (pendingBreakpoint.disabled || isSameLocation)) {
if (newGeneratedLocation && (pendingBreakpoint.disabled || isSameLocation)) {
// Make sure the breakpoint is installed on all source actors.
if (!pendingBreakpoint.disabled) {
await client.setBreakpoint(breakpointLocation, pendingBreakpoint.options);
await client.setBreakpoint(
makeBreakpointLocation(getState(), newGeneratedLocation),
pendingBreakpoint.options
);
}
const originalText = getTextAtPosition(source, previousLocation);
const text = getTextAtPosition(generatedSource, generatedLocation);
const text = getTextAtPosition(generatedSource, newGeneratedLocation);
return createSyncData(
pendingBreakpoint,
scopedLocation,
scopedGeneratedLocation,
newLocation,
newGeneratedLocation,
previousLocation,
text,
originalText
@ -177,9 +153,9 @@ export async function syncBreakpointPromise(
}
// clear server breakpoints if they exist and we have moved
await client.removeBreakpoint(breakpointLocation);
await client.removeBreakpoint(generatedLocation);
if (!possiblePosition || !scopedGeneratedLocation.line) {
if (!newGeneratedLocation) {
return { previousLocation, breakpoint: null };
}
@ -187,17 +163,17 @@ export async function syncBreakpointPromise(
// If we are not disabled, set the breakpoint on the server and get
// that info so we can set it on our breakpoints.
await client.setBreakpoint(
scopedGeneratedLocation,
makeBreakpointLocation(getState(), newGeneratedLocation),
pendingBreakpoint.options
);
const originalText = getTextAtPosition(source, scopedLocation);
const text = getTextAtPosition(generatedSource, scopedGeneratedLocation);
const originalText = getTextAtPosition(source, newLocation);
const text = getTextAtPosition(generatedSource, newGeneratedLocation);
return createSyncData(
pendingBreakpoint,
scopedLocation,
scopedGeneratedLocation,
newLocation,
newGeneratedLocation,
previousLocation,
text,
originalText
@ -207,22 +183,16 @@ export async function syncBreakpointPromise(
/**
* Syncing a breakpoint add breakpoint information that is stored, and
* contact the server for more data.
*
* @memberof actions/breakpoints
* @static
* @param {String} $1.sourceId String value
* @param {PendingBreakpoint} $1.location PendingBreakpoint value
*/
export function syncBreakpoint(
sourceId: SourceId,
pendingBreakpoint: PendingBreakpoint
) {
return async ({ dispatch, getState, client, sourceMaps }: ThunkArgs) => {
return async (thunkArgs: ThunkArgs) => {
const { dispatch } = thunkArgs;
const response = await syncBreakpointPromise(
getState,
client,
sourceMaps,
dispatch,
thunkArgs,
sourceId,
pendingBreakpoint
);
@ -232,7 +202,6 @@ export function syncBreakpoint(
}
const { breakpoint, previousLocation } = response;
return dispatch(
({
type: "SYNC_BREAKPOINT",

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

@ -9,6 +9,7 @@ Array [
"index": 0,
"name": undefined,
"offset": Object {
"column": 1,
"line": 2,
"sourceId": "a",
"sourceUrl": "http://localhost:8000/examples/a",
@ -16,13 +17,15 @@ Array [
},
"disabled": false,
"generatedLocation": Object {
"column": 1,
"line": 2,
"sourceId": "a",
"sourceUrl": "http://localhost:8000/examples/a",
},
"id": "a:2:",
"id": "a:2:1",
"loading": false,
"location": Object {
"column": 1,
"line": 2,
"sourceId": "a",
"sourceUrl": "http://localhost:8000/examples/a",
@ -64,6 +67,7 @@ Object {
"index": 0,
"name": undefined,
"offset": Object {
"column": 0,
"line": 1,
"sourceId": "a.js",
"sourceUrl": "http://localhost:8000/examples/a.js",
@ -71,6 +75,7 @@ Object {
},
"disabled": false,
"generatedLocation": Object {
"column": 0,
"line": 1,
"sourceId": "a.js",
"sourceUrl": "http://localhost:8000/examples/a.js",
@ -102,6 +107,7 @@ Array [
"index": 0,
"name": undefined,
"offset": Object {
"column": 1,
"line": 5,
"sourceId": "a",
"sourceUrl": "http://localhost:8000/examples/a",
@ -109,13 +115,15 @@ Array [
},
"disabled": true,
"generatedLocation": Object {
"column": 1,
"line": 5,
"sourceId": "a",
"sourceUrl": "http://localhost:8000/examples/a",
},
"id": "a:5:",
"id": "a:5:1",
"loading": false,
"location": Object {
"column": 1,
"line": 5,
"sourceId": "a",
"sourceUrl": "http://localhost:8000/examples/a",

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

@ -31,8 +31,18 @@ describe("breakpointPositions", () => {
selectors.getBreakpointPositionsForSource(getState(), "foo")
).toEqual([
{
location: { line: 9, column: 1, sourceId: "foo" },
generatedLocation: { line: 9, column: 1, sourceId: "foo" }
location: {
line: 9,
column: 1,
sourceId: "foo",
sourceUrl: "http://localhost:8000/examples/foo"
},
generatedLocation: {
line: 9,
column: 1,
sourceId: "foo",
sourceUrl: "http://localhost:8000/examples/foo"
}
}
]);
});
@ -63,8 +73,18 @@ describe("breakpointPositions", () => {
selectors.getBreakpointPositionsForSource(getState(), "foo")
).toEqual([
{
location: { line: 9, column: 1, sourceId: "foo" },
generatedLocation: { line: 9, column: 1, sourceId: "foo" }
location: {
line: 9,
column: 1,
sourceId: "foo",
sourceUrl: "http://localhost:8000/examples/foo"
},
generatedLocation: {
line: 9,
column: 1,
sourceId: "foo",
sourceUrl: "http://localhost:8000/examples/foo"
}
}
]);

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

@ -14,18 +14,34 @@ import {
import { simpleMockThreadClient } from "../../tests/helpers/threadClient.js";
function mockClient(positionsResponse = {}) {
return {
...simpleMockThreadClient,
getBreakpointPositions: async () => positionsResponse
};
}
describe("breakpoints", () => {
it("should add a breakpoint", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "2": [1] }));
const loc1 = {
sourceId: "a",
line: 2,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const source = makeSource("a");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
await dispatch(
actions.setSelectedLocation(source, {
line: 1,
column: 1,
sourceId: source.id
})
);
await dispatch(actions.addBreakpoint(loc1));
expect(selectors.getBreakpointCount(getState())).toEqual(1);
@ -38,15 +54,24 @@ describe("breakpoints", () => {
});
it("should not show a breakpoint that does not have text", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const loc1 = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const source = makeSource("a");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
await dispatch(
actions.setSelectedLocation(source, {
line: 1,
column: 1,
sourceId: source.id
})
);
await dispatch(actions.addBreakpoint(loc1));
expect(selectors.getBreakpointCount(getState())).toEqual(1);
@ -56,15 +81,24 @@ describe("breakpoints", () => {
});
it("should show a disabled breakpoint that does not have text", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const loc1 = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const source = makeSource("a");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
await dispatch(
actions.setSelectedLocation(source, {
line: 1,
column: 1,
sourceId: source.id
})
);
const breakpoint = await dispatch(actions.addBreakpoint(loc1));
await dispatch(actions.disableBreakpoint(breakpoint));
@ -75,16 +109,24 @@ describe("breakpoints", () => {
});
it("should not re-add a breakpoint", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const loc1 = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const source = makeSource("a");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
await dispatch(
actions.setSelectedLocation(source, {
line: 1,
column: 1,
sourceId: source.id
})
);
await dispatch(actions.addBreakpoint(loc1));
expect(selectors.getBreakpointCount(getState())).toEqual(1);
@ -96,17 +138,21 @@ describe("breakpoints", () => {
});
it("should remove a breakpoint", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(
mockClient({ "5": [1], "6": [2] })
);
const loc1 = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const loc2 = {
sourceId: "b",
line: 6,
column: 2,
sourceUrl: "http://localhost:8000/examples/b"
};
@ -118,6 +164,14 @@ describe("breakpoints", () => {
await dispatch(actions.newSource(bSource));
await dispatch(actions.loadSourceText(bSource));
await dispatch(
actions.setSelectedLocation(aSource, {
line: 1,
column: 1,
sourceId: aSource.id
})
);
await dispatch(actions.addBreakpoint(loc1));
await dispatch(actions.addBreakpoint(loc2));
@ -131,17 +185,21 @@ describe("breakpoints", () => {
});
it("should disable a breakpoint", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(
mockClient({ "5": [1], "6": [2] })
);
const loc1 = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const loc2 = {
sourceId: "b",
line: 6,
column: 2,
sourceUrl: "http://localhost:8000/examples/b"
};
@ -163,10 +221,13 @@ describe("breakpoints", () => {
});
it("should enable breakpoint", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(
mockClient({ "5": [1], "6": [2] })
);
const loc = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
@ -187,17 +248,21 @@ describe("breakpoints", () => {
});
it("should toggle all the breakpoints", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(
mockClient({ "5": [1], "6": [2] })
);
const loc1 = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
const loc2 = {
sourceId: "b",
line: 6,
column: 2,
sourceUrl: "http://localhost:8000/examples/b"
};
@ -216,6 +281,7 @@ describe("breakpoints", () => {
let bp1 = selectors.getBreakpoint(getState(), loc1);
let bp2 = selectors.getBreakpoint(getState(), loc2);
expect(bp1 && bp1.disabled).toBe(true);
expect(bp2 && bp2.disabled).toBe(true);
@ -228,16 +294,16 @@ describe("breakpoints", () => {
});
it("should toggle a breakpoint at a location", async () => {
const location = { sourceId: "foo1", line: 5 };
const getBp = () => selectors.getBreakpoint(getState(), location);
const loc = { sourceId: "foo1", line: 5, column: 1 };
const getBp = () => selectors.getBreakpoint(getState(), loc);
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const source = makeSource("foo1");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
await dispatch(actions.selectLocation({ sourceId: "foo1", line: 1 }));
await dispatch(actions.selectLocation(loc));
await dispatch(actions.toggleBreakpointAtLine(5));
const bp = getBp();
@ -248,10 +314,10 @@ describe("breakpoints", () => {
});
it("should disable/enable a breakpoint at a location", async () => {
const location = { sourceId: "foo1", line: 5 };
const location = { sourceId: "foo1", line: 5, column: 1 };
const getBp = () => selectors.getBreakpoint(getState(), location);
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const source = makeSource("foo1");
await dispatch(actions.newSource(source));
@ -272,11 +338,12 @@ describe("breakpoints", () => {
});
it("should set the breakpoint condition", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const loc = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
@ -301,15 +368,19 @@ describe("breakpoints", () => {
});
it("should set the condition and enable a breakpoint", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "5": [1] }));
const loc = {
sourceId: "a",
line: 5,
column: 1,
sourceUrl: "http://localhost:8000/examples/a"
};
await dispatch(actions.newSource(makeSource("a")));
const source = makeSource("a");
await dispatch(actions.newSource(source));
await dispatch(actions.loadSourceText(source));
const breakpoint = await dispatch(actions.addBreakpoint(loc));
await dispatch(actions.disableBreakpoint(breakpoint));
@ -330,11 +401,12 @@ describe("breakpoints", () => {
});
it("should remap breakpoints on pretty print", async () => {
const { dispatch, getState } = createStore(simpleMockThreadClient);
const { dispatch, getState } = createStore(mockClient({ "1": [0] }));
const loc = {
sourceId: "a.js",
line: 1,
column: 0,
sourceUrl: "http://localhost:8000/examples/a.js"
};

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

@ -30,7 +30,7 @@ const mockThreadClient = {
evaluate: async () => {},
evaluateInFrame: async () => {},
evaluateExpressions: async () => {},
resume: async () => {},
getFrameScopes: async frame => frame.scope,
setBreakpoint: () => new Promise(_resolve => {}),
sourceContents: ({ source }) => {
@ -69,7 +69,8 @@ const mockThreadClient = {
});
}
});
}
},
getBreakpointPositions: async () => ({})
};
const mockFrameId = "1";
@ -320,7 +321,8 @@ describe("pause", () => {
getOriginalSourceText: async () => ({
source: "fn fooBar() {}\nfn barZoo() { fooBar() }",
contentType: "text/rust"
})
}),
getGeneratedRangesForOriginal: async () => []
};
const store = createStore(mockThreadClient, {}, sourceMapsMock);

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

@ -6,6 +6,8 @@
import { PROMISE } from "../utils/middleware/promise";
import { getGeneratedSource, getSource } from "../../selectors";
import { setBreakpointPositions } from "../breakpoints";
import * as parser from "../../workers/parser";
import { isLoaded, isOriginal } from "../../utils/source";
import { Telemetry } from "devtools-modules";
@ -86,8 +88,9 @@ export function loadSourceText(source: ?Source) {
await dispatch(loadSourceText(generatedSource));
}
if (!newSource.isWasm) {
if (!newSource.isWasm && isLoaded(newSource)) {
await parser.setSource(newSource);
await dispatch(setBreakpointPositions(newSource.id));
}
// signal that the action is finished

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

@ -6,7 +6,7 @@
import assert from "../../utils/assert";
import { recordEvent } from "../../utils/telemetry";
import { remapBreakpoints } from "../breakpoints";
import { remapBreakpoints, setBreakpointPositions } from "../breakpoints";
import { setSymbols } from "../ast";
import { prettyPrint } from "../../workers/pretty-print";
@ -66,8 +66,8 @@ export function createPrettySource(sourceId: string) {
};
setSource(loadedPrettySource);
dispatch(({ type: "UPDATE_SOURCE", source: loadedPrettySource }: Action));
await dispatch(setBreakpointPositions(loadedPrettySource.id));
return prettySource;
};

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

@ -23,7 +23,7 @@ import { loadSourceText } from "./loadSourceText";
import { prefs } from "../../utils/prefs";
import { shouldPrettyPrint, isMinified } from "../../utils/source";
import { createLocation } from "../../utils/location";
import { getMappedLocation } from "../../utils/source-maps";
import { mapLocation } from "../../utils/source-maps";
import {
getSource,
@ -133,7 +133,7 @@ export function selectLocation(
selectedSource &&
isOriginalId(selectedSource.id) != isOriginalId(location.sourceId)
) {
location = await getMappedLocation(getState(), sourceMaps, location);
location = await mapLocation(getState(), sourceMaps, location);
source = getSourceFromId(getState(), location.sourceId);
}
@ -192,11 +192,7 @@ export function jumpToMappedLocation(location: SourceLocation) {
return;
}
const pairedLocation = await getMappedLocation(
getState(),
sourceMaps,
location
);
const pairedLocation = await mapLocation(getState(), sourceMaps, location);
return dispatch(selectSpecificLocation({ ...pairedLocation }));
};

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

@ -46,7 +46,8 @@ describe("loadSourceText", () => {
new Promise(r => {
count++;
resolve = r;
})
}),
getBreakpointPositions: async () => ({})
});
const id = "foo";
const baseSource = makeSource(id, { loadedState: "unloaded" });
@ -78,7 +79,8 @@ describe("loadSourceText", () => {
new Promise(r => {
count++;
resolve = r;
})
}),
getBreakpointPositions: async () => ({})
});
const id = "foo";
const baseSource = makeSource(id, { loadedState: "unloaded" });

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

@ -230,7 +230,8 @@ describe("sources", () => {
{
getOriginalLocation: async location => ({ ...location, line: 12 }),
getGeneratedLocation: async location => ({ ...location, line: 12 }),
getOriginalSourceText: async () => ({ source: "" })
getOriginalSourceText: async () => ({ source: "" }),
getGeneratedRangesForOriginal: async () => []
}
);
@ -254,10 +255,16 @@ describe("sources", () => {
const { dispatch, getState } = createStore(
sourceThreadClient,
{},
{ getOriginalLocation: async location => ({ ...location, line: 12 }) }
{
getOriginalLocation: async location => ({ ...location, line: 12 }),
getGeneratedRangesForOriginal: async () => [],
getOriginalSourceText: async () => ({ source: "" })
}
);
await dispatch(actions.newSource(makeSource("base.js")));
const baseSource = makeOriginalSource("base.js");
await dispatch(actions.newSource(baseSource));
await dispatch(actions.selectSource(baseSource.id));

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

@ -2,7 +2,7 @@
exports[`initializing when pending breakpoints exist in prefs syncs pending breakpoints 1`] = `
Object {
"http://localhost:8000/examples/bar.js:5:": Object {
"http://localhost:8000/examples/bar.js:5:1": Object {
"astLocation": Object {
"index": 0,
"name": undefined,
@ -12,12 +12,12 @@ Object {
},
"disabled": false,
"generatedLocation": Object {
"column": undefined,
"column": 1,
"line": 5,
"sourceUrl": "http://localhost:8000/examples/bar.js",
},
"location": Object {
"column": undefined,
"column": 1,
"line": 5,
"sourceId": "",
"sourceUrl": "http://localhost:8000/examples/bar.js",
@ -38,13 +38,13 @@ Object {
"offset": Object {
"column": 0,
"line": 5,
"sourceId": "foo.js/originalSource",
"sourceId": "foo.js",
"sourceUrl": "http://localhost:8000/examples/foo.js",
},
},
"disabled": false,
"generatedLocation": Object {
"column": undefined,
"column": 0,
"line": 5,
"sourceUrl": "http://localhost:8000/examples/foo.js",
},
@ -69,13 +69,13 @@ Object {
"offset": Object {
"column": 0,
"line": 5,
"sourceId": "foo/originalSource",
"sourceId": "foo",
"sourceUrl": "http://localhost:8000/examples/foo",
},
},
"disabled": false,
"generatedLocation": Object {
"column": undefined,
"column": 0,
"line": 5,
"sourceUrl": "http://localhost:8000/examples/foo",
},
@ -100,13 +100,13 @@ Object {
"offset": Object {
"column": 0,
"line": 5,
"sourceId": "foo2/originalSource",
"sourceId": "foo2",
"sourceUrl": "http://localhost:8000/examples/foo2",
},
},
"disabled": false,
"generatedLocation": Object {
"column": undefined,
"column": 0,
"line": 5,
"sourceUrl": "http://localhost:8000/examples/foo2",
},

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

@ -35,7 +35,8 @@ const threadClient = {
getFrameScopes: async () => {},
evaluate: async expression => ({ result: evaluationResult[expression] }),
evaluateExpressions: async expressions =>
expressions.map(expression => ({ result: evaluationResult[expression] }))
expressions.map(expression => ({ result: evaluationResult[expression] })),
getBreakpointPositions: async () => ({})
};
const sourceMaps = {
@ -43,7 +44,8 @@ const sourceMaps = {
id,
text: sourceTexts[id],
contentType: "text/javascript"
})
}),
getGeneratedRangesForOriginal: async () => []
};
const sourceTexts = {

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

@ -11,12 +11,12 @@ export function mockPendingBreakpoint(overrides: Object = {}) {
sourceId: "",
sourceUrl: sourceUrl || "http://localhost:8000/examples/bar.js",
line: line || 5,
column: column || undefined
column: column || 1
},
generatedLocation: {
sourceUrl: sourceUrl || "http://localhost:8000/examples/bar.js",
line: line || 5,
column: column || undefined
column: column || 1
},
astLocation: {
name: undefined,

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

@ -76,5 +76,6 @@ export const sourceThreadClient = {
},
threadClient: async () => {},
getFrameScopes: async () => {},
evaluateExpressions: async () => {}
evaluateExpressions: async () => {},
getBreakpointPositions: async () => ({})
};

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

@ -23,11 +23,11 @@ const {
} = selectors;
const threadClient = {
sourceContents: () =>
Promise.resolve({
source: "function foo1() {\n const foo = 5; return foo;\n}",
contentType: "text/javascript"
})
sourceContents: async () => ({
source: "function foo1() {\n const foo = 5; return foo;\n}",
contentType: "text/javascript"
}),
getBreakpointPositions: async () => ({})
};
describe("navigation", () => {

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

@ -47,13 +47,31 @@ import {
waitForState
} from "../../utils/test-head";
import sourceMaps from "devtools-source-map";
import { makePendingLocationId } from "../../utils/breakpoint";
function mockClient(bpPos = {}) {
return {
...simpleMockThreadClient,
getBreakpointPositions: async () => bpPos
};
}
function mockSourceMaps() {
return {
...sourceMaps,
getGeneratedRangesForOriginal: async () => [
{ start: { line: 0, column: 0 }, end: { line: 10, column: 10 } }
]
};
}
describe("when adding breakpoints", () => {
it("a corresponding pending breakpoint should be added", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const source = makeOriginalSource("foo.js");
@ -86,8 +104,9 @@ describe("when adding breakpoints", () => {
it("add a corresponding pendingBreakpoint for each addition", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const source1 = makeOriginalSource("foo");
@ -106,14 +125,19 @@ describe("when adding breakpoints", () => {
await dispatch(actions.addBreakpoint(breakpoint2.location));
const pendingBps = selectors.getPendingBreakpoints(getState());
// NOTE the sourceId should be `foo2/originalSource`, but is `foo2`
// because we do not have a real source map for `getOriginalLocation`
// to map.
expect(pendingBps[breakpointLocationId1]).toMatchSnapshot();
expect(pendingBps[breakpointLocationId2]).toMatchSnapshot();
});
it("hidden breakponts do not create pending bps", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const source = makeOriginalSource("foo");
@ -131,8 +155,9 @@ describe("when adding breakpoints", () => {
it("remove a corresponding pending breakpoint when deleting", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
await dispatch(actions.newSource(makeSource("foo")));
@ -161,8 +186,9 @@ describe("when adding breakpoints", () => {
describe("when changing an existing breakpoint", () => {
it("updates corresponding pendingBreakpoint", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const bp = generateBreakpoint("foo");
const id = makePendingLocationId(bp.location);
@ -183,8 +209,9 @@ describe("when changing an existing breakpoint", () => {
it("if disabled, updates corresponding pendingBreakpoint", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const bp = generateBreakpoint("foo");
const id = makePendingLocationId(bp.location);
@ -204,8 +231,9 @@ describe("when changing an existing breakpoint", () => {
it("does not delete the pre-existing pendingBreakpoint", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const bp = generateBreakpoint("foo.js");
@ -229,8 +257,9 @@ describe("when changing an existing breakpoint", () => {
describe("initializing when pending breakpoints exist in prefs", () => {
it("syncs pending breakpoints", async () => {
const { getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const bps = selectors.getPendingBreakpoints(getState());
expect(bps).toMatchSnapshot();
@ -238,8 +267,9 @@ describe("initializing when pending breakpoints exist in prefs", () => {
it("re-adding breakpoints update existing pending breakpoints", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const bar = generateBreakpoint("bar.js");
@ -257,8 +287,9 @@ describe("initializing when pending breakpoints exist in prefs", () => {
it("adding bps doesn't remove existing pending breakpoints", async () => {
const { dispatch, getState } = createStore(
simpleMockThreadClient,
loadInitialState()
mockClient({ "5": [0] }),
loadInitialState(),
mockSourceMaps()
);
const bp = generateBreakpoint("foo.js");
@ -277,8 +308,9 @@ describe("initializing when pending breakpoints exist in prefs", () => {
describe("initializing with disabled pending breakpoints in prefs", () => {
it("syncs breakpoints with pending breakpoints", async () => {
const store = createStore(
simpleMockThreadClient,
loadInitialState({ disabled: true })
mockClient({ "5": [1] }),
loadInitialState({ disabled: true }),
mockSourceMaps()
);
const { getState, dispatch } = store;
@ -295,7 +327,7 @@ describe("initializing with disabled pending breakpoints in prefs", () => {
const bp = selectors.getBreakpointForLocation(getState(), {
line: 5,
column: undefined,
column: 1,
sourceUrl: source.url,
sourceId: source.id
});
@ -309,7 +341,11 @@ describe("initializing with disabled pending breakpoints in prefs", () => {
describe("adding sources", () => {
it("corresponding breakpoints are added for a single source", async () => {
const store = createStore(simpleMockThreadClient, loadInitialState());
const store = createStore(
mockClient({ "5": [1] }),
loadInitialState({ disabled: true }),
mockSourceMaps()
);
const { getState, dispatch } = store;
expect(selectors.getBreakpointCount(getState())).toEqual(0);
@ -327,7 +363,7 @@ describe("adding sources", () => {
it("corresponding breakpoints are added to the original source", async () => {
const source = makeOriginalSource("bar.js", { sourceMapURL: "foo" });
const store = createStore(simpleMockThreadClient, loadInitialState(), {
const store = createStore(mockClient({ "5": [1] }), loadInitialState(), {
getOriginalURLs: async () => [source.url],
getOriginalSourceText: async () => ({ source: "" }),
getGeneratedLocation: async (location, _source) => ({
@ -335,7 +371,10 @@ describe("adding sources", () => {
column: location.column,
sourceId: _source.id
}),
getOriginalLocation: async location => location
getOriginalLocation: async location => location,
getGeneratedRangesForOriginal: async () => [
{ start: { line: 0, column: 0 }, end: { line: 10, column: 10 } }
]
});
const { getState, dispatch } = store;
@ -351,7 +390,11 @@ describe("adding sources", () => {
});
it("add corresponding breakpoints for multiple sources", async () => {
const store = createStore(simpleMockThreadClient, loadInitialState());
const store = createStore(
mockClient({ "5": [1] }),
loadInitialState({ disabled: true }),
mockSourceMaps()
);
const { getState, dispatch } = store;
expect(selectors.getBreakpointCount(getState())).toEqual(0);

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

@ -18,41 +18,30 @@ const {
getTextSearchStatus
} = selectors;
const threadClient = {
sourceContents: function({ source }) {
return new Promise((resolve, reject) => {
switch (source) {
case "foo1":
resolve({
source: "function foo1() {\n const foo = 5; return foo;\n}",
contentType: "text/javascript"
});
break;
case "foo2":
resolve({
source: "function foo2(x, y) {\n return x + y;\n}",
contentType: "text/javascript"
});
break;
case "bar":
resolve({
source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
contentType: "text/javascript"
});
break;
case "bar:formatted":
resolve({
source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
contentType: "text/javascript"
});
break;
}
reject(`unknown source: ${source}`);
});
const sources = {
foo1: {
source: "function foo1() {\n const foo = 5; return foo;\n}",
contentType: "text/javascript"
},
foo2: {
source: "function foo2(x, y) {\n return x + y;\n}",
contentType: "text/javascript"
},
bar: {
source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
contentType: "text/javascript"
},
"bar:formatted": {
source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
contentType: "text/javascript"
}
};
const threadClient = {
sourceContents: async ({ source }) => sources[source],
getBreakpointPositions: async () => ({})
};
describe("project text search", () => {
it("should add a project text search query", () => {
const { dispatch, getState } = createStore();
@ -87,7 +76,8 @@ describe("project text search", () => {
source: "function bla(x, y) {\n const bar = 4; return 2;\n}",
contentType: "text/javascript"
}),
getOriginalURLs: async () => [source2.url]
getOriginalURLs: async () => [source2.url],
getGeneratedRangesForOriginal: async () => []
};
const { dispatch, getState } = createStore(threadClient, {}, mockMaps);

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

@ -10,7 +10,7 @@ import type { ThunkArgs } from "../../types";
const blacklist = [
"SET_POPUP_OBJECT_PROPERTIES",
"SET_PAUSE_POINTS",
"ADD_BREAKPOINT_POSITIONS",
"SET_SYMBOLS",
"OUT_OF_SCOPE_LOCATIONS",
"MAP_SCOPES",
@ -18,7 +18,9 @@ const blacklist = [
"ADD_SCOPES",
"IN_SCOPE_LINES",
"REMOVE_BREAKPOINT",
"NODE_PROPERTIES_LOADED"
"NODE_PROPERTIES_LOADED",
"SET_FOCUSED_SOURCE_ITEM",
"NODE_EXPAND"
];
function cloneAction(action: any) {

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

@ -12,12 +12,14 @@ import type {
ActorId,
BreakpointLocation,
BreakpointOptions,
PendingLocation,
EventListenerBreakpoints,
Frame,
FrameId,
Script,
SourceId,
SourceActor,
Source,
Worker,
Range
} from "../../types";
@ -175,9 +177,10 @@ function removeXHRBreakpoint(path: string, method: string) {
// Get the string key to use for a breakpoint location.
// See also duplicate code in breakpoint-actor-map.js :(
function locationKey(location) {
const { sourceUrl, sourceId, line, column } = location;
return `${(sourceUrl: any)}:${(sourceId: any)}:${line}:${(column: any)}`;
function locationKey(location: BreakpointLocation) {
const { sourceUrl, line, column } = location;
const sourceId = location.sourceId || "";
return `${(sourceUrl: any)}:${sourceId}:${line}:${(column: any)}`;
}
function waitForWorkers(shouldWait: boolean) {
@ -200,8 +203,8 @@ async function setBreakpoint(
await forEachWorkerThread(thread => thread.setBreakpoint(location, options));
}
async function removeBreakpoint(location: BreakpointLocation) {
delete breakpoints[locationKey(location)];
async function removeBreakpoint(location: PendingLocation) {
delete breakpoints[locationKey((location: any))];
await threadClient.removeBreakpoint(location);
// Remove breakpoints without waiting for the thread to respond, for the same
@ -405,9 +408,10 @@ function getMainThread() {
}
async function getBreakpointPositions(
sourceActor: SourceActor,
source: Source,
range: ?Range
): Promise<{ [string]: number[] }> {
const sourceActor = source.actors[0];
const { thread, actor } = sourceActor;
const sourceThreadClient = lookupThreadClient(thread);
const sourceClient = sourceThreadClient.source({ actor });

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

@ -18,6 +18,7 @@ import type {
Script,
Source,
Pause,
PendingLocation,
Frame,
SourceId,
Worker,
@ -343,7 +344,7 @@ export type ThreadClient = {
pauseGrip: (Grip | Function) => ObjectClient,
pauseOnExceptions: (boolean, boolean) => Promise<*>,
setBreakpoint: (BreakpointLocation, BreakpointOptions) => Promise<*>,
removeBreakpoint: BreakpointLocation => Promise<*>,
removeBreakpoint: PendingLocation => Promise<*>,
setXHRBreakpoint: (path: string, method: string) => Promise<boolean>,
removeXHRBreakpoint: (path: string, method: string) => Promise<boolean>,
interrupt: () => Promise<*>,

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

@ -11,7 +11,10 @@ import BreakpointsContextMenu from "../BreakpointsContextMenu";
import { createBreakpoint } from "../../../../utils/breakpoint";
import { buildMenu } from "devtools-contextmenu";
import { makeMockSource } from "../../../../utils/test-mockup";
import {
makeMockSource,
makeMappedLocation
} from "../../../../utils/test-mockup";
jest.mock("devtools-contextmenu");
@ -24,12 +27,12 @@ function render(disabled = false) {
function generateDefaults(disabled) {
const breakpoints = [
createBreakpoint(
{
makeMappedLocation({
line: 1,
column: undefined,
sourceId: "source-https://example.com/main.js",
sourceUrl: "https://example.com/main.js"
},
}),
{
id: "https://example.com/main.js:1:",
disabled: disabled,
@ -41,12 +44,12 @@ function generateDefaults(disabled) {
}
),
createBreakpoint(
{
makeMappedLocation({
line: 2,
column: undefined,
sourceId: "source-https://example.com/main.js",
sourceUrl: "https://example.com/main.js"
},
}),
{
id: "https://example.com/main.js:2:",
disabled: disabled,
@ -56,12 +59,12 @@ function generateDefaults(disabled) {
}
),
createBreakpoint(
{
makeMappedLocation({
line: 3,
column: undefined,
sourceId: "source-https://example.com/main.js",
sourceUrl: "https://example.com/main.js"
},
}),
{
id: "https://example.com/main.js:3:",
disabled: disabled,

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

@ -13,6 +13,7 @@ import {
} from "../breakpoints";
import { createBreakpoint } from "../../utils/breakpoint";
import { makeMappedLocation } from "../../utils/test-mockup";
function initializeStateWith(data) {
const state = initialBreakpointsState();
@ -24,12 +25,15 @@ describe("Breakpoints Selectors", () => {
it("it gets a breakpoint for an original source", () => {
const sourceId = "server1.conn1.child1/source1/originalSource";
const matchingBreakpoints = {
id1: createBreakpoint({ line: 1, sourceId: sourceId }, { options: {} })
id1: createBreakpoint(
makeMappedLocation({ line: 1, sourceId: sourceId }),
{ options: {} }
)
};
const otherBreakpoints = {
id2: createBreakpoint(
{ line: 1, sourceId: "not-this-source" },
makeMappedLocation({ line: 1, sourceId: "not-this-source" }),
{ options: {} }
)
};
@ -54,12 +58,11 @@ describe("Breakpoints Selectors", () => {
const generatedSourceId = "random-source";
const matchingBreakpoints = {
id1: createBreakpoint(
makeMappedLocation(
{ line: 1, sourceId: "original-source-id-1" },
{ line: 1, sourceId: generatedSourceId }
),
{
line: 1,
sourceId: "original-source-id-1"
},
{
generatedLocation: { line: 1, sourceId: generatedSourceId },
options: {}
}
)
@ -67,12 +70,11 @@ describe("Breakpoints Selectors", () => {
const otherBreakpoints = {
id2: createBreakpoint(
makeMappedLocation(
{ line: 1, sourceId: "original-source-id-2" },
{ line: 1, sourceId: "not-this-source" }
),
{
line: 1,
sourceId: "original-source-id-2"
},
{
generatedLocation: { line: 1, sourceId: "not-this-source" },
options: {}
}
)

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

@ -28,7 +28,11 @@ function getBreakpointsForSource(
breakpoints: Breakpoint[]
) {
return breakpoints
.sort((a, b) => a.location.line - b.location.line)
.sort(
(a, b) =>
getSelectedLocation(a, selectedSource).line -
getSelectedLocation(b, selectedSource).line
)
.filter(
bp =>
!bp.options.hidden &&
@ -42,9 +46,12 @@ function getBreakpointsForSource(
function findBreakpointSources(
sources: SourcesMap,
breakpoints: Breakpoint[]
breakpoints: Breakpoint[],
selectedSource: ?Source
): Source[] {
const sourceIds: string[] = uniq(breakpoints.map(bp => bp.location.sourceId));
const sourceIds: string[] = uniq(
breakpoints.map(bp => getSelectedLocation(bp, selectedSource).sourceId)
);
const breakpointSources = sourceIds
.map(id => sources[id])
@ -58,7 +65,7 @@ export const getBreakpointSources: Selector<BreakpointSources> = createSelector(
getSources,
getSelectedSource,
(breakpoints: Breakpoint[], sources: SourcesMap, selectedSource: ?Source) =>
findBreakpointSources(sources, breakpoints)
findBreakpointSources(sources, breakpoints, selectedSource)
.map(source => ({
source,
breakpoints: getBreakpointsForSource(

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

@ -30,8 +30,7 @@ export {
} from "./breakpointAtLocation";
export {
getVisibleBreakpoints,
getFirstVisibleBreakpoints,
getFirstVisibleBreakpointPosition
getFirstVisibleBreakpoints
} from "./visibleBreakpoints";
export { inComponent } from "./inComponent";
export { isSelectedFrameVisible } from "./isSelectedFrameVisible";
@ -39,7 +38,7 @@ export { getCallStackFrames } from "./getCallStackFrames";
export { getVisibleSelectedFrame } from "./visibleSelectedFrame";
export { getBreakpointSources } from "./breakpointSources";
export { getXHRBreakpoints, shouldPauseOnAnyXHR } from "./breakpoints";
export { visibleColumnBreakpoints } from "./visibleColumnBreakpoints";
export * from "./visibleColumnBreakpoints";
import { objectInspector } from "devtools-reps";

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

@ -7,22 +7,14 @@
import { createSelector } from "reselect";
import { uniqBy } from "lodash";
import {
getBreakpointsList,
getBreakpointPositionsForLine
} from "../reducers/breakpoints";
import { getBreakpointsList } from "../reducers/breakpoints";
import { getSelectedSource } from "../reducers/sources";
import { sortBreakpoints } from "../utils/breakpoint";
import { sortSelectedBreakpoints } from "../utils/breakpoint";
import { getSelectedLocation } from "../utils/source-maps";
import type { Breakpoint, Source, SourceLocation } from "../types";
import type { Selector, State } from "../reducers/types";
function isVisible(breakpoint: Breakpoint, selectedSource: Source) {
const location = getSelectedLocation(breakpoint, selectedSource);
return location.sourceId === selectedSource.id;
}
import type { Breakpoint, Source } from "../types";
import type { Selector } from "../reducers/types";
/*
* Finds the breakpoints, which appear in the selected source.
@ -31,35 +23,34 @@ export const getVisibleBreakpoints: Selector<?(Breakpoint[])> = createSelector(
getSelectedSource,
getBreakpointsList,
(selectedSource: ?Source, breakpoints: Breakpoint[]) => {
if (selectedSource == null) {
if (!selectedSource) {
return null;
}
// FIXME: Even though selectedSource is checked above, it fails type
// checking for isVisible
const source: Source = selectedSource;
return breakpoints.filter(bp => isVisible(bp, source));
return breakpoints.filter(
bp =>
selectedSource &&
getSelectedLocation(bp, selectedSource).sourceId === selectedSource.id
);
}
);
export function getFirstVisibleBreakpointPosition(
state: State,
location: SourceLocation
): ?SourceLocation {
const { sourceId, line } = location;
const positions = getBreakpointPositionsForLine(state, sourceId, line);
return positions && positions[0].location;
}
/*
* Finds the first breakpoint per line, which appear in the selected source.
*/
export const getFirstVisibleBreakpoints: Selector<
Breakpoint[]
> = createSelector(getVisibleBreakpoints, breakpoints => {
if (!breakpoints) {
return [];
}
> = createSelector(
getVisibleBreakpoints,
getSelectedSource,
(breakpoints, selectedSource) => {
if (!breakpoints || !selectedSource) {
return [];
}
return (uniqBy(sortBreakpoints(breakpoints), bp => bp.location.line): any);
});
return (uniqBy(
sortSelectedBreakpoints(breakpoints, selectedSource),
bp => getSelectedLocation(bp, selectedSource).line
): any);
}
);

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

@ -3,24 +3,28 @@
* 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 { groupBy, sortedUniqBy } from "lodash";
import { groupBy } from "lodash";
import { createSelector } from "reselect";
import {
getViewport,
getSource,
getSelectedSource,
getBreakpointPositions
getBreakpointPositions,
getBreakpointPositionsForSource
} from "../selectors";
import { getVisibleBreakpoints } from "./visibleBreakpoints";
import { makeBreakpointId } from "../utils/breakpoint";
import type { Selector } from "../reducers/types";
import { getSelectedLocation } from "../utils/source-maps";
import type { Selector, State } from "../reducers/types";
import type {
SourceLocation,
PartialPosition,
Breakpoint,
Range,
BreakpointPositions
BreakpointPositions,
BreakpointPosition,
Source
} from "../types";
export type ColumnBreakpoint = {|
@ -44,13 +48,20 @@ function inViewport(viewport, location) {
return viewport && contains(location, viewport);
}
function groupBreakpoints(breakpoints) {
function groupBreakpoints(breakpoints, selectedSource) {
if (!breakpoints) {
return {};
}
const map: any = groupBy(breakpoints, ({ location }) => location.line);
const map: any = groupBy(
breakpoints,
breakpoint => getSelectedLocation(breakpoint, selectedSource).line
);
for (const line in map) {
map[line] = groupBy(map[line], ({ location }) => location.column);
map[line] = groupBy(
map[line],
breakpoint => getSelectedLocation(breakpoint, selectedSource).column
);
}
return map;
@ -65,70 +76,74 @@ function findBreakpoint(location, breakpointMap) {
}
}
function getLineCount(columnBreakpoints) {
function filterByLineCount(positions, selectedSource) {
const lineCount = {};
columnBreakpoints.forEach(({ location: { line } }) => {
for (const breakpoint of positions) {
const { line } = getSelectedLocation(breakpoint, selectedSource);
if (!lineCount[line]) {
lineCount[line] = 0;
}
lineCount[line] = lineCount[line] + 1;
});
}
return lineCount;
return positions.filter(
breakpoint =>
lineCount[getSelectedLocation(breakpoint, selectedSource).line] > 1
);
}
export function formatColumnBreakpoints(columnBreakpoints: ColumnBreakpoints) {
console.log(
"Column Breakpoints\n\n",
columnBreakpoints
.map(
({ location, breakpoint }) =>
`(${location.line}, ${location.column || ""}) ${
breakpoint && breakpoint.disabled ? "disabled" : ""
}`
)
.join("\n")
);
function filterVisible(positions, selectedSource, viewport) {
return positions.filter(columnBreakpoint => {
const location = getSelectedLocation(columnBreakpoint, selectedSource);
return inViewport(viewport, location);
});
}
function filterByBreakpoints(positions, selectedSource, breakpointMap) {
return positions.filter(position => {
const location = getSelectedLocation(position, selectedSource);
return breakpointMap[location.line];
});
}
function formatPositions(
positions: BreakpointPositions,
selectedSource,
breakpointMap
) {
return (positions: any).map((position: BreakpointPosition) => {
const location = getSelectedLocation(position, selectedSource);
return {
location,
breakpoint: findBreakpoint(location, breakpointMap)
};
});
}
export function getColumnBreakpoints(
positions: ?BreakpointPositions,
breakpoints: ?(Breakpoint[]),
viewport: Range
viewport: Range,
selectedSource: ?Source
) {
if (!positions) {
return [];
}
const breakpointMap = groupBreakpoints(breakpoints);
// We only want to show a column breakpoint if several conditions are matched
// 1. there is a breakpoint on that line
// 2. the position is in the current viewport
// 4. it is the first breakpoint to appear at that generated location
// 5. there is atleast one other breakpoint on that line
// - it is the first breakpoint to appear at an the original location
// - the position is in the current viewport
// - there is atleast one other breakpoint on that line
// - there is a breakpoint on that line
const breakpointMap = groupBreakpoints(breakpoints, selectedSource);
let columnBreakpoints = positions.filter(
({ location }) =>
breakpointMap[location.line] && inViewport(viewport, location)
);
positions = filterByLineCount(positions, selectedSource);
positions = filterVisible(positions, selectedSource, viewport);
positions = filterByBreakpoints(positions, selectedSource, breakpointMap);
// 4. Only show one column breakpoint per generated location
columnBreakpoints = sortedUniqBy(columnBreakpoints, ({ generatedLocation }) =>
makeBreakpointId(generatedLocation)
);
// 5. Check that there is atleast one other possible breakpoint on the line
const lineCount = getLineCount(columnBreakpoints);
columnBreakpoints = columnBreakpoints.filter(
({ location: { line } }) => lineCount[line] > 1
);
return (columnBreakpoints: any).map(({ location }) => ({
location,
breakpoint: findBreakpoint(location, breakpointMap)
}));
return formatPositions(positions, selectedSource, breakpointMap);
}
const getVisibleBreakpointPositions = createSelector(
@ -146,3 +161,35 @@ export const visibleColumnBreakpoints: Selector<
getSelectedSource,
getColumnBreakpoints
);
export function getFirstBreakpointPosition(
state: State,
{ line, sourceId }: SourceLocation
) {
const positions = getBreakpointPositionsForSource(state, sourceId);
const source = getSource(state, sourceId);
if (!source || !positions) {
return;
}
return positions.find(
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
);
}

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

@ -33,7 +33,7 @@ export function getASTLocation(
return { name: undefined, offset: location, index: 0 };
}
export async function findScopeByName(
export async function findFunctionByName(
source: Source,
name: ?string,
index: number

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

@ -0,0 +1,23 @@
// @flow
/* 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 { comparePosition } from "../location";
import type {
BreakpointPositions,
SourceLocation,
Position
} from "../../types";
export function findPosition(
positions: ?BreakpointPositions,
location: Position | SourceLocation
) {
if (!positions) {
return null;
}
return positions.find(pos => comparePosition(pos.location, location));
}

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

@ -13,7 +13,8 @@ import assert from "../assert";
import { features } from "../prefs";
import { getSelectedLocation } from "../source-maps";
export { getASTLocation, findScopeByName } from "./astBreakpointLocation";
export * from "./astBreakpointLocation";
export * from "./breakpointPositions";
import type {
Source,
@ -23,7 +24,8 @@ import type {
PendingLocation,
Breakpoint,
BreakpointLocation,
PendingBreakpoint
PendingBreakpoint,
MappedLocation
} from "../../types";
import type { State } from "../../reducers/types";
@ -39,15 +41,6 @@ export function firstString(...args: string[]) {
return null;
}
export function locationMoved(
location: SourceLocation,
newLocation: SourceLocation
) {
return (
location.line !== newLocation.line || location.column !== newLocation.column
);
}
// The ID for a Breakpoint is derived from its location in its Source.
export function makeBreakpointId(location: SourceLocation) {
const { sourceId, line, column } = location;
@ -164,25 +157,19 @@ export function breakpointExists(state: State, location: SourceLocation) {
}
export function createBreakpoint(
location: SourceLocation,
mappedLocation: MappedLocation,
overrides: Object = {}
): Breakpoint {
const {
disabled,
generatedLocation,
astLocation,
text,
originalText,
options
} = overrides;
const { disabled, astLocation, text, originalText, options } = overrides;
const defaultASTLocation = {
name: undefined,
offset: location,
offset: mappedLocation.location,
index: 0
};
const properties = {
id: makeBreakpointId(location),
id: makeBreakpointId(mappedLocation.location),
...mappedLocation,
options: {
condition: options.condition || null,
logValue: options.logValue || null,
@ -191,8 +178,6 @@ export function createBreakpoint(
disabled: disabled || false,
loading: false,
astLocation: astLocation || defaultASTLocation,
generatedLocation: generatedLocation || location,
location,
text,
originalText
};

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

@ -9,5 +9,6 @@ DIRS += [
DebuggerModules(
'astBreakpointLocation.js',
'breakpointPositions.js',
'index.js',
)

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

@ -4,7 +4,7 @@
// @flow
import type { SourceLocation, SourceId } from "../types";
import type { PartialPosition, SourceLocation, SourceId } from "../types";
type IncompleteLocation = {
sourceId: SourceId,
@ -13,6 +13,10 @@ type IncompleteLocation = {
sourceUrl?: string
};
export function comparePosition(a: ?PartialPosition, b: ?PartialPosition) {
return a && b && a.line == b.line && a.column == b.column;
}
export function createLocation({
sourceId,
line = 1,

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

@ -41,7 +41,6 @@ export async function getGeneratedLocation(
export async function getOriginalLocation(
generatedLocation: SourceLocation,
source: Source,
sourceMaps: SourceMaps
) {
if (isOriginalId(generatedLocation.sourceId)) {
@ -55,6 +54,36 @@ export async function getMappedLocation(
state: Object,
sourceMaps: Object,
location: SourceLocation
): Promise<MappedLocation> {
const source = getSource(state, location.sourceId);
if (!source) {
throw new Error(`no source ${location.sourceId}`);
}
if (isOriginalId(location.sourceId)) {
const generatedLocation = await getGeneratedLocation(
state,
source,
location,
sourceMaps
);
return { location, generatedLocation };
}
const generatedLocation = location;
const originalLocation = await sourceMaps.getOriginalLocation(
generatedLocation,
source
);
return { location: originalLocation, generatedLocation };
}
export async function mapLocation(
state: Object,
sourceMaps: Object,
location: SourceLocation
): Promise<SourceLocation> {
const source = getSource(state, location.sourceId);
@ -69,10 +98,14 @@ export async function getMappedLocation(
return sourceMaps.getOriginalLocation(location, source);
}
export function isOriginalSource(source: ?Source) {
return source && isOriginalId(source.id);
}
export function getSelectedLocation(
mappedLocation: MappedLocation,
selectedSource: ?Source
) {
): SourceLocation {
return selectedSource && isGenerated(selectedSource)
? mappedLocation.generatedLocation
: mappedLocation.location;

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

@ -45,6 +45,14 @@ function createStore(client: any, initialState: any = {}, sourceMapsMock: any) {
newSources: sources => store.dispatch(actions.newSources(sources))
});
store.thunkArgs = () => ({
dispatch: store.dispatch,
getState: store.getState,
client,
sourceMaps,
panel: {}
});
return store;
}

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

@ -22,6 +22,7 @@ import type {
WasmSource,
Source,
SourceId,
SourceLocation,
Why
} from "../types";
@ -153,6 +154,14 @@ function makeMockExpression(value: Object): Expression {
};
}
export function makeMappedLocation(
location: SourceLocation,
generatedLocation: ?SourceLocation
) {
generatedLocation = generatedLocation || location;
return { location, generatedLocation };
}
export {
makeMockSource,
makeMockWasmSource,

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

@ -10,9 +10,12 @@ add_task(async function() {
getState
} = dbg;
await selectSource(dbg, "scripts.html");
// Make sure we can set a top-level breakpoint and it will be hit on
// reload.
await addBreakpoint(dbg, "scripts.html", 21);
reload(dbg);
await waitForDispatch(dbg, "NAVIGATE");
@ -22,7 +25,7 @@ add_task(async function() {
assertPausedLocation(dbg);
await resume(dbg);
// Create an eval script that pauses itself.
info('Create an eval script that pauses itself.')
invokeInTab("doEval");
await waitForPaused(dbg);

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

@ -13,6 +13,7 @@ function skipPausing(dbg) {
add_task(async function() {
let dbg = await initDebugger("doc-scripts.html");
await selectSource(dbg, "simple3")
await addBreakpoint(dbg, "simple3", 2);
await skipPausing(dbg);

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

@ -2,16 +2,6 @@
* 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/>. */
function findBreakpoint(dbg, url, line, column = 0) {
const {
selectors: { getBreakpoint },
getState
} = dbg;
const source = findSource(dbg, url);
const location = { sourceId: source.id, line, column };
return getBreakpoint(getState(), location);
}
function getLineEl(dbg, line) {
const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
return lines[line - 1];
@ -116,7 +106,7 @@ async function setLogPoint(dbg, index, value) {
add_task(async function() {
const dbg = await initDebugger("doc-scripts.html", "simple2");
await pushPref("devtools.debugger.features.column-breakpoints", false);
await pushPref("devtools.debugger.features.column-breakpoints", true);
await pushPref("devtools.debugger.features.log-points", true);
await selectSource(dbg, "simple2");

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

@ -42,24 +42,24 @@ add_task(async function() {
await waitForLoadedSource(dbg, "debugger-statements.html");
assertPausedLocation(dbg);
// resume
info("resume");
await clickResume(dbg);
await waitForPaused(dbg);
assertPausedLocation(dbg);
// step over
info("step over");
await clickStepOver(dbg);
assertPausedLocation(dbg);
// step into
info("step into");
await clickStepIn(dbg);
assertPausedLocation(dbg);
// step over
info("step over");
await clickStepOver(dbg);
assertPausedLocation(dbg);
// step out
info("step out");
await clickStepOut(dbg);
assertPausedLocation(dbg);
});

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

@ -53,12 +53,4 @@ add_task(async function() {
await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
is(getBreakpointCount(getState()), 0, "No breakpoints exist");
assertEditorBreakpoint(dbg, 4, false);
// Ensure that clicking the gutter removes all breakpoints on a given line
await addBreakpoint(dbg, source, 4, 0);
await addBreakpoint(dbg, source, 4, 1);
await addBreakpoint(dbg, source, 4, 2);
clickGutter(dbg, 4);
await waitForState(dbg, state => dbg.selectors.getBreakpointCount(state) === 0);
assertEditorBreakpoint(dbg, 4, false);
});

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

@ -18,8 +18,8 @@ add_task(async function() {
const simple2 = findSource(dbg, "simple2.js");
// Set the initial breakpoint.
await selectSource(dbg, "simple1");
await addBreakpoint(dbg, simple1, 4);
ok(!getSelectedSource(getState()), "No selected source");
// Call the function that we set a breakpoint in.
invokeInTab("main");
@ -43,6 +43,7 @@ add_task(async function() {
// Make sure that we can set a breakpoint on a line out of the
// viewport, and that pausing there scrolls the editor to it.
let longSrc = findSource(dbg, "long.js");
await selectSource(dbg, "long.js");
await addBreakpoint(dbg, longSrc, 66);
invokeInTab("testModel");

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

@ -29,6 +29,7 @@ add_task(async function() {
await waitForPaused(dbg);
await navigate(dbg, "doc-scripts.html", "simple1.js");
await selectSource(dbg, "simple1");
await addBreakpoint(dbg, "simple1.js", 4);
invokeInTab("main");
await waitForPaused(dbg);

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

@ -22,6 +22,7 @@ add_task(async function() {
// Make sure that we can set a breakpoint on a line out of the
// viewport, and that pausing there scrolls the editor to it.
let longSrc = findSource(dbg, "long.js");
await selectSource(dbg, "long.js")
await addBreakpoint(dbg, longSrc, 66);
invokeInTab("testModel");
await waitForPaused(dbg, "long.js");

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

@ -16,7 +16,7 @@ add_task(async function() {
// this is not implemented yet
// assertHighlightLocation(dbg, "math.min.js:formatted", 18);
// await selectSource(dbg, "math.min.js")
await addBreakpoint(dbg, ppSrc, 18);
invokeInTab("arithmetic");

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

@ -1,6 +1,7 @@
add_task(async function() {
const dbg = await initDebugger("doc-react.html", "App.js");
await selectSource(dbg, "App.js");
await addBreakpoint(dbg, "App.js", 11);
info('Test previewing an immutable Map inside of a react component')

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

@ -28,7 +28,7 @@ add_task(async function() {
await waitForSelectedSource(dbg, "sjs_code_reload.sjs");
const source = findSource(dbg, "sjs_code_reload");
const location = { sourceId: source.id, line: 6 };
const location = { sourceId: source.id, line: 6, column: 2 };
await waitForBreakpoint(dbg, location);

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

@ -79,7 +79,7 @@ add_task(async function() {
[`aVar === "var3"`, `aLet === "let3"`, `aConst === "const3"`]
);
await evalInConsoleAtPoint(dbg, "webpack3-babel6", "babel-classes", { line: 8, column: 6 }, [
await evalInConsoleAtPoint(dbg, "webpack3-babel6", "babel-classes", { line: 8, column: 16 }, [
`this.hasOwnProperty("bound")`,
]);

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

@ -85,7 +85,7 @@ function targetToFlags(target) {
const rollupOptimized = isRollup ? "(optimized away)" : null;
const webpackImportGetter = isWebpack ? "Getter" : null;
const webpack4ImportGetter = isWebpack4 ? "Getter" : null;
const maybeLineStart = hasBabel ? col => col : col => 0;
const maybeLineStart = col => col;
const defaultExport = isWebpack4
? name => `${name}()`
: name => [name, "(optimized away)"];
@ -183,7 +183,7 @@ async function testEvalMaps(dbg) {
]) {
const { defaultExport } = targetToFlags(target);
await breakpointScopes(dbg, target, "eval-maps", { line: 14, column: 0 }, [
await breakpointScopes(dbg, target, "eval-maps", { line: 14, column: 4 }, [
"Block",
["<this>", "Window"],
["three", "5"],

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

@ -26,12 +26,14 @@ add_task(async function() {
await navigate(dbg, "sourcemaps-reload/doc-sourcemaps-reload.html", "v1.js");
info('Add initial breakpoint');
await selectSource(dbg, "v1.js");
await addBreakpoint(dbg, "v1.js", 6);
let breakpoint = getBreakpoints(dbg)[0];
is(breakpoint.location.line, 6);
info('Reload with a new version of the file');
let syncBp = waitForDispatch(dbg, "SYNC_BREAKPOINT");
await navigate(dbg, "doc-sourcemaps-reload2.html", "v1.js");
@ -41,10 +43,14 @@ add_task(async function() {
is(breakpoint.location.line, 9);
is(breakpoint.generatedLocation.line, 73);
info('Add a second breakpoint');
await addBreakpoint(dbg, "v1.js", 13);
is(dbg.selectors.getBreakpointCount(dbg.getState()), 2, "No breakpoints");
// NOTE: When we reload, the `foo` function and the
// module is no longer 13 lines long
info('Reload and observe no breakpoints')
syncBp = waitForDispatch(dbg, "SYNC_BREAKPOINT", 2);
await navigate(dbg, "doc-sourcemaps-reload3.html", "v1.js");
await waitForSource(dbg, "v1");

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

@ -30,18 +30,7 @@ add_task(async function() {
"Original source text loaded correctly"
);
// Test that breakpoint sliding is not attempted. The breakpoint
// should not move anywhere.
await addBreakpoint(dbg, entrySrc, 13);
is(getBreakpointCount(getState()), 1, "One breakpoint exists");
ok(
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
"Breakpoint has correct line"
);
await addBreakpoint(dbg, entrySrc, 5);
await addBreakpoint(dbg, entrySrc, 15, 0);
await disableBreakpoint(dbg, entrySrc, 15, 0);
@ -52,11 +41,11 @@ add_task(async function() {
await waitForPaused(dbg);
assertPausedLocation(dbg);
await waitForBreakpointCount(dbg, 3);
is(getBreakpointCount(getState()), 3, "Three breakpoints exist");
await waitForBreakpointCount(dbg, 2);
is(getBreakpointCount(getState()), 2, "Three breakpoints exist");
ok(
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 15, column: 0 }),
"Breakpoint has correct line"
);
@ -64,6 +53,7 @@ add_task(async function() {
getBreakpoint(getState(), {
sourceId: entrySrc.id,
line: 15,
column: 0,
disabled: true
}),
"Breakpoint has correct line"

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

@ -17,8 +17,9 @@ function assertBreakpointExists(dbg, source, line) {
);
}
function assertEditorBreakpoint(dbg, line, shouldExist) {
const exists = !!getLineEl(dbg, line).querySelector(".new-breakpoint");
async function assertEditorBreakpoint(dbg, line, shouldExist) {
const el = await getLineEl(dbg, line);
const exists = !!el.querySelector(".new-breakpoint");
ok(
exists === shouldExist,
"Breakpoint " +
@ -28,13 +29,17 @@ function assertEditorBreakpoint(dbg, line, shouldExist) {
);
}
function getLineEl(dbg, line) {
const lines = dbg.win.document.querySelectorAll(".CodeMirror-code > div");
return lines[line - 1];
async function getLineEl(dbg, line) {
let el = await codeMirrorGutterElement(dbg, line);
while (el && !el.matches(".CodeMirror-code > div")) {
el = el.parentElement;
}
return el;
}
function clickGutter(dbg, line) {
clickElement(dbg, "gutter", line);
async function clickGutter(dbg, line) {
const el = await codeMirrorGutterElement(dbg, line);
clickDOMElement(dbg, el);
}
add_task(async function() {
@ -54,11 +59,11 @@ add_task(async function() {
await selectSource(dbg, bundleSrc);
await clickGutter(dbg, 13);
await clickGutter(dbg, 70);
await waitForDispatch(dbg, "ADD_BREAKPOINT");
assertEditorBreakpoint(dbg, 13, true);
assertEditorBreakpoint(dbg, 70, true);
await clickGutter(dbg, 13);
await clickGutter(dbg, 70);
await waitForDispatch(dbg, "REMOVE_BREAKPOINT");
is(getBreakpointCount(getState()), 0, "No breakpoints exists");

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

@ -14,13 +14,14 @@ add_task(async function() {
// shutting down.
dbg.client.waitForWorkers(true);
await selectSource(dbg, "simple-worker.js");
await addBreakpoint(dbg, workerSource, 1);
invokeInTab("startWorker");
await waitForPaused(dbg, "simple-worker.js");
// We should be paused at the first line of simple-worker.js
assertPausedAtSourceAndLine(dbg, workerSource.id, 1);
await removeBreakpoint(dbg, workerSource.id, 1);
await removeBreakpoint(dbg, workerSource.id, 1, 12);
await resume(dbg);
// Make sure that suspending activity in the worker when attaching does not
@ -31,5 +32,5 @@ add_task(async function() {
// We should be paused in the message listener in simple-worker.js
assertPausedAtSourceAndLine(dbg, workerSource.id, 10);
await removeBreakpoint(dbg, workerSource.id, 10);
await removeBreakpoint(dbg, workerSource.id, 10, 2);
});

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

@ -90,6 +90,7 @@ add_task(async function() {
await addBreakpoint(dbg, "simple-worker", 10);
invokeInTab("sayHello");
dbg.actions.selectThread(worker1Thread);
await waitForPaused(dbg);
assertPausedAtSourceAndLine(dbg, workerSource.id, 10);

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

@ -19,6 +19,7 @@
// source. It also needs to introduce a new global variable so
// it's not immediately garbage collected.
inline_script = function () { var x = 5; };
inline_script();
</script>
</body>
</html>

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

@ -17,6 +17,8 @@ var { Toolbox } = require("devtools/client/framework/toolbox");
var { Task } = require("devtools/shared/task");
var asyncStorage = require("devtools/shared/async-storage");
const { getSelectedLocation } = require("devtools/client/debugger/new/src/utils/source-maps");
const sourceUtils = {
isLoaded: source => source.loadedState === "loaded"
};
@ -219,10 +221,17 @@ async function waitForElementWithSelector(dbg, selector) {
}
function waitForSelectedSource(dbg, url) {
const {
getSelectedSource,
hasSymbols,
hasSourceMetaData,
hasBreakpointPositions
} = dbg.selectors;
return waitForState(
dbg,
state => {
const source = dbg.selectors.getSelectedSource(state);
const source = getSelectedSource(state);
const isLoaded = source && sourceUtils.isLoaded(source);
if (!isLoaded) {
return false;
@ -237,13 +246,9 @@ function waitForSelectedSource(dbg, url) {
return false;
}
// wait for async work to be done
const hasSymbols = dbg.selectors.hasSymbols(state, source);
const hasSourceMetaData = dbg.selectors.hasSourceMetaData(
state,
source.id
);
return hasSymbols && hasSourceMetaData;
return hasSymbols(state, source) &&
hasSourceMetaData( state, source.id) &&
hasBreakpointPositions(state, source.id);
},
"selected source"
);
@ -731,6 +736,14 @@ async function navigate(dbg, url, ...sources) {
return waitForSources(dbg, ...sources);
}
function getFirstBreakpointColumn(dbg, {line, sourceId}) {
const {getSource, getFirstBreakpointPosition} = dbg.selectors;
const source = getSource(dbg.getState(), sourceId)
const position = getFirstBreakpointPosition(dbg.getState(), { line, sourceId });
return getSelectedLocation(position, source).column;
}
/**
* Adds a breakpoint to a source at line/col.
*
@ -742,11 +755,15 @@ async function navigate(dbg, url, ...sources) {
* @return {Promise}
* @static
*/
function addBreakpoint(dbg, source, line, column) {
async function addBreakpoint(dbg, source, line, column) {
source = findSource(dbg, source);
const sourceId = source.id;
column = column || getFirstBreakpointColumn(dbg, {line, sourceId: source.id});
const bpCount = dbg.selectors.getBreakpointCount(dbg.getState());
Cu.forceGC();
dbg.actions.addBreakpoint({ sourceId, line, column });
return waitForDispatch(dbg, "ADD_BREAKPOINT");
await waitForDispatch(dbg, "ADD_BREAKPOINT");
is(dbg.selectors.getBreakpointCount(dbg.getState()), bpCount + 1, "a new breakpoint was created");
}
function disableBreakpoint(dbg, source, line, column) {
@ -758,22 +775,11 @@ function disableBreakpoint(dbg, source, line, column) {
function findBreakpoint(dbg, url, line) {
const {
selectors: { getBreakpoint },
selectors: { getBreakpoint, getBreakpointsList },
getState
} = dbg;
const source = findSource(dbg, url);
let column;
if (
Services.prefs.getBoolPref("devtools.debugger.features.column-breakpoints")
) {
({ column } = dbg.selectors.getFirstVisibleBreakpointPosition(
dbg.store.getState(),
{
sourceId: source.id,
line
}
));
}
const column = getFirstBreakpointColumn(dbg, {line, sourceId: source.id });
return getBreakpoint(getState(), { sourceId: source.id, line, column });
}
@ -894,6 +900,7 @@ async function assertScopes(dbg, items) {
*/
function removeBreakpoint(dbg, sourceId, line, column) {
const source = dbg.selectors.getSource(dbg.getState(), sourceId);
column = column || getFirstBreakpointColumn(dbg, {line, sourceId});
const location = { sourceId, sourceUrl: source.url, line, column };
const bp = dbg.selectors.getBreakpointForLocation(dbg.getState(), location);
dbg.actions.removeBreakpoint(bp);
@ -1220,8 +1227,15 @@ async function clickElement(dbg, elementName, ...args) {
}
function clickElementWithSelector(dbg, selector) {
clickDOMElement(
dbg,
findElementWithSelector(dbg, selector)
);
}
function clickDOMElement(dbg, element) {
EventUtils.synthesizeMouseAtCenter(
findElementWithSelector(dbg, selector),
element,
{},
dbg.win
);
@ -1318,6 +1332,33 @@ async function waitForScrolling(codeMirror) {
});
}
async function codeMirrorGutterElement(dbg, line) {
info(`CodeMirror line ${line}`);
const cm = getCM(dbg);
const position = { line: line - 1, ch: 0 };
cm.scrollIntoView(position, 0);
await waitForScrolling(cm);
const coords = getCoordsFromPosition(cm, position);
const { left, top } = coords;
// Adds a vertical offset due to increased line height
// https://github.com/firefox-devtools/debugger/pull/7934
const lineHeightOffset = 3;
const tokenEl = dbg.win.document.elementFromPoint(
left,
top + lineHeightOffset
);
if (!tokenEl) {
throw new Error("Failed to find element for line " + line);
}
return tokenEl;
}
async function hoverAtPos(dbg, { line, ch }) {
info(`Hovering at ${line}, ${ch}`);
const cm = getCM(dbg);

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

@ -1,6 +1,6 @@
/* global toolbox, createDebuggerContext, waitForSources, testUrl,
waitForPaused, addBreakpoint, assertPausedLocation, stepIn,
findSource, removeBreakpoint, resume */
findSource, removeBreakpoint, resume, selectSource */
info(`START: ${new Error().lineNumber}`);
@ -34,6 +34,7 @@ info(`START: ${new Error().lineNumber}`);
script.click();
const onPaused = waitForPaused(dbg);
await selectSource(dbg, fileName);
await addBreakpoint(dbg, fileName, 2);
await onPaused;

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

@ -7249,6 +7249,8 @@ async function getGeneratedRangesForOriginal(sourceId, url, mergeUnmappedRegions
assert(isOriginalId(sourceId), "Source is not an original source");
const map = await getSourceMap(originalToGeneratedId(sourceId));
// NOTE: this is only needed for Flow
if (!map) {
return [];
}
@ -7258,9 +7260,18 @@ async function getGeneratedRangesForOriginal(sourceId, url, mergeUnmappedRegions
map.computeColumnSpans();
}
const cachedGeneratedMappingsForOriginal = GENERATED_MAPPINGS.get(map);
if (cachedGeneratedMappingsForOriginal) {
return cachedGeneratedMappingsForOriginal;
if (!GENERATED_MAPPINGS.has(map)) {
GENERATED_MAPPINGS.set(map, new Map());
}
const generatedRangesMap = GENERATED_MAPPINGS.get(map);
if (!generatedRangesMap) {
return [];
}
if (generatedRangesMap.has(sourceId)) {
// NOTE we need to coerce the result to an array for Flow
return generatedRangesMap.get(sourceId) || [];
}
// Gather groups of mappings on the generated file, with new groups created
@ -7322,7 +7333,7 @@ async function getGeneratedRangesForOriginal(sourceId, url, mergeUnmappedRegions
}
}
GENERATED_MAPPINGS.set(map, generatedMappingsForOriginal);
generatedRangesMap.set(sourceId, generatedMappingsForOriginal);
return generatedMappingsForOriginal;
}

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

@ -20,6 +20,7 @@ add_task(async function() {
const toolbox = gDevTools.getToolbox(hud.target);
const dbg = createDebuggerContext(toolbox);
await selectSource(dbg, "test-closure-optimized-out.html");
await addBreakpoint(dbg, "test-closure-optimized-out.html", breakpointLine);
// Cause the debuggee to pause

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

@ -239,8 +239,10 @@ async function addBreakpoint(dbg, line, url) {
const location = {
sourceId: source.id,
line,
column: 0,
};
await selectSource(dbg, url);
const onDispatched = waitForDispatch(dbg, "ADD_BREAKPOINT");
dbg.actions.addBreakpoint(location);
return onDispatched;