From 662cf5a1e672fe06b86d483ce46bbe2edcdb5389 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 10 May 2017 14:07:06 -0600 Subject: [PATCH] Bug 1361853 - use client-side source map service in network monitor; r=Honza MozReview-Commit-ID: A0lDkK12x8E --HG-- extra : rebase_source : c7a2389126289a8390780a308487936f26ceab5f --- .eslintignore | 3 +- devtools/client/netmonitor/index.html | 3 +- .../client/netmonitor/src/components/app.js | 6 +- .../src/components/monitor-panel.js | 9 +- .../src/components/network-details-panel.js | 4 + .../src/components/stack-trace-panel.js | 5 +- .../netmonitor/src/components/tabbox-panel.js | 5 +- devtools/client/netmonitor/test/browser.ini | 5 + .../test/browser_net_cause_source_map.js | 58 ++++++++++++ .../netmonitor/test/html_maps-test-page.html | 24 +++++ devtools/client/netmonitor/test/xhr_bundle.js | 91 +++++++++++++++++++ .../client/netmonitor/test/xhr_bundle.js.map | 1 + .../client/netmonitor/test/xhr_original.js | 13 +++ 13 files changed, 219 insertions(+), 8 deletions(-) create mode 100644 devtools/client/netmonitor/test/browser_net_cause_source_map.js create mode 100644 devtools/client/netmonitor/test/html_maps-test-page.html create mode 100644 devtools/client/netmonitor/test/xhr_bundle.js create mode 100644 devtools/client/netmonitor/test/xhr_bundle.js.map create mode 100644 devtools/client/netmonitor/test/xhr_original.js diff --git a/.eslintignore b/.eslintignore index 1ec0b73278d8..72c09f6d00bf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -78,7 +78,7 @@ 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/** @@ -173,6 +173,7 @@ 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 diff --git a/devtools/client/netmonitor/index.html b/devtools/client/netmonitor/index.html index 2b6b45cfaa65..269cd6cd5b77 100644 --- a/devtools/client/netmonitor/index.html +++ b/devtools/client/netmonitor/index.html @@ -45,7 +45,8 @@ 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); }, diff --git a/devtools/client/netmonitor/src/components/app.js b/devtools/client/netmonitor/src/components/app.js index b2d71e745585..856864567414 100644 --- a/devtools/client/netmonitor/src/components/app.js +++ b/devtools/client/netmonitor/src/components/app.js @@ -21,10 +21,10 @@ 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() ) ); } @@ -33,6 +33,8 @@ App.displayName = "App"; App.propTypes = { statisticsOpen: PropTypes.bool.isRequired, + // Service to enable the source map feature. + sourceMapService: PropTypes.object, }; module.exports = connect( diff --git a/devtools/client/netmonitor/src/components/monitor-panel.js b/devtools/client/netmonitor/src/components/monitor-panel.js index c74fe187f5b6..9772d39b5516 100644 --- a/devtools/client/netmonitor/src/components/monitor-panel.js +++ b/devtools/client/netmonitor/src/components/monitor-panel.js @@ -38,6 +38,8 @@ const MonitorPanel = createClass({ 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, }, @@ -102,7 +104,7 @@ const MonitorPanel = createClass({ }, 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( @@ -118,7 +120,10 @@ const MonitorPanel = createClass({ 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, diff --git a/devtools/client/netmonitor/src/components/network-details-panel.js b/devtools/client/netmonitor/src/components/network-details-panel.js index d1699b5b098c..f7915868884d 100644 --- a/devtools/client/netmonitor/src/components/network-details-panel.js +++ b/devtools/client/netmonitor/src/components/network-details-panel.js @@ -27,6 +27,7 @@ function NetworkDetailsPanel({ cloneSelectedRequest, request, selectTab, + sourceMapService, }) { if (!request) { return null; @@ -39,6 +40,7 @@ function NetworkDetailsPanel({ activeTabId, request, selectTab, + sourceMapService, }) : CustomRequestPanel({ cloneSelectedRequest, @@ -56,6 +58,8 @@ NetworkDetailsPanel.propTypes = { open: PropTypes.bool, request: PropTypes.object, selectTab: PropTypes.func.isRequired, + // Service to enable the source map feature. + sourceMapService: PropTypes.object, }; module.exports = connect( diff --git a/devtools/client/netmonitor/src/components/stack-trace-panel.js b/devtools/client/netmonitor/src/components/stack-trace-panel.js index 8bdaa692717e..79f37ffd3a4a 100644 --- a/devtools/client/netmonitor/src/components/stack-trace-panel.js +++ b/devtools/client/netmonitor/src/components/stack-trace-panel.js @@ -16,7 +16,7 @@ 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 ( @@ -24,6 +24,7 @@ function StackTracePanel({ request }) { StackTrace({ stacktrace, onViewSourceInDebugger: ({ url, line }) => viewSourceInDebugger(url, line), + sourceMapService, }), ) ); @@ -33,6 +34,8 @@ StackTracePanel.displayName = "StackTracePanel"; StackTracePanel.propTypes = { request: PropTypes.object.isRequired, + // Service to enable the source map feature. + sourceMapService: PropTypes.object, }; module.exports = StackTracePanel; diff --git a/devtools/client/netmonitor/src/components/tabbox-panel.js b/devtools/client/netmonitor/src/components/tabbox-panel.js index 2c9eaf2aa5a2..510054fc85eb 100644 --- a/devtools/client/netmonitor/src/components/tabbox-panel.js +++ b/devtools/client/netmonitor/src/components/tabbox-panel.js @@ -41,6 +41,7 @@ function TabboxPanel({ cloneSelectedRequest, request, selectTab, + sourceMapService, }) { if (!request) { return null; @@ -88,7 +89,7 @@ function TabboxPanel({ id: "stack-trace", title: STACK_TRACE_TITLE, }, - StackTracePanel({ request }), + StackTracePanel({ request, sourceMapService }), ), request.securityState && request.securityState !== "insecure" && TabPanel({ @@ -108,6 +109,8 @@ TabboxPanel.propTypes = { cloneSelectedRequest: PropTypes.func.isRequired, request: PropTypes.object, selectTab: PropTypes.func.isRequired, + // Service to enable the source map feature. + sourceMapService: PropTypes.object, }; module.exports = connect( diff --git a/devtools/client/netmonitor/test/browser.ini b/devtools/client/netmonitor/test/browser.ini index fb40594c755d..435260e10809 100644 --- a/devtools/client/netmonitor/test/browser.ini +++ b/devtools/client/netmonitor/test/browser.ini @@ -22,6 +22,7 @@ support-files = 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 @@ -50,6 +51,9 @@ support-files = 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] @@ -58,6 +62,7 @@ support-files = [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] diff --git a/devtools/client/netmonitor/test/browser_net_cause_source_map.js b/devtools/client/netmonitor/test/browser_net_cause_source_map.js new file mode 100644 index 000000000000..392ba91b5947 --- /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); +}); diff --git a/devtools/client/netmonitor/test/html_maps-test-page.html b/devtools/client/netmonitor/test/html_maps-test-page.html new file mode 100644 index 000000000000..401d472e73ab --- /dev/null +++ b/devtools/client/netmonitor/test/html_maps-test-page.html @@ -0,0 +1,24 @@ + + + + + + + + + + Network Monitor source maps test page + + + + + + + + diff --git a/devtools/client/netmonitor/test/xhr_bundle.js b/devtools/client/netmonitor/test/xhr_bundle.js new file mode 100644 index 000000000000..00001de5a2f5 --- /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 diff --git a/devtools/client/netmonitor/test/xhr_bundle.js.map b/devtools/client/netmonitor/test/xhr_bundle.js.map new file mode 100644 index 000000000000..c28ee2a8f9cd --- /dev/null +++ b/devtools/client/netmonitor/test/xhr_bundle.js.map @@ -0,0 +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 diff --git a/devtools/client/netmonitor/test/xhr_original.js b/devtools/client/netmonitor/test/xhr_original.js new file mode 100644 index 000000000000..10059ab23e70 --- /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;