зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1433373 - Use source mapping for console jump to definition button r=nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D38096 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
ffed33b20b
Коммит
ada58cbb70
|
@ -21,10 +21,16 @@ FunctionRep.propTypes = {
|
|||
object: PropTypes.object.isRequired,
|
||||
parameterNames: PropTypes.array,
|
||||
onViewSourceInDebugger: PropTypes.func,
|
||||
sourceMapService: PropTypes.object,
|
||||
};
|
||||
|
||||
function FunctionRep(props) {
|
||||
const { object: grip, onViewSourceInDebugger, recordTelemetryEvent } = props;
|
||||
const {
|
||||
object: grip,
|
||||
onViewSourceInDebugger,
|
||||
recordTelemetryEvent,
|
||||
sourceMapService,
|
||||
} = props;
|
||||
|
||||
let jumpToDefinitionButton;
|
||||
if (
|
||||
|
@ -37,14 +43,19 @@ function FunctionRep(props) {
|
|||
className: "jump-definition",
|
||||
draggable: false,
|
||||
title: "Jump to definition",
|
||||
onClick: e => {
|
||||
onClick: async e => {
|
||||
// Stop the event propagation so we don't trigger ObjectInspector
|
||||
// expand/collapse.
|
||||
e.stopPropagation();
|
||||
if (recordTelemetryEvent) {
|
||||
recordTelemetryEvent("jump_to_definition");
|
||||
}
|
||||
onViewSourceInDebugger(grip.location);
|
||||
|
||||
const sourceLocation = await getSourceLocation(
|
||||
grip.location,
|
||||
sourceMapService
|
||||
);
|
||||
onViewSourceInDebugger(sourceLocation);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -181,6 +192,24 @@ function supportsObject(grip, noGrip = false) {
|
|||
return type == "Function";
|
||||
}
|
||||
|
||||
async function getSourceLocation(location, sourceMapService) {
|
||||
if (!sourceMapService) {
|
||||
return location;
|
||||
}
|
||||
try {
|
||||
const originalLocation = await sourceMapService.originalPositionFor(
|
||||
location.url,
|
||||
location.line,
|
||||
location.column
|
||||
);
|
||||
if (originalLocation) {
|
||||
const { sourceUrl, line, column, sourceId } = originalLocation;
|
||||
return { url: sourceUrl, line, column, sourceId };
|
||||
}
|
||||
} catch (e) {}
|
||||
return location;
|
||||
}
|
||||
|
||||
// Exports from this module
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -298,8 +298,11 @@ describe("Function - Anonymous generator function", () => {
|
|||
});
|
||||
|
||||
describe("Function - Jump to definition", () => {
|
||||
it("renders an icon when onViewSourceInDebugger props is provided", () => {
|
||||
const onViewSourceInDebugger = jest.fn();
|
||||
it("renders an icon when onViewSourceInDebugger props is provided", async () => {
|
||||
let onViewSourceInDebugger;
|
||||
const onViewSourceCalled = new Promise(resolve => {
|
||||
onViewSourceInDebugger = jest.fn(resolve);
|
||||
});
|
||||
const object = stubs.get("getRandom");
|
||||
const renderedComponent = renderRep(object, {
|
||||
onViewSourceInDebugger,
|
||||
|
@ -310,6 +313,7 @@ describe("Function - Jump to definition", () => {
|
|||
type: "click",
|
||||
stopPropagation: () => {},
|
||||
});
|
||||
await onViewSourceCalled;
|
||||
|
||||
expect(node.exists()).toBeTruthy();
|
||||
expect(onViewSourceInDebugger.mock.calls).toHaveLength(1);
|
||||
|
@ -362,7 +366,7 @@ describe("Function - Jump to definition", () => {
|
|||
const object = {
|
||||
...stubs.get("getRandom"),
|
||||
};
|
||||
object.location.url = null;
|
||||
object.location = { ...object.location, url: null };
|
||||
const renderedComponent = renderRep(object, {
|
||||
onViewSourceInDebugger: () => {},
|
||||
});
|
||||
|
@ -380,6 +384,39 @@ describe("Function - Jump to definition", () => {
|
|||
const node = renderedComponent.find(".jump-definition");
|
||||
expect(node.exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it("applies source mapping to the object's location", async () => {
|
||||
let onViewSourceInDebugger;
|
||||
const onViewSourceCalled = new Promise(resolve => {
|
||||
onViewSourceInDebugger = jest.fn(resolve);
|
||||
});
|
||||
|
||||
const object = stubs.get("getRandom");
|
||||
const { url, line, column } = object.location;
|
||||
const sourceId = "test source id";
|
||||
const originalPositionFor = jest.fn(() =>
|
||||
Promise.resolve({ sourceUrl: url, line, column, sourceId })
|
||||
);
|
||||
|
||||
const renderedComponent = renderRep(object, {
|
||||
onViewSourceInDebugger,
|
||||
sourceMapService: { originalPositionFor },
|
||||
});
|
||||
|
||||
const node = renderedComponent.find(".jump-definition");
|
||||
node.simulate("click", {
|
||||
type: "click",
|
||||
stopPropagation: () => {},
|
||||
});
|
||||
await onViewSourceCalled;
|
||||
|
||||
expect(originalPositionFor.mock.calls).toHaveLength(1);
|
||||
expect(originalPositionFor.mock.calls[0]).toEqual([url, line, column]);
|
||||
expect(onViewSourceInDebugger.mock.calls).toHaveLength(1);
|
||||
console.log(onViewSourceInDebugger.mock.calls[0]);
|
||||
const expectedLocation = { ...object.location, sourceId };
|
||||
expect(onViewSourceInDebugger.mock.calls[0][0]).toEqual(expectedLocation);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Function - Simplify name", () => {
|
||||
|
|
|
@ -2843,14 +2843,16 @@ const IGNORED_SOURCE_URLS = ["debugger eval code"];
|
|||
FunctionRep.propTypes = {
|
||||
object: PropTypes.object.isRequired,
|
||||
parameterNames: PropTypes.array,
|
||||
onViewSourceInDebugger: PropTypes.func
|
||||
onViewSourceInDebugger: PropTypes.func,
|
||||
sourceMapService: PropTypes.object
|
||||
};
|
||||
|
||||
function FunctionRep(props) {
|
||||
const {
|
||||
object: grip,
|
||||
onViewSourceInDebugger,
|
||||
recordTelemetryEvent
|
||||
recordTelemetryEvent,
|
||||
sourceMapService
|
||||
} = props;
|
||||
let jumpToDefinitionButton;
|
||||
|
||||
|
@ -2859,7 +2861,7 @@ function FunctionRep(props) {
|
|||
className: "jump-definition",
|
||||
draggable: false,
|
||||
title: "Jump to definition",
|
||||
onClick: e => {
|
||||
onClick: async e => {
|
||||
// Stop the event propagation so we don't trigger ObjectInspector
|
||||
// expand/collapse.
|
||||
e.stopPropagation();
|
||||
|
@ -2868,7 +2870,8 @@ function FunctionRep(props) {
|
|||
recordTelemetryEvent("jump_to_definition");
|
||||
}
|
||||
|
||||
onViewSourceInDebugger(grip.location);
|
||||
const sourceLocation = await getSourceLocation(grip.location, sourceMapService);
|
||||
onViewSourceInDebugger(sourceLocation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2986,6 +2989,33 @@ function supportsObject(grip, noGrip = false) {
|
|||
}
|
||||
|
||||
return type == "Function";
|
||||
}
|
||||
|
||||
async function getSourceLocation(location, sourceMapService) {
|
||||
if (!sourceMapService) {
|
||||
return location;
|
||||
}
|
||||
|
||||
try {
|
||||
const originalLocation = await sourceMapService.originalPositionFor(location.url, location.line, location.column);
|
||||
|
||||
if (originalLocation) {
|
||||
const {
|
||||
sourceUrl,
|
||||
line,
|
||||
column,
|
||||
sourceId
|
||||
} = originalLocation;
|
||||
return {
|
||||
url: sourceUrl,
|
||||
line,
|
||||
column,
|
||||
sourceId
|
||||
};
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return location;
|
||||
} // Exports from this module
|
||||
|
||||
|
||||
|
|
|
@ -41,6 +41,9 @@ support-files =
|
|||
test-certificate-messages.html
|
||||
test-click-function-to-source.html
|
||||
test-click-function-to-source.js
|
||||
test-click-function-to-mapped-source.html
|
||||
test-click-function-to-source.min.js
|
||||
test-click-function-to-source.min.js.map
|
||||
test-closure-optimized-out.html
|
||||
test-console-filters.html
|
||||
test-console-filter-by-regex-input.html
|
||||
|
@ -298,6 +301,7 @@ tags = mcb
|
|||
skip-if = fission
|
||||
[browser_webconsole_clear_cache.js]
|
||||
[browser_webconsole_click_function_to_source.js]
|
||||
[browser_webconsole_click_function_to_mapped_source.js]
|
||||
[browser_webconsole_clickable_urls.js]
|
||||
[browser_webconsole_close_unfocused_window.js]
|
||||
[browser_webconsole_closing_after_completion.js]
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that clicking on a function in a source-mapped file displays its
|
||||
// original source in the debugger. See Bug 1433373.
|
||||
|
||||
"use strict";
|
||||
|
||||
requestLongerTimeout(5);
|
||||
|
||||
const TEST_URI =
|
||||
"http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/browser/" +
|
||||
"test-click-function-to-mapped-source.html";
|
||||
|
||||
const TEST_ORIGINAL_URI =
|
||||
"http://example.com/browser/devtools/client/webconsole/" +
|
||||
"test/browser/" +
|
||||
"test-click-function-to-source.js";
|
||||
|
||||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Log a function");
|
||||
const onLoggedFunction = waitForMessage(hud, "function foo");
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
|
||||
content.wrappedJSObject.foo();
|
||||
});
|
||||
const { node } = await onLoggedFunction;
|
||||
const jumpIcon = node.querySelector(".jump-definition");
|
||||
ok(jumpIcon, "A jump to definition button is rendered, as expected");
|
||||
|
||||
info("Click on the jump to definition button.");
|
||||
jumpIcon.click();
|
||||
|
||||
info("Wait for the Debugger panel to open.");
|
||||
const toolbox = hud.toolbox;
|
||||
await toolbox.getPanelWhenReady("jsdebugger");
|
||||
|
||||
const dbg = createDebuggerContext(toolbox);
|
||||
await waitForSelectedSource(dbg, TEST_ORIGINAL_URI);
|
||||
await waitForSelectedLocation(dbg, 9);
|
||||
|
||||
const pendingLocation = dbg.selectors.getPendingSelectedLocation();
|
||||
const { url, line, column } = pendingLocation;
|
||||
|
||||
is(url, TEST_ORIGINAL_URI, "Debugger is open at the expected file");
|
||||
is(line, 9, "Debugger is open at the expected line");
|
||||
// If we loaded the original file, we'd have column 12 for the function's
|
||||
// start position, but 9 is correct for the location in the source map.
|
||||
is(column, 9, "Debugger is open at the expected column");
|
||||
});
|
|
@ -20,12 +20,6 @@ const TEST_SCRIPT_URI =
|
|||
add_task(async function() {
|
||||
const hud = await openNewTabAndConsole(TEST_URI);
|
||||
|
||||
info("Open the Debugger panel.");
|
||||
await openDebugger();
|
||||
|
||||
info("And right after come back to the Console panel.");
|
||||
await openConsole();
|
||||
|
||||
info("Log a function");
|
||||
const onLoggedFunction = waitForMessage(hud, "function foo");
|
||||
ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
|
||||
|
@ -38,11 +32,15 @@ add_task(async function() {
|
|||
info("Click on the jump to definition button.");
|
||||
jumpIcon.click();
|
||||
|
||||
info("Wait for the Debugger panel to open.");
|
||||
const toolbox = hud.toolbox;
|
||||
await toolbox.getPanelWhenReady("jsdebugger");
|
||||
|
||||
const dbg = createDebuggerContext(toolbox);
|
||||
await waitForSelectedSource(dbg, TEST_SCRIPT_URI);
|
||||
|
||||
const pendingLocation = dbg.selectors.getPendingSelectedLocation();
|
||||
const { line } = pendingLocation;
|
||||
const { line, column } = pendingLocation;
|
||||
is(line, 9, "Debugger is open at the expected line");
|
||||
is(column, 12, "Debugger is open at the expected column");
|
||||
});
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html dir="ltr" xml:lang="en-US" lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Click on function should point to source</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript" src="test-click-function-to-source.min.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
3
devtools/client/webconsole/test/browser/test-click-function-to-source.min.js
поставляемый
Normal file
3
devtools/client/webconsole/test/browser/test-click-function-to-source.min.js
поставляемый
Normal file
|
@ -0,0 +1,3 @@
|
|||
/* eslint-disable */
|
||||
function foo(){console.log(foo)}
|
||||
//# sourceMappingURL=test-click-function-to-source.min.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["test-click-function-to-source.js"],"names":["foo","console","log"],"mappings":";AAQA,SAASA,MACPC,QAAQC,IAAIF"}
|
|
@ -69,6 +69,7 @@ function getObjectInspector(grip, serviceContainer, override = {}) {
|
|||
onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger,
|
||||
recordTelemetryEvent: serviceContainer.recordTelemetryEvent,
|
||||
openLink: serviceContainer.openLink,
|
||||
sourceMapService: serviceContainer.sourceMapService,
|
||||
renderStacktrace: stacktrace =>
|
||||
createElement(SmartTrace, {
|
||||
key: "stacktrace",
|
||||
|
|
|
@ -123,6 +123,7 @@ const previewers = {
|
|||
grip.location = {
|
||||
url: obj.script.url,
|
||||
line: obj.script.startLine,
|
||||
column: obj.script.startColumn
|
||||
};
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче