--- a/.eslintignore
+++ b/.eslintignore
@@ -73,17 +73,17 @@ browser/extensions/pdfjs/content/web**
browser/extensions/pocket/content/panels/js/tmpl.js
browser/extensions/pocket/content/panels/js/vendor/**
browser/locales/**
# generated or library files in activity-stream
browser/extensions/activity-stream/data/content/activity-stream.bundle.js
browser/extensions/activity-stream/vendor/**
# imported from chromium
browser/extensions/mortar/**
-
+
# devtools/ exclusions
devtools/client/canvasdebugger/**
devtools/client/commandline/**
devtools/client/debugger/**
devtools/client/framework/**
!devtools/client/framework/devtools.js
!devtools/client/framework/devtools-browser.js
!devtools/client/framework/selection.js
@@ -168,16 +168,17 @@ devtools/server/actors/utils/automation-
# Ignore devtools files testing sourcemaps / code style
devtools/client/debugger/test/mochitest/code_binary_search.js
devtools/client/debugger/test/mochitest/code_math.min.js
devtools/client/debugger/test/mochitest/code_math_bogus_map.js
devtools/client/debugger/test/mochitest/code_ugly*
devtools/client/debugger/test/mochitest/code_worker-source-map.js
devtools/client/framework/test/code_ugly*
devtools/client/inspector/markup/test/events_bundle.js
+devtools/client/netmonitor/test/xhr_bundle.js
devtools/server/tests/unit/babel_and_browserify_script_with_source_map.js
devtools/server/tests/unit/setBreakpoint*
devtools/server/tests/unit/sourcemapped.js
# dom/ exclusions
dom/animation/**
dom/archivereader/**
dom/asmjscache/**
--- a/devtools/client/netmonitor/index.html
+++ b/devtools/client/netmonitor/index.html
@@ -40,17 +40,18 @@
this.mount = document.querySelector("#mount");
const connection = {
tabConnection: {
tabTarget: toolbox.target,
},
toolbox,
};
const App = createFactory(require("./src/components/app"));
- render(Provider({ store }, App()), this.mount);
+ const sourceMapService = toolbox.sourceMapURLService;
+ render(Provider({ store }, App({ sourceMapService })), this.mount);
return onFirefoxConnect(connection, actions, store.getState);
},
destroy() {
unmountComponentAtNode(this.mount);
return onDisconnect();
}
};
--- a/devtools/client/netmonitor/src/components/app.js
+++ b/devtools/client/netmonitor/src/components/app.js
@@ -16,25 +16,27 @@ const MonitorPanel = createFactory(requi
const StatisticsPanel = createFactory(require("./statistics-panel"));
const { div } = DOM;
/*
* App component
* The top level component for representing main panel
*/
-function App({ statisticsOpen }) {
+function App({ statisticsOpen, sourceMapService }) {
return (
div({ className: "network-monitor" },
- !statisticsOpen ? MonitorPanel() : StatisticsPanel()
+ !statisticsOpen ? MonitorPanel({sourceMapService}) : StatisticsPanel()
)
);
}
App.displayName = "App";
App.propTypes = {
statisticsOpen: PropTypes.bool.isRequired,
+ // Service to enable the source map feature.
+ sourceMapService: PropTypes.object,
};
module.exports = connect(
(state) => ({ statisticsOpen: state.ui.statisticsOpen }),
)(App);
--- a/devtools/client/netmonitor/src/components/monitor-panel.js
+++ b/devtools/client/netmonitor/src/components/monitor-panel.js
@@ -33,16 +33,18 @@ const MediaQueryList = window.matchMedia
const MonitorPanel = createClass({
displayName: "MonitorPanel",
propTypes: {
isEmpty: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired,
openNetworkDetails: PropTypes.func.isRequired,
request: PropTypes.object,
+ // Service to enable the source map feature.
+ sourceMapService: PropTypes.object,
updateRequest: PropTypes.func.isRequired,
},
getInitialState() {
return {
isVerticalSpliter: MediaQueryList.matches,
};
},
@@ -97,33 +99,36 @@ const MonitorPanel = createClass({
onLayoutChange() {
this.setState({
isVerticalSpliter: MediaQueryList.matches,
});
},
render() {
- let { isEmpty, networkDetailsOpen } = this.props;
+ let { isEmpty, networkDetailsOpen, sourceMapService } = this.props;
let initialWidth = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-width");
let initialHeight = Services.prefs.getIntPref(
"devtools.netmonitor.panes-network-details-height");
return (
div({ className: "monitor-panel" },
Toolbar(),
SplitBox({
className: "devtools-responsive-container",
initialWidth: `${initialWidth}px`,
initialHeight: `${initialHeight}px`,
minSize: "50px",
maxSize: "80%",
splitterSize: "1px",
startPanel: RequestList({ isEmpty }),
- endPanel: networkDetailsOpen && NetworkDetailsPanel({ ref: "endPanel" }),
+ endPanel: networkDetailsOpen && NetworkDetailsPanel({
+ ref: "endPanel",
+ sourceMapService,
+ }),
endPanelCollapsed: !networkDetailsOpen,
endPanelControl: true,
vert: this.state.isVerticalSpliter,
}),
)
);
}
});
--- a/devtools/client/netmonitor/src/components/network-details-panel.js
+++ b/devtools/client/netmonitor/src/components/network-details-panel.js
@@ -22,28 +22,30 @@ const { div } = DOM;
/*
* Network details panel component
*/
function NetworkDetailsPanel({
activeTabId,
cloneSelectedRequest,
request,
selectTab,
+ sourceMapService,
}) {
if (!request) {
return null;
}
return (
div({ className: "network-details-panel" },
!request.isCustom ?
TabboxPanel({
activeTabId,
request,
selectTab,
+ sourceMapService,
}) :
CustomRequestPanel({
cloneSelectedRequest,
request,
})
)
);
}
@@ -51,16 +53,18 @@ function NetworkDetailsPanel({
NetworkDetailsPanel.displayName = "NetworkDetailsPanel";
NetworkDetailsPanel.propTypes = {
activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired,
open: PropTypes.bool,
request: PropTypes.object,
selectTab: PropTypes.func.isRequired,
+ // Service to enable the source map feature.
+ sourceMapService: PropTypes.object,
};
module.exports = connect(
(state) => ({
activeTabId: state.ui.detailsPanelSelectedTab,
request: getSelectedRequest(state),
}),
(dispatch) => ({
--- a/devtools/client/netmonitor/src/components/stack-trace-panel.js
+++ b/devtools/client/netmonitor/src/components/stack-trace-panel.js
@@ -11,28 +11,31 @@ const {
} = require("devtools/client/shared/vendor/react");
const { viewSourceInDebugger } = require("../connector/index");
const { div } = DOM;
// Components
const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
-function StackTracePanel({ request }) {
+function StackTracePanel({ request, sourceMapService }) {
let { stacktrace } = request.cause;
return (
div({ className: "panel-container" },
StackTrace({
stacktrace,
onViewSourceInDebugger: ({ url, line }) => viewSourceInDebugger(url, line),
+ sourceMapService,
}),
)
);
}
StackTracePanel.displayName = "StackTracePanel";
StackTracePanel.propTypes = {
request: PropTypes.object.isRequired,
+ // Service to enable the source map feature.
+ sourceMapService: PropTypes.object,
};
module.exports = StackTracePanel;
--- a/devtools/client/netmonitor/src/components/tabbox-panel.js
+++ b/devtools/client/netmonitor/src/components/tabbox-panel.js
@@ -36,16 +36,17 @@ const TIMINGS_TITLE = L10N.getStr("netmo
* Tabbox panel component
* Display the network request details
*/
function TabboxPanel({
activeTabId,
cloneSelectedRequest,
request,
selectTab,
+ sourceMapService,
}) {
if (!request) {
return null;
}
return (
Tabbar({
activeTabId,
@@ -83,17 +84,17 @@ function TabboxPanel({
},
TimingsPanel({ request }),
),
request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 &&
TabPanel({
id: "stack-trace",
title: STACK_TRACE_TITLE,
},
- StackTracePanel({ request }),
+ StackTracePanel({ request, sourceMapService }),
),
request.securityState && request.securityState !== "insecure" &&
TabPanel({
id: "security",
title: SECURITY_TITLE,
},
SecurityPanel({ request }),
),
@@ -103,16 +104,18 @@ function TabboxPanel({
TabboxPanel.displayName = "TabboxPanel";
TabboxPanel.propTypes = {
activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired,
request: PropTypes.object,
selectTab: PropTypes.func.isRequired,
+ // Service to enable the source map feature.
+ sourceMapService: PropTypes.object,
};
module.exports = connect(
(state) => ({
activeTabId: state.ui.detailsPanelSelectedTab,
request: getSelectedRequest(state),
}),
(dispatch) => ({
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
html_infinite-get-page.html
html_json-b64.html
html_json-basic.html
html_json-custom-mime-test-page.html
html_json-long-test-page.html
html_json-malformed-test-page.html
html_json-text-mime-test-page.html
html_jsonp-test-page.html
+ html_maps-test-page.html
html_navigate-test-page.html
html_params-test-page.html
html_post-data-test-page.html
html_post-json-test-page.html
html_post-raw-test-page.html
html_post-raw-with-headers-test-page.html
html_simple-test-page.html
html_single-get-page.html
@@ -45,24 +46,28 @@ support-files =
sjs_simple-test-server.sjs
sjs_sorting-test-server.sjs
sjs_status-codes-test-server.sjs
sjs_truncate-test-server.sjs
test-image.png
service-workers/status-codes.html
service-workers/status-codes-service-worker.js
!/devtools/client/framework/test/shared-head.js
+ xhr_bundle.js
+ xhr_bundle.js.map
+ xhr_original.js
[browser_net_accessibility-01.js]
[browser_net_accessibility-02.js]
[browser_net_api-calls.js]
[browser_net_autoscroll.js]
[browser_net_cached-status.js]
[browser_net_cause.js]
[browser_net_cause_redirect.js]
+[browser_net_cause_source_map.js]
[browser_net_service-worker-status.js]
[browser_net_charts-01.js]
[browser_net_charts-02.js]
[browser_net_charts-03.js]
[browser_net_charts-04.js]
[browser_net_charts-05.js]
[browser_net_charts-06.js]
[browser_net_charts-07.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_cause_source_map.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests if request cause is reported correctly when using source maps.
+ */
+
+const CAUSE_FILE_NAME = "html_maps-test-page.html";
+const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME;
+
+const N_EXPECTED_REQUESTS = 4;
+
+add_task(function* () {
+ // the initNetMonitor function clears the network request list after the
+ // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
+ // and only then load the real thing from CAUSE_URL - we want to catch
+ // all the requests the page is making, not only the XHRs.
+ // We can't use about:blank here, because initNetMonitor checks that the
+ // page has actually made at least one request.
+ let { tab, monitor } = yield initNetMonitor(SIMPLE_URL);
+
+ let { document, store, windowRequire } = monitor.panelWin;
+ let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+
+ store.dispatch(Actions.batchEnable(false));
+ let waitPromise = waitForNetworkEvents(monitor, N_EXPECTED_REQUESTS);
+ tab.linkedBrowser.loadURI(CAUSE_URL);
+ yield waitPromise;
+
+ info("Clicking item and waiting for details panel to open");
+ waitPromise = waitForDOM(document, ".network-details-panel");
+ let xhrRequestItem = document.querySelectorAll(".request-list-item")[3];
+ EventUtils.sendMouseEvent({ type: "mousedown" }, xhrRequestItem);
+ yield waitPromise;
+
+ info("Clicking stack tab and waiting for stack panel to open");
+ waitPromise = waitForDOM(document, "#stack-trace-panel");
+ let stackTab = document.querySelector("#stack-trace-tab");
+ EventUtils.sendMouseEvent({ type: "click" }, stackTab);
+ yield waitPromise;
+
+ info("Waiting for source maps to be applied");
+ yield waitUntil(() => {
+ let frames = document.querySelectorAll(".frame-link");
+ return frames && frames.length >= 2 &&
+ frames[0].textContent.includes("xhr_original") &&
+ frames[1].textContent.includes("xhr_original");
+ });
+
+ let frames = document.querySelectorAll(".frame-link");
+ is(frames.length, 3, "should have 3 stack frames");
+ is(frames[0].textContent, `reallydoxhr xhr_original.js:6`);
+ is(frames[1].textContent, `doxhr xhr_original.js:10`);
+
+ yield teardown(monitor);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_maps-test-page.html
@@ -0,0 +1,24 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+ <meta http-equiv="Pragma" content="no-cache" />
+ <meta http-equiv="Expires" content="0" />
+ <title>Network Monitor source maps test page</title>
+ <link rel="stylesheet" type="text/css" href="stylesheet_request" />
+ </head>
+
+ <body>
+ <script type="text/javascript" src="xhr_bundle.js" charset="utf-8"></script>
+ <script type="text/javascript">
+ "use strict";
+
+ /* globals doxhr */
+ doxhr();
+ </script>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/xhr_bundle.js
@@ -0,0 +1,91 @@
+/******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // identity function for calling harmony imports with the correct context
+/******/ __webpack_require__.i = function(value) { return value; };
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, {
+/******/ configurable: false,
+/******/ enumerable: true,
+/******/ get: getter
+/******/ });
+/******/ }
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = 0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+function reallydoxhr() {
+ let z = new XMLHttpRequest();
+ z.open("get", "test-image.png", true);
+ z.send();
+}
+
+function doxhr() {
+ reallydoxhr();
+}
+
+window.doxhr = doxhr;
+
+
+/***/ })
+/******/ ]);
+//# sourceMappingURL=xhr_bundle.js.map
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/xhr_bundle.js.map
@@ -0,0 +1,1 @@
+{"version":3,"sources":["webpack:///webpack/bootstrap 1f90f505700f55e4a0b4","webpack:///./xhr_original.js"],"names":[],"mappings":";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA,mDAA2C,cAAc;;AAEzD;AACA;AACA;AACA;AACA;AACA;AACA;AACA,aAAK;AACL;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;AAEA;AACA;;;;;;;;AChEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA","file":"xhr_bundle.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 1f90f505700f55e4a0b4","\"use strict\";\n\nfunction reallydoxhr() {\n let z = new XMLHttpRequest();\n z.open(\"get\", \"test-image.png\", true);\n z.send();\n}\n\nfunction doxhr() {\n reallydoxhr();\n}\n\nwindow.doxhr = doxhr;\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./xhr_original.js\n// module id = 0\n// module chunks = 0"],"sourceRoot":""}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/xhr_original.js
@@ -0,0 +1,13 @@
+"use strict";
+
+function reallydoxhr() {
+ let z = new XMLHttpRequest();
+ z.open("get", "test-image.png", true);
+ z.send();
+}
+
+function doxhr() {
+ reallydoxhr();
+}
+
+window.doxhr = doxhr;