From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- .../client/debugger/test/mochitest/browser.ini | 297 + .../test/mochitest/browser_dbg-async-stepping.js | 23 + .../test/mochitest/browser_dbg-asyncstacks.js | 21 + .../test/mochitest/browser_dbg-audiocontext.js | 20 + .../browser_dbg-backgroundtask-debugging.js | 151 + .../debugger/test/mochitest/browser_dbg-bfcache.js | 98 + .../test/mochitest/browser_dbg-blackbox-all.js | 215 + .../mochitest/browser_dbg-blackbox-original.js | 53 + .../test/mochitest/browser_dbg-blackbox.js | 742 + .../mochitest/browser_dbg-breaking-from-console.js | 38 + .../test/mochitest/browser_dbg-breaking.js | 55 + .../browser_dbg-breakpoint-skipping-console.js | 20 + .../mochitest/browser_dbg-breakpoint-skipping.js | 87 + .../mochitest/browser_dbg-breakpoints-actions.js | 84 + .../mochitest/browser_dbg-breakpoints-columns.js | 131 + .../browser_dbg-breakpoints-cond-shortcut.js | 147 + .../browser_dbg-breakpoints-cond-source-maps.js | 45 + .../test/mochitest/browser_dbg-breakpoints-cond.js | 136 + .../browser_dbg-breakpoints-debugger-statement.js | 94 + .../browser_dbg-breakpoints-duplicate-functions.js | 38 + .../browser_dbg-breakpoints-in-evaled-sources.js | 92 + .../test/mochitest/browser_dbg-breakpoints-list.js | 189 + .../mochitest/browser_dbg-breakpoints-popup.js | 250 + ...bg-breakpoints-reloading-with-source-changes.js | 186 + .../mochitest/browser_dbg-breakpoints-reloading.js | 124 + ...browser_dbg-breakpoints-same-file-per-target.js | 114 + .../browser_dbg-breakpoints-scroll-to-log.js | 61 + ...wser_dbg-breakpoints-sourcemap-with-sections.js | 27 + .../test/mochitest/browser_dbg-breakpoints.js | 102 + ...browser_dbg-browser-toolbox-unselected-pause.js | 67 + .../browser_dbg-browser-toolbox-workers.js | 55 + .../test/mochitest/browser_dbg-call-stack.js | 115 + .../test/mochitest/browser_dbg-chrome-create.js | 61 + .../test/mochitest/browser_dbg-command-click.js | 39 + .../test/mochitest/browser_dbg-console-async.js | 50 + .../test/mochitest/browser_dbg-console-eval.js | 37 + .../test/mochitest/browser_dbg-console-link.js | 29 + .../mochitest/browser_dbg-console-map-bindings.js | 47 + .../debugger/test/mochitest/browser_dbg-console.js | 28 + .../browser_dbg-content-script-sources.js | 52 + .../browser_dbg-continue-to-here-click.js | 48 + .../test/mochitest/browser_dbg-continue-to-here.js | 58 + .../mochitest/browser_dbg-custom-formatters.js | 158 + .../test/mochitest/browser_dbg-debug-line.js | 46 + .../test/mochitest/browser_dbg-debugger-buttons.js | 97 + ...browser_dbg-dom-mutation-breakpoints-fission.js | 111 + .../browser_dbg-dom-mutation-breakpoints.js | 170 + .../mochitest/browser_dbg-eager-eval-skip-pause.js | 19 + .../test/mochitest/browser_dbg-editor-exception.js | 26 + .../test/mochitest/browser_dbg-editor-gutter.js | 144 + .../test/mochitest/browser_dbg-editor-highlight.js | 50 + .../test/mochitest/browser_dbg-editor-mode.js | 18 + .../test/mochitest/browser_dbg-editor-scroll.js | 48 + .../test/mochitest/browser_dbg-editor-select.js | 53 + .../test/mochitest/browser_dbg-ember-quickstart.js | 25 + .../test/mochitest/browser_dbg-es-module-worker.js | 75 + .../test/mochitest/browser_dbg-eval-throw.js | 18 + .../browser_dbg-event-breakpoints-fission.js | 81 + .../mochitest/browser_dbg-event-breakpoints.js | 294 + .../test/mochitest/browser_dbg-event-handler.js | 18 + .../mochitest/browser_dbg-expressions-error.js | 51 + .../mochitest/browser_dbg-expressions-focus.js | 30 + .../mochitest/browser_dbg-expressions-thread.js | 97 + .../mochitest/browser_dbg-expressions-watch.js | 91 + .../test/mochitest/browser_dbg-expressions.js | 73 + ...extension-inspectedWindow-debugger-statement.js | 88 + .../test/mochitest/browser_dbg-features-asm.js | 92 + .../browser_dbg-features-breakable-lines.js | 92 + .../browser_dbg-features-breakable-positions.js | 284 + .../mochitest/browser_dbg-features-breakpoints.js | 72 + ...ser_dbg-features-browser-toolbox-source-tree.js | 122 + .../browser_dbg-features-source-text-content.js | 565 + .../mochitest/browser_dbg-features-source-tree.js | 554 + .../test/mochitest/browser_dbg-features-tabs.js | 62 + .../test/mochitest/browser_dbg-features-wasm.js | 168 + .../browser_dbg-fission-frame-breakpoint.js | 50 + .../browser_dbg-fission-frame-pause-exceptions.js | 54 + .../mochitest/browser_dbg-fission-frame-sources.js | 47 + .../browser_dbg-fission-project-search.js | 52 + .../mochitest/browser_dbg-fission-switch-target.js | 32 + .../browser_dbg-gc-breakpoint-positions.js | 17 + .../test/mochitest/browser_dbg-gc-sources.js | 29 + .../test/mochitest/browser_dbg-go-to-line.js | 64 + .../test/mochitest/browser_dbg-highlights-calls.js | 31 + .../test/mochitest/browser_dbg-html-breakpoints.js | 54 + .../mochitest/browser_dbg-idb-run-to-completion.js | 15 + .../debugger/test/mochitest/browser_dbg-iframes.js | 60 + .../test/mochitest/browser_dbg-inline-cache.js | 146 + .../mochitest/browser_dbg-inline-exceptions.js | 28 + .../test/mochitest/browser_dbg-inline-preview.js | 111 + .../mochitest/browser_dbg-inline-script-offset.js | 40 + .../mochitest/browser_dbg-inspector-integration.js | 140 + ...-integration-reloading-compressed-sourcemaps.js | 22 + ...ntegration-reloading-uncompressed-sourcemaps.js | 22 + .../mochitest/browser_dbg-javascript-tracer.js | 226 + .../mochitest/browser_dbg-keyboard-navigation.js | 41 + .../browser_dbg-keyboard-shortcuts-modal.js | 49 + .../mochitest/browser_dbg-keyboard-shortcuts.js | 58 + .../test/mochitest/browser_dbg-layout-changes.js | 94 + .../test/mochitest/browser_dbg-link-reload.js | 65 + .../test/mochitest/browser_dbg-log-events.js | 25 + .../mochitest/browser_dbg-log-point-mapping.js | 44 + .../mochitest/browser_dbg-log-points-workers.js | 30 + .../test/mochitest/browser_dbg-log-points.js | 41 + .../browser_dbg-many-breakpoints-same-line.js | 93 + .../test/mochitest/browser_dbg-merge-scopes.js | 55 + .../browser_dbg-message-run-to-completion.js | 24 + .../test/mochitest/browser_dbg-minified.js | 33 + .../browser_dbg-navigation-when-paused.js | 42 + .../test/mochitest/browser_dbg-navigation.js | 73 + ...dbg-no-duplicate-breakpoints-on-frame-reload.js | 101 + .../test/mochitest/browser_dbg-old-breakpoint.js | 111 + .../test/mochitest/browser_dbg-outline-filter.js | 82 + .../test/mochitest/browser_dbg-outline-focus.js | 39 + .../test/mochitest/browser_dbg-outline-pretty.js | 30 + .../debugger/test/mochitest/browser_dbg-outline.js | 89 + .../test/mochitest/browser_dbg-overrides.js | 128 + .../test/mochitest/browser_dbg-pause-exceptions.js | 139 + .../test/mochitest/browser_dbg-pause-on-next.js | 21 + .../test/mochitest/browser_dbg-pause-points.js | 97 + .../test/mochitest/browser_dbg-pause-ux.js | 52 + .../mochitest/browser_dbg-paused-overlay-iframe.js | 76 + .../browser_dbg-paused-overlay-loading.js | 48 + .../test/mochitest/browser_dbg-paused-overlay.js | 73 + ...browser_dbg-pretty-print-breakpoints-columns.js | 119 + .../browser_dbg-pretty-print-breakpoints-delete.js | 113 + .../browser_dbg-pretty-print-breakpoints.js | 126 + .../mochitest/browser_dbg-pretty-print-console.js | 66 + .../mochitest/browser_dbg-pretty-print-flow.js | 78 + .../browser_dbg-pretty-print-inline-scripts.js | 256 + .../browser_dbg-pretty-print-paused-anonymous.js | 148 + .../mochitest/browser_dbg-pretty-print-paused.js | 35 + .../browser_dbg-pretty-print-sourcemap.js | 157 + .../test/mochitest/browser_dbg-pretty-print.js | 134 + .../test/mochitest/browser_dbg-preview-frame.js | 47 + .../test/mochitest/browser_dbg-preview-getter.js | 56 + .../test/mochitest/browser_dbg-preview-module.js | 36 + .../mochitest/browser_dbg-preview-source-maps.js | 45 + .../debugger/test/mochitest/browser_dbg-preview.js | 123 + .../test/mochitest/browser_dbg-project-root.js | 119 + .../test/mochitest/browser_dbg-project-search.js | 258 + .../test/mochitest/browser_dbg-quick-open.js | 166 + .../test/mochitest/browser_dbg-react-app.js | 34 + .../test/mochitest/browser_dbg-react-jsx.js | 20 + ...rowser_dbg-reducer-cleanup-on-target-removal.js | 174 + .../test/mochitest/browser_dbg-reloading.js | 39 + .../browser_dbg-remember-expanded-scopes.js | 41 + .../test/mochitest/browser_dbg-restart-frame.js | 34 + .../test/mochitest/browser_dbg-returnvalues.js | 67 + .../mochitest/browser_dbg-scopes-duplicated.js | 192 + .../test/mochitest/browser_dbg-scopes-mutations.js | 93 + .../test/mochitest/browser_dbg-scopes-xrays.js | 67 + .../debugger/test/mochitest/browser_dbg-scopes.js | 34 + .../browser_dbg-scroll-run-to-completion.js | 25 + .../mochitest/browser_dbg-search-file-paused.js | 71 + .../browser_dbg-search-file-retains-query.js | 40 + .../test/mochitest/browser_dbg-search-file.js | 146 + .../browser_dbg-settings-disable-javascript.js | 49 + .../test/mochitest/browser_dbg-slow-script.js | 91 + .../test/mochitest/browser_dbg-source-pragma.js | 26 + .../mochitest/browser_dbg-sourceURL-breakpoint.js | 18 + .../browser_dbg-sourcemapped-breakpoint-console.js | 86 + .../mochitest/browser_dbg-sourcemapped-preview.js | 206 + .../mochitest/browser_dbg-sourcemapped-scopes.js | 1646 + .../mochitest/browser_dbg-sourcemapped-stepping.js | 158 + .../mochitest/browser_dbg-sourcemapped-toggle.js | 48 + .../test/mochitest/browser_dbg-sourcemaps-bogus.js | 63 + .../browser_dbg-sourcemaps-breakpoints.js | 35 + .../mochitest/browser_dbg-sourcemaps-disabled.js | 23 + .../mochitest/browser_dbg-sourcemaps-ignorelist.js | 75 + .../mochitest/browser_dbg-sourcemaps-indexed.js | 51 + .../mochitest/browser_dbg-sourcemaps-redirect.js | 54 + .../browser_dbg-sourcemaps-reloading-quickly.js | 28 + .../mochitest/browser_dbg-sourcemaps-reloading.js | 62 + .../test/mochitest/browser_dbg-sourcemaps.js | 124 + .../test/mochitest/browser_dbg-sourcemaps2.js | 51 + .../test/mochitest/browser_dbg-sourcemaps3.js | 65 + .../browser_dbg-sources-project-search.js | 134 + .../mochitest/browser_dbg-state-based-panels.js | 164 + .../test/mochitest/browser_dbg-step-in-navigate.js | 34 + .../mochitest/browser_dbg-step-in-uninitialized.js | 47 + .../test/mochitest/browser_dbg-stepping.js | 47 + .../test/mochitest/browser_dbg-tabs-keyboard.js | 29 + .../mochitest/browser_dbg-tabs-pretty-print.js | 46 + .../browser_dbg-tabs-without-urls-selected.js | 28 + .../mochitest/browser_dbg-tabs-without-urls.js | 46 + .../debugger/test/mochitest/browser_dbg-tabs.js | 46 + .../test/mochitest/browser_dbg-toggling-tools.js | 19 + .../test/mochitest/browser_dbg-ua-widgets.js | 47 + .../test/mochitest/browser_dbg-unselected-pause.js | 203 + .../test/mochitest/browser_dbg-watchpoints.js | 122 + ...rowser_dbg-windowless-service-workers-reload.js | 32 + .../browser_dbg-windowless-service-workers.js | 174 + ...wser_dbg-windowless-workers-early-breakpoint.js | 40 + .../mochitest/browser_dbg-windowless-workers.js | 165 + .../test/mochitest/browser_dbg-worker-exception.js | 26 + .../test/mochitest/browser_dbg-worker-module.js | 19 + .../test/mochitest/browser_dbg-worker-nested.js | 15 + .../test/mochitest/browser_dbg-worker-scopes.js | 102 + .../test/mochitest/browser_dbg-wrong-fetch.js | 27 + .../test/mochitest/browser_dbg-xhr-breakpoints.js | 236 + .../mochitest/browser_dbg-xhr-run-to-completion.js | 54 + .../debugger/test/mochitest/examples/README.md | 14 + .../client/debugger/test/mochitest/examples/asm.js | 10 + .../debugger/test/mochitest/examples/async.js | 10 + .../test/mochitest/examples/big-sourcemap.html | 40 + .../examples/big-sourcemap_files/bundle.js | 53505 ++++++++++++ .../examples/big-sourcemap_files/bundle.js.map | 1 + .../test/mochitest/examples/collected-bundle.js | 76 + .../test/mochitest/examples/collected-bundle2.js | 75 + .../mochitest/examples/collected-bundle2.js.map | 1 + .../examples/collected-bundle2.js^headers^ | 1 + .../test/mochitest/examples/different_html.sjs | 31 + .../test/mochitest/examples/doc-all-workers.html | 32 + .../debugger/test/mochitest/examples/doc-asm.html | 20 + .../test/mochitest/examples/doc-async.html | 16 + .../test/mochitest/examples/doc-audiocontext.html | 49 + .../test/mochitest/examples/doc-bfcache1.html | 7 + .../test/mochitest/examples/doc-bfcache2.html | 6 + .../test/mochitest/examples/doc-blackbox-all.html | 15 + .../test/mochitest/examples/doc-command-click.html | 12 + .../examples/doc-content-script-sources.html | 13 + .../examples/doc-debugger-statements.html | 31 + .../test/mochitest/examples/doc-dom-mutation.html | 21 + .../examples/doc-duplicate-functions.html | 27 + .../test/mochitest/examples/doc-early-xhr.html | 16 + .../test/mochitest/examples/doc-editor-scroll.html | 17 + .../test/mochitest/examples/doc-eval-throw.html | 9 + .../examples/doc-event-breakpoints-fission.html | 23 + .../mochitest/examples/doc-event-breakpoints.html | 27 + .../test/mochitest/examples/doc-event-handler.html | 10 + .../test/mochitest/examples/doc-exceptions.html | 10 + .../test/mochitest/examples/doc-frames-async.html | 21 + .../test/mochitest/examples/doc-frames.html | 21 + .../examples/doc-gc-breakpoint-positions.html | 23 + .../test/mochitest/examples/doc-gc-sources.html | 26 + .../mochitest/examples/doc-html-breakpoints.html | 25 + .../examples/doc-idb-run-to-completion.html | 30 + .../test/mochitest/examples/doc-iframes.html | 20 + .../mochitest/examples/doc-inline-preview.html | 16 + .../examples/doc-inline-script-offset.html | 15 + .../test/mochitest/examples/doc-merge-scopes.html | 29 + .../doc-message-run-to-completion-frame.html | 1 + .../examples/doc-message-run-to-completion.html | 19 + .../test/mochitest/examples/doc-minified.html | 20 + .../test/mochitest/examples/doc-minified2.html | 21 + .../test/mochitest/examples/doc-module-worker.html | 10 + .../examples/doc-navigation-when-paused.html | 17 + .../test/mochitest/examples/doc-nested-worker.html | 11 + .../test/mochitest/examples/doc-on-load.html | 21 + .../test/mochitest/examples/doc-pause-points.html | 16 + .../examples/doc-pretty-print-inline-scripts.html | 44 + .../test/mochitest/examples/doc-pretty.html | 14 + .../mochitest/examples/doc-preview-getter.html | 11 + .../test/mochitest/examples/doc-preview.html | 27 + .../test/mochitest/examples/doc-react-jsx.html | 111 + .../test/mochitest/examples/doc-react.html | 10 + .../test/mochitest/examples/doc-reload-link.html | 5 + .../examples/doc-remember-expanded-scopes.html | 7 + .../test/mochitest/examples/doc-return-values.html | 50 + .../test/mochitest/examples/doc-scopes-xrays.html | 11 + .../test/mochitest/examples/doc-script-mutate.html | 18 + .../mochitest/examples/doc-script-switching.html | 19 + .../mochitest/examples/doc-scripts-debugger.html | 20 + .../test/mochitest/examples/doc-scripts.html | 35 + .../examples/doc-scroll-run-to-completion.html | 27 + .../mochitest/examples/doc-service-workers.html | 39 + .../test/mochitest/examples/doc-slow-script.html | 18 + .../test/mochitest/examples/doc-source-pragma.html | 7 + .../examples/doc-sourceURL-breakpoint.html | 9 + .../mochitest/examples/doc-sourcemap-bogus.html | 15 + .../test/mochitest/examples/doc-sourcemapped.html | 574 + .../examples/doc-sourcemaps-ignorelist.html | 14 + .../mochitest/examples/doc-sourcemaps-indexed.html | 21 + .../test/mochitest/examples/doc-sourcemaps.html | 15 + .../test/mochitest/examples/doc-sourcemaps2.html | 21 + .../test/mochitest/examples/doc-sourcemaps3.html | 16 + .../test/mochitest/examples/doc-sources.html | 25 + .../examples/doc-step-in-uninitialized.html | 11 + .../test/mochitest/examples/doc-strict.html | 21 + .../mochitest/examples/doc-wasm-sourcemaps.html | 23 + .../test/mochitest/examples/doc-watchpoints.html | 31 + .../doc-windowless-workers-early-breakpoint.html | 19 + .../mochitest/examples/doc-windowless-workers.html | 25 + .../mochitest/examples/doc-worker-exception.html | 10 + .../test/mochitest/examples/doc-worker-scopes.html | 15 + .../examples/doc-xhr-run-to-completion.html | 41 + .../debugger/test/mochitest/examples/doc-xhr.html | 14 + .../examples/doc_dbg-custom-formatters.html | 31 + .../doc_dbg-fission-frame-pause-exceptions.html | 20 + .../doc_dbg-fission-frame-sources-frame.html | 12 + .../examples/doc_dbg-fission-frame-sources.html | 13 + .../examples/doc_dbg-fission-pause-exceptions.html | 13 + ...doc_dbg-same-source-distinct-threads-frame.html | 13 + .../doc_dbg-same-source-distinct-threads.html | 21 + .../test/mochitest/examples/dom-mutation.js | 26 + .../test/mochitest/examples/dom-mutation.js.map | 1 + .../examples/ember/quickstart/.editorconfig | 20 + .../mochitest/examples/ember/quickstart/.ember-cli | 9 + .../examples/ember/quickstart/.eslintrc.js | 38 + .../mochitest/examples/ember/quickstart/.gitignore | 23 + .../examples/ember/quickstart/.travis.yml | 26 + .../examples/ember/quickstart/.watchmanconfig | 3 + .../ember/quickstart/dist/assets/quickstart.css | 0 .../ember/quickstart/dist/assets/quickstart.js | 277 + .../ember/quickstart/dist/assets/quickstart.map | 1 + .../ember/quickstart/dist/assets/test-support.css | 471 + .../ember/quickstart/dist/assets/test-support.js | 12050 +++ .../ember/quickstart/dist/assets/test-support.map | 1 + .../examples/ember/quickstart/dist/assets/tests.js | 62 + .../ember/quickstart/dist/assets/tests.map | 1 + .../ember/quickstart/dist/assets/vendor.css | 97 + .../ember/quickstart/dist/assets/vendor.js | 86407 +++++++++++++++++++ .../ember/quickstart/dist/assets/vendor.map | 1 + .../examples/ember/quickstart/dist/index.html | 26 + .../examples/ember/quickstart/dist/robots.txt | 3 + .../examples/ember/quickstart/dist/testem.js | 19 + .../debugger/test/mochitest/examples/entry.js | 16 + .../test/mochitest/examples/event-breakpoints.js | 69 + .../debugger/test/mochitest/examples/exceptions.js | 88 + .../debugger/test/mochitest/examples/fetch.js | 5 + .../debugger/test/mochitest/examples/frames.js | 24 + .../mochitest/examples/html-breakpoints-slow.js | 3 + .../test/mochitest/examples/inline-preview.js | 60 + .../test/mochitest/examples/inner-worker.js | 8 + .../debugger/test/mochitest/examples/long.js | 76 + .../examples/map-with-failed-original-request.js | 26 + .../examples/map-with-failed-original-request.map | 1 + .../debugger/test/mochitest/examples/math.min.js | 3 + .../mochitest/examples/nested/nested-source.js | 3 + .../test/mochitest/examples/non-existant-map.js | 8 + .../debugger/test/mochitest/examples/opts.js | 3 + .../test/mochitest/examples/outer-worker.js | 8 + .../debugger/test/mochitest/examples/output.js | 5 + .../test/mochitest/examples/pause-points.js | 43 + .../debugger/test/mochitest/examples/pretty.js | 10 + .../test/mochitest/examples/preview-getter.js | 15 + .../debugger/test/mochitest/examples/preview.js | 66 + .../test/mochitest/examples/react/.gitignore | 20 + .../test/mochitest/examples/react/README.md | 4 + .../examples/react/build/asset-manifest.json | 4 + .../test/mochitest/examples/react/build/index.html | 1 + .../test/mochitest/examples/react/build/main.js | 6762 ++ .../mochitest/examples/react/build/main.js.map | 1 + .../examples/react/build/service-worker.js | 1 + .../mochitest/examples/react/config-overrides.js | 16 + .../test/mochitest/examples/react/package.json | 21 + .../mochitest/examples/react/public/index.html | 25 + .../test/mochitest/examples/react/src/App.js | 25 + .../test/mochitest/examples/react/src/index.js | 5 + .../test/mochitest/examples/react/yarn.lock | 7090 ++ .../mochitest/examples/reload/code_reload_1.js | 3 + .../test/mochitest/examples/same-script.js | 9 + .../test/mochitest/examples/scopes-worker.js | 13 + .../test/mochitest/examples/script-mutate.js | 20 + .../test/mochitest/examples/script-switching-01.js | 9 + .../test/mochitest/examples/script-switching-02.js | 14 + .../test/mochitest/examples/service-worker.sjs | 38 + .../test/mochitest/examples/shared-worker.js | 1 + .../test/mochitest/examples/simple-worker.js | 11 + .../debugger/test/mochitest/examples/simple1.js | 63 + .../debugger/test/mochitest/examples/simple2.js | 6 + .../debugger/test/mochitest/examples/simple3.js | 19 + .../debugger/test/mochitest/examples/simple4.js | 15 + .../test/mochitest/examples/sjs_slow-load.sjs | 31 + .../test/mochitest/examples/source-pragma.js | 6 + .../test/mochitest/examples/source-pragma.js.map | 10 + .../cache/f67752e2d3a8fd18a75558156ccfd8c764b544a1 | 0 .../test/mochitest/examples/sourcemapped/README.md | 16 + .../test/mochitest/examples/sourcemapped/build.js | 82 + .../examples/sourcemapped/builds/parcel/index.js | 69 + .../sourcemapped/builds/parcel/package.json | 6 + .../examples/sourcemapped/builds/parcel/yarn.lock | 4128 + .../sourcemapped/builds/rollup-babel6/index.js | 82 + .../sourcemapped/builds/rollup-babel6/package.json | 12 + .../sourcemapped/builds/rollup-babel6/yarn.lock | 755 + .../sourcemapped/builds/rollup-babel7/index.js | 82 + .../sourcemapped/builds/rollup-babel7/package.json | 13 + .../sourcemapped/builds/rollup-babel7/yarn.lock | 1115 + .../examples/sourcemapped/builds/rollup/index.js | 76 + .../sourcemapped/builds/rollup/package.json | 11 + .../examples/sourcemapped/builds/rollup/yarn.lock | 337 + .../sourcemapped/builds/webpack3-babel6/index.js | 81 + .../builds/webpack3-babel6/package.json | 12 + .../sourcemapped/builds/webpack3-babel6/yarn.lock | 2823 + .../sourcemapped/builds/webpack3-babel7/index.js | 81 + .../builds/webpack3-babel7/package.json | 13 + .../sourcemapped/builds/webpack3-babel7/yarn.lock | 3089 + .../examples/sourcemapped/builds/webpack3/index.js | 65 + .../sourcemapped/builds/webpack3/package.json | 10 + .../sourcemapped/builds/webpack3/yarn.lock | 2202 + .../sourcemapped/builds/webpack4-babel6/index.js | 82 + .../builds/webpack4-babel6/package.json | 12 + .../sourcemapped/builds/webpack4-babel6/yarn.lock | 2787 + .../sourcemapped/builds/webpack4-babel7/index.js | 82 + .../builds/webpack4-babel7/package.json | 13 + .../sourcemapped/builds/webpack4-babel7/yarn.lock | 3047 + .../examples/sourcemapped/builds/webpack4/index.js | 66 + .../sourcemapped/builds/webpack4/package.json | 10 + .../sourcemapped/builds/webpack4/yarn.lock | 2194 + .../fixtures/babel-bindings-with-flow/input.js | 10 + .../fixtures/babel-bindings-with-flow/src/mod.js | 1 + .../sourcemapped/fixtures/babel-classes/input.js | 15 + .../fixtures/babel-flowtype-bindings/input.js | 10 + .../fixtures/babel-flowtype-bindings/src/mod.js | 2 + .../sourcemapped/fixtures/classes/input.js | 22 + .../sourcemapped/fixtures/esmodules-cjs/input.js | 41 + .../fixtures/esmodules-cjs/src/mod1.js | 1 + .../fixtures/esmodules-cjs/src/mod10.js | 1 + .../fixtures/esmodules-cjs/src/mod11.js | 1 + .../fixtures/esmodules-cjs/src/mod12.js | 2 + .../fixtures/esmodules-cjs/src/mod2.js | 1 + .../fixtures/esmodules-cjs/src/mod3.js | 1 + .../fixtures/esmodules-cjs/src/mod4.js | 2 + .../fixtures/esmodules-cjs/src/mod5.js | 1 + .../fixtures/esmodules-cjs/src/mod6.js | 1 + .../fixtures/esmodules-cjs/src/mod7.js | 1 + .../fixtures/esmodules-cjs/src/mod8.js | 2 + .../fixtures/esmodules-cjs/src/mod9.js | 1 + .../fixtures/esmodules-cjs/src/optimized-out.js | 1 + .../sourcemapped/fixtures/esmodules-es6/input.js | 41 + .../fixtures/esmodules-es6/src/mod1.js | 1 + .../fixtures/esmodules-es6/src/mod10.js | 1 + .../fixtures/esmodules-es6/src/mod11.js | 1 + .../fixtures/esmodules-es6/src/mod12.js | 2 + .../fixtures/esmodules-es6/src/mod2.js | 1 + .../fixtures/esmodules-es6/src/mod3.js | 1 + .../fixtures/esmodules-es6/src/mod4.js | 2 + .../fixtures/esmodules-es6/src/mod5.js | 1 + .../fixtures/esmodules-es6/src/mod6.js | 1 + .../fixtures/esmodules-es6/src/mod7.js | 1 + .../fixtures/esmodules-es6/src/mod8.js | 2 + .../fixtures/esmodules-es6/src/mod9.js | 1 + .../fixtures/esmodules-es6/src/optimized-out.js | 1 + .../sourcemapped/fixtures/esmodules/input.js | 41 + .../sourcemapped/fixtures/esmodules/src/mod1.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod10.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod11.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod12.js | 2 + .../sourcemapped/fixtures/esmodules/src/mod2.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod3.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod4.js | 2 + .../sourcemapped/fixtures/esmodules/src/mod5.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod6.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod7.js | 1 + .../sourcemapped/fixtures/esmodules/src/mod8.js | 2 + .../sourcemapped/fixtures/esmodules/src/mod9.js | 1 + .../fixtures/esmodules/src/optimized-out.js | 1 + .../sourcemapped/fixtures/eval-maps/input.js | 16 + .../sourcemapped/fixtures/for-loops/input.js | 15 + .../examples/sourcemapped/fixtures/for-of/input.js | 12 + .../sourcemapped/fixtures/functions/input.js | 14 + .../sourcemapped/fixtures/lex-and-nonlex/input.js | 9 + .../fixtures/line-start-bindings-es6/input.js | 23 + .../sourcemapped/fixtures/modules-cjs/input.js | 8 + .../out-of-order-declarations-cjs/input.js | 17 + .../out-of-order-declarations-cjs/src/mod.js | 1 + .../sourcemapped/fixtures/shadowed-vars/input.js | 21 + .../step-over-for-of-array-closure/input.js | 10 + .../fixtures/step-over-for-of-array/input.js | 10 + .../fixtures/step-over-for-of-closure/input.js | 13 + .../fixtures/step-over-for-of/input.js | 11 + .../fixtures/step-over-function-params/input.js | 8 + .../fixtures/step-over-regenerator-await/input.js | 15 + .../sourcemapped/fixtures/switches/input.js | 13 + .../fixtures/this-arguments-bindings/input.js | 14 + .../sourcemapped/fixtures/try-catches/input.js | 10 + .../sourcemapped/fixtures/type-module/input.js | 8 + .../sourcemapped/fixtures/type-script-cjs/input.js | 10 + .../fixtures/typescript-classes/input.ts | 51 + .../fixtures/typescript-classes/src/mod.ts | 8 + .../fixtures/webpack-functions/input.js | 12 + .../fixtures/webpack-line-mappings/input.js | 18 + .../fixtures/webpack-line-mappings/src/mod1.js | 1 + .../output/parcel/babel-bindings-with-flow.js | 132 + .../output/parcel/babel-bindings-with-flow.map | 1 + .../sourcemapped/output/parcel/babel-classes.js | 132 + .../sourcemapped/output/parcel/babel-classes.map | 1 + .../output/parcel/babel-flowtype-bindings.js | 131 + .../output/parcel/babel-flowtype-bindings.map | 1 + .../examples/sourcemapped/output/parcel/classes.js | 137 + .../sourcemapped/output/parcel/classes.map | 1 + .../sourcemapped/output/parcel/esmodules-cjs.js | 259 + .../sourcemapped/output/parcel/esmodules-cjs.map | 1 + .../sourcemapped/output/parcel/esmodules-es6.js | 259 + .../sourcemapped/output/parcel/esmodules-es6.map | 1 + .../sourcemapped/output/parcel/esmodules.js | 259 + .../sourcemapped/output/parcel/esmodules.map | 1 + .../sourcemapped/output/parcel/eval-maps.js | 130 + .../sourcemapped/output/parcel/eval-maps.map | 1 + .../sourcemapped/output/parcel/for-loops.js | 130 + .../sourcemapped/output/parcel/for-loops.map | 1 + .../examples/sourcemapped/output/parcel/for-of.js | 127 + .../examples/sourcemapped/output/parcel/for-of.map | 1 + .../sourcemapped/output/parcel/functions.js | 129 + .../sourcemapped/output/parcel/functions.map | 1 + .../sourcemapped/output/parcel/lex-and-nonlex.js | 124 + .../sourcemapped/output/parcel/lex-and-nonlex.map | 1 + .../output/parcel/line-start-bindings-es6.js | 137 + .../output/parcel/line-start-bindings-es6.map | 1 + .../sourcemapped/output/parcel/modules-cjs.js | 117 + .../sourcemapped/output/parcel/modules-cjs.map | 1 + .../output/parcel/out-of-order-declarations-cjs.js | 144 + .../parcel/out-of-order-declarations-cjs.map | 1 + .../sourcemapped/output/parcel/shadowed-vars.js | 136 + .../sourcemapped/output/parcel/shadowed-vars.map | 1 + .../parcel/step-over-for-of-array-closure.js | 125 + .../parcel/step-over-for-of-array-closure.map | 1 + .../output/parcel/step-over-for-of-array.js | 125 + .../output/parcel/step-over-for-of-array.map | 1 + .../output/parcel/step-over-for-of-closure.js | 128 + .../output/parcel/step-over-for-of-closure.map | 1 + .../sourcemapped/output/parcel/step-over-for-of.js | 126 + .../output/parcel/step-over-for-of.map | 1 + .../output/parcel/step-over-function-params.js | 123 + .../output/parcel/step-over-function-params.map | 1 + .../output/parcel/step-over-regenerator-await.js | 130 + .../output/parcel/step-over-regenerator-await.map | 1 + .../sourcemapped/output/parcel/switches.js | 129 + .../sourcemapped/output/parcel/switches.map | 1 + .../output/parcel/this-arguments-bindings.js | 129 + .../output/parcel/this-arguments-bindings.map | 1 + .../sourcemapped/output/parcel/try-catches.js | 125 + .../sourcemapped/output/parcel/try-catches.map | 1 + .../sourcemapped/output/parcel/type-module.js | 123 + .../sourcemapped/output/parcel/type-module.map | 1 + .../sourcemapped/output/parcel/type-script-cjs.js | 119 + .../sourcemapped/output/parcel/type-script-cjs.map | 1 + .../output/parcel/typescript-classes.js | 221 + .../output/parcel/typescript-classes.map | 1 + .../output/parcel/webpack-functions.js | 126 + .../output/parcel/webpack-functions.map | 1 + .../output/parcel/webpack-line-mappings.js | 147 + .../output/parcel/webpack-line-mappings.map | 1 + .../rollup-babel6/babel-bindings-with-flow.js | 17 + .../rollup-babel6/babel-bindings-with-flow.js.map | 1 + .../output/rollup-babel6/babel-classes.js | 37 + .../output/rollup-babel6/babel-classes.js.map | 1 + .../rollup-babel6/babel-flowtype-bindings.js | 15 + .../rollup-babel6/babel-flowtype-bindings.js.map | 1 + .../sourcemapped/output/rollup-babel6/classes.js | 49 + .../output/rollup-babel6/classes.js.map | 1 + .../output/rollup-babel6/esmodules-es6.js | 57 + .../output/rollup-babel6/esmodules-es6.js.map | 1 + .../sourcemapped/output/rollup-babel6/esmodules.js | 57 + .../output/rollup-babel6/esmodules.js.map | 1 + .../sourcemapped/output/rollup-babel6/eval-maps.js | 18 + .../output/rollup-babel6/eval-maps.js.map | 1 + .../sourcemapped/output/rollup-babel6/for-loops.js | 23 + .../output/rollup-babel6/for-loops.js.map | 1 + .../sourcemapped/output/rollup-babel6/for-of.js | 21 + .../output/rollup-babel6/for-of.js.map | 1 + .../sourcemapped/output/rollup-babel6/functions.js | 22 + .../output/rollup-babel6/functions.js.map | 1 + .../output/rollup-babel6/lex-and-nonlex.js | 21 + .../output/rollup-babel6/lex-and-nonlex.js.map | 1 + .../rollup-babel6/line-start-bindings-es6.js | 20 + .../rollup-babel6/line-start-bindings-es6.js.map | 1 + .../output/rollup-babel6/shadowed-vars.js | 17 + .../output/rollup-babel6/shadowed-vars.js.map | 1 + .../step-over-for-of-array-closure.js | 26 + .../step-over-for-of-array-closure.js.map | 1 + .../output/rollup-babel6/step-over-for-of-array.js | 20 + .../rollup-babel6/step-over-for-of-array.js.map | 1 + .../rollup-babel6/step-over-for-of-closure.js | 48 + .../rollup-babel6/step-over-for-of-closure.js.map | 1 + .../output/rollup-babel6/step-over-for-of.js | 40 + .../output/rollup-babel6/step-over-for-of.js.map | 1 + .../rollup-babel6/step-over-function-params.js | 25 + .../rollup-babel6/step-over-function-params.js.map | 1 + .../rollup-babel6/step-over-regenerator-await.js | 47 + .../step-over-regenerator-await.js.map | 1 + .../sourcemapped/output/rollup-babel6/switches.js | 19 + .../output/rollup-babel6/switches.js.map | 1 + .../rollup-babel6/this-arguments-bindings.js | 25 + .../rollup-babel6/this-arguments-bindings.js.map | 1 + .../output/rollup-babel6/try-catches.js | 16 + .../output/rollup-babel6/try-catches.js.map | 1 + .../output/rollup-babel6/type-module.js | 16 + .../output/rollup-babel6/type-module.js.map | 1 + .../output/rollup-babel6/webpack-functions.js | 19 + .../output/rollup-babel6/webpack-functions.js.map | 1 + .../output/rollup-babel6/webpack-line-mappings.js | 28 + .../rollup-babel6/webpack-line-mappings.js.map | 1 + .../rollup-babel7/babel-bindings-with-flow.js | 15 + .../rollup-babel7/babel-bindings-with-flow.js.map | 1 + .../output/rollup-babel7/babel-classes.js | 71 + .../output/rollup-babel7/babel-classes.js.map | 1 + .../rollup-babel7/babel-flowtype-bindings.js | 14 + .../rollup-babel7/babel-flowtype-bindings.js.map | 1 + .../sourcemapped/output/rollup-babel7/classes.js | 70 + .../output/rollup-babel7/classes.js.map | 1 + .../output/rollup-babel7/esmodules-es6.js | 55 + .../output/rollup-babel7/esmodules-es6.js.map | 1 + .../sourcemapped/output/rollup-babel7/esmodules.js | 55 + .../output/rollup-babel7/esmodules.js.map | 1 + .../sourcemapped/output/rollup-babel7/eval-maps.js | 16 + .../output/rollup-babel7/eval-maps.js.map | 1 + .../sourcemapped/output/rollup-babel7/for-loops.js | 26 + .../output/rollup-babel7/for-loops.js.map | 1 + .../sourcemapped/output/rollup-babel7/for-of.js | 21 + .../output/rollup-babel7/for-of.js.map | 1 + .../sourcemapped/output/rollup-babel7/functions.js | 23 + .../output/rollup-babel7/functions.js.map | 1 + .../output/rollup-babel7/lex-and-nonlex.js | 25 + .../output/rollup-babel7/lex-and-nonlex.js.map | 1 + .../rollup-babel7/line-start-bindings-es6.js | 18 + .../rollup-babel7/line-start-bindings-es6.js.map | 1 + .../output/rollup-babel7/shadowed-vars.js | 16 + .../output/rollup-babel7/shadowed-vars.js.map | 1 + .../step-over-for-of-array-closure.js | 26 + .../step-over-for-of-array-closure.js.map | 1 + .../output/rollup-babel7/step-over-for-of-array.js | 20 + .../rollup-babel7/step-over-for-of-array.js.map | 1 + .../rollup-babel7/step-over-for-of-closure.js | 27 + .../rollup-babel7/step-over-for-of-closure.js.map | 1 + .../output/rollup-babel7/step-over-for-of.js | 19 + .../output/rollup-babel7/step-over-for-of.js.map | 1 + .../rollup-babel7/step-over-function-params.js | 30 + .../rollup-babel7/step-over-function-params.js.map | 1 + .../rollup-babel7/step-over-regenerator-await.js | 81 + .../step-over-regenerator-await.js.map | 1 + .../sourcemapped/output/rollup-babel7/switches.js | 20 + .../output/rollup-babel7/switches.js.map | 1 + .../rollup-babel7/this-arguments-bindings.js | 26 + .../rollup-babel7/this-arguments-bindings.js.map | 1 + .../output/rollup-babel7/try-catches.js | 16 + .../output/rollup-babel7/try-catches.js.map | 1 + .../output/rollup-babel7/type-module.js | 16 + .../output/rollup-babel7/type-module.js.map | 1 + .../output/rollup-babel7/webpack-functions.js | 19 + .../output/rollup-babel7/webpack-functions.js.map | 1 + .../output/rollup-babel7/webpack-line-mappings.js | 30 + .../rollup-babel7/webpack-line-mappings.js.map | 1 + .../examples/sourcemapped/output/rollup/classes.js | 27 + .../sourcemapped/output/rollup/classes.js.map | 1 + .../sourcemapped/output/rollup/esmodules-es6.js | 57 + .../output/rollup/esmodules-es6.js.map | 1 + .../sourcemapped/output/rollup/esmodules.js | 57 + .../sourcemapped/output/rollup/esmodules.js.map | 1 + .../sourcemapped/output/rollup/eval-maps.js | 18 + .../sourcemapped/output/rollup/eval-maps.js.map | 1 + .../sourcemapped/output/rollup/for-loops.js | 22 + .../sourcemapped/output/rollup/for-loops.js.map | 1 + .../examples/sourcemapped/output/rollup/for-of.js | 18 + .../sourcemapped/output/rollup/for-of.js.map | 1 + .../sourcemapped/output/rollup/functions.js | 22 + .../sourcemapped/output/rollup/functions.js.map | 1 + .../sourcemapped/output/rollup/lex-and-nonlex.js | 17 + .../output/rollup/lex-and-nonlex.js.map | 1 + .../output/rollup/line-start-bindings-es6.js | 20 + .../output/rollup/line-start-bindings-es6.js.map | 1 + .../sourcemapped/output/rollup/shadowed-vars.js | 16 + .../output/rollup/shadowed-vars.js.map | 1 + .../rollup/step-over-for-of-array-closure.js | 18 + .../rollup/step-over-for-of-array-closure.js.map | 1 + .../output/rollup/step-over-for-of-array.js | 18 + .../output/rollup/step-over-for-of-array.js.map | 1 + .../output/rollup/step-over-for-of-closure.js | 21 + .../output/rollup/step-over-for-of-closure.js.map | 1 + .../sourcemapped/output/rollup/step-over-for-of.js | 19 + .../output/rollup/step-over-for-of.js.map | 1 + .../output/rollup/step-over-function-params.js | 16 + .../output/rollup/step-over-function-params.js.map | 1 + .../output/rollup/step-over-regenerator-await.js | 23 + .../rollup/step-over-regenerator-await.js.map | 1 + .../sourcemapped/output/rollup/switches.js | 18 + .../sourcemapped/output/rollup/switches.js.map | 1 + .../output/rollup/this-arguments-bindings.js | 22 + .../output/rollup/this-arguments-bindings.js.map | 1 + .../sourcemapped/output/rollup/try-catches.js | 16 + .../sourcemapped/output/rollup/try-catches.js.map | 1 + .../sourcemapped/output/rollup/type-module.js | 16 + .../sourcemapped/output/rollup/type-module.js.map | 1 + .../output/rollup/typescript-classes.js | 97 + .../output/rollup/typescript-classes.js.map | 1 + .../output/rollup/webpack-functions.js | 20 + .../output/rollup/webpack-functions.js.map | 1 + .../output/rollup/webpack-line-mappings.js | 26 + .../output/rollup/webpack-line-mappings.js.map | 1 + .../webpack3-babel6/babel-bindings-with-flow.js | 96 + .../babel-bindings-with-flow.js.map | 1 + .../output/webpack3-babel6/babel-classes.js | 107 + .../output/webpack3-babel6/babel-classes.js.map | 1 + .../webpack3-babel6/babel-flowtype-bindings.js | 95 + .../webpack3-babel6/babel-flowtype-bindings.js.map | 1 + .../sourcemapped/output/webpack3-babel6/classes.js | 121 + .../output/webpack3-babel6/classes.js.map | 1 + .../output/webpack3-babel6/esmodules-cjs.js | 280 + .../output/webpack3-babel6/esmodules-cjs.js.map | 1 + .../output/webpack3-babel6/esmodules-es6.js | 224 + .../output/webpack3-babel6/esmodules-es6.js.map | 1 + .../output/webpack3-babel6/esmodules.js | 217 + .../output/webpack3-babel6/esmodules.js.map | 1 + .../output/webpack3-babel6/eval-maps.js | 93 + .../output/webpack3-babel6/eval-maps.js.map | 1 + .../output/webpack3-babel6/for-loops.js | 94 + .../output/webpack3-babel6/for-loops.js.map | 1 + .../sourcemapped/output/webpack3-babel6/for-of.js | 92 + .../output/webpack3-babel6/for-of.js.map | 1 + .../output/webpack3-babel6/functions.js | 91 + .../output/webpack3-babel6/functions.js.map | 1 + .../output/webpack3-babel6/lex-and-nonlex.js | 90 + .../output/webpack3-babel6/lex-and-nonlex.js.map | 1 + .../webpack3-babel6/line-start-bindings-es6.js | 100 + .../webpack3-babel6/line-start-bindings-es6.js.map | 1 + .../output/webpack3-babel6/modules-cjs.js | 85 + .../output/webpack3-babel6/modules-cjs.js.map | 1 + .../out-of-order-declarations-cjs.js | 115 + .../out-of-order-declarations-cjs.js.map | 1 + .../output/webpack3-babel6/shadowed-vars.js | 102 + .../output/webpack3-babel6/shadowed-vars.js.map | 1 + .../step-over-for-of-array-closure.js | 95 + .../step-over-for-of-array-closure.js.map | 1 + .../webpack3-babel6/step-over-for-of-array.js | 89 + .../webpack3-babel6/step-over-for-of-array.js.map | 1 + .../webpack3-babel6/step-over-for-of-closure.js | 117 + .../step-over-for-of-closure.js.map | 1 + .../output/webpack3-babel6/step-over-for-of.js | 109 + .../output/webpack3-babel6/step-over-for-of.js.map | 1 + .../webpack3-babel6/step-over-function-params.js | 95 + .../step-over-function-params.js.map | 1 + .../webpack3-babel6/step-over-regenerator-await.js | 116 + .../step-over-regenerator-await.js.map | 1 + .../output/webpack3-babel6/switches.js | 91 + .../output/webpack3-babel6/switches.js.map | 1 + .../webpack3-babel6/this-arguments-bindings.js | 94 + .../webpack3-babel6/this-arguments-bindings.js.map | 1 + .../output/webpack3-babel6/try-catches.js | 87 + .../output/webpack3-babel6/try-catches.js.map | 1 + .../output/webpack3-babel6/type-module.js | 84 + .../output/webpack3-babel6/type-module.js.map | 1 + .../output/webpack3-babel6/type-script-cjs.js | 87 + .../output/webpack3-babel6/type-script-cjs.js.map | 1 + .../output/webpack3-babel6/typescript-classes.js | 75 + .../webpack3-babel6/typescript-classes.js.map | 1 + .../output/webpack3-babel6/webpack-functions.js | 88 + .../webpack3-babel6/webpack-functions.js.map | 1 + .../webpack3-babel6/webpack-line-mappings.js | 105 + .../webpack3-babel6/webpack-line-mappings.js.map | 1 + .../webpack3-babel7/babel-bindings-with-flow.js | 94 + .../babel-bindings-with-flow.js.map | 1 + .../output/webpack3-babel7/babel-classes.js | 112 + .../output/webpack3-babel7/babel-classes.js.map | 1 + .../webpack3-babel7/babel-flowtype-bindings.js | 92 + .../webpack3-babel7/babel-flowtype-bindings.js.map | 1 + .../sourcemapped/output/webpack3-babel7/classes.js | 125 + .../output/webpack3-babel7/classes.js.map | 1 + .../output/webpack3-babel7/esmodules-cjs.js | 290 + .../output/webpack3-babel7/esmodules-cjs.js.map | 1 + .../output/webpack3-babel7/esmodules-es6.js | 217 + .../output/webpack3-babel7/esmodules-es6.js.map | 1 + .../output/webpack3-babel7/esmodules.js | 210 + .../output/webpack3-babel7/esmodules.js.map | 1 + .../output/webpack3-babel7/eval-maps.js | 89 + .../output/webpack3-babel7/eval-maps.js.map | 1 + .../output/webpack3-babel7/for-loops.js | 97 + .../output/webpack3-babel7/for-loops.js.map | 1 + .../sourcemapped/output/webpack3-babel7/for-of.js | 91 + .../output/webpack3-babel7/for-of.js.map | 1 + .../output/webpack3-babel7/functions.js | 92 + .../output/webpack3-babel7/functions.js.map | 1 + .../output/webpack3-babel7/lex-and-nonlex.js | 90 + .../output/webpack3-babel7/lex-and-nonlex.js.map | 1 + .../webpack3-babel7/line-start-bindings-es6.js | 97 + .../webpack3-babel7/line-start-bindings-es6.js.map | 1 + .../output/webpack3-babel7/modules-cjs.js | 85 + .../output/webpack3-babel7/modules-cjs.js.map | 1 + .../out-of-order-declarations-cjs.js | 118 + .../out-of-order-declarations-cjs.js.map | 1 + .../output/webpack3-babel7/shadowed-vars.js | 101 + .../output/webpack3-babel7/shadowed-vars.js.map | 1 + .../step-over-for-of-array-closure.js | 95 + .../step-over-for-of-array-closure.js.map | 1 + .../webpack3-babel7/step-over-for-of-array.js | 89 + .../webpack3-babel7/step-over-for-of-array.js.map | 1 + .../webpack3-babel7/step-over-for-of-closure.js | 96 + .../step-over-for-of-closure.js.map | 1 + .../output/webpack3-babel7/step-over-for-of.js | 88 + .../output/webpack3-babel7/step-over-for-of.js.map | 1 + .../webpack3-babel7/step-over-function-params.js | 100 + .../step-over-function-params.js.map | 1 + .../webpack3-babel7/step-over-regenerator-await.js | 118 + .../step-over-regenerator-await.js.map | 1 + .../output/webpack3-babel7/switches.js | 92 + .../output/webpack3-babel7/switches.js.map | 1 + .../webpack3-babel7/this-arguments-bindings.js | 95 + .../webpack3-babel7/this-arguments-bindings.js.map | 1 + .../output/webpack3-babel7/try-catches.js | 87 + .../output/webpack3-babel7/try-catches.js.map | 1 + .../output/webpack3-babel7/type-module.js | 84 + .../output/webpack3-babel7/type-module.js.map | 1 + .../output/webpack3-babel7/type-script-cjs.js | 87 + .../output/webpack3-babel7/type-script-cjs.js.map | 1 + .../output/webpack3-babel7/webpack-functions.js | 88 + .../webpack3-babel7/webpack-functions.js.map | 1 + .../webpack3-babel7/webpack-line-mappings.js | 106 + .../webpack3-babel7/webpack-line-mappings.js.map | 1 + .../sourcemapped/output/webpack3/classes.js | 100 + .../sourcemapped/output/webpack3/classes.js.map | 1 + .../sourcemapped/output/webpack3/esmodules-cjs.js | 236 + .../output/webpack3/esmodules-cjs.js.map | 1 + .../sourcemapped/output/webpack3/esmodules-es6.js | 236 + .../output/webpack3/esmodules-es6.js.map | 1 + .../sourcemapped/output/webpack3/esmodules.js | 236 + .../sourcemapped/output/webpack3/esmodules.js.map | 1 + .../sourcemapped/output/webpack3/eval-maps.js | 94 + .../sourcemapped/output/webpack3/eval-maps.js.map | 1 + .../sourcemapped/output/webpack3/for-loops.js | 93 + .../sourcemapped/output/webpack3/for-loops.js.map | 1 + .../sourcemapped/output/webpack3/for-of.js | 90 + .../sourcemapped/output/webpack3/for-of.js.map | 1 + .../sourcemapped/output/webpack3/functions.js | 92 + .../sourcemapped/output/webpack3/functions.js.map | 1 + .../sourcemapped/output/webpack3/lex-and-nonlex.js | 87 + .../output/webpack3/lex-and-nonlex.js.map | 1 + .../output/webpack3/line-start-bindings-es6.js | 101 + .../output/webpack3/line-start-bindings-es6.js.map | 1 + .../sourcemapped/output/webpack3/modules-cjs.js | 83 + .../output/webpack3/modules-cjs.js.map | 1 + .../webpack3/out-of-order-declarations-cjs.js | 104 + .../webpack3/out-of-order-declarations-cjs.js.map | 1 + .../sourcemapped/output/webpack3/shadowed-vars.js | 98 + .../output/webpack3/shadowed-vars.js.map | 1 + .../webpack3/step-over-for-of-array-closure.js | 88 + .../webpack3/step-over-for-of-array-closure.js.map | 1 + .../output/webpack3/step-over-for-of-array.js | 88 + .../output/webpack3/step-over-for-of-array.js.map | 1 + .../output/webpack3/step-over-for-of-closure.js | 91 + .../webpack3/step-over-for-of-closure.js.map | 1 + .../output/webpack3/step-over-for-of.js | 89 + .../output/webpack3/step-over-for-of.js.map | 1 + .../output/webpack3/step-over-function-params.js | 86 + .../webpack3/step-over-function-params.js.map | 1 + .../output/webpack3/step-over-regenerator-await.js | 93 + .../webpack3/step-over-regenerator-await.js.map | 1 + .../sourcemapped/output/webpack3/switches.js | 91 + .../sourcemapped/output/webpack3/switches.js.map | 1 + .../output/webpack3/this-arguments-bindings.js | 92 + .../output/webpack3/this-arguments-bindings.js.map | 1 + .../sourcemapped/output/webpack3/try-catches.js | 88 + .../output/webpack3/try-catches.js.map | 1 + .../sourcemapped/output/webpack3/type-module.js | 85 + .../output/webpack3/type-module.js.map | 1 + .../output/webpack3/type-script-cjs.js | 85 + .../output/webpack3/type-script-cjs.js.map | 1 + .../output/webpack3/typescript-classes.js | 179 + .../output/webpack3/typescript-classes.js.map | 1 + .../output/webpack3/webpack-functions.js | 90 + .../output/webpack3/webpack-functions.js.map | 1 + .../output/webpack3/webpack-line-mappings.js | 105 + .../output/webpack3/webpack-line-mappings.js.map | 1 + .../webpack4-babel6/babel-bindings-with-flow.js | 129 + .../babel-bindings-with-flow.js.map | 1 + .../output/webpack4-babel6/babel-classes.js | 134 + .../output/webpack4-babel6/babel-classes.js.map | 1 + .../webpack4-babel6/babel-flowtype-bindings.js | 128 + .../webpack4-babel6/babel-flowtype-bindings.js.map | 1 + .../sourcemapped/output/webpack4-babel6/classes.js | 148 + .../output/webpack4-babel6/classes.js.map | 1 + .../output/webpack4-babel6/esmodules-cjs.js | 362 + .../output/webpack4-babel6/esmodules-cjs.js.map | 1 + .../output/webpack4-babel6/esmodules-es6.js | 309 + .../output/webpack4-babel6/esmodules-es6.js.map | 1 + .../output/webpack4-babel6/esmodules.js | 309 + .../output/webpack4-babel6/esmodules.js.map | 1 + .../output/webpack4-babel6/eval-maps.js | 120 + .../output/webpack4-babel6/eval-maps.js.map | 1 + .../output/webpack4-babel6/for-loops.js | 121 + .../output/webpack4-babel6/for-loops.js.map | 1 + .../sourcemapped/output/webpack4-babel6/for-of.js | 119 + .../output/webpack4-babel6/for-of.js.map | 1 + .../output/webpack4-babel6/functions.js | 118 + .../output/webpack4-babel6/functions.js.map | 1 + .../output/webpack4-babel6/lex-and-nonlex.js | 117 + .../output/webpack4-babel6/lex-and-nonlex.js.map | 1 + .../webpack4-babel6/line-start-bindings-es6.js | 127 + .../webpack4-babel6/line-start-bindings-es6.js.map | 1 + .../output/webpack4-babel6/modules-cjs.js | 112 + .../output/webpack4-babel6/modules-cjs.js.map | 1 + .../out-of-order-declarations-cjs.js | 147 + .../out-of-order-declarations-cjs.js.map | 1 + .../output/webpack4-babel6/shadowed-vars.js | 129 + .../output/webpack4-babel6/shadowed-vars.js.map | 1 + .../step-over-for-of-array-closure.js | 122 + .../step-over-for-of-array-closure.js.map | 1 + .../webpack4-babel6/step-over-for-of-array.js | 116 + .../webpack4-babel6/step-over-for-of-array.js.map | 1 + .../webpack4-babel6/step-over-for-of-closure.js | 144 + .../step-over-for-of-closure.js.map | 1 + .../output/webpack4-babel6/step-over-for-of.js | 136 + .../output/webpack4-babel6/step-over-for-of.js.map | 1 + .../webpack4-babel6/step-over-function-params.js | 122 + .../step-over-function-params.js.map | 1 + .../webpack4-babel6/step-over-regenerator-await.js | 143 + .../step-over-regenerator-await.js.map | 1 + .../output/webpack4-babel6/switches.js | 118 + .../output/webpack4-babel6/switches.js.map | 1 + .../webpack4-babel6/this-arguments-bindings.js | 121 + .../webpack4-babel6/this-arguments-bindings.js.map | 1 + .../output/webpack4-babel6/try-catches.js | 114 + .../output/webpack4-babel6/try-catches.js.map | 1 + .../output/webpack4-babel6/type-module.js | 111 + .../output/webpack4-babel6/type-module.js.map | 1 + .../output/webpack4-babel6/type-script-cjs.js | 114 + .../output/webpack4-babel6/type-script-cjs.js.map | 1 + .../output/webpack4-babel6/webpack-functions.js | 115 + .../webpack4-babel6/webpack-functions.js.map | 1 + .../webpack4-babel6/webpack-line-mappings.js | 138 + .../webpack4-babel6/webpack-line-mappings.js.map | 1 + .../webpack4-babel7/babel-bindings-with-flow.js | 127 + .../babel-bindings-with-flow.js.map | 1 + .../output/webpack4-babel7/babel-classes.js | 139 + .../output/webpack4-babel7/babel-classes.js.map | 1 + .../webpack4-babel7/babel-flowtype-bindings.js | 125 + .../webpack4-babel7/babel-flowtype-bindings.js.map | 1 + .../sourcemapped/output/webpack4-babel7/classes.js | 152 + .../output/webpack4-babel7/classes.js.map | 1 + .../output/webpack4-babel7/esmodules-cjs.js | 372 + .../output/webpack4-babel7/esmodules-cjs.js.map | 1 + .../output/webpack4-babel7/esmodules-es6.js | 302 + .../output/webpack4-babel7/esmodules-es6.js.map | 1 + .../output/webpack4-babel7/esmodules.js | 302 + .../output/webpack4-babel7/esmodules.js.map | 1 + .../output/webpack4-babel7/eval-maps.js | 116 + .../output/webpack4-babel7/eval-maps.js.map | 1 + .../output/webpack4-babel7/for-loops.js | 124 + .../output/webpack4-babel7/for-loops.js.map | 1 + .../sourcemapped/output/webpack4-babel7/for-of.js | 118 + .../output/webpack4-babel7/for-of.js.map | 1 + .../output/webpack4-babel7/functions.js | 119 + .../output/webpack4-babel7/functions.js.map | 1 + .../output/webpack4-babel7/lex-and-nonlex.js | 117 + .../output/webpack4-babel7/lex-and-nonlex.js.map | 1 + .../webpack4-babel7/line-start-bindings-es6.js | 124 + .../webpack4-babel7/line-start-bindings-es6.js.map | 1 + .../output/webpack4-babel7/modules-cjs.js | 112 + .../output/webpack4-babel7/modules-cjs.js.map | 1 + .../out-of-order-declarations-cjs.js | 150 + .../out-of-order-declarations-cjs.js.map | 1 + .../output/webpack4-babel7/shadowed-vars.js | 128 + .../output/webpack4-babel7/shadowed-vars.js.map | 1 + .../step-over-for-of-array-closure.js | 122 + .../step-over-for-of-array-closure.js.map | 1 + .../webpack4-babel7/step-over-for-of-array.js | 116 + .../webpack4-babel7/step-over-for-of-array.js.map | 1 + .../webpack4-babel7/step-over-for-of-closure.js | 123 + .../step-over-for-of-closure.js.map | 1 + .../output/webpack4-babel7/step-over-for-of.js | 115 + .../output/webpack4-babel7/step-over-for-of.js.map | 1 + .../webpack4-babel7/step-over-function-params.js | 127 + .../step-over-function-params.js.map | 1 + .../webpack4-babel7/step-over-regenerator-await.js | 145 + .../step-over-regenerator-await.js.map | 1 + .../output/webpack4-babel7/switches.js | 119 + .../output/webpack4-babel7/switches.js.map | 1 + .../webpack4-babel7/this-arguments-bindings.js | 122 + .../webpack4-babel7/this-arguments-bindings.js.map | 1 + .../output/webpack4-babel7/try-catches.js | 114 + .../output/webpack4-babel7/try-catches.js.map | 1 + .../output/webpack4-babel7/type-module.js | 111 + .../output/webpack4-babel7/type-module.js.map | 1 + .../output/webpack4-babel7/type-script-cjs.js | 114 + .../output/webpack4-babel7/type-script-cjs.js.map | 1 + .../output/webpack4-babel7/webpack-functions.js | 115 + .../webpack4-babel7/webpack-functions.js.map | 1 + .../webpack4-babel7/webpack-line-mappings.js | 139 + .../webpack4-babel7/webpack-line-mappings.js.map | 1 + .../sourcemapped/output/webpack4/classes.js | 127 + .../sourcemapped/output/webpack4/classes.js.map | 1 + .../sourcemapped/output/webpack4/esmodules-cjs.js | 321 + .../output/webpack4/esmodules-cjs.js.map | 1 + .../sourcemapped/output/webpack4/esmodules-es6.js | 321 + .../output/webpack4/esmodules-es6.js.map | 1 + .../sourcemapped/output/webpack4/esmodules.js | 321 + .../sourcemapped/output/webpack4/esmodules.js.map | 1 + .../sourcemapped/output/webpack4/eval-maps.js | 121 + .../sourcemapped/output/webpack4/eval-maps.js.map | 1 + .../sourcemapped/output/webpack4/for-loops.js | 120 + .../sourcemapped/output/webpack4/for-loops.js.map | 1 + .../sourcemapped/output/webpack4/for-of.js | 117 + .../sourcemapped/output/webpack4/for-of.js.map | 1 + .../sourcemapped/output/webpack4/functions.js | 119 + .../sourcemapped/output/webpack4/functions.js.map | 1 + .../sourcemapped/output/webpack4/lex-and-nonlex.js | 114 + .../output/webpack4/lex-and-nonlex.js.map | 1 + .../output/webpack4/line-start-bindings-es6.js | 128 + .../output/webpack4/line-start-bindings-es6.js.map | 1 + .../sourcemapped/output/webpack4/modules-cjs.js | 110 + .../output/webpack4/modules-cjs.js.map | 1 + .../webpack4/out-of-order-declarations-cjs.js | 137 + .../webpack4/out-of-order-declarations-cjs.js.map | 1 + .../sourcemapped/output/webpack4/shadowed-vars.js | 125 + .../output/webpack4/shadowed-vars.js.map | 1 + .../webpack4/step-over-for-of-array-closure.js | 115 + .../webpack4/step-over-for-of-array-closure.js.map | 1 + .../output/webpack4/step-over-for-of-array.js | 115 + .../output/webpack4/step-over-for-of-array.js.map | 1 + .../output/webpack4/step-over-for-of-closure.js | 118 + .../webpack4/step-over-for-of-closure.js.map | 1 + .../output/webpack4/step-over-for-of.js | 116 + .../output/webpack4/step-over-for-of.js.map | 1 + .../output/webpack4/step-over-function-params.js | 113 + .../webpack4/step-over-function-params.js.map | 1 + .../output/webpack4/step-over-regenerator-await.js | 120 + .../webpack4/step-over-regenerator-await.js.map | 1 + .../sourcemapped/output/webpack4/switches.js | 118 + .../sourcemapped/output/webpack4/switches.js.map | 1 + .../output/webpack4/this-arguments-bindings.js | 119 + .../output/webpack4/this-arguments-bindings.js.map | 1 + .../sourcemapped/output/webpack4/try-catches.js | 115 + .../output/webpack4/try-catches.js.map | 1 + .../sourcemapped/output/webpack4/type-module.js | 112 + .../output/webpack4/type-module.js.map | 1 + .../output/webpack4/type-script-cjs.js | 112 + .../output/webpack4/type-script-cjs.js.map | 1 + .../output/webpack4/typescript-classes.js | 211 + .../output/webpack4/typescript-classes.js.map | 1 + .../output/webpack4/webpack-functions.js | 117 + .../output/webpack4/webpack-functions.js.map | 1 + .../output/webpack4/webpack-line-mappings.js | 138 + .../output/webpack4/webpack-line-mappings.js.map | 1 + .../mochitest/examples/sourcemapped/package.json | 16 + .../examples/sourcemapped/polyfill-bundle.js | 9037 ++ .../mochitest/examples/sourcemapped/tsconfig.json | 10 + .../test/mochitest/examples/sourcemapped/yarn.lock | 2177 + .../mochitest/examples/sourcemaps-indexed/main.js | 15 + .../examples/sourcemaps-indexed/main.js.map | 30 + .../examples/sourcemaps-indexed/main.min.js | 2 + .../sourcemaps-reload-compressed/README.md | 6 + .../sourcemaps-reload-compressed/package.json | 20 + .../v1/bundle-with-another-original.js | 2 + .../v1/bundle-with-another-original.js.map | 1 + .../sourcemaps-reload-compressed/v1/bundle.js | 2 + .../sourcemaps-reload-compressed/v1/bundle.js.map | 1 + .../sourcemaps-reload-compressed/v1/iframe.html | 29 + .../sourcemaps-reload-compressed/v1/index.html | 74 + .../sourcemaps-reload-compressed/v1/onload.js | 21 + .../v1/original-with-no-update.js | 9 + .../sourcemaps-reload-compressed/v1/original.js | 10 + .../sourcemaps-reload-compressed/v1/query.js.x=1 | 1 + .../sourcemaps-reload-compressed/v1/query.js.x=2 | 1 + .../sourcemaps-reload-compressed/v1/query2.js.y=3 | 1 + .../v1/removed-original.js | 5 + .../v1/replaced-bundle.js | 2 + .../v1/replaced-bundle.js.map | 1 + .../sourcemaps-reload-compressed/v1/same-url.sjs | 20 + .../sourcemaps-reload-compressed/v1/script.js | 8 + .../v1/test-functions.js | 14 + .../v1/webpack.config.js | 49 + .../v2/another-original.js | 15 + .../v2/bundle-with-another-original.js | 2 + .../v2/bundle-with-another-original.js.map | 1 + .../sourcemaps-reload-compressed/v2/bundle.js | 2 + .../sourcemaps-reload-compressed/v2/bundle.js.map | 1 + .../sourcemaps-reload-compressed/v2/iframe.html | 29 + .../sourcemaps-reload-compressed/v2/index.html | 32 + .../v2/new-original.js | 6 + .../v2/original-with-no-update.js | 9 + .../sourcemaps-reload-compressed/v2/original.js | 13 + .../v2/replaced-bundle.js | 2 + .../v2/replaced-bundle.js.map | 1 + .../sourcemaps-reload-compressed/v2/script.js | 22 + .../v2/webpack.config.js | 54 + .../sourcemaps-reload-compressed/v3/bundle.js | 2 + .../sourcemaps-reload-compressed/v3/bundle.js.map | 1 + .../sourcemaps-reload-compressed/v3/index.html | 17 + .../sourcemaps-reload-compressed/v3/original.js | 3 + .../v3/webpack.config.js | 31 + .../sourcemaps-reload-uncompressed/README.md | 11 + .../sourcemaps-reload-uncompressed/package.json | 19 + .../v1/bundle-with-another-original.js | 89 + .../v1/bundle-with-another-original.js.map | 1 + .../sourcemaps-reload-uncompressed/v1/bundle.js | 90 + .../v1/bundle.js.map | 1 + .../sourcemaps-reload-uncompressed/v1/iframe.html | 29 + .../sourcemaps-reload-uncompressed/v1/index.html | 77 + .../sourcemaps-reload-uncompressed/v1/onload.js | 21 + .../v1/original-with-no-update.js | 9 + .../sourcemaps-reload-uncompressed/v1/original.js | 10 + .../sourcemaps-reload-uncompressed/v1/query.js.x=1 | 1 + .../sourcemaps-reload-uncompressed/v1/query.js.x=2 | 1 + .../v1/query2.js.y=3 | 1 + .../v1/react-component-module.js | 2 + .../v1/removed-original.js | 5 + .../v1/replaced-bundle.js | 85 + .../v1/replaced-bundle.js.map | 1 + .../sourcemaps-reload-uncompressed/v1/same-url.sjs | 20 + .../sourcemaps-reload-uncompressed/v1/script.js | 8 + .../v1/test-functions.js | 14 + .../v1/webpack.config.js | 49 + .../v2/another-original.js | 15 + .../v2/bundle-with-another-original.js | 110 + .../v2/bundle-with-another-original.js.map | 1 + .../sourcemaps-reload-uncompressed/v2/bundle.js | 93 + .../v2/bundle.js.map | 1 + .../sourcemaps-reload-uncompressed/v2/iframe.html | 29 + .../sourcemaps-reload-uncompressed/v2/index.html | 32 + .../v2/new-original.js | 6 + .../v2/original-with-no-update.js | 9 + .../sourcemaps-reload-uncompressed/v2/original.js | 13 + .../v2/replaced-bundle.js | 86 + .../v2/replaced-bundle.js.map | 1 + .../sourcemaps-reload-uncompressed/v2/script.js | 22 + .../v2/webpack.config.js | 54 + .../sourcemaps-reload-uncompressed/v3/bundle.js | 83 + .../v3/bundle.js.map | 1 + .../sourcemaps-reload-uncompressed/v3/index.html | 17 + .../sourcemaps-reload-uncompressed/v3/original.js | 3 + .../v3/webpack.config.js | 31 + .../examples/sourcemaps-with-ignorelist/bundle.js | 35 + .../sourcemaps-with-ignorelist/bundle.js.map | 1 + .../sourcemaps-with-ignorelist/package.json | 15 + .../sourcemaps-with-ignorelist/rollup.config.js | 14 + .../sourcemaps-with-ignorelist/src/index.js | 11 + .../sourcemaps-with-ignorelist/src/original-1.js | 4 + .../sourcemaps-with-ignorelist/src/original-2.js | 4 + .../sourcemaps-with-ignorelist/src/original-3.js | 4 + .../sourcemaps-with-ignorelist/src/original-4.js | 4 + .../sourcemaps-with-ignorelist/src/original-5.js | 3 + .../examples/sourcemaps-with-sections/xbundle.js | 7 + .../sourcemaps-with-sections/xbundle.js.map | 25 + .../test/mochitest/examples/sourcemaps/bundle.js | 96 + .../mochitest/examples/sourcemaps/bundle.js.map | 21 + .../test/mochitest/examples/sourcemaps2/main.js | 15 + .../mochitest/examples/sourcemaps2/main.js.map | 1 + .../mochitest/examples/sourcemaps2/main.min.js | 2 + .../test/mochitest/examples/sourcemaps3/.babelrc | 1 + .../test/mochitest/examples/sourcemaps3/.gitignore | 2 + .../test/mochitest/examples/sourcemaps3/bundle.js | 2 + .../mochitest/examples/sourcemaps3/bundle.js.map | 1 + .../mochitest/examples/sourcemaps3/package.json | 19 + .../test/mochitest/examples/sourcemaps3/sorted.js | 43 + .../test/mochitest/examples/sourcemaps3/test.js | 7 + .../examples/sourcemaps3/webpack.config.js | 31 + .../debugger/test/mochitest/examples/sum/sum.js | 3 + .../test/mochitest/examples/sum/sum.min.js | 2 + .../test/mochitest/examples/sum/sum.min.js.map | 1 + .../debugger/test/mochitest/examples/times2.js | 3 + .../debugger/test/mochitest/examples/top-level.js | 3 + .../debugger/test/mochitest/examples/trigger-gc.js | 4 + .../mochitest/examples/wasm-sourcemaps/README.md | 20 + .../test/mochitest/examples/wasm-sourcemaps/fib.c | 17 + .../examples/wasm-sourcemaps/fib.debug.wasm | Bin 0 -> 1333 bytes .../mochitest/examples/wasm-sourcemaps/fib.wasm | Bin 0 -> 1516 bytes .../examples/wasm-sourcemaps/fib.wasm.map | 1 + .../test/mochitest/examples/webpack.config.js | 8 + .../test/mochitest/examples/worker-exception.js | 4 + devtools/client/debugger/test/mochitest/head.js | 255 + .../integration-tests/1-reload-same-original.js | 140 + .../2-reload-replaced-original.js | 128 + .../3-reload-changed-generated.js | 179 + .../test/mochitest/integration-tests/README.md | 29 + .../client/debugger/test/mochitest/shared-head.js | 2719 + 1154 files changed, 259179 insertions(+) create mode 100644 devtools/client/debugger/test/mochitest/browser.ini create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-async-stepping.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-asyncstacks.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-audiocontext.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-backgroundtask-debugging.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-bfcache.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-blackbox-all.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-blackbox-original.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-blackbox.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breaking-from-console.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breaking.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping-console.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-actions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-columns.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-shortcut.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-source-maps.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-debugger-statement.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-duplicate-functions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-in-evaled-sources.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-list.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading-with-source-changes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-same-file-per-target.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-scroll-to-log.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-sourcemap-with-sections.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-browser-toolbox-unselected-pause.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-browser-toolbox-workers.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-chrome-create.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-command-click.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-console-async.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-console-eval.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-console-link.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-console.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here-click.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-custom-formatters.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-debug-line.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-debugger-buttons.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints-fission.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-eager-eval-skip-pause.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-editor-exception.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-editor-gutter.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-editor-highlight.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-editor-mode.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-editor-scroll.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-editor-select.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-ember-quickstart.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-eval-throw.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-fission.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-event-handler.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-expressions-error.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-expressions-focus.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-expressions-thread.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-expressions-watch.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-expressions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-extension-inspectedWindow-debugger-statement.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-asm.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-lines.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-positions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-browser-toolbox-source-tree.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-source-text-content.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-source-tree.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-tabs.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-features-wasm.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-breakpoint.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-pause-exceptions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-sources.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-fission-project-search.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-fission-switch-target.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-gc-breakpoint-positions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-gc-sources.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-go-to-line.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-highlights-calls.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-html-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-idb-run-to-completion.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-iframes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-inline-cache.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-inline-exceptions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-inline-preview.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-inline-script-offset.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-inspector-integration.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-compressed-sourcemaps.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-uncompressed-sourcemaps.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-keyboard-navigation.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts-modal.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-layout-changes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-link-reload.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-log-events.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-log-points.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-many-breakpoints-same-line.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-merge-scopes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-message-run-to-completion.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-minified.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-navigation-when-paused.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-navigation.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-no-duplicate-breakpoints-on-frame-reload.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-outline-filter.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-outline-focus.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-outline-pretty.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-outline.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-overrides.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pause-exceptions.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pause-on-next.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pause-points.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pause-ux.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-iframe.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-loading.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-columns.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-delete.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-console.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-flow.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-inline-scripts.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-sourcemap.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-pretty-print.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-preview-frame.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-preview-getter.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-preview-module.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-preview-source-maps.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-preview.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-project-root.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-project-search.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-react-app.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-react-jsx.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-reducer-cleanup-on-target-removal.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-reloading.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-remember-expanded-scopes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-restart-frame.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-returnvalues.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-scopes-duplicated.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-scopes-mutations.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-scopes-xrays.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-scopes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-scroll-run-to-completion.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-search-file-paused.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-search-file-retains-query.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-search-file.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-settings-disable-javascript.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-slow-script.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourceURL-breakpoint.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-breakpoint-console.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-preview.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-scopes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-stepping.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-toggle.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-bogus.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-disabled.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-ignorelist.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-indexed.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-redirect.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading-quickly.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps2.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps3.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-sources-project-search.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-step-in-uninitialized.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-stepping.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-tabs-keyboard.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-tabs-pretty-print.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls-selected.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-tabs.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-toggling-tools.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-ua-widgets.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-unselected-pause.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-watchpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers-reload.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-windowless-workers-early-breakpoint.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-windowless-workers.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-worker-exception.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-worker-module.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-worker-nested.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-worker-scopes.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-wrong-fetch.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-xhr-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/browser_dbg-xhr-run-to-completion.js create mode 100644 devtools/client/debugger/test/mochitest/examples/README.md create mode 100644 devtools/client/debugger/test/mochitest/examples/asm.js create mode 100644 devtools/client/debugger/test/mochitest/examples/async.js create mode 100644 devtools/client/debugger/test/mochitest/examples/big-sourcemap.html create mode 100644 devtools/client/debugger/test/mochitest/examples/big-sourcemap_files/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/big-sourcemap_files/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/collected-bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/collected-bundle2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/collected-bundle2.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/collected-bundle2.js^headers^ create mode 100644 devtools/client/debugger/test/mochitest/examples/different_html.sjs create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-all-workers.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-asm.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-async.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-audiocontext.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-bfcache1.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-bfcache2.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-blackbox-all.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-command-click.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-content-script-sources.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-debugger-statements.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-duplicate-functions.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-early-xhr.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-editor-scroll.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-eval-throw.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-event-breakpoints-fission.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-event-breakpoints.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-event-handler.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-exceptions.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-frames-async.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-frames.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-gc-breakpoint-positions.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-gc-sources.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-html-breakpoints.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-idb-run-to-completion.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-iframes.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-inline-preview.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-inline-script-offset.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-merge-scopes.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-message-run-to-completion-frame.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-message-run-to-completion.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-minified.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-minified2.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-module-worker.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-navigation-when-paused.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-nested-worker.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-on-load.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-pause-points.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-pretty-print-inline-scripts.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-pretty.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-preview-getter.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-preview.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-react-jsx.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-react.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-reload-link.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-remember-expanded-scopes.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-return-values.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-scopes-xrays.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-script-mutate.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-script-switching.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-scripts-debugger.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-scripts.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-scroll-run-to-completion.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-service-workers.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-slow-script.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-source-pragma.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourceURL-breakpoint.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemap-bogus.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemapped.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemaps-ignorelist.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemaps-indexed.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemaps.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemaps2.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sourcemaps3.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-sources.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-step-in-uninitialized.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-strict.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-wasm-sourcemaps.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-watchpoints.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-windowless-workers-early-breakpoint.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-windowless-workers.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-worker-exception.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-worker-scopes.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-xhr-run-to-completion.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc-xhr.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-custom-formatters.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-fission-frame-pause-exceptions.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-fission-frame-sources-frame.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-fission-frame-sources.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-fission-pause-exceptions.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-same-source-distinct-threads-frame.html create mode 100644 devtools/client/debugger/test/mochitest/examples/doc_dbg-same-source-distinct-threads.html create mode 100644 devtools/client/debugger/test/mochitest/examples/dom-mutation.js create mode 100644 devtools/client/debugger/test/mochitest/examples/dom-mutation.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/.editorconfig create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/.ember-cli create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/.eslintrc.js create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/.gitignore create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/.travis.yml create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/.watchmanconfig create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/quickstart.css create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/quickstart.js create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/quickstart.map create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/test-support.css create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/test-support.js create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/test-support.map create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/tests.js create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/tests.map create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/vendor.css create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/vendor.js create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/assets/vendor.map create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/robots.txt create mode 100644 devtools/client/debugger/test/mochitest/examples/ember/quickstart/dist/testem.js create mode 100644 devtools/client/debugger/test/mochitest/examples/entry.js create mode 100644 devtools/client/debugger/test/mochitest/examples/event-breakpoints.js create mode 100644 devtools/client/debugger/test/mochitest/examples/exceptions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/fetch.js create mode 100644 devtools/client/debugger/test/mochitest/examples/frames.js create mode 100644 devtools/client/debugger/test/mochitest/examples/html-breakpoints-slow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/inline-preview.js create mode 100644 devtools/client/debugger/test/mochitest/examples/inner-worker.js create mode 100644 devtools/client/debugger/test/mochitest/examples/long.js create mode 100644 devtools/client/debugger/test/mochitest/examples/map-with-failed-original-request.js create mode 100644 devtools/client/debugger/test/mochitest/examples/map-with-failed-original-request.map create mode 100644 devtools/client/debugger/test/mochitest/examples/math.min.js create mode 100644 devtools/client/debugger/test/mochitest/examples/nested/nested-source.js create mode 100644 devtools/client/debugger/test/mochitest/examples/non-existant-map.js create mode 100644 devtools/client/debugger/test/mochitest/examples/opts.js create mode 100644 devtools/client/debugger/test/mochitest/examples/outer-worker.js create mode 100644 devtools/client/debugger/test/mochitest/examples/output.js create mode 100644 devtools/client/debugger/test/mochitest/examples/pause-points.js create mode 100644 devtools/client/debugger/test/mochitest/examples/pretty.js create mode 100644 devtools/client/debugger/test/mochitest/examples/preview-getter.js create mode 100644 devtools/client/debugger/test/mochitest/examples/preview.js create mode 100644 devtools/client/debugger/test/mochitest/examples/react/.gitignore create mode 100644 devtools/client/debugger/test/mochitest/examples/react/README.md create mode 100644 devtools/client/debugger/test/mochitest/examples/react/build/asset-manifest.json create mode 100644 devtools/client/debugger/test/mochitest/examples/react/build/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/react/build/main.js create mode 100644 devtools/client/debugger/test/mochitest/examples/react/build/main.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/react/build/service-worker.js create mode 100644 devtools/client/debugger/test/mochitest/examples/react/config-overrides.js create mode 100644 devtools/client/debugger/test/mochitest/examples/react/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/react/public/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/react/src/App.js create mode 100644 devtools/client/debugger/test/mochitest/examples/react/src/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/react/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/reload/code_reload_1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/same-script.js create mode 100644 devtools/client/debugger/test/mochitest/examples/scopes-worker.js create mode 100644 devtools/client/debugger/test/mochitest/examples/script-mutate.js create mode 100644 devtools/client/debugger/test/mochitest/examples/script-switching-01.js create mode 100644 devtools/client/debugger/test/mochitest/examples/script-switching-02.js create mode 100644 devtools/client/debugger/test/mochitest/examples/service-worker.sjs create mode 100644 devtools/client/debugger/test/mochitest/examples/shared-worker.js create mode 100644 devtools/client/debugger/test/mochitest/examples/simple-worker.js create mode 100644 devtools/client/debugger/test/mochitest/examples/simple1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/simple2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/simple3.js create mode 100644 devtools/client/debugger/test/mochitest/examples/simple4.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sjs_slow-load.sjs create mode 100644 devtools/client/debugger/test/mochitest/examples/source-pragma.js create mode 100644 devtools/client/debugger/test/mochitest/examples/source-pragma.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/.rpt2_cache/120bf459f95b8b447674b99c6fe5d040b99b6a97/types/cache/f67752e2d3a8fd18a75558156ccfd8c764b544a1 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/README.md create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/build.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/parcel/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/parcel/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/parcel/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup-babel6/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup-babel6/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup-babel6/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup-babel7/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup-babel7/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup-babel7/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/rollup/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3-babel6/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3-babel6/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3-babel6/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3-babel7/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3-babel7/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3-babel7/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack3/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4-babel6/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4-babel6/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4-babel6/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4-babel7/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4-babel7/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4-babel7/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/builds/webpack4/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/babel-bindings-with-flow/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/babel-bindings-with-flow/src/mod.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/babel-classes/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/babel-flowtype-bindings/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/babel-flowtype-bindings/src/mod.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/classes/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod10.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod11.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod12.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod3.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod4.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod5.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod7.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod8.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/mod9.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-cjs/src/optimized-out.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod10.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod11.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod12.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod3.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod4.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod5.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod7.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod8.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/mod9.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules-es6/src/optimized-out.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod10.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod11.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod12.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod3.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod4.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod5.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod7.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod8.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/mod9.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/esmodules/src/optimized-out.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/eval-maps/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/for-loops/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/for-of/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/functions/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/lex-and-nonlex/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/line-start-bindings-es6/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/modules-cjs/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/out-of-order-declarations-cjs/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/out-of-order-declarations-cjs/src/mod.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/shadowed-vars/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/step-over-for-of-array-closure/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/step-over-for-of-array/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/step-over-for-of-closure/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/step-over-for-of/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/step-over-function-params/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/step-over-regenerator-await/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/switches/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/this-arguments-bindings/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/try-catches/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/type-module/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/type-script-cjs/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/typescript-classes/input.ts create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/typescript-classes/src/mod.ts create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/webpack-functions/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/webpack-line-mappings/input.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/fixtures/webpack-line-mappings/src/mod1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/babel-bindings-with-flow.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/babel-classes.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/babel-flowtype-bindings.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/classes.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/esmodules-cjs.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/esmodules-es6.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/esmodules.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/eval-maps.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/for-loops.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/for-of.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/functions.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/lex-and-nonlex.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/line-start-bindings-es6.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/modules-cjs.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/out-of-order-declarations-cjs.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/shadowed-vars.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of-array-closure.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of-array.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of-closure.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-for-of.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-function-params.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/step-over-regenerator-await.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/switches.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/this-arguments-bindings.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/try-catches.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/type-module.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/type-script-cjs.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/typescript-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/typescript-classes.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/webpack-functions.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/parcel/webpack-line-mappings.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/babel-bindings-with-flow.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/babel-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/babel-flowtype-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel6/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/babel-bindings-with-flow.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/babel-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/babel-flowtype-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup-babel7/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/typescript-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/typescript-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/rollup/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/babel-bindings-with-flow.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/babel-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/babel-flowtype-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/esmodules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/modules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/out-of-order-declarations-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/type-script-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/typescript-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/typescript-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel6/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/babel-bindings-with-flow.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/babel-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/babel-flowtype-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/esmodules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/modules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/out-of-order-declarations-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/type-script-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3-babel7/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/esmodules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/modules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/out-of-order-declarations-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/type-script-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/typescript-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/typescript-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack3/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/babel-bindings-with-flow.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/babel-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/babel-flowtype-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/esmodules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/modules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/out-of-order-declarations-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/type-script-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel6/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/babel-bindings-with-flow.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/babel-bindings-with-flow.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/babel-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/babel-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/babel-flowtype-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/babel-flowtype-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/esmodules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/modules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/out-of-order-declarations-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/type-script-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4-babel7/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/esmodules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/esmodules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/esmodules-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/esmodules-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/esmodules.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/esmodules.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/eval-maps.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/eval-maps.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/for-loops.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/for-loops.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/lex-and-nonlex.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/lex-and-nonlex.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/line-start-bindings-es6.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/line-start-bindings-es6.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/modules-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/modules-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/out-of-order-declarations-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/out-of-order-declarations-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/shadowed-vars.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/shadowed-vars.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of-array-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of-array-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of-array.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of-array.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of-closure.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of-closure.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-for-of.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-function-params.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-function-params.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-regenerator-await.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/step-over-regenerator-await.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/switches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/switches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/this-arguments-bindings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/this-arguments-bindings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/try-catches.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/try-catches.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/type-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/type-module.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/type-script-cjs.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/type-script-cjs.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/typescript-classes.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/typescript-classes.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/webpack-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/webpack-functions.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/webpack-line-mappings.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/output/webpack4/webpack-line-mappings.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/polyfill-bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/tsconfig.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemapped/yarn.lock create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-indexed/main.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-indexed/main.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-indexed/main.min.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/README.md create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/bundle-with-another-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/bundle-with-another-original.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/iframe.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/onload.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/original-with-no-update.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/query.js.x=1 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/query.js.x=2 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/query2.js.y=3 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/removed-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/replaced-bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/replaced-bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/same-url.sjs create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/script.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/test-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v1/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/another-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/bundle-with-another-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/bundle-with-another-original.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/iframe.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/new-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/original-with-no-update.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/replaced-bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/replaced-bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/script.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v2/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v3/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v3/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v3/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v3/original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-compressed/v3/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/README.md create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/bundle-with-another-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/bundle-with-another-original.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/iframe.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/onload.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/original-with-no-update.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/query.js.x=1 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/query.js.x=2 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/query2.js.y=3 create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/react-component-module.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/removed-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/replaced-bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/replaced-bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/same-url.sjs create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/script.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/test-functions.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v1/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/another-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/bundle-with-another-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/bundle-with-another-original.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/iframe.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/new-original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/original-with-no-update.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/replaced-bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/replaced-bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/script.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v2/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v3/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v3/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v3/index.html create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v3/original.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-reload-uncompressed/v3/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/rollup.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/src/index.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/src/original-1.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/src/original-2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/src/original-3.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/src/original-4.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-ignorelist/src/original-5.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-sections/xbundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps-with-sections/xbundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps2/main.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps2/main.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps2/main.min.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/.babelrc create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/.gitignore create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/bundle.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/bundle.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/package.json create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/sorted.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/test.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sourcemaps3/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sum/sum.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sum/sum.min.js create mode 100644 devtools/client/debugger/test/mochitest/examples/sum/sum.min.js.map create mode 100644 devtools/client/debugger/test/mochitest/examples/times2.js create mode 100644 devtools/client/debugger/test/mochitest/examples/top-level.js create mode 100644 devtools/client/debugger/test/mochitest/examples/trigger-gc.js create mode 100644 devtools/client/debugger/test/mochitest/examples/wasm-sourcemaps/README.md create mode 100644 devtools/client/debugger/test/mochitest/examples/wasm-sourcemaps/fib.c create mode 100644 devtools/client/debugger/test/mochitest/examples/wasm-sourcemaps/fib.debug.wasm create mode 100644 devtools/client/debugger/test/mochitest/examples/wasm-sourcemaps/fib.wasm create mode 100644 devtools/client/debugger/test/mochitest/examples/wasm-sourcemaps/fib.wasm.map create mode 100644 devtools/client/debugger/test/mochitest/examples/webpack.config.js create mode 100644 devtools/client/debugger/test/mochitest/examples/worker-exception.js create mode 100644 devtools/client/debugger/test/mochitest/head.js create mode 100644 devtools/client/debugger/test/mochitest/integration-tests/1-reload-same-original.js create mode 100644 devtools/client/debugger/test/mochitest/integration-tests/2-reload-replaced-original.js create mode 100644 devtools/client/debugger/test/mochitest/integration-tests/3-reload-changed-generated.js create mode 100644 devtools/client/debugger/test/mochitest/integration-tests/README.md create mode 100644 devtools/client/debugger/test/mochitest/shared-head.js (limited to 'devtools/client/debugger/test/mochitest') diff --git a/devtools/client/debugger/test/mochitest/browser.ini b/devtools/client/debugger/test/mochitest/browser.ini new file mode 100644 index 0000000000..91d898715e --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser.ini @@ -0,0 +1,297 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +skip-if = + os == 'linux' && debug && bits == 32 + asan # Frequent failures when opening tabs due to OOM issues, bug 1760260 +support-files = + examples/* + integration-tests/* + head.js + shared-head.js + !/devtools/client/framework/browser-toolbox/test/helpers-browser-toolbox.js + !/devtools/client/inspector/test/head.js + !/devtools/client/inspector/test/shared-head.js + !/devtools/client/shared/test/shared-head.js + !/devtools/client/shared/test/telemetry-test-helpers.js + !/devtools/client/shared/test/highlighter-test-actor.js + !/devtools/client/webconsole/test/browser/shared-head.js +prefs = + # Disable randomly spawning processes during tests. + # After enabling windowless service workers, a process spawning + # will trigger an update of the service workers list which can + # fail the test if it occurs during shutdown (unhandled promise + # rejection). + dom.ipc.processPrelaunch.enabled=false + # This pref has to be set before the process starts + devtools.debugger.features.javascript-tracing=true + +# Integration tests: +[browser_dbg-integration-reloading-compressed-sourcemaps.js] +[browser_dbg-integration-reloading-uncompressed-sourcemaps.js] + +# Feature tests: +[browser_dbg-features-asm.js] +[browser_dbg-features-breakable-lines.js] +[browser_dbg-features-breakable-positions.js] +skip-if = + win11_2009 # Bug 1798331 +[browser_dbg-features-breakpoints.js] +[browser_dbg-features-source-tree.js] +[browser_dbg-features-browser-toolbox-source-tree.js] +skip-if = + asan # Bug 1591064 + win10_2004 # Bug 1767702 + win11_2009 # Bug 1798331 +[browser_dbg-features-source-text-content.js] +[browser_dbg-features-tabs.js] +[browser_dbg-features-wasm.js] + +# Other tests: +[browser_dbg-asyncstacks.js] +[browser_dbg-audiocontext.js] +[browser_dbg-async-stepping.js] +[browser_dbg-sourcemapped-breakpoint-console.js] +skip-if = (os == "win" && ccov) # Bug 1453549 +[browser_dbg-xhr-breakpoints.js] +[browser_dbg-xhr-run-to-completion.js] +[browser_dbg-scroll-run-to-completion.js] +[browser_dbg-source-pragma.js] +[browser_dbg-sourcemapped-scopes.js] +# Bug 1441545, 1536253 - very slow on debug +skip-if = ccov || debug || tsan || (verify && debug && (os == 'linux')) +[browser_dbg-sourcemapped-stepping.js] +skip-if = true # original stepping is currently disabled +[browser_dbg-sourcemapped-toggle.js] +[browser_dbg-sourcemapped-preview.js] +skip-if = os == "win" # Bug 1448523, Bug 1448450 +[browser_dbg-breaking.js] +[browser_dbg-breaking-from-console.js] +skip-if = debug # Window leaks: bug 1575332 +[browser_dbg-breakpoints.js] +[browser_dbg-breakpoints-actions.js] +[browser_dbg-breakpoints-columns.js] +[browser_dbg-breakpoints-cond.js] +[browser_dbg-breakpoints-cond-shortcut.js] +[browser_dbg-breakpoints-cond-source-maps.js] +[browser_dbg-breakpoints-debugger-statement.js] +[browser_dbg-breakpoints-duplicate-functions.js] +[browser_dbg-breakpoints-in-evaled-sources.js] +[browser_dbg-breakpoints-list.js] +[browser_dbg-breakpoints-popup.js] +skip-if = + (os == 'linux' && debug) || tsan # Bug 1750199 + apple_catalina && !debug # Bug 1767705 +[browser_dbg-continue-to-here.js] +[browser_dbg-continue-to-here-click.js] +skip-if = os == "win" +[browser_dbg-breakpoints-reloading-with-source-changes.js] +[browser_dbg-breakpoints-reloading.js] +[browser_dbg-breakpoints-same-file-per-target.js] +[browser_dbg-breakpoints-scroll-to-log.js] +[browser_dbg-breakpoints-sourcemap-with-sections.js] +[browser_dbg-breakpoint-skipping.js] +[browser_dbg-breakpoint-skipping-console.js] +[browser_dbg-call-stack.js] +[browser_dbg-dom-mutation-breakpoints-fission.js] +[browser_dbg-dom-mutation-breakpoints.js] +[browser_dbg-scopes.js] +[browser_dbg-backgroundtask-debugging.js] +skip-if = + asan # Bug 1591064 +[browser_dbg-chrome-create.js] +skip-if = (verify && !debug && (os == 'linux')) +[browser_dbg-command-click.js] +[browser_dbg-console.js] +[browser_dbg-console-async.js] +[browser_dbg-console-eval.js] +[browser_dbg-console-link.js] +[browser_dbg-console-map-bindings.js] +[browser_dbg-content-script-sources.js] +skip-if = (os == "win" && ccov) # Bug 1424154 +[browser_dbg-debug-line.js] +[browser_dbg-debugger-buttons.js] +[browser_dbg-editor-gutter.js] +[browser_dbg-eager-eval-skip-pause.js] +[browser_dbg-editor-exception.js] +[browser_dbg-editor-mode.js] +[browser_dbg-editor-scroll.js] +[browser_dbg-editor-select.js] +[browser_dbg-editor-highlight.js] +[browser_dbg-ember-quickstart.js] +skip-if = debug # Window leaks: bug 1575332 +[browser_dbg-expressions.js] +[browser_dbg-expressions-error.js] +[browser_dbg-expressions-focus.js] +[browser_dbg-expressions-thread.js] +skip-if = !fission # threads panel only shows remote frame when fission is enabled. +[browser_dbg-expressions-watch.js] +[browser_dbg-extension-inspectedWindow-debugger-statement.js] +[browser_dbg-fission-frame-breakpoint.js] +[browser_dbg-fission-frame-pause-exceptions.js] +[browser_dbg-fission-frame-sources.js] +[browser_dbg-fission-project-search.js] +[browser_dbg-fission-switch-target.js] +[browser_dbg-go-to-line.js] +[browser_dbg-highlights-calls.js] +[browser_dbg-html-breakpoints.js] +skip-if = (os == 'linux' && debug) || tsan # Bug 1802862 +[browser_dbg-iframes.js] +[browser_dbg-inline-cache.js] +skip-if = ccov && os == 'win' # Bug 1443132 +[browser_dbg-inline-preview.js] +skip-if = true # bug 1607636 +[browser_dbg-inline-exceptions.js] +[browser_dbg-inspector-integration.js] +[browser_dbg-keyboard-navigation.js] +[browser_dbg-keyboard-shortcuts-modal.js] +[browser_dbg-keyboard-shortcuts.js] +[browser_dbg-layout-changes.js] +[browser_dbg-link-reload.js] +skip-if = + os == 'linux' && asan # Bug 1715866 + os == 'win' && bits == 64 # Bug 1715866 +[browser_dbg-overrides.js] +[browser_dbg-log-events.js] +[browser_dbg-log-point-mapping.js] +[browser_dbg-log-points.js] +[browser_dbg-log-points-workers.js] +[browser_dbg-outline.js] +[browser_dbg-outline-focus.js] +skip-if = verify +[browser_dbg-outline-pretty.js] +[browser_dbg-outline-filter.js] +[browser_dbg-pause-exceptions.js] +skip-if = !debug && (os == "win" && os_version == "6.1") # Bug 1456441 +[browser_dbg-pause-on-next.js] +[browser_dbg-pause-ux.js] +skip-if = os == "win" +[browser_dbg-paused-overlay-iframe.js] +[browser_dbg-paused-overlay-loading.js] +[browser_dbg-paused-overlay.js] +[browser_dbg-navigation-when-paused.js] +[browser_dbg-navigation.js] +skip-if = (verify && debug && (os == 'mac')) || (os == 'linux' && debug && bits == 64) || (os == 'mac' && debug) # Bug 1307249 +[browser_dbg-no-duplicate-breakpoints-on-frame-reload.js] +[browser_dbg-minified.js] +[browser_dbg-pretty-print.js] +[browser_dbg-pretty-print-breakpoints-columns.js] +[browser_dbg-pretty-print-breakpoints-delete.js] +[browser_dbg-pretty-print-breakpoints.js] +[browser_dbg-pretty-print-console.js] +[browser_dbg-pretty-print-inline-scripts.js] +[browser_dbg-pretty-print-paused.js] +[browser_dbg-pretty-print-sourcemap.js] +[browser_dbg-pretty-print-paused-anonymous.js] +[browser_dbg-pretty-print-flow.js] +[browser_dbg-preview-getter.js] +[browser_dbg-preview.js] +skip-if = + os == "win" + (os == "linux" && !asan && !debug && !swgl && !ccov) +[browser_dbg-preview-frame.js] +skip-if = os == "win" +[browser_dbg-preview-module.js] +skip-if = os == "win" +[browser_dbg-preview-source-maps.js] +skip-if = os == "win" +[browser_dbg-react-jsx.js] +[browser_dbg-returnvalues.js] +[browser_dbg-reloading.js] +skip-if = true +[browser_dbg-pause-points.js] +[browser_dbg-scopes-mutations.js] +[browser_dbg-search-file-retains-query.js] +skip-if = os == "win" # Bug 1393121 +[browser_dbg-search-file.js] +skip-if = os == "win" # Bug 1393121 +[browser_dbg-search-file-paused.js] +skip-if = os == "win" # Bug 1393121 +[browser_dbg-quick-open.js] +skip-if = + os == "win" + os == 'linux' && bits == 64 && debug # Bug 1721999 +[browser_dbg-blackbox-all.js] +[browser_dbg-blackbox-original.js] +[browser_dbg-blackbox.js] +[browser_dbg-state-based-panels.js] +[browser_dbg-sourcemaps.js] +[browser_dbg-sourcemaps-breakpoints.js] +[browser_dbg-sourcemaps-disabled.js] +[browser_dbg-sourcemaps-ignorelist.js] +[browser_dbg-sourcemaps-indexed.js] +skip-if = os == "win" || (verify) # Bug 1434792 +[browser_dbg-sourcemaps-redirect.js] +[browser_dbg-sourcemaps-reloading.js] +[browser_dbg-sourcemaps-reloading-quickly.js] +[browser_dbg-sourcemaps2.js] +[browser_dbg-sourcemaps3.js] +[browser_dbg-sourcemaps-bogus.js] +skip-if = os == 'linux' && !asan # bug 1447118 +[browser_dbg-stepping.js] +skip-if = debug || (verify && (os == 'win')) || (os == "win" && os_version == "6.1") +[browser_dbg-step-in-uninitialized.js] +[browser_dbg-tabs.js] +[browser_dbg-tabs-keyboard.js] +skip-if = os == "win" +[browser_dbg-tabs-pretty-print.js] +[browser_dbg-tabs-without-urls.js] +[browser_dbg-tabs-without-urls-selected.js] +[browser_dbg-toggling-tools.js] +[browser_dbg-react-app.js] +skip-if = + os == "win" + os == "linux" && tsan # Bug 1813196 +[browser_dbg-windowless-workers.js] +[browser_dbg-windowless-workers-early-breakpoint.js] +[browser_dbg-worker-exception.js] +[browser_dbg-windowless-service-workers-reload.js] +skip-if = !serviceworker_e10s # parent intercept mode required. +[browser_dbg-windowless-service-workers.js] +skip-if = + !serviceworker_e10s # parent intercept mode is needed bug 1588154. Bug 1613543, the test consistently timeouts on Linux coverage builds and WR debug builds. + (os == 'linux' && (ccov || (!swgl && debug))) + os == "linux" && bits == 64 && debug # Bug 1732486 +[browser_dbg-worker-scopes.js] +[browser_dbg-event-handler.js] +[browser_dbg-event-breakpoints-fission.js] +[browser_dbg-event-breakpoints.js] +[browser_dbg-eval-throw.js] +[browser_dbg-sourceURL-breakpoint.js] +[browser_dbg-old-breakpoint.js] +skip-if = os == "linux" || os == "mac" #Bug 1644044 +[browser_dbg-idb-run-to-completion.js] +[browser_dbg-inline-script-offset.js] +[browser_dbg-scopes-duplicated.js] +[browser_dbg-scopes-xrays.js] +[browser_dbg-merge-scopes.js] +[browser_dbg-message-run-to-completion.js] +[browser_dbg-remember-expanded-scopes.js] +[browser_dbg-bfcache.js] +[browser_dbg-gc-breakpoint-positions.js] +[browser_dbg-gc-sources.js] +[browser_dbg-watchpoints.js] +skip-if = true # Bug 1814093 +[browser_dbg-browser-toolbox-unselected-pause.js] +skip-if = + asan # Bug 1591064 + os == "win" && fission && debug # intermittent on fission, Bug 1720165 - test timed out +[browser_dbg-browser-toolbox-workers.js] +skip-if = asan || !nightly_build || fission # Bug 1591064, parent intercept mode is needed bug 1588154, Disable frequent fission intermittents Bug 1675020 +[browser_dbg-wrong-fetch.js] +[browser_dbg-worker-module.js] +[browser_dbg-worker-nested.js] +[browser_dbg-step-in-navigate.js] +[browser_dbg-settings-disable-javascript.js] +[browser_dbg-restart-frame.js] +[browser_dbg-many-breakpoints-same-line.js] +[browser_dbg-unselected-pause.js] +[browser_dbg-slow-script.js] +[browser_dbg-project-root.js] +[browser_dbg-project-search.js] +[browser_dbg-sources-project-search.js] +[browser_dbg-javascript-tracer.js] +[browser_dbg-es-module-worker.js] +[browser_dbg-ua-widgets.js] +[browser_dbg-custom-formatters.js] +[browser_dbg-reducer-cleanup-on-target-removal.js] diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-async-stepping.js b/devtools/client/debugger/test/mochitest/browser_dbg-async-stepping.js new file mode 100644 index 0000000000..d04783d666 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-async-stepping.js @@ -0,0 +1,23 @@ +/* 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 . */ + +// Tests async stepping will step over await statements + +"use strict"; + +add_task(async function test() { + const dbg = await initDebugger("doc-async.html", "async.js"); + + await selectSource(dbg, "async.js"); + await addBreakpoint(dbg, "async.js", 8); + invokeInTab("main"); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "async.js").id, 8); + + await stepOver(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "async.js").id, 9); + + await assertBreakpoint(dbg, 8); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-asyncstacks.js b/devtools/client/debugger/test/mochitest/browser_dbg-asyncstacks.js new file mode 100644 index 0000000000..f99eb66496 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-asyncstacks.js @@ -0,0 +1,21 @@ +/* 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 . */ + +// Tests that async stacks include the async separator + +"use strict"; + +add_task(async function () { + pushPref("devtools.debugger.features.async-captured-stacks", true); + const dbg = await initDebugger("doc-frames-async.html"); + + invokeInTab("main"); + await waitForPaused(dbg); + + is(findElement(dbg, "frame", 1).innerText, "sleep\ndoc-frames-async.html:13"); + is( + findElement(dbg, "frame", 2).innerText, + "async\nmain\ndoc-frames-async.html:17" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-audiocontext.js b/devtools/client/debugger/test/mochitest/browser_dbg-audiocontext.js new file mode 100644 index 0000000000..f77fedbe2a --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-audiocontext.js @@ -0,0 +1,20 @@ +/* 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 . */ + +// Test the AudioContext are paused and resume appropriately when using the +// debugger. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-audiocontext.html"); + + await invokeInTab("myFunction"); + await invokeInTab("suspendAC"); + invokeInTab("debuggerStatement"); + await waitForPaused(dbg); + await resume(dbg); + await invokeInTab("checkACState"); + ok(true, "No AudioContext state transition are caused by the debugger"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-backgroundtask-debugging.js b/devtools/client/debugger/test/mochitest/browser_dbg-backgroundtask-debugging.js new file mode 100644 index 0000000000..c22c3bb49b --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-backgroundtask-debugging.js @@ -0,0 +1,151 @@ +/* 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 . */ + +/** + * Tests that `--backgroundtask` debugging works. + * + * This test is subtle. We launch a `--backgroundtask` with `--jsdebugger` and + * `--wait-for-jsdebugger` within the test. The background task infrastructure + * launches a browser toolbox, and the test connects to that browser toolbox + * instance. The test drives the instance, verifying that the automatically + * placed breakpoint paused execution. It then closes the browser toolbox, + * which resumes the execution and the task exits. + * + * In the future, it would be nice to change the task's running environment, for + * example by redefining a failing exit code to exit code 0. Attempts to do + * this have so far not been robust in automation. + */ + +"use strict"; + +requestLongerTimeout(4); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/browser-toolbox/test/helpers-browser-toolbox.js", + this +); + +const { BackgroundTasksTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/BackgroundTasksTestUtils.sys.mjs" +); +BackgroundTasksTestUtils.init(this); +const do_backgroundtask = BackgroundTasksTestUtils.do_backgroundtask.bind( + BackgroundTasksTestUtils +); + +add_task(async function test_backgroundtask_debugger() { + // In this test, the background task infrastructure launches the browser + // toolbox. The browser toolbox profile prefs are taken from the default + // profile (which is the standard place for background tasks to look for + // task-specific configuration). The current test profile will be + // considered the default profile by the background task apparatus, so this + // is how we configure the browser toolbox profile prefs. + // + // These prefs are not set for the background task under test directly: the + // relevant prefs are set in the background task defaults. + await pushPref("devtools.chrome.enabled", true); + await pushPref("devtools.debugger.remote-enabled", true); + await pushPref("devtools.browsertoolbox.enable-test-server", true); + await pushPref("devtools.debugger.prompt-connection", false); + + // Before we start the background task, the preference file must be flushed to disk. + Services.prefs.savePrefFile(null); + + // This invokes the test-only background task `BackgroundTask_jsdebugger.jsm`. + const p = do_backgroundtask("jsdebugger", { + extraArgs: [`--jsdebugger`, "--wait-for-jsdebugger"], + extraEnv: { + // Force the current test profile to be considered the default profile. + MOZ_BACKGROUNDTASKS_DEFAULT_PROFILE_PATH: Services.dirsvc.get( + "ProfD", + Ci.nsIFile + ).path, + }, + }); + + ok(true, "Launched background task"); + + const existingProcessClose = async () => { + const exitCode = await p; + return { exitCode }; + }; + const ToolboxTask = await initBrowserToolboxTask({ existingProcessClose }); + + await ToolboxTask.importFunctions({ + checkEvaluateInTopFrame, + evaluateInTopFrame, + createDebuggerContext, + expandAllScopes, + findElement, + findElementWithSelector, + getSelector, + getThreadContext, + getVisibleSelectedFrameLine, + isPaused, + resume, + stepOver, + toggleObjectInspectorNode, + toggleScopeNode, + waitForElement, + waitForLoadedScopes, + waitForPaused, + waitForResumed, + waitForSelectedSource, + waitForState, + waitUntil, + createLocation, + log: (msg, data) => + console.log(`${msg} ${!data ? "" : JSON.stringify(data)}`), + info: (msg, data) => + console.info(`${msg} ${!data ? "" : JSON.stringify(data)}`), + }); + + // ToolboxTask.spawn passes input arguments by stringify-ing them via string + // concatenation. But functions do not survive this process, so we manually + // recreate (in the toolbox process) the single function the `expandAllScopes` + // invocation in this test needs. + await ToolboxTask.spawn(selectors, async _selectors => { + this.selectors = _selectors; + this.selectors.scopeNode = i => + `.scopes-list .tree-node:nth-child(${i}) .object-label`; + }); + + // The debugger should automatically be selected. + await ToolboxTask.spawn(null, async () => { + await waitUntil(() => gToolbox.currentToolId == "jsdebugger"); + }); + ok(true, "Debugger selected"); + + // The debugger should automatically pause. + await ToolboxTask.spawn(null, async () => { + try { + /* global gToolbox */ + // Wait for the debugger to finish loading. + await gToolbox.getPanelWhenReady("jsdebugger"); + + const dbg = createDebuggerContext(gToolbox); + + // Scopes are supposed to be automatically expanded, but with + // `setBreakpointOnLoad` that doesn't seem to be happening. Explicitly + // expand scopes so that they are expanded for `waitForPaused`. + await expandAllScopes(dbg); + + await waitForPaused(dbg); + + if (!gToolbox.isHighlighted("jsdebugger")) { + throw new Error("Debugger not highlighted"); + } + } catch (e) { + console.log("Caught exception in spawn", e); + throw e; + } + }); + ok(true, "Paused in backgroundtask script"); + + // If we `resume`, then the task completes and the Browser Toolbox exits, + // which isn't handled cleanly by `spawn`, resulting in a test time out. This + // closes the toolbox "from the inside", which continues execution. The test + // then waits for the background task to terminate with exit code 0. + await ToolboxTask.destroy(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-bfcache.js b/devtools/client/debugger/test/mochitest/browser_dbg-bfcache.js new file mode 100644 index 0000000000..d66bd294bc --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-bfcache.js @@ -0,0 +1,98 @@ +/* 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 . */ + +// Test the debugger when navigating using the BFCache. + +"use strict"; + +PromiseTestUtils.allowMatchingRejectionsGlobally(/Connection closed/); + +add_task(async function () { + info("Run test with bfcacheInParent DISABLED"); + await pushPref("fission.bfcacheInParent", false); + await testSourcesOnNavigation(); + await testDebuggerPauseStateOnNavigation(); + + // bfcacheInParent only works if sessionHistoryInParent is enable + // so only test it if both settings are enabled. + if (Services.appinfo.sessionHistoryInParent) { + info("Run test with bfcacheInParent ENABLED"); + await pushPref("fission.bfcacheInParent", true); + await testSourcesOnNavigation(); + await testDebuggerPauseStateOnNavigation(); + } +}); + +async function testSourcesOnNavigation() { + info( + "Test that sources appear in the debugger when navigating using the BFCache" + ); + const dbg = await initDebugger("doc-bfcache1.html"); + + await navigate(dbg, "doc-bfcache2.html", "doc-bfcache2.html"); + + invokeInTab("goBack"); + await waitForSources(dbg, "doc-bfcache1.html"); + + invokeInTab("goForward"); + await waitForSources(dbg, "doc-bfcache2.html"); + ok(true, "Found sources after BFCache navigations"); + + await dbg.toolbox.closeToolbox(); +} + +async function testDebuggerPauseStateOnNavigation() { + info("Test the debugger pause state when navigating using the BFCache"); + + info("Open debugger on the first page"); + const dbg = await initDebugger("doc-bfcache1.html"); + + await addBreakpoint(dbg, "doc-bfcache1.html", 4); + + info("Navigate to the second page"); + await navigate(dbg, "doc-bfcache2.html"); + await waitForSources(dbg, "doc-bfcache2.html"); + + info("Navigate back to the first page (which should resurect from bfcache)"); + await goBack(`${EXAMPLE_URL}doc-bfcache1.html`); + await waitForSources(dbg, "doc-bfcache1.html"); + + // We paused when navigation back to bfcache1.html + // The previous navigation will prevent the page from completing its load. + // And we will do the same with this reload, which will pause page load + // and we will navigate forward and never complete the reload page load. + info("Reload the first page (which was in bfcache)"); + await reloadWhenPausedBeforePageLoaded(dbg); + await waitForPaused(dbg); + + ok(dbg.toolbox.isHighlighted("jsdebugger"), "Debugger is highlighted"); + + info( + "Navigate forward to the second page (which should also coming from bfcache)" + ); + await goForward(`${EXAMPLE_URL}doc-bfcache2.html`); + + await waitUntil(() => !dbg.toolbox.isHighlighted("jsdebugger")); + ok(true, "Debugger is not highlighted"); + + dbg.toolbox.closeToolbox(); +} + +async function goBack(expectedUrl) { + const onLocationChange = BrowserTestUtils.waitForLocationChange( + gBrowser, + expectedUrl + ); + gBrowser.goBack(); + await onLocationChange; +} + +async function goForward(expectedUrl) { + const onLocationChange = BrowserTestUtils.waitForLocationChange( + gBrowser, + expectedUrl + ); + gBrowser.goForward(); + await onLocationChange; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-blackbox-all.js b/devtools/client/debugger/test/mochitest/browser_dbg-blackbox-all.js new file mode 100644 index 0000000000..8080d3c145 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-blackbox-all.js @@ -0,0 +1,215 @@ +/* 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 . */ + +// This contains tests for +// 1) The context menu items that blackboxes multiple files in the Sources Panel. +// Checks if submenu works correctly for options: +// - 'Un/Blackbox files in this group' +// - 'Un/Blackbox files outside this group' +// - 'Un/Blackbox files in this directory' +// - 'Un/Blackbox files outside this directory' +// 2) The context menu item to hide/show the blackboxed files. + +"use strict"; + +const SOURCE_FILES = { + nestedSource: "nested-source.js", + codeReload1: "code_reload_1.js", +}; + +const NODE_SELECTORS = { + nodeBlackBoxAll: "#node-blackbox-all", + nodeBlackBoxAllInside: "#node-blackbox-all-inside", + nodeUnBlackBoxAllInside: "#node-unblackbox-all-inside", + nodeBlackBoxAllOutside: "#node-blackbox-all-outside", + nodeUnBlackBoxAllOutside: "#node-unblackbox-all-outside", +}; + +add_task(async function testBlackBoxOnMultipleFiles() { + const dbg = await initDebugger( + "doc-blackbox-all.html", + SOURCE_FILES.nestedSource, + SOURCE_FILES.codeReload1 + ); + + // Expand the SourceTree and wait for all sources to be visible, + // so that we can assert the visible state of blackboxing in the source tree. + await waitForSourcesInSourceTree(dbg, Object.values(SOURCE_FILES)); + + info("Loads the source file and sets a breakpoint at line 2."); + await selectSource(dbg, SOURCE_FILES.nestedSource); + await addBreakpoint(dbg, SOURCE_FILES.nestedSource, 2); + + info("Selecting the source will highlight it and expand the tree down to it"); + await waitForAllElements(dbg, "sourceTreeFolderNode", 3); + const sourceTreeFolderNodeEls = findAllElements(dbg, "sourceTreeFolderNode"); + const sourceTreeRootNodeEl = findElement(dbg, "sourceTreeRootNode"); + + info("Blackbox files in this directory."); + rightClickEl(dbg, sourceTreeFolderNodeEls[1]); + await waitForContextMenu(dbg); + await openContextMenuSubmenu(dbg, NODE_SELECTORS.nodeBlackBoxAll); + await assertContextMenuLabel( + dbg, + NODE_SELECTORS.nodeBlackBoxAllInside, + "Ignore files in this directory" + ); + await assertContextMenuLabel( + dbg, + NODE_SELECTORS.nodeBlackBoxAllOutside, + "Ignore files outside this directory" + ); + selectContextMenuItem(dbg, NODE_SELECTORS.nodeBlackBoxAllInside); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + await waitForBlackboxCount(dbg, 1); + await waitForRequestsToSettle(dbg); + + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.nestedSource, true); + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.codeReload1, false); + + info("The invoked function is blackboxed and the debugger does not pause."); + invokeInTab("computeSomething"); + assertNotPaused(dbg); + + info("Unblackbox files outside this directory."); + rightClickEl(dbg, sourceTreeFolderNodeEls[2]); + await waitForContextMenu(dbg); + await openContextMenuSubmenu(dbg, NODE_SELECTORS.nodeBlackBoxAll); + await assertContextMenuLabel( + dbg, + NODE_SELECTORS.nodeBlackBoxAllInside, + "Ignore files in this directory" + ); + await assertContextMenuLabel( + dbg, + NODE_SELECTORS.nodeUnBlackBoxAllOutside, + "Unignore files outside this directory" + ); + selectContextMenuItem(dbg, NODE_SELECTORS.nodeUnBlackBoxAllOutside); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); + await waitForBlackboxCount(dbg, 0); + await waitForRequestsToSettle(dbg); + info("Wait for any breakpoints in the source to get enabled"); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.nestedSource, false); + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.codeReload1, false); + + info("All sources are unblackboxed and the debugger pauses on line 2."); + invokeInTab("computeSomething"); + await waitForPaused(dbg); + await resume(dbg); + + info("Blackbox files in this group."); + rightClickEl(dbg, sourceTreeRootNodeEl); + await waitForContextMenu(dbg); + await assertContextMenuLabel( + dbg, + NODE_SELECTORS.nodeBlackBoxAllInside, + "Ignore files in this group" + ); + selectContextMenuItem(dbg, NODE_SELECTORS.nodeBlackBoxAllInside); + await waitForBlackboxCount(dbg, 2); + await waitForRequestsToSettle(dbg); + + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.nestedSource, true); + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.codeReload1, true); + + info("Unblackbox files in this group."); + rightClickEl(dbg, sourceTreeRootNodeEl); + await waitForContextMenu(dbg); + await assertContextMenuLabel( + dbg, + NODE_SELECTORS.nodeUnBlackBoxAllInside, + "Unignore files in this group" + ); + selectContextMenuItem(dbg, NODE_SELECTORS.nodeUnBlackBoxAllInside); + await waitForBlackboxCount(dbg, 0); + await waitForRequestsToSettle(dbg); + + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.nestedSource, false); + assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.codeReload1, false); +}); + +add_task(async function testHideAndShowBlackBoxedFiles() { + Services.prefs.setBoolPref("devtools.debugger.hide-ignored-sources", false); + + const dbg = await initDebugger( + "doc-blackbox-all.html", + SOURCE_FILES.nestedSource, + SOURCE_FILES.codeReload1 + ); + + await waitForSourcesInSourceTree(dbg, Object.values(SOURCE_FILES)); + await selectSource(dbg, SOURCE_FILES.nestedSource); + clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + + info("Assert and click the hide ignored files button in the settings menu"); + await toggleDebbuggerSettingsMenuItem(dbg, { + className: ".debugger-settings-menu-item-hide-ignored-sources", + isChecked: false, + }); + + info("Wait until ignored sources are no longer visible"); + await waitUntil( + () => !findSourceNodeWithText(dbg, SOURCE_FILES.nestedSource) + ); + + is( + Services.prefs.getBoolPref("devtools.debugger.hide-ignored-sources"), + true, + "Hide ignored files is enabled" + ); + + is( + dbg.win.document.querySelector(".source-list-footer").innerText, + "Ignored sources are hidden.\nShow all sources", + "Notification is visible with the correct message" + ); + + info("Assert that newly ignored files are automatically hidden"); + await selectSource(dbg, SOURCE_FILES.codeReload1); + await triggerSourceTreeContextMenu( + dbg, + findSourceNodeWithText(dbg, SOURCE_FILES.codeReload1), + "#node-menu-blackbox" + ); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + await waitUntil(() => !findSourceNodeWithText(dbg, SOURCE_FILES.codeReload1)); + + info("Show the hidden ignored files using the button in the notification"); + clickElementWithSelector(dbg, ".source-list-footer button"); + + info("Wait until ignored sources are visible"); + await waitUntil(() => findSourceNodeWithText(dbg, SOURCE_FILES.nestedSource)); + + is( + Services.prefs.getBoolPref("devtools.debugger.hide-ignored-sources"), + false, + "Hide ignored files is disabled" + ); + + ok( + !document.querySelector(".source-list-footer"), + "Notification is no longer visible" + ); +}); + +function waitForBlackboxCount(dbg, count) { + return waitForState( + dbg, + state => Object.keys(dbg.selectors.getBlackBoxRanges()).length === count + ); +} + +function assertSourceNodeIsBlackBoxed(dbg, sourceFilename, shouldBeBlackBoxed) { + const treeItem = findSourceNodeWithText(dbg, sourceFilename); + ok(treeItem, `Found tree item for ${sourceFilename}`); + is( + !!treeItem.querySelector(".img.blackBox"), + shouldBeBlackBoxed, + `${sourceFilename} is ${shouldBeBlackBoxed ? "" : "not"} blackboxed` + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-blackbox-original.js b/devtools/client/debugger/test/mochitest/browser_dbg-blackbox-original.js new file mode 100644 index 0000000000..c1bf46da7f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-blackbox-original.js @@ -0,0 +1,53 @@ +/* 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 . */ + +// This source map does not have source contents, so it's fetched separately + +"use strict"; + +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger( + "doc-sourcemaps3.html", + "bundle.js", + "sorted.js", + "test.js" + ); + dbg.actions.toggleMapScopes(); + + const sortedSrc = findSource(dbg, "sorted.js"); + await selectSource(dbg, sortedSrc); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + + const sourceTab = findElementWithSelector(dbg, ".source-tab.active"); + ok( + sourceTab.querySelector(".img.blackBox"), + "Source tab has a blackbox icon" + ); + + const treeItem = findElementWithSelector(dbg, ".tree-node.focused"); + ok( + treeItem.querySelector(".img.blackBox"), + "Source tree item has a blackbox icon" + ); + + // breakpoint at line 38 in sorted + await addBreakpoint(dbg, sortedSrc, 38); + // invoke test + invokeInTab("test"); + // should not pause + assertNotPaused(dbg); + + // unblackbox + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); + + // click on test + invokeInTab("test"); + // should pause + await waitForPaused(dbg); + + ok(true, "blackbox works"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-blackbox.js b/devtools/client/debugger/test/mochitest/browser_dbg-blackbox.js new file mode 100644 index 0000000000..9542473408 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-blackbox.js @@ -0,0 +1,742 @@ +/* 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 . */ + +// This test covers all the blackboxing functionality relating to a selected +// source open in the debugger editor. + +"use strict"; + +requestLongerTimeout(5); + +const contextMenuItems = { + ignoreSource: { selector: "#node-menu-blackbox", label: "Ignore source" }, + unignoreSource: { selector: "#node-menu-blackbox", label: "Unignore source" }, + ignoreLines: { selector: "#node-menu-blackbox-lines", label: "Ignore lines" }, + unignoreLines: { + selector: "#node-menu-blackbox-lines", + label: "Unignore lines", + }, + ignoreLine: { selector: "#node-menu-blackbox-line", label: "Ignore line" }, + unignoreLine: { + selector: "#node-menu-blackbox-line", + label: "Unignore line", + }, +}; + +const SOURCE_IS_FULLY_IGNORED = "source"; +const SOURCE_LINES_ARE_IGNORED = "line"; +const SOURCE_IS_NOT_IGNORED = "none"; + +// Tests basic functionality for blackbox source and blackbox single and multiple lines +add_task(async function testAllBlackBox() { + // using the doc-command-click.html as it has a simple js file we can use + // testing. + const file = "simple4.js"; + const dbg = await initDebugger("doc-command-click.html", file); + + const source = findSource(dbg, file); + + await selectSource(dbg, source); + + await addBreakpoint(dbg, file, 8); + + await testBlackBoxSource(dbg, source); + await testBlackBoxMultipleLines(dbg, source); + await testBlackBoxSingleLine(dbg, source); +}); + +// Test that the blackboxed lines are persisted accross reloads and still work accordingly. +add_task(async function testBlackBoxOnReload() { + const file = "simple4.js"; + const dbg = await initDebugger("doc-command-click.html", file); + + const source = findSource(dbg, file); + + await selectSource(dbg, source); + + // Adding 2 breakpoints in funcB() and funcC() which + // would be hit in order. + await addBreakpoint(dbg, file, 8); + await addBreakpoint(dbg, file, 12); + + info("Reload without any blackboxing to make all necesary postions are hit"); + const onReloaded = reload(dbg, file); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resume(dbg); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 8); + await resume(dbg); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 12); + await resumeAndWaitForPauseCounter(dbg); + + info("Wait for reload to complete after resume"); + await onReloaded; + + assertNotPaused(dbg); + + info("Ignoring line 2 using the gutter context menu"); + await openContextMenuInDebugger(dbg, "gutter", 2); + await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); + + info("Ignoring line 7 to 9 using the editor context menu"); + await selectEditorLinesAndOpenContextMenu(dbg, { startLine: 7, endLine: 9 }); + await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); + + const onReloaded2 = reload(dbg, file); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 12); + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded2; + + assertNotPaused(dbg); + + info( + "Check that the expected blackbox context menu state is correct across reload" + ); + + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 2, + nonBlackBoxedLine: 4, + blackBoxedLines: [7, 9], + nonBlackBoxedLines: [3, 4], + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 2, + nonBlackBoxedLine: 4, + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); +}); + +add_task(async function testBlackBoxOnToolboxRestart() { + const dbg = await initDebugger("doc-command-click.html", "simple4.js"); + const source = findSource(dbg, "simple4.js"); + + await selectSource(dbg, source); + + const onReloaded = reload(dbg, "simple4.js"); + await waitForPaused(dbg); + + info("Assert it paused at the debugger statement"); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resume(dbg); + await onReloaded; + + info("Ignoring line 2 using the gutter context menu"); + await openContextMenuInDebugger(dbg, "gutter", 2); + await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); + + await reloadBrowser(); + // Wait a little bit incase of a pause + await wait(1000); + + info("Assert that the debugger no longer pauses on the debugger statement"); + assertNotPaused(dbg); + + info("Close the toolbox"); + await dbg.toolbox.closeToolbox(); + + info("Reopen the toolbox on the debugger"); + const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger"); + const dbg2 = createDebuggerContext(toolbox); + await waitForSelectedSource(dbg2, findSource(dbg2, "simple4.js")); + + await reloadBrowser(); + // Wait a little incase of a pause + await wait(1000); + + info("Assert that debbuger still does not pause on the debugger statement"); + assertNotPaused(dbg2); +}); + +async function testBlackBoxSource(dbg, source) { + info("Start testing blackboxing the whole source"); + + info("Assert the blackbox context menu items before any blackboxing is done"); + // When the source is not blackboxed there are no blackboxed lines + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackBoxedLines: null, + nonBlackBoxedLines: [3, 4], + blackboxedSourceState: SOURCE_IS_NOT_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackboxedSourceState: SOURCE_IS_NOT_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: null, + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), + }); + + info( + "Blackbox the whole simple4.js source file using the editor context menu" + ); + await openContextMenuInDebugger(dbg, "CodeMirrorLines"); + await selectBlackBoxContextMenuItem(dbg, "blackbox"); + + info("Assert that all lines in the source are styled correctly"); + assertIgnoredStyleInSourceLines(dbg, { hasBlackboxedLinesClass: true }); + + info("Assert that the source tree for simple4.js has the ignored style"); + const node = findSourceNodeWithText(dbg, "simple4.js"); + ok( + node.querySelector(".blackboxed"), + "simple4.js node does not have the ignored style" + ); + + invokeInTab("funcA"); + + info( + "The debugger statement on line 2 and the breakpoint on line 8 should not be hit" + ); + assertNotPaused(dbg); + + info("Assert the blackbox context menu items after blackboxing is done"); + // When the whole source is blackboxed there are no nonBlackboxed lines + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 2, + nonBlackBoxedLine: null, + blackBoxedLines: [3, 5], + nonBlackBoxedLines: null, + blackboxedSourceState: SOURCE_IS_FULLY_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 2, + nonBlackBoxedLine: null, + blackboxedSourceState: SOURCE_IS_FULLY_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); + + info("Unblackbox the whole source using the sourcetree context menu"); + rightClickEl(dbg, findSourceNodeWithText(dbg, "simple4.js")); + await waitForContextMenu(dbg); + await selectBlackBoxContextMenuItem(dbg, "blackbox"); + + info("Assert that all lines in the source are un-styled correctly"); + assertIgnoredStyleInSourceLines(dbg, { hasBlackboxedLinesClass: false }); + + info( + "Assert that the source tree for simple4.js does not have the ignored style" + ); + const nodeAfterBlackbox = findSourceNodeWithText(dbg, "simple4.js"); + ok( + !nodeAfterBlackbox.querySelector(".blackboxed"), + "simple4.js node still has the ignored style" + ); + + invokeInTab("funcA"); + + info("assert the pause at the debugger statement on line 2"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resume(dbg); + + info("assert the pause at the breakpoint set on line 8"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 8); + await resumeAndWaitForPauseCounter(dbg); + + assertNotPaused(dbg); + + // When the source is not blackboxed there are no blackboxed lines + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackBoxedLines: null, + nonBlackBoxedLines: [3, 4], + blackboxedSourceState: SOURCE_IS_NOT_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackboxedSourceState: SOURCE_IS_NOT_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: null, + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); +} + +async function testBlackBoxMultipleLines(dbg, source) { + info("Blackbox lines 7 to 13 using the editor content menu items"); + await selectEditorLinesAndOpenContextMenu(dbg, { startLine: 7, endLine: 13 }); + await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); + + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 3, + blackBoxedLines: [7, 9], + nonBlackBoxedLines: [3, 4], + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 3, + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); + + info("Assert that the ignored lines are styled correctly"); + assertIgnoredStyleInSourceLines(dbg, { + lines: [7, 13], + hasBlackboxedLinesClass: true, + }); + + info("Assert that the source tree for simple4.js has the ignored style"); + const node = findSourceNodeWithText(dbg, "simple4.js"); + + ok( + node.querySelector(".blackboxed"), + "simple4.js node does not have the ignored style" + ); + + invokeInTab("funcA"); + + info("assert the pause at the debugger statement on line 2"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resumeAndWaitForPauseCounter(dbg); + + info( + "The breakpoint set on line 8 should not get hit as its within the blackboxed range" + ); + assertNotPaused(dbg); + + info("Unblackbox lines 7 to 13"); + await selectEditorLinesAndOpenContextMenu(dbg, { startLine: 7, endLine: 13 }); + await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); + + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackBoxedLines: null, + nonBlackBoxedLines: [3, 4], + blackboxedSourceState: SOURCE_IS_NOT_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackboxedSourceState: SOURCE_IS_NOT_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: null, + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); + + info("Assert that the un-ignored lines are no longer have the style"); + assertIgnoredStyleInSourceLines(dbg, { + lines: [7, 13], + hasBlackboxedLinesClass: false, + }); + + info( + "Assert that the source tree for simple4.js does not have the ignored style" + ); + const nodeAfterBlackbox = findSourceNodeWithText(dbg, "simple4.js"); + ok( + !nodeAfterBlackbox.querySelector(".blackboxed"), + "simple4.js still has the ignored style" + ); + + invokeInTab("funcA"); + + // assert the pause at the debugger statement on line 2 + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resume(dbg); + + // assert the pause at the breakpoint set on line 8 + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 8); + await resumeAndWaitForPauseCounter(dbg); + + assertNotPaused(dbg); +} + +async function testBlackBoxSingleLine(dbg, source) { + info("Black box line 2 of funcA() with the debugger statement"); + await openContextMenuInDebugger(dbg, "gutter", 2); + await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); + + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 2, + nonBlackBoxedLine: 4, + blackBoxedLines: null, + nonBlackBoxedLines: [3, 4], + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: null, + nonBlackBoxedLine: 4, + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple4.js"), + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); + + info("Assert that the ignored line 2 is styled correctly"); + assertIgnoredStyleInSourceLines(dbg, { + lines: [2], + hasBlackboxedLinesClass: true, + }); + + info("Black box line 4 of funcC() with the debugger statement"); + await openContextMenuInDebugger(dbg, "gutter", 4); + await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); + + info("Assert that the ignored line 4 is styled correctly"); + assertIgnoredStyleInSourceLines(dbg, { + lines: [4], + hasBlackboxedLinesClass: true, + }); + + invokeInTab("funcA"); + + // assert the pause at the breakpoint set on line 8 + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 8); + await resumeAndWaitForPauseCounter(dbg); + + assertNotPaused(dbg); + + info("Un-blackbox line 2 of funcA()"); + selectEditorLines(dbg, 2, 2); + await openContextMenuInDebugger(dbg, "CodeMirrorLines"); + await selectBlackBoxContextMenuItem(dbg, "blackbox-line"); + + await assertEditorBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 4, + nonBlackBoxedLine: 3, + blackBoxedLines: null, + nonBlackBoxedLines: [11, 12], + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertGutterBlackBoxBoxContextMenuItems(dbg, { + blackboxedLine: 4, + nonBlackBoxedLine: 3, + blackboxedSourceState: SOURCE_LINES_ARE_IGNORED, + }); + + await assertSourceTreeBlackBoxBoxContextMenuItems(dbg, { + blackBoxedSourceTreeNode: null, + nonBlackBoxedSourceTreeNode: findSourceNodeWithText(dbg, "simple2.js"), + }); + + info("Assert that the un-ignored line 2 is styled correctly"); + assertIgnoredStyleInSourceLines(dbg, { + lines: [2], + hasBlackboxedLinesClass: false, + }); + + invokeInTab("funcA"); + + // assert the pause at the debugger statement on line 2 + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resume(dbg); + + // assert the pause at the breakpoint set on line 8 + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 8); + await resume(dbg); + + assertNotPaused(dbg); +} + +// Resume and wait for the thread context's `pauseCounter` to get +// updated. +async function resumeAndWaitForPauseCounter(dbg) { + const prevThreadPauseCounter = getThreadContext(dbg).pauseCounter; + await resume(dbg); + return waitFor( + () => getThreadContext(dbg).pauseCounter > prevThreadPauseCounter + ); +} + +async function assertContextMenuDisabled(dbg, selector, shouldBeDisabled) { + const item = await waitFor(() => findContextMenu(dbg, selector)); + is( + item.disabled, + shouldBeDisabled, + `The the context menu item is ${ + shouldBeDisabled ? "disabled" : "not disabled" + }` + ); +} + +/** + * Asserts that the gutter blackbox context menu items which are visible are correct + * @params {Object} dbg + * @params {Array} testFixtures + * Details needed for the assertion. Any blackboxed/nonBlackboxed lines + * and any blackboxed/nonBlackboxed sources + */ +async function assertGutterBlackBoxBoxContextMenuItems(dbg, testFixtures) { + const { blackboxedLine, nonBlackBoxedLine, blackboxedSourceState } = + testFixtures; + if (blackboxedLine) { + info( + "Asserts that the gutter context menu items when clicking on the gutter of a blackboxed line" + ); + const popup = await openContextMenuInDebugger( + dbg, + "gutter", + blackboxedLine + ); + // When the whole source is blackboxed the the gutter visually shows `ignore line` + // but it is disabled indicating that individual lines cannot be nonBlackboxed. + const item = + blackboxedSourceState == SOURCE_IS_FULLY_IGNORED + ? contextMenuItems.ignoreLine + : contextMenuItems.unignoreLine; + + await assertContextMenuLabel(dbg, item.selector, item.label); + await assertContextMenuDisabled( + dbg, + item.selector, + blackboxedSourceState == SOURCE_IS_FULLY_IGNORED + ); + await closeContextMenu(dbg, popup); + } + + if (nonBlackBoxedLine) { + info( + "Asserts that the gutter context menu items when clicking on the gutter of a nonBlackboxed line" + ); + const popup = await openContextMenuInDebugger( + dbg, + "gutter", + nonBlackBoxedLine + ); + const item = contextMenuItems.ignoreLine; + await assertContextMenuLabel(dbg, item.selector, item.label); + await assertContextMenuDisabled(dbg, item.selector, false); + await closeContextMenu(dbg, popup); + } +} + +/** + * Asserts that the source tree blackbox context menu items which are visible are correct + * @params {Object} dbg + * @params {Array} testFixtures + * Details needed for the assertion. Any blackboxed/nonBlackboxed sources + */ +async function assertSourceTreeBlackBoxBoxContextMenuItems(dbg, testFixtures) { + const { blackBoxedSourceTreeNode, nonBlackBoxedSourceTreeNode } = + testFixtures; + if (blackBoxedSourceTreeNode) { + info( + "Asserts that the source tree blackbox context menu items when clicking on a blackboxed source tree node" + ); + rightClickEl(dbg, blackBoxedSourceTreeNode); + const popup = await waitForContextMenu(dbg); + const item = contextMenuItems.unignoreSource; + await assertContextMenuLabel(dbg, item.selector, item.label); + await closeContextMenu(dbg, popup); + } + + if (nonBlackBoxedSourceTreeNode) { + info( + "Asserts that the source tree blackbox context menu items when clicking on an un-blackboxed sorce tree node" + ); + rightClickEl(dbg, nonBlackBoxedSourceTreeNode); + const popup = await waitForContextMenu(dbg); + const _item = contextMenuItems.ignoreSource; + await assertContextMenuLabel(dbg, _item.selector, _item.label); + await closeContextMenu(dbg, popup); + } +} + +/** + * Asserts that the editor blackbox context menu items which are visible are correct + * @params {Object} dbg + * @params {Array} testFixtures + * Details needed for the assertion. Any blackboxed/nonBlackboxed lines + * and any blackboxed/nonBlackboxed sources + */ +async function assertEditorBlackBoxBoxContextMenuItems(dbg, testFixtures) { + const { + blackboxedLine, + nonBlackBoxedLine, + blackBoxedLines, + nonBlackBoxedLines, + blackboxedSourceState, + } = testFixtures; + + if (blackboxedLine) { + info( + "Asserts the editor blackbox context menu items when right-clicking on a single blackboxed line" + ); + const popup = await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: blackboxedLine, + }); + + const expectedContextMenuItems = [contextMenuItems.unignoreSource]; + + if (blackboxedSourceState !== SOURCE_IS_FULLY_IGNORED) { + expectedContextMenuItems.push(contextMenuItems.unignoreLine); + } + + for (const expectedContextMenuItem of expectedContextMenuItems) { + info( + "Checking context menu item " + + expectedContextMenuItem.selector + + " with label " + + expectedContextMenuItem.label + ); + await assertContextMenuLabel( + dbg, + expectedContextMenuItem.selector, + expectedContextMenuItem.label + ); + } + await closeContextMenu(dbg, popup); + } + + if (nonBlackBoxedLine) { + info( + "Asserts the editor blackbox context menu items when right-clicking on a single non-blackboxed line" + ); + const popup = await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: nonBlackBoxedLine, + }); + + const expectedContextMenuItems = [ + blackboxedSourceState == SOURCE_IS_NOT_IGNORED + ? contextMenuItems.ignoreSource + : contextMenuItems.unignoreSource, + contextMenuItems.ignoreLine, + ]; + + for (const expectedContextMenuItem of expectedContextMenuItems) { + info( + "Checking context menu item " + + expectedContextMenuItem.selector + + " with label " + + expectedContextMenuItem.label + ); + await assertContextMenuLabel( + dbg, + expectedContextMenuItem.selector, + expectedContextMenuItem.label + ); + } + await closeContextMenu(dbg, popup); + } + + if (blackBoxedLines) { + info( + "Asserts the editor blackbox context menu items when right-clicking on multiple blackboxed lines" + ); + const popup = await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: blackBoxedLines[0], + endLine: blackBoxedLines[1], + }); + + const expectedContextMenuItems = [contextMenuItems.unignoreSource]; + + if (blackboxedSourceState !== SOURCE_IS_FULLY_IGNORED) { + expectedContextMenuItems.push(contextMenuItems.unignoreLines); + } + + for (const expectedContextMenuItem of expectedContextMenuItems) { + info( + "Checking context menu item " + + expectedContextMenuItem.selector + + " with label " + + expectedContextMenuItem.label + ); + await assertContextMenuLabel( + dbg, + expectedContextMenuItem.selector, + expectedContextMenuItem.label + ); + } + await closeContextMenu(dbg, popup); + } + + if (nonBlackBoxedLines) { + info( + "Asserts the editor blackbox context menu items when right-clicking on multiple non-blackboxed lines" + ); + const popup = await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: nonBlackBoxedLines[0], + endLine: nonBlackBoxedLines[1], + }); + + const expectedContextMenuItems = [ + blackboxedSourceState == SOURCE_IS_NOT_IGNORED + ? contextMenuItems.ignoreSource + : contextMenuItems.unignoreSource, + ]; + + if (blackboxedSourceState !== SOURCE_IS_FULLY_IGNORED) { + expectedContextMenuItems.push(contextMenuItems.ignoreLines); + } + + for (const expectedContextMenuItem of expectedContextMenuItems) { + info( + "Checking context menu item " + + expectedContextMenuItem.selector + + " with label " + + expectedContextMenuItem.label + ); + await assertContextMenuLabel( + dbg, + expectedContextMenuItem.selector, + expectedContextMenuItem.label + ); + } + await closeContextMenu(dbg, popup); + } +} + +/** + * Selects a range of lines + * @param {Object} dbg + * @param {Number} startLine + * @param {Number} endLine + */ +function selectEditorLines(dbg, startLine, endLine) { + getCM(dbg).setSelection( + { line: startLine - 1, ch: 0 }, + { line: endLine, ch: 0 } + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breaking-from-console.js b/devtools/client/debugger/test/mochitest/browser_dbg-breaking-from-console.js new file mode 100644 index 0000000000..3a29506002 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breaking-from-console.js @@ -0,0 +1,38 @@ +/* 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 . */ + +"use strict"; + +// Tests that `debugger` statements are hit before the debugger even +// initializes and it properly highlights the right location in the +// debugger. + +add_task(async function () { + const url = `${EXAMPLE_URL}doc-script-switching.html`; + const toolbox = await openNewTabAndToolbox(url, "webconsole"); + + // Type "debugger" into console + const wrapper = toolbox.getPanel("webconsole").hud.ui.wrapper; + const onSelected = toolbox.once("jsdebugger-selected"); + wrapper.dispatchEvaluateExpression("debugger"); + + // Wait for the debugger to be selected and make sure it's paused + await onSelected; + is(toolbox.threadFront.state, "paused"); + + // Create a dbg context + const dbg = createDebuggerContext(toolbox); + + // Make sure the thread is paused in the right source and location + await waitForPaused(dbg); + const selectedSource = dbg.selectors.getSelectedSource(); + ok( + !selectedSource.url, + "The selected source is the console evaluation and doesn't have a URL" + ); + is(getCM(dbg).getValue(), "debugger"); + assertPausedAtSourceAndLine(dbg, selectedSource.id, 1); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breaking.js b/devtools/client/debugger/test/mochitest/browser_dbg-breaking.js new file mode 100644 index 0000000000..59594aea07 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breaking.js @@ -0,0 +1,55 @@ +/* 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 . */ + +"use strict"; + +// Tests the breakpoints are hit in various situations. + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + const { + selectors: { getSelectedSource }, + } = dbg; + + await selectSource(dbg, "doc-scripts.html"); + + // Make sure we can set a top-level breakpoint and it will be hit on + // reload. + await addBreakpoint(dbg, "doc-scripts.html", 21); + + const onReloaded = reload(dbg, "doc-scripts.html"); + + await waitForPaused(dbg); + + let whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is(whyPaused, "Paused on breakpoint"); + + assertPausedAtSourceAndLine(dbg, findSource(dbg, "doc-scripts.html").id, 21); + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; + + info("Create an eval script that pauses itself."); + invokeInTab("doEval"); + await waitForPaused(dbg); + const source = getSelectedSource(); + ok(!source.url, "It is an eval source"); + assertPausedAtSourceAndLine(dbg, source.id, 2); + + whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is(whyPaused, "Paused on debugger statement"); + + await resume(dbg); + + await addBreakpoint(dbg, source, 5); + invokeInTab("evaledFunc"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 5); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping-console.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping-console.js new file mode 100644 index 0000000000..73acdce3c1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping-console.js @@ -0,0 +1,20 @@ +/* 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 . */ + +/* + * Test that debugger statements are skipped when + * skip pausing is enabled. + */ + +"use strict"; + +add_task(async function () { + const toolbox = await initPane("doc-scripts.html", "webconsole", [ + ["devtools.debugger.skip-pausing", true], + ]); + await navigateTo(`${EXAMPLE_URL}doc-debugger-statements.html`); + + await hasConsoleMessage({ toolbox }, "done!"); + ok(true, "We reached the end"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping.js new file mode 100644 index 0000000000..c766a0e549 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoint-skipping.js @@ -0,0 +1,87 @@ +/* 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 . */ + +/* + * Tests toggling the skip pausing button and + * invoking functions without pausing. + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + await selectSource(dbg, "simple3.js"); + + info("Adding a breakpoint should remove the skipped pausing state"); + await skipPausing(dbg); + await waitForState(dbg, state => dbg.selectors.getSkipPausing()); + await addBreakpoint(dbg, "simple3.js", 2); + await waitForState(dbg, state => !dbg.selectors.getSkipPausing()); + invokeInTab("simple"); + await waitForPaused(dbg); + ok(true, "The breakpoint has been hit after a breakpoint was created"); + await resume(dbg); + + info("Toggling a breakpoint should remove the skipped pausing state"); + // First disable the breakpoint to ensure skip pausing gets turned off + // during a disable + await skipPausing(dbg); + await disableBreakpoint(dbg, 0); + await waitForState(dbg, state => !dbg.selectors.getSkipPausing()); + // Then re-enable the breakpoint to ensure skip pausing gets turned off + // during an enable + await skipPausing(dbg); + await waitForState(dbg, state => dbg.selectors.getSkipPausing()); + toggleBreakpoint(dbg, 0); + await waitForState(dbg, state => !dbg.selectors.getSkipPausing()); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + invokeInTab("simple"); + await waitForPaused(dbg); + ok(true, "The breakpoint has been hit after skip pausing was disabled"); + await resume(dbg); + + info("Disabling a breakpoint should remove the skipped pausing state"); + await addBreakpoint(dbg, "simple3.js", 3); + await skipPausing(dbg); + await disableBreakpoint(dbg, 0); + await waitForState(dbg, state => !dbg.selectors.getSkipPausing()); + invokeInTab("simple"); + await waitForPaused(dbg); + ok(true, "The breakpoint has been hit after skip pausing was disabled again"); + await resume(dbg); + + info("Removing a breakpoint should remove the skipped pause state"); + toggleBreakpoint(dbg, 0); + await skipPausing(dbg); + const source = findSource(dbg, "simple3.js"); + removeBreakpoint(dbg, source.id, 3); + const wait = waitForDispatch(dbg.store, "TOGGLE_SKIP_PAUSING"); + await waitForState(dbg, state => !dbg.selectors.getSkipPausing()); + await wait; + invokeInTab("simple"); + await waitForPaused(dbg); + // Unfortunately required as the test harness throws if preview doesn't + // complete before the end of the test. + await waitForDispatch(dbg.store, "ADD_INLINE_PREVIEW"); + ok(true, "Breakpoint is hit after a breakpoint was removed"); + await resume(dbg); +}); + +function skipPausing(dbg) { + clickElementWithSelector(dbg, ".command-bar-skip-pausing"); + return waitForState(dbg, state => dbg.selectors.getSkipPausing()); +} + +function toggleBreakpoint(dbg, index) { + const breakpoints = findAllElements(dbg, "breakpointItems"); + const bp = breakpoints[index]; + const input = bp.querySelector("input"); + input.click(); +} + +async function disableBreakpoint(dbg, index) { + const disabled = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + toggleBreakpoint(dbg, index); + await disabled; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-actions.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-actions.js new file mode 100644 index 0000000000..900c55e7fa --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-actions.js @@ -0,0 +1,84 @@ +/* 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 . */ + +"use strict"; + +// Tests to see if we can trigger a breakpoint action via the context menu +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple2.js"); + await selectSource(dbg, "simple2.js"); + await waitForSelectedSource(dbg, "simple2.js"); + + await addBreakpoint(dbg, "simple2.js", 3); + + await openFirstBreakpointContextMenu(dbg); + // select "Remove breakpoint" + selectContextMenuItem(dbg, selectors.breakpointContextMenu.remove); + + await waitForState(dbg, state => dbg.selectors.getBreakpointCount() === 0); + ok(true, "successfully removed the breakpoint"); +}); + +// Tests "disable others", "enable others" and "remove others" context actions +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + await selectSource(dbg, "simple1.js"); + await waitForSelectedSource(dbg, "simple1.js"); + + await addBreakpoint(dbg, "simple1.js", 4); + await addBreakpoint(dbg, "simple1.js", 5); + await addBreakpoint(dbg, "simple1.js", 6); + + await openFirstBreakpointContextMenu(dbg); + // select "Disable Others" + let dispatched = waitForDispatch(dbg.store, "SET_BREAKPOINT", 2); + selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableOthers); + await waitForState(dbg, state => + dbg.selectors + .getBreakpointsList() + .every(bp => (bp.location.line !== 4) === bp.disabled) + ); + await dispatched; + ok(true, "breakpoint at 4 is the only enabled breakpoint"); + + await openFirstBreakpointContextMenu(dbg); + // select "Disable All" + dispatched = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + selectContextMenuItem(dbg, selectors.breakpointContextMenu.disableAll); + await waitForState(dbg, state => + dbg.selectors.getBreakpointsList().every(bp => bp.disabled) + ); + await dispatched; + ok(true, "all breakpoints are disabled"); + + await openFirstBreakpointContextMenu(dbg); + // select "Enable Others" + dispatched = waitForDispatch(dbg.store, "SET_BREAKPOINT", 2); + selectContextMenuItem(dbg, selectors.breakpointContextMenu.enableOthers); + await waitForState(dbg, state => + dbg.selectors + .getBreakpointsList() + .every(bp => (bp.location.line === 4) === bp.disabled) + ); + await dispatched; + ok(true, "all breakpoints except line 1 are enabled"); + + await openFirstBreakpointContextMenu(dbg); + // select "Remove Others" + dispatched = waitForDispatch(dbg.store, "REMOVE_BREAKPOINT", 2); + selectContextMenuItem(dbg, selectors.breakpointContextMenu.removeOthers); + await waitForState( + dbg, + state => + dbg.selectors.getBreakpointsList().length === 1 && + dbg.selectors.getBreakpointsList()[0].location.line === 4 + ); + await dispatched; + ok(true, "remaining breakpoint should be on line 4"); +}); + +async function openFirstBreakpointContextMenu(dbg) { + rightClickElement(dbg, "breakpointItem", 2); + await waitForContextMenu(dbg); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-columns.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-columns.js new file mode 100644 index 0000000000..2caf8f9142 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-columns.js @@ -0,0 +1,131 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + await selectSource(dbg, "long.js"); + + info("1. Add a column breakpoint on line 32"); + await enableFirstBreakpoint(dbg); + + info("2. Click on the second breakpoint on line 32"); + await enableSecondBreakpoint(dbg); + + info("3. Disable second breakpoint using shift-click"); + await shiftClickDisable(dbg); + + info("4. Re-enable second breakpoint using shift-click"); + await shiftClickEnable(dbg); + + info("5. Add a condition to the first breakpoint"); + await setConditionalBreakpoint(dbg, 0, "foo"); + + info("6. Add a log to the first breakpoint"); + await setLogPoint(dbg, 0, "bar"); + + info("7. Disable the first breakpoint"); + await disableBreakpoint(dbg, 0); + + info("8. Remove the first breakpoint"); + await removeFirstBreakpoint(dbg); + + info("9. Add a condition to the second breakpoint"); + await setConditionalBreakpoint(dbg, 1, "foo2"); + + info("10. Test removing the breakpoints by clicking in the gutter"); + await removeAllBreakpoints(dbg, 32, 0); +}); + +async function enableFirstBreakpoint(dbg) { + getCM(dbg).setCursor({ line: 32, ch: 0 }); + await addBreakpoint(dbg, "long.js", 32); + const bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + + ok(bpMarkers.length === 2, "2 column breakpoints"); + assertClass(bpMarkers[0], "active"); + assertClass(bpMarkers[1], "active", false); +} + +async function enableSecondBreakpoint(dbg) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + + bpMarkers[1].click(); + await waitForBreakpointCount(dbg, 2); + + bpMarkers = findAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[1], "active"); + await waitForAllElements(dbg, "breakpointItems", 2); +} + +// disable active column bp with shift-click. +async function shiftClickDisable(dbg) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + shiftClickElement(dbg, "columnBreakpoints"); + bpMarkers = findAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[0], "disabled"); +} + +// re-enable disabled column bp with shift-click. +async function shiftClickEnable(dbg) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + shiftClickElement(dbg, "columnBreakpoints"); + bpMarkers = findAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[0], "active"); +} + +async function setConditionalBreakpoint(dbg, index, condition) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + rightClickEl(dbg, bpMarkers[index]); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, selectors.addConditionItem); + await typeInPanel(dbg, condition); + await waitForCondition(dbg, condition); + + bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[index], "has-condition"); +} + +async function setLogPoint(dbg, index, expression) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + rightClickEl(dbg, bpMarkers[index]); + await waitForContextMenu(dbg); + + selectContextMenuItem(dbg, selectors.addLogItem); + await typeInPanel(dbg, expression); + await waitForLog(dbg, expression); + + bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[index], "has-log"); +} + +async function disableBreakpoint(dbg, index) { + rightClickElement(dbg, "columnBreakpoints"); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, selectors.disableItem); + + await waitForState(dbg, state => { + const bp = dbg.selectors.getBreakpointsList()[index]; + return bp.disabled; + }); + + const bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[0], "disabled"); +} + +async function removeFirstBreakpoint(dbg) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + + bpMarkers[0].click(); + bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[0], "active", false); +} + +async function removeAllBreakpoints(dbg, line, count) { + await clickGutter(dbg, 32); + await waitForBreakpointCount(dbg, 0); + + ok(!findAllElements(dbg, "columnBreakpoints").length); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-shortcut.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-shortcut.js new file mode 100644 index 0000000000..7976a091a9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-shortcut.js @@ -0,0 +1,147 @@ +/* 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 . */ + +// Test opening conditional panel using keyboard shortcut. +// Should access the closest breakpoint to a passed in cursorPosition. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "long.js"); + + await selectSource(dbg, "long.js"); + await waitForSelectedSource(dbg, "long.js"); + + info( + "toggle conditional panel with shortcut: no breakpoints, default cursorPosition" + ); + pressKey(dbg, "toggleCondPanel"); + await waitForConditionalPanelFocus(dbg); + ok( + !!getConditionalPanel(dbg, 1), + "conditional panel panel is open on line 1" + ); + is( + dbg.selectors.getConditionalPanelLocation().line, + 1, + "conditional panel location is line 1" + ); + info("close conditional panel"); + pressKey(dbg, "Escape"); + + info( + "toggle conditional panel with shortcut: cursor on line 32, no breakpoints" + ); + // codemirror editor offset: cursorPosition will be line + 1, column + 1 + getCM(dbg).setCursor({ line: 31, ch: 1 }); + pressKey(dbg, "toggleCondPanel"); + + await waitForConditionalPanelFocus(dbg); + ok( + !!getConditionalPanel(dbg, 32), + "conditional panel panel is open on line 32" + ); + is( + dbg.selectors.getConditionalPanelLocation().line, + 32, + "conditional panel location is line 32" + ); + info("close conditional panel"); + pressKey(dbg, "Escape"); + + info("add active column breakpoint on line 32 and set cursorPosition"); + await enableFirstBreakpoint(dbg); + getCM(dbg).setCursor({ line: 31, ch: 1 }); + info( + "toggle conditional panel with shortcut and add condition to first breakpoint" + ); + setConditionalBreakpoint(dbg, "1"); + await waitForCondition(dbg, 1); + const firstBreakpoint = findColumnBreakpoint(dbg, "long.js", 32, 2); + is( + firstBreakpoint.options.condition, + "1", + "first breakpoint created with condition using shortcut" + ); + + info("set cursor at second breakpoint position and activate breakpoint"); + getCM(dbg).setCursor({ line: 31, ch: 25 }); + + await enableSecondBreakpoint(dbg); + info( + "toggle conditional panel with shortcut and add condition to second breakpoint" + ); + setConditionalBreakpoint(dbg, "2"); + await waitForCondition(dbg, 2); + const secondBreakpoint = findColumnBreakpoint(dbg, "long.js", 32, 26); + is( + secondBreakpoint.options.condition, + "2", + "second breakpoint created with condition using shortcut" + ); + + info( + "set cursor position near first breakpoint, toggle conditional panel and edit breakpoint" + ); + getCM(dbg).setCursor({ line: 31, ch: 7 }); + info("toggle conditional panel and edit condition using shortcut"); + setConditionalBreakpoint(dbg, "2"); + ok( + !!waitForCondition(dbg, "12"), + "breakpoint closest to cursor position has been edited" + ); + + info("close conditional panel"); + pressKey(dbg, "Escape"); + + info( + "set cursor position near second breakpoint, toggle conditional panel and edit breakpoint" + ); + getCM(dbg).setCursor({ line: 31, ch: 21 }); + info("toggle conditional panel and edit condition using shortcut"); + setConditionalBreakpoint(dbg, "3"); + ok( + !!waitForCondition(dbg, "13"), + "breakpoint closest to cursor position has been edited" + ); +}); + +// from test/mochitest/browser_dbg-breakpoints-cond-source-maps.js +function getConditionalPanel(dbg, line) { + return getCM(dbg).doc.getLineHandle(line - 1).widgets[0]; +} + +// from devtools browser_dbg-breakpoints-cond-source-maps.js +async function waitForConditionalPanelFocus(dbg) { + await waitFor(() => dbg.win.document.activeElement.tagName === "TEXTAREA"); +} + +// from browser_dbg-breakpoints-columns.js +async function enableFirstBreakpoint(dbg) { + getCM(dbg).setCursor({ line: 32, ch: 0 }); + await addBreakpoint(dbg, "long.js", 32); + const bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + + ok(bpMarkers.length === 2, "2 column breakpoints"); + assertClass(bpMarkers[0], "active"); + assertClass(bpMarkers[1], "active", false); +} + +async function enableSecondBreakpoint(dbg) { + let bpMarkers = await waitForAllElements(dbg, "columnBreakpoints"); + + bpMarkers[1].click(); + await waitForBreakpointCount(dbg, 2); + + bpMarkers = findAllElements(dbg, "columnBreakpoints"); + assertClass(bpMarkers[1], "active"); + await waitForAllElements(dbg, "breakpointItems", 2); +} + +// modified method from browser_dbg-breakpoints-columns.js +// use shortcut to open conditional panel. +function setConditionalBreakpoint(dbg, condition) { + pressKey(dbg, "toggleCondPanel"); + typeInPanel(dbg, condition); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-source-maps.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-source-maps.js new file mode 100644 index 0000000000..3e9a9d5087 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond-source-maps.js @@ -0,0 +1,45 @@ +/* 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 . */ + +// Confirms that a conditional panel is opened at the +// correct location in generated files. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-sourcemaps.html", "entry.js"); + + await selectSource(dbg, "bundle.js"); + getCM(dbg).scrollIntoView({ line: 55, ch: 0 }); + + await setLogPoint(dbg, 55); + await waitForConditionalPanelFocus(dbg); + ok( + !!getConditionalPanel(dbg, 55), + "conditional panel panel is open on line 55" + ); + is( + dbg.selectors.getConditionalPanelLocation().line, + 55, + "conditional panel location is line 55" + ); +}); + +async function setLogPoint(dbg, index) { + const gutterEl = await getEditorLineGutter(dbg, index); + rightClickEl(dbg, gutterEl); + await waitForContextMenu(dbg); + selectContextMenuItem( + dbg, + `${selectors.addLogItem},${selectors.editLogItem}` + ); +} + +async function waitForConditionalPanelFocus(dbg) { + await waitFor(() => dbg.win.document.activeElement.tagName === "TEXTAREA"); +} + +function getConditionalPanel(dbg, line) { + return getCM(dbg).doc.getLineHandle(line - 1).widgets[0]; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond.js new file mode 100644 index 0000000000..9a3f5f5611 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-cond.js @@ -0,0 +1,136 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple2.js"); + + await selectSource(dbg, "simple2.js"); + await waitForSelectedSource(dbg, "simple2.js"); + + info("Set condition `1`"); + await setConditionalBreakpoint(dbg, 5, "1"); + await waitForCondition(dbg, 1); + + let bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.condition, "1", "breakpoint is created with the condition"); + await assertConditionBreakpoint(dbg, 5); + + info("Edit the conditional breakpoint set above"); + await setConditionalBreakpoint(dbg, 5, "2"); + await waitForCondition(dbg, 12); + + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.condition, "12", "breakpoint is created with the condition"); + await assertConditionBreakpoint(dbg, 5); + + info("Hit 'Enter' when the cursor is in the conditional statement"); + rightClickElement(dbg, "gutter", 5); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, `${selectors.editConditionItem}`); + await waitForConditionalPanelFocus(dbg); + pressKey(dbg, "Left"); + pressKey(dbg, "Enter"); + await waitForCondition(dbg, 12); + + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.condition, "12", "Hit 'Enter' doesn't add a new line"); + + info("Hit 'Alt+Enter' when the cursor is in the conditional statement"); + rightClickElement(dbg, "gutter", 5); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, `${selectors.editConditionItem}`); + await waitForConditionalPanelFocus(dbg); + pressKey(dbg, "Left"); + pressKey(dbg, "AltEnter"); + pressKey(dbg, "Enter"); + await waitForCondition(dbg, "1\n2"); + + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.condition, "1\n2", "Hit 'Alt+Enter' adds a new line"); + + clickElement(dbg, "gutter", 5); + await waitForDispatch(dbg.store, "REMOVE_BREAKPOINT"); + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp, undefined, "breakpoint was removed"); + await assertNoBreakpoint(dbg, 5); + + info("Adding a condition to a breakpoint"); + clickElement(dbg, "gutter", 5); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + await setConditionalBreakpoint(dbg, 5, "1"); + await waitForCondition(dbg, 1); + + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.condition, "1", "breakpoint is created with the condition"); + await assertConditionBreakpoint(dbg, 5); + + info("Double click the conditional breakpoint in secondary pane"); + dblClickElement(dbg, "conditionalBreakpointInSecPane"); + is( + dbg.win.document.activeElement.tagName, + "TEXTAREA", + "The textarea of conditional breakpoint panel is focused" + ); + + info("Click the conditional breakpoint in secondary pane"); + await clickElement(dbg, "conditionalBreakpointInSecPane"); + const conditonalPanel = findElement(dbg, "conditionalPanel"); + is(conditonalPanel, null, "The conditional breakpoint panel is closed"); + + rightClickElement(dbg, "breakpointItem", 2); + await waitForContextMenu(dbg); + info('select "remove condition"'); + selectContextMenuItem(dbg, selectors.breakpointContextMenu.removeCondition); + await waitForBreakpointWithoutCondition(dbg, "simple2.js", 5); + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.condition, null, "breakpoint condition removed"); + + info('Add "log point"'); + await setLogPoint(dbg, 5, "44"); + await waitForLog(dbg, 44); + await assertLogBreakpoint(dbg, 5); + + bp = findBreakpoint(dbg, "simple2.js", 5); + is(bp.options.logValue, "44", "breakpoint condition removed"); + + await altClickElement(dbg, "gutter", 6); + bp = await waitForBreakpoint(dbg, "simple2.js", 6); + is(bp.options.logValue, "displayName", "logPoint has default value"); + + info("Double click the logpoint in secondary pane"); + dblClickElement(dbg, "logPointInSecPane"); + is( + dbg.win.document.activeElement.tagName, + "TEXTAREA", + "The textarea of logpoint panel is focused" + ); + + info("Click the logpoint in secondary pane"); + await clickElement(dbg, "logPointInSecPane"); + const logPointPanel = findElement(dbg, "logPointPanel"); + is(logPointPanel, null, "The logpoint panel is closed"); +}); + +function waitForBreakpointWithoutCondition(dbg, url, line) { + return waitForState(dbg, () => { + const bp = findBreakpoint(dbg, url, line); + return bp && !bp.options.condition; + }); +} + +async function setConditionalBreakpoint(dbg, index, condition) { + // Make this work with either add or edit menu items + const { addConditionItem, editConditionItem } = selectors; + const selector = `${addConditionItem},${editConditionItem}`; + rightClickElement(dbg, "gutter", index); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, selector); + typeInPanel(dbg, condition); +} + +async function waitForConditionalPanelFocus(dbg) { + await waitFor(() => dbg.win.document.activeElement.tagName === "TEXTAREA"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-debugger-statement.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-debugger-statement.js new file mode 100644 index 0000000000..3f1f08a949 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-debugger-statement.js @@ -0,0 +1,94 @@ +/* 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 . */ + +// Test enabling and disabling a debugger statement using editor context menu + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-pause-points.html", "pause-points.js"); + await selectSource(dbg, "pause-points.js"); + await waitForSelectedSource(dbg, "pause-points.js"); + + info("Disable the first debugger statement on line 12 by gutter menu"); + rightClickElement(dbg, "gutter", 12); + await waitForContextMenu(dbg); + selectContextMenuItem( + dbg, + selectors.breakpointContextMenu.disableDbgStatement + ); + await waitForCondition(dbg, "false"); + + let bp = findBreakpoint(dbg, "pause-points.js", 12); + is( + bp.options.condition, + "false", + "The debugger statement has a conditional breakpoint with 'false' as statement" + ); + + info("Enable the previously disabled debugger statement by gutter menu"); + rightClickElement(dbg, "gutter", 12); + await waitForContextMenu(dbg); + selectContextMenuItem( + dbg, + selectors.breakpointContextMenu.enableDbgStatement + ); + await waitForBreakpointWithoutCondition(dbg, "pause-points.js", 12, 0); + + bp = findBreakpoint(dbg, "pause-points.js", 12); + is(bp.options.condition, null, "The conditional statement is removed"); + + info("Enable the breakpoint for the second debugger statement on line 12"); + let bpElements = await waitForAllElements(dbg, "columnBreakpoints"); + ok(bpElements.length === 2, "2 column breakpoints"); + assertClass(bpElements[0], "active"); + assertClass(bpElements[1], "active", false); + + bpElements[1].click(); + await waitForBreakpointCount(dbg, 2); + bpElements = findAllElements(dbg, "columnBreakpoints"); + assertClass(bpElements[1], "active"); + + info("Disable the second debugger statement by breakpoint menu"); + rightClickEl(dbg, bpElements[1]); + await waitForContextMenu(dbg); + selectContextMenuItem( + dbg, + selectors.breakpointContextMenu.disableDbgStatement + ); + await waitForCondition(dbg, "false"); + + bp = findBreakpoints(dbg, "pause-points.js", 12)[1]; + is( + bp.options.condition, + "false", + "The second debugger statement has a conditional breakpoint with 'false' as statement" + ); + + info("Enable the second debugger statement by breakpoint menu"); + bpElements = findAllElements(dbg, "columnBreakpoints"); + assertClass(bpElements[1], "has-condition"); + rightClickEl(dbg, bpElements[1]); + await waitForContextMenu(dbg); + selectContextMenuItem( + dbg, + selectors.breakpointContextMenu.enableDbgStatement + ); + await waitForBreakpointWithoutCondition(dbg, "pause-points.js", 12, 1); + + bp = findBreakpoints(dbg, "pause-points.js", 12)[1]; + is(bp.options.condition, null, "The conditional statement is removed"); +}); + +function waitForBreakpointWithoutCondition(dbg, url, line, index) { + return waitForState(dbg, () => { + const bp = findBreakpoints(dbg, url, line)[index]; + return bp && !bp.options.condition; + }); +} + +function findBreakpoints(dbg, url, line) { + const source = findSource(dbg, url); + return dbg.selectors.getBreakpointsForSource(source.id, line); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-duplicate-functions.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-duplicate-functions.js new file mode 100644 index 0000000000..bb1fa3d64b --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-duplicate-functions.js @@ -0,0 +1,38 @@ +/* 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 . */ + +// Tests to make sure we do not accidentally slide the breakpoint up to the first +// function with the same name in the file. +// TODO: Likely to remove this test when removing the breakpoint sliding functionality + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-duplicate-functions.html", + "doc-duplicate-functions.html" + ); + let source = findSource(dbg, "doc-duplicate-functions.html"); + + await selectSource(dbg, source); + await addBreakpoint(dbg, source, 21); + + await reload(dbg, "doc-duplicate-functions.html"); + + await waitForState(dbg, state => dbg.selectors.getBreakpointCount() == 1); + + const firstBreakpoint = dbg.selectors.getBreakpointsList()[0]; + is(firstBreakpoint.location.line, 21, "Breakpoint is on line 21"); + + // Make sure the breakpoint set on line 19 gets hit + await invokeInTab("b"); + invokeInTab("func"); + await waitForPaused(dbg); + + source = findSource(dbg, "doc-duplicate-functions.html"); + assertPausedAtSourceAndLine(dbg, source.id, 21); + await assertBreakpoint(dbg, 21); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-in-evaled-sources.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-in-evaled-sources.js new file mode 100644 index 0000000000..a4569dbfda --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-in-evaled-sources.js @@ -0,0 +1,92 @@ +/* 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 . */ + +// this evaled source includes a "debugger" statement so that it gets +// automatically opened in the debugger when it is executed. +// We wrap it in a setTimeout to avoid errors in the webconsole actor which +// would still be processing the outcome of the evaluation after we destroy +// the thread actor. + +"use strict"; + +const EVALED_SOURCE_TEXT = `setTimeout(function() { + debugger; + console.log("SECOND LINE"); +}, 10)`; + +/** + * Check against blank debugger panel issues when attempting to restore + * breakpoints set in evaled sources (Bug 1720512). + * + * The STRs triggering this bug require to: + * - set a valid breakpoint on a regular source + * - then set a breakpoint on an evaled source + * - close and reopen the debugger + * + * This test will follow those STRs while also performing a few additional + * checks (eg verify breakpoints can be hit at various stages of the test). + */ +add_task(async function () { + info("Open the debugger and set a breakpoint on a regular script"); + const dbg = await initDebugger("doc-scripts.html"); + await selectSource(dbg, "doc-scripts.html"); + await addBreakpoint(dbg, "doc-scripts.html", 21); + + info("Switch to the console and evaluate a source with a debugger statement"); + const { hud } = await dbg.toolbox.selectTool("webconsole"); + const onSelected = dbg.toolbox.once("jsdebugger-selected"); + await hud.ui.wrapper.dispatchEvaluateExpression(EVALED_SOURCE_TEXT); + + info("Wait for the debugger to be selected"); + await onSelected; + + info("Wait for the debugger to be paused on the debugger statement"); + await waitForPaused(dbg); + + is( + getCM(dbg).getValue(), + EVALED_SOURCE_TEXT, + "The debugger is showing the evaled source" + ); + + const evaledSource = dbg.selectors.getSelectedSource(); + assertPausedAtSourceAndLine(dbg, evaledSource.id, 2); + + info("Add a breakpoint in the evaled source"); + await addBreakpoint(dbg, evaledSource, 3); + + info("Resume and check that we hit the breakpoint"); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, evaledSource.id, 3); + + info("Close the toolbox"); + await dbg.toolbox.closeToolbox(); + + info("Reopen the toolbox on the debugger"); + const toolbox2 = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger"); + const dbg2 = createDebuggerContext(toolbox2); + + // The initial regression tested here led to a blank debugger, + // if we can see the doc-scripts.html source, this already means the debugger + // is functional. + await waitForSources(dbg2, "doc-scripts.html"); + + // Wait until codeMirror is rendered before reloading the debugger. + await waitFor(() => findElement(dbg2, "codeMirror")); + + info("Reload to check if we hit the breakpoint added in doc-scripts.html"); + const onReloaded = reload(dbg2); + + await waitForDispatch(dbg2.store, "NAVIGATE"); + await waitForSelectedSource(dbg2, "doc-scripts.html"); + await waitForPaused(dbg2); + + const scriptSource = dbg2.selectors.getSelectedSource(); + assertPausedAtSourceAndLine(dbg2, scriptSource.id, 21); + await resume(dbg2); + + info("Wait for reload to complete after resume"); + await onReloaded; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-list.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-list.js new file mode 100644 index 0000000000..369f755e79 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-list.js @@ -0,0 +1,189 @@ +/* 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 . */ + +// Testing displaying breakpoints in the breakpoints list and the tooltip +// shows the source url. + +"use strict"; + +add_task(async function testBreakpointsListForMultipleTargets() { + const dbg = await initDebugger( + "doc_dbg-fission-frame-sources.html", + "simple1.js", + "simple2.js" + ); + + info("Add breakpoint to the source (simple1.js) in the main thread"); + await selectSource(dbg, "simple1.js"); + const source1 = findSource(dbg, "simple1.js"); + await addBreakpoint(dbg, "simple1.js", 5); + + info("Add breakpoint to the source (simple2.js) in the frame"); + await selectSource(dbg, "simple2.js"); + const source2 = findSource(dbg, "simple2.js"); + await addBreakpoint(dbg, "simple2.js", 3); + + const breakpointHeadings = findAllElements(dbg, "breakpointHeadings"); + const breakpointItems = findAllElements(dbg, "breakpointItems"); + + is( + breakpointHeadings.length, + 2, + "The breakpoint list shows two breakpoints sources" + ); + is( + breakpointItems.length, + 2, + "The breakpoint list shows only two breakpoints" + ); + + is( + breakpointHeadings[0].title, + source1.url, + "The breakpoint heading tooltip shows the source info for the first breakpoint" + ); + is( + breakpointHeadings[0].textContent, + "simple1.js", + "The info displayed for the breakpoint heading of the 1st breakpoint is correct" + ); + is( + breakpointItems[0].textContent, + "func();5:17", + "The info displayed for the 1st breakpoint is correct" + ); + + is( + breakpointHeadings[1].title, + source2.url, + "The breakpoint heading tooltip shows the source info for the second breakpoint" + ); + is( + breakpointHeadings[1].textContent, + "simple2.js", + "The info displayed for the breakpoint heading of the 2nd breakpoint is correct" + ); + is( + breakpointItems[1].textContent, + "return x + y;3:4", + "The info displayed for the 2nd breakpoint is correct" + ); + + await removeBreakpoint(dbg, source1.id, 5); + await removeBreakpoint(dbg, source2.id, 3); +}); + +add_task(async function testBreakpointsListForOriginalFiles() { + const dbg = await initDebugger("doc-sourcemaps.html", "entry.js"); + + info("Add breakpoint to the entry.js (original source)"); + await selectSource(dbg, "entry.js"); + const source = findSource(dbg, "entry.js"); + await addBreakpoint(dbg, "entry.js", 5); + + const breakpointHeadings = findAllElements(dbg, "breakpointHeadings"); + const breakpointItems = findAllElements(dbg, "breakpointItems"); + + is( + breakpointHeadings.length, + 1, + "The breakpoint list shows one breakpoints sources" + ); + is( + breakpointItems.length, + 1, + "The breakpoint list shows only one breakpoints" + ); + + is( + breakpointHeadings[0].title, + source.url, + "The breakpoint heading tooltip shows the source info for the first breakpoint" + ); + is( + breakpointHeadings[0].textContent, + "entry.js", + "The info displayed for the breakpoint heading of the 1st breakpoint is correct" + ); + is( + breakpointItems[0].textContent, + "output(times2(1));5", + "The info displayed for the 1st breakpoint is correct" + ); + + await removeBreakpoint(dbg, source.id, 5); +}); + +add_task(async function testBreakpointsListForIgnoredLines() { + const dbg = await initDebugger("doc-sourcemaps.html", "entry.js"); + + info("Add breakpoint to the entry.js (original source)"); + await selectSource(dbg, "entry.js"); + await addBreakpoint(dbg, "entry.js", 5); + + info("Ignoring line 5 to 6 which has a breakpoint already set"); + await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: 5, + endLine: 6, + }); + await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); + + info("Assert the breakpoint on the ignored line"); + let breakpointItems = findAllElements(dbg, "breakpointItems"); + is( + breakpointItems[0].textContent, + "output(times2(1));5", + "The info displayed for the 1st breakpoint is correct" + ); + const firstBreakpointCheck = breakpointItems[0].querySelector("input"); + ok( + firstBreakpointCheck.disabled, + "The first breakpoint checkbox on an ignored line is disabled" + ); + ok( + !firstBreakpointCheck.checked, + "The first breakpoint on an ignored line is not checked" + ); + + info("Ignoring line 8 to 9 which currently has not breakpoint"); + await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: 8, + endLine: 9, + }); + await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); + + await addBreakpointViaGutter(dbg, 9); + + breakpointItems = findAllElements(dbg, "breakpointItems"); + is( + breakpointItems[1].textContent, + "output(times2(3));9", + "The info displayed for the 2nd breakpoint is correct" + ); + const secondBreakpointCheck = breakpointItems[1].querySelector("input"); + ok( + secondBreakpointCheck.disabled, + "The second breakpoint checkbox on an ignored line is disabled" + ); + ok( + !secondBreakpointCheck.checked, + "The second breakpoint on an ignored line is not checked" + ); + + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); + + info("Assert that both breakpoints are now enabled"); + breakpointItems = findAllElements(dbg, "breakpointItems"); + [...breakpointItems].forEach(breakpointItem => { + const check = breakpointItem.querySelector("input"); + ok( + !check.disabled, + "The breakpoint checkbox on the unignored line is enabled" + ); + ok(check.checked, "The breakpoint on the unignored line is checked"); + }); + + await dbg.toolbox.closeToolbox(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js new file mode 100644 index 0000000000..0c55538a49 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js @@ -0,0 +1,250 @@ +/* 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 . */ + +// Verify that we hit breakpoints on popups + +"use strict"; + +const TEST_URI = "https://example.org/document-builder.sjs?html=main page"; +const POPUP_URL = `https://example.com/document-builder.sjs?html=${escape(`popup for breakpoints + +`)}`; +const POPUP_DEBUGGER_STATEMENT_URL = `https://example.com/document-builder.sjs?html=${escape(`popup with debugger; + +`)}`; + +function isPopupPaused(popupBrowsingContext) { + return SpecialPowers.spawn(popupBrowsingContext, [], function (url) { + return content.wrappedJSObject.paused; + }); +} + +async function openPopup(popupUrl, browser = gBrowser.selectedBrowser) { + const onPopupTabSelected = BrowserTestUtils.waitForEvent( + gBrowser.tabContainer, + "TabSelect" + ); + const popupBrowsingContext = await SpecialPowers.spawn( + browser, + [popupUrl], + function (url) { + const popup = content.open(url); + return popup.browsingContext; + } + ); + await onPopupTabSelected; + is( + gBrowser.selectedBrowser.browsingContext, + popupBrowsingContext, + "The popup is the selected tab" + ); + return popupBrowsingContext; +} + +async function closePopup(browsingContext) { + const onPreviousTabSelected = BrowserTestUtils.waitForEvent( + gBrowser.tabContainer, + "TabSelect" + ); + await SpecialPowers.spawn(browsingContext, [], function () { + content.close(); + }); + await onPreviousTabSelected; +} + +add_task(async function testPausedByBreakpoint() { + await pushPref("devtools.popups.debug", true); + + info("Test breakpoints set in popup scripts"); + const dbg = await initDebuggerWithAbsoluteURL(TEST_URI); + + info("Open the popup in order to be able to set a breakpoint"); + const firstPopupBrowsingContext = await openPopup(POPUP_URL); + + await waitForSource(dbg, POPUP_URL); + const source = findSource(dbg, POPUP_URL); + + await selectSource(dbg, source); + await addBreakpoint(dbg, source, 4); + + info("Now close and reopen the popup"); + await closePopup(firstPopupBrowsingContext); + + info("Re-open the popup"); + const popupBrowsingContext = await openPopup(POPUP_URL); + await waitForPaused(dbg); + is( + await isPopupPaused(popupBrowsingContext), + true, + "The popup is really paused" + ); + + await waitForSource(dbg, POPUP_URL); + assertPausedAtSourceAndLine(dbg, source.id, 4); + + await resume(dbg); + is( + await isPopupPaused(popupBrowsingContext), + false, + "The popup resumed its execution" + ); +}); + +add_task(async function testPausedByDebuggerStatement() { + info("Test debugger statements in popup scripts"); + const dbg = await initDebuggerWithAbsoluteURL(TEST_URI); + + info("Open a popup with a debugger statement"); + const popupBrowsingContext = await openPopup(POPUP_DEBUGGER_STATEMENT_URL); + await waitForPaused(dbg); + is( + await isPopupPaused(popupBrowsingContext), + true, + "The popup is really paused" + ); + + const source = findSource(dbg, POPUP_DEBUGGER_STATEMENT_URL); + assertPausedAtSourceAndLine(dbg, source.id, 4); + + await resume(dbg); + is( + await isPopupPaused(popupBrowsingContext), + false, + "The popup resumed its execution" + ); +}); + +add_task(async function testPausedInTwoPopups() { + info("Test being paused in two popup at the same time"); + const dbg = await initDebuggerWithAbsoluteURL(TEST_URI); + + info("Open the popup in order to be able to set a breakpoint"); + const browser = gBrowser.selectedBrowser; + const popupBrowsingContext = await openPopup(POPUP_URL); + + await waitForSource(dbg, POPUP_URL); + const source = findSource(dbg, POPUP_URL); + + await selectSource(dbg, source); + await addBreakpoint(dbg, source, 4); + + info("Now close and reopen the popup"); + await closePopup(popupBrowsingContext); + + info("Open a first popup which will hit the breakpoint"); + const firstPopupBrowsingContext = await openPopup(POPUP_URL); + await waitForPaused(dbg); + const { targetCommand } = dbg.commands; + const firstTarget = targetCommand + .getAllTargets([targetCommand.TYPES.FRAME]) + .find(targetFront => targetFront.url == POPUP_URL); + is( + firstTarget.browsingContextID, + firstPopupBrowsingContext.id, + "The popup target matches the popup BrowsingContext" + ); + const firstThread = (await firstTarget.getFront("thread")).actorID; + is( + dbg.selectors.getCurrentThread(), + firstThread, + "The popup thread is automatically selected on pause" + ); + is( + await isPopupPaused(firstPopupBrowsingContext), + true, + "The first popup is really paused" + ); + + info("Open a second popup which will also hit the breakpoint"); + let onAvailable; + const onNewTarget = new Promise(resolve => { + onAvailable = ({ targetFront }) => { + if ( + targetFront.url == POPUP_URL && + targetFront.browsingContextID != firstPopupBrowsingContext.id + ) { + targetCommand.unwatchTargets({ + types: [targetCommand.TYPES.FRAME], + onAvailable, + }); + resolve(targetFront); + } + }; + }); + await targetCommand.watchTargets({ + types: [targetCommand.TYPES.FRAME], + onAvailable, + }); + const secondPopupBrowsingContext = await openPopup(POPUP_URL, browser); + info("Wait for second popup's target"); + const popupTarget = await onNewTarget; + is( + popupTarget.browsingContextID, + secondPopupBrowsingContext.id, + "The new target matches the popup WindowGlobal" + ); + const secondThread = (await popupTarget.getFront("thread")).actorID; + await waitForPausedThread(dbg, secondThread); + is( + dbg.selectors.getCurrentThread(), + secondThread, + "The second popup thread is automatically selected on pause" + ); + is( + await isPopupPaused(secondPopupBrowsingContext), + true, + "The second popup is really paused" + ); + + info("Resume the execution of the second popup"); + await resume(dbg); + is( + await isPopupPaused(secondPopupBrowsingContext), + false, + "The second popup resumed its execution" + ); + is( + await isPopupPaused(firstPopupBrowsingContext), + true, + "The first popup is still paused" + ); + + info("Resume the execution of the first popup"); + await dbg.actions.selectThread(getContext(dbg), firstThread); + await resume(dbg); + is( + await isPopupPaused(firstPopupBrowsingContext), + false, + "The first popup resumed its execution" + ); +}); + +add_task(async function testClosingOriginalTab() { + info( + "Test closing the toolbox on the original tab while the popup is kept open" + ); + const dbg = await initDebuggerWithAbsoluteURL(TEST_URI); + await dbg.toolbox.selectTool("webconsole"); + + info("Open a popup"); + const originalTab = gBrowser.selectedTab; + await openPopup("about:blank"); + await wait(1000); + const popupTab = gBrowser.selectedTab; + gBrowser.selectedTab = originalTab; + info("Close the toolbox from the original tab"); + await dbg.toolbox.closeToolbox(); + await wait(1000); + info("Re-select the popup"); + gBrowser.selectedTab = popupTab; + await wait(1000); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading-with-source-changes.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading-with-source-changes.js new file mode 100644 index 0000000000..a95599a2b4 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading-with-source-changes.js @@ -0,0 +1,186 @@ +/* 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 . */ + +// This tests breakpoints resyncing when source content changes +// after reload. +"use strict"; + +const httpServer = createTestHTTPServer(); +httpServer.registerContentType("html", "text/html"); +httpServer.registerContentType("js", "application/javascript"); + +httpServer.registerPathHandler( + "/doc-breakpoint-reload.html", + (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + + + + `); + } +); + +let views = 0; +httpServer.registerPathHandler("/script.js", (request, response) => { + response.setHeader("Content-Type", "application/javascript"); + // The script contents to serve on reload of script.js. Each relaod + // cycles through the script content. + const content = [ + // CONTENT 1: Source content with 1 function + // The breakpoint will be set on line 3 (i.e with the `return` statement) + `function bar() { + const prefix = "long"; + return prefix + "bar"; +} +console.log(bar());`, + + // CONTENT 2: Source content with 2 functions, where the breakpoint is now in a + // different function though the line does not change. + `function foo() { + const prefix = "long"; + return prefix + "foo"; +} + +function bar() { + const prefix = "long"; + return prefix + "bar"; +} +console.log(bar(), foo());`, + + // CONTENT 3: Source content with comments and 1 function, where the breakpoint is + // is now at a line with comments (non-breakable line). + `// This is a random comment which is here +// to move the function a couple of lines +// down, making sure the breakpoint is on +// a non-breakable line. +function bar() { + const prefix = "long"; + return prefix + "bar"; +} +console.log(bar());`, + + // CONTENT 4: Source content with just a comment where the line which the breakpoint + // is supposed to be on no longer exists. + `// one line comment`, + ]; + + response.write(content[views % content.length]); + views++; +}); + +const BASE_URL = `http://localhost:${httpServer.identity.primaryPort}/`; + +add_task(async function testBreakpointInFunctionRelocation() { + info("Start test for relocation of breakpoint set in a function"); + const dbg = await initDebuggerWithAbsoluteURL( + `${BASE_URL}doc-breakpoint-reload.html`, + "script.js" + ); + + let source = findSource(dbg, "script.js"); + await selectSource(dbg, source); + + info("Add breakpoint in bar()"); + await addBreakpoint(dbg, source, 3); + + info( + "Assert the text content on line 3 to make sure the breakpoint was set in bar()" + ); + assertTextContentOnLine(dbg, 3, 'return prefix + "bar";'); + + info("Check that only one breakpoint is set in the reducer"); + is(dbg.selectors.getBreakpointCount(), 1, "Only one breakpoint exists"); + + info("Check that only one breakpoint is set on the server"); + is( + dbg.client.getServerBreakpointsList().length, + 1, + "Only one breakpoint exists on the server" + ); + + info( + "Reload should change the source content to CONTENT 2 i.e 2 functions foo() and bar()" + ); + const onReloaded = reload(dbg); + await waitForPaused(dbg); + + source = findSource(dbg, "script.js"); + + info("Assert that the breakpoint pauses on line 3"); + assertPausedAtSourceAndLine(dbg, source.id, 3); + + info("Assert that the breakpoint is visible on line 3"); + await assertBreakpoint(dbg, 3); + + info("Assert the text content on line 3 to make sure we are paused in foo()"); + assertTextContentOnLine(dbg, 3, 'return prefix + "foo";'); + + info("Check that only one breakpoint currently exists in the reducer"); + is(dbg.selectors.getBreakpointCount(), 1, "One breakpoint exists"); + + info("Check that only one breakpoint exist on the server"); + is( + dbg.client.getServerBreakpointsList().length, + 1, + "Only one breakpoint exists on the server" + ); + + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; + + info( + "Reload should change the source content to CONTENT 3 i.e comments and 1 function bar()" + ); + await reload(dbg); + await waitForSelectedSource(dbg, "script.js"); + + await assertNotPaused(dbg); + + info( + "Assert that the breakpoint is not visible on line 3 which is a non-breakable line" + ); + await assertNoBreakpoint(dbg, 3); + + info("Check that no breakpoint exists in the reducer"); + is(dbg.selectors.getBreakpointCount(), 0, "Breakpoint has been removed"); + + // See comment on line 175 + info("Check that one breakpoint exists on the server"); + is( + dbg.client.getServerBreakpointsList().length, + 1, + "Breakpoint has been removed on the server" + ); + + info( + "Reload should change the source content to CONTENT 4 which is just a one comment line" + ); + await reload(dbg); + await waitForSelectedSource(dbg, "script.js"); + + // There will initially be zero breakpoints, but wait to make sure none are + // installed while syncing. + await wait(1000); + assertNotPaused(dbg); + + info("Assert that the source content is one comment line"); + assertTextContentOnLine(dbg, 1, "// one line comment"); + + info("Check that no breakpoint still exists in the reducer"); + is(dbg.selectors.getBreakpointCount(), 0, "No breakpoint exists"); + + // Breakpoints do not get removed in this situation, to support breakpoints in + // inline sources which load and get hit progressively. See the browser_dbg-breakpoints-reloading.js + // test for this behaviour. + info("Check that one breakpoint exists on the server"); + is( + dbg.client.getServerBreakpointsList().length, + 1, + "One breakpoint exists on the server" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading.js new file mode 100644 index 0000000000..ca953445ba --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-reloading.js @@ -0,0 +1,124 @@ +/* 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 . */ + +// Tests breakpoints syncing when reloading + +"use strict"; + +requestLongerTimeout(3); + +// Tests that a breakpoint set is correctly synced after reload +// and gets hit correctly. +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js", "long.js"); + + await selectSource(dbg, "simple1.js"); + + // Setting 2 breakpoints, one on line 61 which is expected + // to get hit, while the other on line 56 which is not expected + // to get hit, but to assert that it correctly set after reload. + await addBreakpointViaGutter(dbg, 61); + await addBreakpointViaGutter(dbg, 56); + + await selectSource(dbg, "long.js"); + + await addBreakpointViaGutter(dbg, 1); + + const onReloaded = reload(dbg); + await waitForPaused(dbg); + + info("Assert that the source is not long.js"); + // Adding this is redundant but just to make it explicit that we + // make sure long.js should not exist yet + assertSourceDoesNotExist(dbg, "long.js"); + + const source = findSource(dbg, "simple1.js"); + + await assertPausedAtSourceAndLine(dbg, source.id, 61); + + info("The breakpoint for long.js does not exist yet"); + await waitForState(dbg, state => dbg.selectors.getBreakpointCount() == 2); + + // The breakpoints are available once their corresponding source + // has been processed. Let's assert that all the breakpoints for + // simple1.js have been restored. + await assertBreakpoint(dbg, 56); + await assertBreakpoint(dbg, 61); + + await resume(dbg); + await waitForPaused(dbg); + + const source2 = findSource(dbg, "long.js"); + + await assertPausedAtSourceAndLine(dbg, source2.id, 1); + + info("All 3 breakpoints from simple1.js and long.js still exist"); + await waitForState(dbg, state => dbg.selectors.getBreakpointCount() == 3); + + await assertBreakpoint(dbg, 1); + + await resume(dbg); + + info("Wait for reload to complete after resume"); + await onReloaded; + + // remove breakpoints so they do not affect other + // tests. + await removeBreakpoint(dbg, source.id, 56); + await removeBreakpoint(dbg, source.id, 61); + await removeBreakpoint(dbg, source2.id, 1); + + await dbg.toolbox.closeToolbox(); +}); + +// Test that pending breakpoints are installed in inline scripts as they are +// sent to the client. +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "doc-scripts.html"); + + await selectSource(dbg, "doc-scripts.html"); + await addBreakpointViaGutter(dbg, 22); + await addBreakpointViaGutter(dbg, 27); + + const onReloaded = reload(dbg, "doc-scripts.html"); + await waitForPaused(dbg); + + const source = findSource(dbg, "doc-scripts.html"); + + await assertPausedAtSourceAndLine(dbg, source.id, 22); + + info("Only the breakpoint for the first inline script should exist"); + await waitForState(dbg, state => dbg.selectors.getBreakpointCount() == 1); + + await assertBreakpoint(dbg, 22); + + // The second breakpoint we added is in a later inline script, and won't + // appear until after we have resumed from the first breakpoint and the + // second inline script has been created. + await assertNoBreakpoint(dbg, 27); + + await resume(dbg); + await waitForPaused(dbg); + + info("All 2 breakpoints from both inline scripts still exist"); + await waitForState(dbg, state => dbg.selectors.getBreakpointCount() == 2); + + await assertPausedAtSourceAndLine(dbg, source.id, 27); + await assertBreakpoint(dbg, 27); + + await resume(dbg); + + info("Wait for reload to complete after resume"); + await onReloaded; + + await removeBreakpoint(dbg, source.id, 22); + await removeBreakpoint(dbg, source.id, 27); + + await dbg.toolbox.closeToolbox(); +}); + +function assertSourceDoesNotExist(dbg, url) { + const source = findSource(dbg, url, { silent: true }); + ok(!source, `Source for ${url} does not exist`); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-same-file-per-target.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-same-file-per-target.js new file mode 100644 index 0000000000..08918c235f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-same-file-per-target.js @@ -0,0 +1,114 @@ +/* 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 . */ + +// Tests shows that breakpoints per url feature works in the debugger. + +"use strict"; + +add_task(async function () { + // This test fails with server side target switching disabled + if (!isFissionEnabled() && !isEveryFrameTargetEnabled()) { + return; + } + + const dbg = await initDebugger( + "doc_dbg-same-source-distinct-threads.html", + "same-script.js" + ); + + // There is one source but 3 source actors. One per same-script.js instance, + // one "); + + // The debugger should automatically be selected. + await ToolboxTask.spawn(null, async () => { + /* global gToolbox */ + await waitUntil(() => gToolbox.currentToolId == "jsdebugger"); + }); + ok(true, "Debugger selected"); + + // The debugger should pause. + await ToolboxTask.spawn(null, async () => { + // Wait for the debugger to finish loading. + await gToolbox.getPanelWhenReady("jsdebugger"); + + const dbg = createDebuggerContext(gToolbox); + await waitForPaused(dbg); + if (!gToolbox.isHighlighted("jsdebugger")) { + throw new Error("Debugger not highlighted"); + } + }); + ok(true, "Paused in new tab"); + + await ToolboxTask.destroy(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-browser-toolbox-workers.js b/devtools/client/debugger/test/mochitest/browser_dbg-browser-toolbox-workers.js new file mode 100644 index 0000000000..f08feb4ba9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-browser-toolbox-workers.js @@ -0,0 +1,55 @@ +/* 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 . */ + +// Test that all kinds of workers show up properly in the multiprocess browser +// toolbox. + +"use strict"; + +requestLongerTimeout(4); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/browser-toolbox/test/helpers-browser-toolbox.js", + this +); + +add_task(async function () { + await pushPref("devtools.browsertoolbox.scope", "everything"); + await pushPref("dom.serviceWorkers.enabled", true); + await pushPref("dom.serviceWorkers.testing.enabled", true); + + const ToolboxTask = await initBrowserToolboxTask(); + await ToolboxTask.importFunctions({ + waitUntil, + waitForAllTargetsToBeAttached, + }); + + await addTab(`${EXAMPLE_URL}doc-all-workers.html`); + + await ToolboxTask.spawn(null, async () => { + /* global gToolbox */ + await gToolbox.selectTool("jsdebugger"); + const dbg = gToolbox.getCurrentPanel().panelWin.dbg; + + await waitUntil(() => { + const threads = dbg.selectors.getThreads(); + function hasWorker(workerName) { + // eslint-disable-next-line max-nested-callbacks + return threads.some(({ name }) => name == workerName); + } + return ( + hasWorker("simple-worker.js") && + hasWorker("shared-worker.js") && + hasWorker("service-worker.sjs") + ); + }); + + await waitForAllTargetsToBeAttached(gToolbox.commands.targetCommand); + }); + ok(true, "All workers appear in browser toolbox debugger"); + + invokeInTab("unregisterServiceWorker"); + + await ToolboxTask.destroy(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js new file mode 100644 index 0000000000..6b557cb24f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js @@ -0,0 +1,115 @@ +/* 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 . */ + +"use strict"; + +// Ignore strange errors when shutting down. +PromiseTestUtils.allowMatchingRejectionsGlobally(/No such actor/); + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + const found = findElement(dbg, "callStackBody"); + is(found, null, "Call stack is hidden"); + + invokeInTab("firstCall"); + await waitForPaused(dbg); + ok(isFrameSelected(dbg, 1, "secondCall"), "the first frame is selected"); + + const button = toggleButton(dbg); + ok(!button, "toggle button shouldn't be there"); +}); + +add_task(async function () { + const dbg = await initDebugger("doc-frames.html"); + + invokeInTab("startRecursion"); + await waitForPaused(dbg); + ok(isFrameSelected(dbg, 1, "recurseA"), "the first frame is selected"); + + // check to make sure that the toggle button isn't there + let button = toggleButton(dbg); + let frames = findAllElements(dbg, "frames"); + is(button.innerText, "Expand rows", "toggle button should be 'expand'"); + is(frames.length, 7, "There should be at most seven frames"); + + button.click(); + + button = toggleButton(dbg); + frames = findAllElements(dbg, "frames"); + is(button.innerText, "Collapse rows", "toggle button should be collapsed"); + is(frames.length, 22, "All of the frames should be shown"); + await waitForSelectedSource(dbg, "frames.js"); +}); + +add_task(async function () { + const url = createMockAngularPage(); + const tab = await addTab(url); + info("Open debugger"); + const toolbox = await openToolboxForTab(tab, "jsdebugger"); + const dbg = createDebuggerContext(toolbox); + + const found = findElement(dbg, "callStackBody"); + is(found, null, "Call stack is hidden"); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("button.pause").click(); + }); + + await waitForPaused(dbg); + const $group = findElementWithSelector(dbg, ".frames .frames-group"); + is( + $group.querySelector(".badge").textContent, + "2", + "Group has expected badge" + ); + is( + $group.querySelector(".group-description-name").textContent, + "Angular", + "Group has expected location" + ); +}); + +function toggleButton(dbg) { + const callStackBody = findElement(dbg, "callStackBody"); + return callStackBody.querySelector(".show-more"); +} + +// Create an HTTP server to simulate an angular app with anonymous functions +// and return the URL. +function createMockAngularPage() { + const httpServer = createTestHTTPServer(); + + httpServer.registerContentType("html", "text/html"); + httpServer.registerContentType("js", "application/javascript"); + + const htmlFilename = "angular-mock.html"; + httpServer.registerPathHandler( + `/${htmlFilename}`, + function (request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + + `); + } + ); + + // Register an angular.js file in order to create a Group with anonymous functions in + // the callstack panel. + httpServer.registerPathHandler("/angular.js", function (request, response) { + response.setHeader("Content-Type", "application/javascript"); + response.write(` + document.querySelector("button.pause").addEventListener("click", () => { + (function() { + debugger; + })(); + }) + `); + }); + + const port = httpServer.identity.primaryPort; + return `http://localhost:${port}/${htmlFilename}`; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-chrome-create.js b/devtools/client/debugger/test/mochitest/browser_dbg-chrome-create.js new file mode 100644 index 0000000000..fdf3c186de --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-chrome-create.js @@ -0,0 +1,61 @@ +/* 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 . */ + +/** + * Tests that a chrome debugger can be created in a new process. + */ + +"use strict"; + +// There are shutdown issues for which multiple rejections are left uncaught. +// See bug 1018184 for resolving these issues. +PromiseTestUtils.allowMatchingRejectionsGlobally(/File closed/); +PromiseTestUtils.allowMatchingRejectionsGlobally(/NS_ERROR_FAILURE/); + +// This test can be slow to run +requestLongerTimeout(5); + +const { BrowserToolboxLauncher } = ChromeUtils.importESModule( + "resource://devtools/client/framework/browser-toolbox/Launcher.sys.mjs" +); + +add_task(async function () { + await pushPref("devtools.chrome.enabled", true); + await pushPref("devtools.debugger.remote-enabled", true); + + info("Call BrowserToolboxLauncher.init"); + const [browserToolboxLauncher, process, profilePath] = await new Promise( + resolve => { + BrowserToolboxLauncher.init({ + onRun: (btl, dbgProcess, dbgProfilePath) => { + info("Browser toolbox process started successfully."); + resolve([btl, dbgProcess, dbgProfilePath]); + }, + }); + } + ); + + ok(process, "The remote debugger process was created"); + ok(process.exitCode == null, "The remote debugger process is running"); + is( + typeof process.pid, + "number", + `The remote debugger process has a proper pid (${process.pid})` + ); + + is( + profilePath, + PathUtils.join(PathUtils.profileDir, "chrome_debugger_profile"), + `The remote debugger profile has the expected path` + ); + + info("Close the browser toolbox"); + await browserToolboxLauncher.close(); + + is( + process.exitCode, + Services.appinfo.OS == "WINNT" ? -9 : -15, + "The remote debugger process died cleanly" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-command-click.js b/devtools/client/debugger/test/mochitest/browser_dbg-command-click.js new file mode 100644 index 0000000000..1f6ad49e4d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-command-click.js @@ -0,0 +1,39 @@ +/* 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 . */ + +// This test checks to see if holding down the command key and clicking on function call +// will jump the debugger to that call. + +"use strict"; + +add_task(async function () { + await pushPref("devtools.debugger.features.command-click", true); + info( + "Checking to see if command click will jump the debugger to another highlighted call." + ); + const dbg = await initDebugger("doc-command-click.html", "simple4.js"); + + const source = findSource(dbg, "simple4.js"); + + await selectSource(dbg, source); + + invokeInTab("funcA"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + + pressKey(dbg, "commandKeyDown"); + await waitForDispatch(dbg.store, "HIGHLIGHT_CALLS"); + const calls = dbg.win.document.querySelectorAll(".highlight-function-calls"); + is(calls.length, 2); + + const funcB = calls[0]; + clickDOMElement(dbg, funcB); + await waitForDispatch(dbg.store, "RESUME"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 3, 2); + const nocalls = dbg.win.document.querySelectorAll( + ".highlight-function-calls" + ); + is(nocalls.length, 0); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js b/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js new file mode 100644 index 0000000000..4c669766b0 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js @@ -0,0 +1,50 @@ +/* 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 . */ + +// Return a promise with a reference to jsterm, opening the split +// console if necessary. This cleans up the split console pref so +// it won't pollute other tests. + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref( + "devtools.debugger.features.map-await-expression", + true + ); + + const dbg = await initDebugger( + "doc-script-switching.html", + "script-switching-01.js" + ); + + await selectSource(dbg, "script-switching-01.js"); + + // open the console + await getDebuggerSplitConsole(dbg); + ok(dbg.toolbox.splitConsole, "Split console is shown."); + + const webConsole = await dbg.toolbox.getPanel("webconsole"); + const wrapper = webConsole.hud.ui.wrapper; + + wrapper.dispatchEvaluateExpression(` + let sleep = async (time, v) => new Promise( + res => setTimeout(() => res(v+'!!!'), time) + ) + `); + + await hasMessageByType(dbg, "sleep", ".command"); + + wrapper.dispatchEvaluateExpression(`await sleep(200, "DONE")`); + await hasMessageByType(dbg, "DONE!!!", ".result"); +}); + +async function hasMessageByType(dbg, msg, typeSelector) { + const webConsole = await dbg.toolbox.getPanel("webconsole"); + const hud = webConsole.hud; + return waitFor( + async () => !!findMessagesByType(hud, msg, typeSelector).length + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console-eval.js b/devtools/client/debugger/test/mochitest/browser_dbg-console-eval.js new file mode 100644 index 0000000000..0e4cf793ec --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console-eval.js @@ -0,0 +1,37 @@ +/* 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 . */ + +// Tests that clicking the DOM node button in any ObjectInspect +// opens the Inspector panel + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple2.js"); + + await selectSource(dbg, "simple2.js", 1); + + clickElement(dbg, "CodeMirrorLines"); + await waitForElementWithSelector(dbg, ".CodeMirror-code"); + + getCM(dbg).setSelection({ line: 0, ch: 0 }, { line: 8, ch: 0 }); + + rightClickElement(dbg, "CodeMirrorLines"); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, "#node-menu-evaluate-in-console"); + + await waitForConsolePanelChange(dbg); + await hasConsoleMessage(dbg, "undefined"); +}); + +function waitForConsolePanelChange(dbg) { + const { toolbox } = dbg; + + return new Promise(resolve => { + toolbox.getPanelWhenReady("webconsole").then(() => { + ok(toolbox.webconsolePanel, "Console is shown."); + resolve(toolbox.webconsolePanel); + }); + }); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console-link.js b/devtools/client/debugger/test/mochitest/browser_dbg-console-link.js new file mode 100644 index 0000000000..3d87fdc819 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console-link.js @@ -0,0 +1,29 @@ +/* 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 . */ + +// Tests opening the console first, clicking a link +// opens the editor at the correct location. + +"use strict"; + +add_task(async function () { + const toolbox = await initPane("doc-script-switching.html", "webconsole"); + const node = await waitForLink(toolbox, "hi"); + node.click(); + + await waitFor(() => toolbox.getPanel("jsdebugger")); + const dbg = createDebuggerContext(toolbox); + await waitForElementWithSelector(dbg, ".CodeMirror-code > .highlight-line"); + assertHighlightLocation(dbg, "script-switching-02.js", 14); +}); + +async function waitForLink(toolbox, messageText) { + return waitFor(async () => { + const [message] = await findConsoleMessages(toolbox, messageText); + if (!message) { + return false; + } + return message.querySelector(".frame-link-source"); + }); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js b/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js new file mode 100644 index 0000000000..26529a75cd --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js @@ -0,0 +1,47 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + const dbg = await initDebugger("doc-strict.html"); + + await getSplitConsole(dbg); + ok(dbg.toolbox.splitConsole, "Split console is shown."); + + invokeInTab("strict", 2); + + await waitForPaused(dbg); + await evaluate(dbg, "var c = 3"); + const msg2 = await evaluate(dbg, "c"); + + is(msg2.trim(), "3"); +}); + +// Return a promise with a reference to jsterm, opening the split +// console if necessary. This cleans up the split console pref so +// it won't pollute other tests. +function getSplitConsole(dbg) { + const { toolbox } = dbg; + + if (!toolbox.splitConsole) { + pressKey(dbg, "Escape"); + } + + return new Promise(resolve => { + toolbox.getPanelWhenReady("webconsole").then(() => { + ok(toolbox.splitConsole, "Split console is shown."); + const jsterm = toolbox.getPanel("webconsole").hud.jsterm; + resolve(jsterm); + }); + }); +} + +async function evaluate(dbg, expression) { + const { toolbox } = dbg; + const { hud } = toolbox.getPanel("webconsole"); + const msg = await evaluateExpressionInConsole(hud, expression); + return msg.innerText; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console.js b/devtools/client/debugger/test/mochitest/browser_dbg-console.js new file mode 100644 index 0000000000..a725f7de43 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console.js @@ -0,0 +1,28 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + const dbg = await initDebugger( + "doc-script-switching.html", + "script-switching-01.js" + ); + + await selectSource(dbg, "script-switching-01.js"); + + // open the console + await getDebuggerSplitConsole(dbg); + ok(dbg.toolbox.splitConsole, "Split console is shown."); + + // close the console + await clickElement(dbg, "codeMirror"); + // First time to focus out of text area + pressKey(dbg, "Escape"); + + // Second time to hide console + pressKey(dbg, "Escape"); + ok(!dbg.toolbox.splitConsole, "Split console is hidden."); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js b/devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js new file mode 100644 index 0000000000..b95b08cbbb --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js @@ -0,0 +1,52 @@ +/* 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 . */ + +// Tests that the content scripts are listed in the source tree. + +"use strict"; + +add_task(async function () { + await pushPref("devtools.chrome.enabled", true); + const extension = await installAndStartContentScriptExtension(); + + let dbg = await initDebugger( + "doc-content-script-sources.html", + "content_script.js" + ); + await selectSource(dbg, "content_script.js"); + await closeTab(dbg, "content_script.js"); + + // Destroy the toolbox and repeat the test in a new toolbox + // and ensures that the content script is still listed. + await dbg.toolbox.destroy(); + + const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger"); + dbg = createDebuggerContext(toolbox); + await waitForSources(dbg, "content_script.js"); + await selectSource(dbg, "content_script.js"); + + await addBreakpoint(dbg, "content_script.js", 2); + + for (let i = 1; i < 3; i++) { + info(`Reloading tab (${i} time)`); + gBrowser.reloadTab(gBrowser.selectedTab); + await waitForPaused(dbg); + await waitForSelectedSource(dbg, "content_script.js"); + + await waitFor( + () => findElementWithSelector(dbg, ".sources-list .focused"), + "Source is focused" + ); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "content_script.js").id, + 2 + ); + await resume(dbg); + } + + await closeTab(dbg, "content_script.js"); + + await extension.unload(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here-click.js b/devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here-click.js new file mode 100644 index 0000000000..67cbbe12ec --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here-click.js @@ -0,0 +1,48 @@ +/* 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 . */ + +// Test cmd+click continuing to a line + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-pause-points.html", "pause-points.js"); + await selectSource(dbg, "pause-points.js"); + await waitForSelectedSource(dbg, "pause-points.js"); + + info( + "Pause the debugger by clicking a button with a click handler containing a debugger statement" + ); + clickElementInTab("#sequences"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + ok(true, "Debugger is paused"); + + info("Cmd+click on a line and check the debugger continues to that line"); + const lineToContinueTo = 31; + const onResumed = waitForResumed(dbg); + await cmdClickLine(dbg, lineToContinueTo); + + // continuing will resume and pause again. Let's wait until we resume so we can properly + // wait for the next pause. + await onResumed; + // waitForPaused properly waits for the scopes to be available + await waitForPaused(dbg); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "pause-points.js").id, + lineToContinueTo, + 4 + ); + ok(true, "Debugger continued to the expected line"); + + info("Resume"); + await resume(dbg); + await waitForRequestsToSettle(dbg); +}); + +async function cmdClickLine(dbg, line) { + await cmdClickGutter(dbg, line); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here.js b/devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here.js new file mode 100644 index 0000000000..baeef0cf8c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-continue-to-here.js @@ -0,0 +1,58 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-pause-points.html", "pause-points.js"); + await selectSource(dbg, "pause-points.js"); + await waitForSelectedSource(dbg, "pause-points.js"); + + info("Test continuing to a line"); + clickElementInTab("#sequences"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + + await continueToLine(dbg, 31); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "pause-points.js").id, + 31, + 4 + ); + await resume(dbg); + + info("Test continuing to a column"); + clickElementInTab("#sequences"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + + await continueToColumn(dbg, { line: 31, ch: 7 }); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "pause-points.js").id, + 31, + 4 + ); + await resume(dbg); +}); + +async function continueToLine(dbg, line) { + rightClickElement(dbg, "gutter", line); + await waitForContextMenu(dbg); + selectContextMenuItem(dbg, selectors.editorContextMenu.continueToHere); + await waitForDispatch(dbg.store, "RESUME"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); +} + +async function continueToColumn(dbg, pos) { + await rightClickAtPos(dbg, pos); + await waitForContextMenu(dbg); + + selectContextMenuItem(dbg, selectors.editorContextMenu.continueToHere); + await waitForDispatch(dbg.store, "RESUME"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-custom-formatters.js b/devtools/client/debugger/test/mochitest/browser_dbg-custom-formatters.js new file mode 100644 index 0000000000..ac6a367f55 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-custom-formatters.js @@ -0,0 +1,158 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Check display of custom formatters in debugger. +const TEST_FILENAME = `doc_dbg-custom-formatters.html`; +const TEST_FUNCTION_NAME = "pauseWithCustomFormattedObject"; +const CUSTOM_FORMATTED_BODY = "customFormattedBody"; +const VARIABLE_NAME = "xyz"; + +add_task(async function () { + // TODO: This preference can be removed once the custom formatters feature is stable enough + await pushPref("devtools.custom-formatters", true); + await pushPref("devtools.custom-formatters.enabled", true); + + const dbg = await initDebugger(TEST_FILENAME); + await selectSource(dbg, TEST_FILENAME); + + info( + "Test that custom formatted objects are displayed as expected in the Watch Expressions panel" + ); + await addExpression(dbg, `({customFormatHeaderAndBody: "body"})`); + const expressionsElements = findAllElements(dbg, "expressionNodes"); + is(expressionsElements.length, 1); + + const customFormattedElement = expressionsElements[0]; + + const headerJsonMlNode = + customFormattedElement.querySelector(".objectBox-jsonml"); + is( + headerJsonMlNode.innerText, + "CUSTOM", + "The object is rendered with a custom formatter" + ); + + is( + customFormattedElement.querySelector(".arrow"), + null, + "The expression is not expandable…" + ); + const customFormattedElementArrow = + customFormattedElement.querySelector(".collapse-button"); + ok(customFormattedElementArrow, "… but the custom formatter is"); + + info("Expanding the Object"); + const onBodyRendered = waitFor( + () => + !!customFormattedElement.querySelector( + ".objectBox-jsonml-body-wrapper .objectBox-jsonml" + ) + ); + + customFormattedElementArrow.click(); + await onBodyRendered; + + ok( + customFormattedElement + .querySelector(".collapse-button") + .classList.contains("expanded"), + "The arrow of the node has the expected class after clicking on it" + ); + + const bodyJsonMlNode = customFormattedElement.querySelector( + ".objectBox-jsonml-body-wrapper > .objectBox-jsonml" + ); + ok(bodyJsonMlNode, "The body is custom formatted"); + is(bodyJsonMlNode?.textContent, "body", "The body text is correct"); + + info("Check that custom formatters are displayed in Scopes panel"); + + // The function has a debugger statement that will pause the debugger + invokeInTab(TEST_FUNCTION_NAME); + + info("Wait for the debugger to be paused"); + await waitForPaused(dbg); + + info( + `Check that '${VARIABLE_NAME}' is in the scopes panel and custom formatted` + ); + const index = 4; + is( + getScopeLabel(dbg, index), + VARIABLE_NAME, + `Got '${VARIABLE_NAME}' at the expected position` + ); + const scopeXElement = findElement(dbg, "scopeValue", index); + is( + scopeXElement.innerText, + "CUSTOM", + `'${VARIABLE_NAME}' is custom formatted in the scopes panel` + ); + const xArrow = scopeXElement.querySelector(".collapse-button"); + ok(xArrow, `'${VARIABLE_NAME}' is expandable`); + + info(`Expanding '${VARIABLE_NAME}'`); + const onScopeBodyRendered = waitFor( + () => + !!scopeXElement.querySelector( + ".objectBox-jsonml-body-wrapper .objectBox-jsonml" + ) + ); + + xArrow.click(); + await onScopeBodyRendered; + const scopeXBodyJsonMlNode = scopeXElement.querySelector( + ".objectBox-jsonml-body-wrapper > .objectBox-jsonml" + ); + ok(scopeXBodyJsonMlNode, "The scope item body is custom formatted"); + is( + scopeXBodyJsonMlNode?.textContent, + CUSTOM_FORMATTED_BODY, + "The scope item body text is correct" + ); + + await resume(dbg); + + info("Check that custom formatters are displayed in the Debugger tooltip"); + + // The function has a debugger statement that will pause the debugger + invokeInTab(TEST_FUNCTION_NAME); + await waitForPaused(dbg); + + await assertPreviewTextValue(dbg, 26, 16, { + expression: VARIABLE_NAME, + text: "CUSTOM", + }); + + const tooltipPopup = findElement(dbg, "previewPopup"); + + const tooltipArrow = tooltipPopup.querySelector(".collapse-button"); + ok(tooltipArrow, `'${VARIABLE_NAME}' is expandable`); + + info(`Expanding '${VARIABLE_NAME}'`); + const onTooltipBodyRendered = waitFor( + () => + !!tooltipPopup.querySelector( + ".objectBox-jsonml-body-wrapper .objectBox-jsonml" + ) + ); + + tooltipArrow.click(); + await onTooltipBodyRendered; + const tooltipBodyJsonMlNode = tooltipPopup.querySelector( + ".objectBox-jsonml-body-wrapper > .objectBox-jsonml" + ); + ok(tooltipBodyJsonMlNode, "The tooltip variable body is custom formatted"); + is( + tooltipBodyJsonMlNode?.textContent, + CUSTOM_FORMATTED_BODY, + "The tooltip variable body text is correct" + ); + + info("Close tooltip"); + dbg.actions.clearPreview(getContext(dbg)); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-debug-line.js b/devtools/client/debugger/test/mochitest/browser_dbg-debug-line.js new file mode 100644 index 0000000000..ab13a1ee50 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-debug-line.js @@ -0,0 +1,46 @@ +/* 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 . */ + +// Test ensures zombie debug lines do not persist +// https://github.com/firefox-devtools/debugger/issues/7755 + +"use strict"; + +add_task(async function () { + // Load test files + const dbg = await initDebugger("doc-sources.html"); + await waitForSources(dbg, "simple1.js", "simple2.js"); + + // Add breakpoint to debug-line-2 + await selectSource(dbg, "simple2.js"); + await addBreakpoint(dbg, "simple2.js", 5); + + // Trigger the breakpoint ane ensure we're paused + invokeInTab("main"); + await waitForPaused(dbg); + await waitForDispatch(dbg.store, "ADD_INLINE_PREVIEW"); + + // Scroll element into view + findElement(dbg, "frame", 2).focus(); + + // Click the call stack to get to debugger-line-1 + const dispatched = waitForDispatch(dbg.store, "ADD_INLINE_PREVIEW"); + await clickElement(dbg, "frame", 2); + await dispatched; + await waitForRequestsToSettle(dbg); + await waitForSelectedSource(dbg, "simple1.js"); + + // Resume, which ends all pausing and would trigger the problem + await resume(dbg); + + // Select the source that had the initial debug line + await selectSource(dbg, "simple2.js"); + + info("Ensuring there's no zombie debug line"); + is( + findAllElements(dbg, "debugLine").length, + 0, + "Debug line no longer exists!" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-debugger-buttons.js b/devtools/client/debugger/test/mochitest/browser_dbg-debugger-buttons.js new file mode 100644 index 0000000000..c466c4865d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-debugger-buttons.js @@ -0,0 +1,97 @@ +/* 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 . */ + +/** + * Test debugger buttons + * 1. resume + * 2. stepOver + * 3. stepIn + * 4. stepOver to the end of a function + * 5. stepUp at the end of a function + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-debugger-statements.html"); + + const onReloaded = reload(dbg, "doc-debugger-statements.html"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 11 + ); + + info("resume"); + await clickResume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 16 + ); + + info("step over"); + await clickStepOver(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 17 + ); + + info("step into"); + await clickStepIn(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 22 + ); + + info("step over"); + await clickStepOver(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 24 + ); + + info("step out"); + await clickStepOut(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 18 + ); + + await resume(dbg); + + info("Wait for reload to complete after resume"); + await onReloaded; +}); + +function clickButton(dbg, button) { + const resumeFired = waitForDispatch(dbg.store, "COMMAND"); + clickElement(dbg, button); + return resumeFired; +} + +async function clickStepOver(dbg) { + await clickButton(dbg, "stepOver"); + return waitForPaused(dbg); +} + +async function clickStepIn(dbg) { + await clickButton(dbg, "stepIn"); + return waitForPaused(dbg); +} + +async function clickStepOut(dbg) { + await clickButton(dbg, "stepOut"); + return waitForPaused(dbg); +} + +async function clickResume(dbg) { + return clickButton(dbg, "resume"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints-fission.js b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints-fission.js new file mode 100644 index 0000000000..462ff35b67 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints-fission.js @@ -0,0 +1,111 @@ +/* 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 . */ + +// Tests dom mutation breakpoints with a remote frame. + +"use strict"; + +// Import helpers for the inspector +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js", + this +); + +/** + * The test page contains an INPUT and a BUTTON. + * + * On "click", the button will update the disabled attribute of the input. + * Updating the attribute via SpecialPowers.spawn would not trigger the DOM + * breakpoint, that's why we need the page itself to have a way of updating + * the attribute. + */ +const TEST_COM_URI = `https://example.com/document-builder.sjs?html=${encodeURI( + ` + ` +)}`; + +// Embed the example.com test page in an example.org iframe. +const TEST_URI = `https://example.org/document-builder.sjs?html= +`; + +add_task(async function () { + await pushPref("devtools.markup.mutationBreakpoints.enabled", true); + await pushPref("devtools.debugger.dom-mutation-breakpoints-visible", true); + + const { inspector, toolbox } = await openInspectorForURL(TEST_URI); + + await selectNodeInFrames(["iframe", "input"], inspector); + info("Adding DOM mutation breakpoints to body"); + const allMenuItems = openContextMenuAndGetAllItems(inspector); + + const attributeMenuItem = allMenuItems.find( + item => item.id === "node-menu-mutation-breakpoint-attribute" + ); + attributeMenuItem.click(); + + info("Switches over to the debugger pane"); + await toolbox.selectTool("jsdebugger"); + + const dbg = createDebuggerContext(toolbox); + + info("Changing attribute to trigger debugger pause"); + const frameBC = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + function () { + return content.document.querySelector("iframe").browsingContext; + } + ); + + info("Confirms that one DOM mutation breakpoint exists"); + const mutationItem = await waitForElement(dbg, "domMutationItem"); + ok(mutationItem, "A DOM mutation breakpoint exists"); + + mutationItem.scrollIntoView(); + + info("Enabling and disabling the DOM mutation breakpoint works"); + const checkbox = mutationItem.querySelector("input"); + checkbox.click(); + await waitFor(() => !checkbox.checked); + + info("Click the button in the remote iframe, should not hit the breakpoint"); + BrowserTestUtils.synthesizeMouseAtCenter("button", {}, frameBC); + + info("Wait until the input is enabled"); + await asyncWaitUntil(() => + SpecialPowers.spawn( + frameBC, + [], + () => !content.document.querySelector("input").disabled + ) + ); + assertNotPaused(dbg, "DOM breakpoint should not have been hit"); + + info("Restore the disabled attribute"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + return SpecialPowers.spawn( + content.document.querySelector("iframe"), + [], + () => + !content.document.querySelector("input").setAttribute("disabled", "") + ); + }); + + checkbox.click(); + await waitFor(() => checkbox.checked); + + info("Click the button in the remote iframe, to trigger the breakpoint"); + BrowserTestUtils.synthesizeMouseAtCenter("button", {}, frameBC); + + info("Wait for paused"); + await waitForPaused(dbg); + info("Resume"); + await resume(dbg); + + info("Removing breakpoints works"); + dbg.win.document.querySelector(".dom-mutation-list .close-btn").click(); + await waitForAllElements(dbg, "domMutationItem", 0, true); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js new file mode 100644 index 0000000000..ca7e9bdc20 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js @@ -0,0 +1,170 @@ +/* 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 . */ + +// Tests adding, disble/enable, and removal of dom mutation breakpoints + +"use strict"; + +// Import helpers for the inspector +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js", + this +); + +const DMB_TEST_URL = + "https://example.com/browser/devtools/client/debugger/test/mochitest/examples/doc-dom-mutation.html"; + +async function enableMutationBreakpoints() { + await pushPref("devtools.markup.mutationBreakpoints.enabled", true); + await pushPref("devtools.debugger.dom-mutation-breakpoints-visible", true); +} + +add_task(async function () { + // Enable features + await enableMutationBreakpoints(); + info("Switches over to the inspector pane"); + + const { inspector, toolbox } = await openInspectorForURL(DMB_TEST_URL); + + { + info("Selecting the body node"); + await selectNode("body", inspector); + + info("Adding DOM mutation breakpoints to body"); + const allMenuItems = openContextMenuAndGetAllItems(inspector); + + const attributeMenuItem = allMenuItems.find( + item => item.id === "node-menu-mutation-breakpoint-attribute" + ); + attributeMenuItem.click(); + + const subtreeMenuItem = allMenuItems.find( + item => item.id === "node-menu-mutation-breakpoint-subtree" + ); + subtreeMenuItem.click(); + } + + { + info("Find and expand the shadow host."); + const hostFront = await getNodeFront("#host", inspector); + const hostContainer = inspector.markup.getContainer(hostFront); + await expandContainer(inspector, hostContainer); + + info("Expand the shadow root"); + const shadowRootContainer = hostContainer.getChildContainers()[0]; + await expandContainer(inspector, shadowRootContainer); + + info("Select the div under the shadow root"); + const divContainer = shadowRootContainer.getChildContainers()[0]; + await selectNode(divContainer.node, inspector); + + const allMenuItems = openContextMenuAndGetAllItems(inspector); + info("Adding attribute breakpoint."); + const attributeMenuItem = allMenuItems.find( + item => item.id === "node-menu-mutation-breakpoint-attribute" + ); + attributeMenuItem.click(); + } + + info("Switches over to the debugger pane"); + await toolbox.selectTool("jsdebugger"); + + const dbg = createDebuggerContext(toolbox); + + info("Confirms that one DOM mutation breakpoint exists"); + const mutationItem = await waitForElement(dbg, "domMutationItem"); + ok(mutationItem, "A DOM mutation breakpoint exists"); + + mutationItem.scrollIntoView(); + + info("Enabling and disabling the DOM mutation breakpoint works"); + const checkbox = mutationItem.querySelector("input"); + checkbox.click(); + await waitFor(() => !checkbox.checked); + checkbox.click(); + await waitFor(() => checkbox.checked); + + info("Changing attribute to trigger debugger pause"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("#attribute").click(); + }); + await waitForPaused(dbg); + let whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is( + whyPaused, + `Paused on DOM mutation\nDOM Mutation: 'attributeModified'\nbody` + ); + + await resume(dbg); + + info("Changing style to trigger debugger pause"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("#style-attribute").click(); + }); + await waitForPaused(dbg); + await resume(dbg); + + info("Changing attribute in shadow dom to trigger debugger pause"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("#shadow-attribute").click(); + }); + await waitForPaused(dbg); + await resume(dbg); + + info("Adding element in subtree to trigger debugger pause"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("#add-in-subtree").click(); + }); + await waitForPaused(dbg); + whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is( + whyPaused, + `Paused on DOM mutation\nDOM Mutation: 'subtreeModified'\nbodyAdded:div#dynamic` + ); + + await resume(dbg); + + info("Removing element in subtree to trigger debugger pause"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("#remove-in-subtree").click(); + }); + await waitForPaused(dbg); + whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is( + whyPaused, + `Paused on DOM mutation\nDOM Mutation: 'subtreeModified'\nbodyRemoved:div#dynamic` + ); + + await resume(dbg); + + info("Blackboxing the source prevents debugger pause"); + await waitForSource(dbg, "dom-mutation.original.js"); + + const source = findSource(dbg, "dom-mutation.original.js"); + + await selectSource(dbg, source); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("#blackbox").click(); + }); + + await waitForPaused(dbg, "click.js"); + await resume(dbg); + + await selectSource(dbg, source); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); + + info("Removing breakpoints works"); + dbg.win.document.querySelector(".dom-mutation-list .close-btn").click(); + await waitForAllElements(dbg, "domMutationItem", 2, true); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-eager-eval-skip-pause.js b/devtools/client/debugger/test/mochitest/browser_dbg-eager-eval-skip-pause.js new file mode 100644 index 0000000000..dcd8f4605b --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-eager-eval-skip-pause.js @@ -0,0 +1,19 @@ +/* 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 . */ + +// Test that eager evaluation skips breakpoints and debugger statements + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-strict.html"); + const { hud } = await getDebuggerSplitConsole(dbg); + + await addBreakpoint(dbg, "doc-strict.html", 15); + setInputValue(hud, "strict()"); + await waitForEagerEvaluationResult(hud, `3`); + + setInputValue(hud, "debugger; 2"); + await waitForEagerEvaluationResult(hud, `2`); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-editor-exception.js b/devtools/client/debugger/test/mochitest/browser_dbg-editor-exception.js new file mode 100644 index 0000000000..9f6ff306a0 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-editor-exception.js @@ -0,0 +1,26 @@ +/* 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 . */ + +// Tests that the editor does not crash when closing a tab (using the contextmenu) + +"use strict"; + +add_task(async function testEditorExceptionClose() { + const dbg = await initDebugger("doc-exceptions.html", "exceptions.js"); + await selectSource(dbg, "exceptions.js"); + is(countTabs(dbg), 1, "Tab for the exceptions.js source is open"); + await openActiveTabContextMenuAndSelectCloseTabItem(dbg); + is(countTabs(dbg), 0, "All tabs are closed"); +}); + +async function openActiveTabContextMenuAndSelectCloseTabItem(dbg) { + const waitForOpen = waitForContextMenu(dbg); + info(`Open the current active tab context menu`); + rightClickElement(dbg, "activeTab"); + await waitForOpen; + const wait = waitForDispatch(dbg.store, "CLOSE_TAB"); + info(`Select the close tab context menu item`); + selectContextMenuItem(dbg, `#node-menu-close-tab`); + return wait; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-editor-gutter.js b/devtools/client/debugger/test/mochitest/browser_dbg-editor-gutter.js new file mode 100644 index 0000000000..1653d9195b --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-editor-gutter.js @@ -0,0 +1,144 @@ +/* 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 . */ + +// Tests the breakpoint gutter and making sure breakpoint icons exist +// correctly + +"use strict"; + +// FIXME bug 1524374 removing breakpoints in this test can cause uncaught +// rejections and make bug 1512742 permafail. +PromiseTestUtils.allowMatchingRejectionsGlobally(/NS_ERROR_NOT_INITIALIZED/); + +add_task(async function testGutterBreakpoints() { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + const source = findSource(dbg, "simple1.js"); + + await selectSource(dbg, source); + + // Make sure that clicking the gutter creates a breakpoint icon. + await clickGutter(dbg, 4); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + is(dbg.selectors.getBreakpointCount(), 1, "One breakpoint exists"); + await assertBreakpoint(dbg, 4); + + // Make sure clicking at the same place removes the icon. + await clickGutter(dbg, 4); + await waitForDispatch(dbg.store, "REMOVE_BREAKPOINT"); + is(dbg.selectors.getBreakpointCount(), 0, "No breakpoints exist"); + await assertNoBreakpoint(dbg, 4); +}); + +add_task(async function testGutterBreakpointsInIgnoredSource() { + info( + "Ensure clicking on gutter to add breakpoint should not un-ignore source" + ); + const dbg = await initDebugger("doc-sourcemaps3.html"); + dbg.actions.toggleMapScopes(); + await waitForSources(dbg, "bundle.js", "sorted.js", "test.js"); + + info("Ignore the source"); + await selectSource(dbg, findSource(dbg, "sorted.js")); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + + // invoke test + invokeInTab("test"); + + // wait to make sure no pause has occured + await wait(1000); + assertNotPaused(dbg); + + info("Ensure gutter breakpoint gets set with click"); + await clickGutter(dbg, 4); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + + info("Assert that the `Enable breakpoint` context menu item is disabled"); + const popup = await openContextMenuInDebugger(dbg, "gutter", 4); + await assertContextMenuItemDisabled( + dbg, + "#node-menu-enable-breakpoint", + true + ); + await closeContextMenu(dbg, popup); + + is(dbg.selectors.getBreakpointCount(), 1, "One breakpoint exists"); + await assertBreakpoint(dbg, 4); + is( + findBreakpoint(dbg, "sorted.js", 4).disabled, + true, + "The breakpoint in the ignored source looks disabled" + ); + + invokeInTab("test"); + + await wait(1000); + assertNotPaused(dbg); + + ok(true, "source is still blackboxed"); +}); + +add_task(async function testGutterBreakpointsForSourceWithIgnoredLines() { + info( + "Asserts that adding a breakpoint to the gutter of an un-ignored line does not un-ignore other ranges in the source" + ); + const dbg = await initDebugger("doc-sourcemaps3.html"); + await waitForSources(dbg, "bundle.js", "sorted.js", "test.js"); + + await selectSource(dbg, findSource(dbg, "sorted.js")); + + info("Ignoring line 17 to 21 using the editor context menu"); + await selectEditorLinesAndOpenContextMenu(dbg, { + startLine: 17, + endLine: 21, + }); + await selectBlackBoxContextMenuItem(dbg, "blackbox-lines"); + + info("Set breakpoint on an un-ignored line"); + await clickGutter(dbg, 4); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + + info("Assert that the `Disable breakpoint` context menu item is enabled"); + const popup = await openContextMenuInDebugger(dbg, "gutter", 4); + await assertContextMenuItemDisabled( + dbg, + "#node-menu-disable-breakpoint", + false + ); + await closeContextMenu(dbg, popup); + + info("Assert that the lines 17 to 21 are still ignored"); + assertIgnoredStyleInSourceLines(dbg, { + lines: [17, 21], + hasBlackboxedLinesClass: true, + }); + + info( + "Asserts that adding a breakpoint to the gutter of an ignored line creates a disabled breakpoint" + ); + info("Set breakpoint on an ignored line"); + await clickGutter(dbg, 19); + await waitForDispatch(dbg.store, "SET_BREAKPOINT"); + + info("Assert that the `Enable breakpoint` context menu item is disabled"); + const popup2 = await openContextMenuInDebugger(dbg, "gutter", 19); + await assertContextMenuItemDisabled( + dbg, + "#node-menu-enable-breakpoint", + true + ); + await closeContextMenu(dbg, popup2); + + info("Assert that the breakpoint on the line 19 is visually disabled"); + is( + findBreakpoint(dbg, "sorted.js", 19).disabled, + true, + "The breakpoint on an ignored line is disabled" + ); +}); + +async function assertContextMenuItemDisabled(dbg, selector, expectedState) { + const item = await waitFor(() => findContextMenu(dbg, selector)); + is(item.disabled, expectedState, "The context menu item is disabled"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-editor-highlight.js b/devtools/client/debugger/test/mochitest/browser_dbg-editor-highlight.js new file mode 100644 index 0000000000..4fd5a6be40 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-editor-highlight.js @@ -0,0 +1,50 @@ +/* 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 . */ + +// Tests that the editor will always highight the right line, no +// matter if the source text doesn't exist yet or even if the source +// doesn't exist. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "long.js"); + const { + selectors: { getSettledSourceTextContent }, + } = dbg; + + // The source itself doesn't even exist yet, and using + // `selectSourceURL` will set a pending request to load this source + // and highlight a specific line. + + await selectSource(dbg, "long.js", 66); + + // TODO: revisit highlighting lines when the debugger opens + // assertHighlightLocation(dbg, "long.js", 66); + + info("Select line 16 and make sure the editor scrolled."); + await selectSource(dbg, "long.js", 16); + await waitForElementWithSelector(dbg, ".CodeMirror-code > .highlight-line"); + assertHighlightLocation(dbg, "long.js", 16); + + info("Select several locations and check that we have one highlight"); + await selectSource(dbg, "long.js", 17); + await selectSource(dbg, "long.js", 18); + assertHighlightLocation(dbg, "long.js", 18); + + // Test jumping to a line in a source that exists but hasn't been + // loaded yet. + info("Select an unloaded source"); + selectSource(dbg, "simple1.js", 6); + + // Make sure the source is in the loading state, wait for it to be + // fully loaded, and check the highlighted line. + const simple1 = findSource(dbg, "simple1.js"); + const location = createLocation({ source: simple1 }); + is(getSettledSourceTextContent(location), null); + + await waitForSelectedSource(dbg, "simple1.js"); + ok(getSettledSourceTextContent(location).value.value); + assertHighlightLocation(dbg, "simple1.js", 6); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-editor-mode.js b/devtools/client/debugger/test/mochitest/browser_dbg-editor-mode.js new file mode 100644 index 0000000000..6cec2e1dbf --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-editor-mode.js @@ -0,0 +1,18 @@ +/* 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 . */ + +// Tests that the editor sets the correct mode for different file +// types + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + + await selectSource(dbg, "simple1.js"); + is(dbg.getCM().getOption("mode").name, "javascript", "Mode is correct"); + + await selectSource(dbg, "doc-scripts.html"); + is(dbg.getCM().getOption("mode").name, "htmlmixed", "Mode is correct"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-editor-scroll.js b/devtools/client/debugger/test/mochitest/browser_dbg-editor-scroll.js new file mode 100644 index 0000000000..a6f6394ee8 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-editor-scroll.js @@ -0,0 +1,48 @@ +/* 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 . */ + +// Tests that the editor keeps proper scroll position per document +// while also moving to the correct location upon pause/breakpoint selection + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + // This test runs too slowly on linux debug. I'd like to figure out + // which is the slowest part of this and make it run faster, but to + // fix a frequent failure allow a longer timeout. + const dbg = await initDebugger("doc-editor-scroll.html"); + + // Set the initial breakpoint. + await selectSource(dbg, "simple1.js"); + await addBreakpoint(dbg, "simple1.js", 26); + + const cm = getCM(dbg); + + info("Open long file, scroll down to line below the fold"); + await selectSource(dbg, "long.js"); + cm.scrollTo(0, 600); + + info("Ensure vertical scroll is the same after switching documents"); + await selectSource(dbg, "simple1.js"); + is(cm.getScrollInfo().top, 0); + await selectSource(dbg, "long.js"); + is(cm.getScrollInfo().top, 600); + + info("Trigger a pause, click on a frame, ensure the right line is selected"); + invokeInTab("doNamedEval"); + await waitForPaused(dbg); + findElement(dbg, "frame", 1).focus(); + await clickElement(dbg, "frame", 1); + ok(cm.getScrollInfo().top != 0, "frame scrolled down to correct location"); + + info("Navigating while paused, goes to the correct location"); + await selectSource(dbg, "long.js"); + is(cm.getScrollInfo().top, 600); + + info("Open new source, ensure it's at 0 scroll"); + await selectSource(dbg, "frames.js"); + is(cm.getScrollInfo().top, 0); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-editor-select.js b/devtools/client/debugger/test/mochitest/browser_dbg-editor-select.js new file mode 100644 index 0000000000..f5002f864f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-editor-select.js @@ -0,0 +1,53 @@ +/* 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 . */ + +// Tests that the editor highlights the correct location when the +// debugger pauses + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + // This test runs too slowly on linux debug. I'd like to figure out + // which is the slowest part of this and make it run faster, but to + // fix a frequent failure allow a longer timeout. + const dbg = await initDebugger("doc-scripts.html"); + + info("Set the initial breakpoint."); + await selectSource(dbg, "simple1.js"); + await addBreakpoint(dbg, "simple1.js", 4); + + info("Call the function that we set a breakpoint in."); + invokeInTab("main"); + await waitForPaused(dbg); + await waitForSelectedSource(dbg, "simple1.js"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "simple1.js").id, 4); + + info("Step into another file."); + await stepOver(dbg); + await stepIn(dbg); + await waitForSelectedSource(dbg, "simple2.js"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "simple2.js").id, 3); + + info("Step out to the initial file."); + await stepOut(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "simple1.js").id, 6); + await resume(dbg); + + info("Make sure that the editor scrolls to the paused location."); + const longSrc = findSource(dbg, "long.js"); + await selectSource(dbg, "long.js"); + await addBreakpoint(dbg, longSrc, 66); + + invokeInTab("testModel"); + await waitForPaused(dbg); + await waitForSelectedSource(dbg, "long.js"); + + assertPausedAtSourceAndLine(dbg, findSource(dbg, "long.js").id, 66); + ok( + isVisibleInEditor(dbg, findElement(dbg, "breakpoint")), + "Breakpoint is visible" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-ember-quickstart.js b/devtools/client/debugger/test/mochitest/browser_dbg-ember-quickstart.js new file mode 100644 index 0000000000..c8ef196da7 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-ember-quickstart.js @@ -0,0 +1,25 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("ember/quickstart/dist/"); + dbg.actions.toggleMapScopes(); + + await invokeWithBreakpoint( + dbg, + "mapTestFunction", + "router.js", + { line: 13, column: 2 }, + async () => { + await assertScopes(dbg, [ + "Module", + ["config", "{\u2026}"], + "EmberRouter:Class()", + "Router:Class()", + ]); + } + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js b/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js new file mode 100644 index 0000000000..93123187e1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js @@ -0,0 +1,75 @@ +/* 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 . */ + +// Test that we can debug workers using ES Modules. + +"use strict"; + +const httpServer = createTestHTTPServer(); +httpServer.registerContentType("html", "text/html"); +httpServer.registerContentType("js", "application/javascript"); + +httpServer.registerPathHandler(`/`, function (request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + `); +}); + +httpServer.registerPathHandler("/worker.js", function (request, response) { + response.setHeader("Content-Type", "application/javascript"); + response.write(` + onmessage = () => { + console.log("breakpoint line"); + postMessage("resume"); + } + `); +}); +const port = httpServer.identity.primaryPort; +const TEST_URL = `http://localhost:${port}/`; +const WORKER_URL = TEST_URL + "worker.js"; + +add_task(async function () { + const dbg = await initDebuggerWithAbsoluteURL(TEST_URL); + + // Note that the following APIs only returns the additional threads + await waitForThreadCount(dbg, 1); + const threads = dbg.selectors.getThreads(); + is(threads.length, 1, "Got the page and the worker threads"); + is(threads[0].name, WORKER_URL, "Thread name is correct"); + + const source = findSource(dbg, "worker.js"); + await selectSource(dbg, source); + await addBreakpoint(dbg, source, 3); + + info("Call worker code to trigger the breakpoint"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.wrappedJSObject.worker.postMessage("foo"); + }); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 3); + assertTextContentOnLine(dbg, 3, `console.log("breakpoint line");`); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + // Create a Promise which will resolve once the worker resumes and send a message + // Hold the promise on window, so that a following task can use it. + content.onWorkerResumed = new Promise( + resolve => (content.wrappedJSObject.worker.onmessage = resolve) + ); + }); + + await resume(dbg); + + info("Wait for the worker to resume its execution and send a message"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + ok( + content.onWorkerResumed, + "The promise created by the previous task exists" + ); + await content.onWorkerResumed; + ok(true, "The worker resumed its execution"); + }); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-eval-throw.js b/devtools/client/debugger/test/mochitest/browser_dbg-eval-throw.js new file mode 100644 index 0000000000..803037d921 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-eval-throw.js @@ -0,0 +1,18 @@ +/* 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 . */ + +// Test that exceptions thrown while evaluating code will pause at the point the +// exception was generated when pausing on uncaught exceptions. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-eval-throw.html"); + await togglePauseOnExceptions(dbg, true, true); + + invokeInTab("evaluateException"); + await waitForPaused(dbg); + const source = dbg.selectors.getSelectedSource(); + ok(!source.url, "Selected source should not have a URL"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-fission.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-fission.js new file mode 100644 index 0000000000..072b840a14 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints-fission.js @@ -0,0 +1,81 @@ +/* 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 . */ + +// Tests early event breakpoints and event breakpoints in a remote frame. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-event-breakpoints-fission.html", + "event-breakpoints.js" + ); + + await selectSource(dbg, "event-breakpoints.js"); + await waitForSelectedSource(dbg, "event-breakpoints.js"); + + await dbg.actions.addEventListenerBreakpoints([ + "event.mouse.click", + "event.xhr.load", + "timer.timeout.set", + ]); + + info("Assert early timeout event breakpoint gets hit"); + const waitForReload = reloadBrowser(); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-event-breakpoints-fission.html").id, + 17 + ); + await resume(dbg); + + await waitForReload; + + info("Assert event breakpoints work in remote frame"); + await invokeAndAssertBreakpoints(dbg); + + info("reload the iframe"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => + content.wrappedJSObject.reloadIframe() + ); + info("Assert event breakpoints work in remote frame after reload"); + await invokeAndAssertBreakpoints(dbg); +}); + +async function invokeAndAssertBreakpoints(dbg) { + invokeInTabRemoteFrame("clickHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "event-breakpoints.js").id, + 12 + ); + await resume(dbg); + + invokeInTabRemoteFrame("xhrHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "event-breakpoints.js").id, + 20 + ); + await resume(dbg); +} + +async function invokeInTabRemoteFrame(fnc, ...args) { + info(`Invoking in tab remote frame: ${fnc}(${args.map(uneval).join(",")})`); + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [fnc, args], + function (_fnc, _args) { + return SpecialPowers.spawn( + content.document.querySelector("iframe"), + [_fnc, _args], + (__fnc, __args) => content.wrappedJSObject[__fnc](...__args) + ); + } + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js new file mode 100644 index 0000000000..416a9bb43f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js @@ -0,0 +1,294 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + await pushPref("apz.scrollend-event.content.enabled", true); + + const dbg = await initDebugger( + "doc-event-breakpoints.html", + "event-breakpoints.js" + ); + await selectSource(dbg, "event-breakpoints.js"); + await waitForSelectedSource(dbg, "event-breakpoints.js"); + const eventBreakpointsSource = findSource(dbg, "event-breakpoints.js"); + + // We want to set each breakpoint individually to test adding/removing breakpoints, see Bug 1748589. + await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click"); + + invokeInTab("clickHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12); + + const whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is( + whyPaused, + `Paused on event breakpoint\nDOM 'click' event`, + "whyPaused does state that the debugger is paused as a result of a click event breakpoint" + ); + await resume(dbg); + + await toggleEventBreakpoint(dbg, "XHR", "event.xhr.load"); + invokeInTab("xhrHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 20); + await resume(dbg); + + await toggleEventBreakpoint(dbg, "Timer", "timer.timeout.set"); + await toggleEventBreakpoint(dbg, "Timer", "timer.timeout.fire"); + invokeInTab("timerHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 27); + await resume(dbg); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 28); + await resume(dbg); + + await toggleEventBreakpoint(dbg, "Script", "script.source.firstStatement"); + invokeInTab("evalHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "eval-test.js").id, 2); + await resume(dbg); + + await toggleEventBreakpoint(dbg, "Control", "event.control.focusin"); + await toggleEventBreakpoint(dbg, "Control", "event.control.focusout"); + invokeOnElement("#focus-text", "focus"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 43); + await resume(dbg); + + // wait for focus-out event to fire + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 48); + await resume(dbg); + + info("Deselect focus events"); + // We need to give the input focus to test composition, but we don't want the + // focus breakpoints to fire. + await toggleEventBreakpoint(dbg, "Control", "event.control.focusin"); + await toggleEventBreakpoint(dbg, "Control", "event.control.focusout"); + + // TODO: Enable this block when you fix bug 1466596 or bug 1690827 + /* + await toggleEventBreakpoint( + dbg, + "Keyboard", + "event.keyboard.compositionstart" + ); + invokeOnElement("#focus-text", "focus"); + + info("Type some characters during composition"); + invokeComposition(); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 53); + await resume(dbg); + + info("Deselect compositionstart and select compositionupdate"); + await toggleEventBreakpoint( + dbg, + "Keyboard", + "event.keyboard.compositionstart" + ); + await toggleEventBreakpoint( + dbg, + "Keyboard", + "event.keyboard.compositionupdate" + ); + + invokeOnElement("#focus-text", "focus"); + + info("Type some characters during composition"); + invokeComposition(); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 58); + await resume(dbg); + + info("Deselect compositionupdate and select compositionend"); + await toggleEventBreakpoint( + dbg, + "Keyboard", + "event.keyboard.compositionupdate" + ); + await toggleEventBreakpoint(dbg, "Keyboard", "event.keyboard.compositionend"); + invokeOnElement("#focus-text", "focus"); + + info("Type some characters during composition"); + invokeComposition(); + + info("Commit the composition"); + EventUtils.synthesizeComposition({ + type: "compositioncommitasis", + key: { key: "KEY_Enter" }, + }); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 63); + await resume(dbg); + */ + + info(`Check that breakpoint can be set on "scrollend"`); + await toggleEventBreakpoint(dbg, "Control", "event.control.scrollend"); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.scrollTo({ top: 20, behavior: "smooth" }); + }); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 68); + await resume(dbg); + + info("Check that the click event breakpoint is still enabled"); + invokeInTab("clickHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12); + await resume(dbg); + + info("Check that disabling an event breakpoint works"); + await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click"); + invokeInTab("clickHandler"); + // wait for a bit to make sure the debugger do not pause + await wait(100); + assertNotPaused(dbg); + + info("Check that we can re-enable event breakpoints"); + await toggleEventBreakpoint(dbg, "Mouse", "event.mouse.click"); + invokeInTab("clickHandler"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 12); + await resume(dbg); + + info( + "Test that we don't pause on event breakpoints when source is blackboxed." + ); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + + invokeInTab("clickHandler"); + // wait for a bit to make sure the debugger do not pause + await wait(100); + assertNotPaused(dbg); + + invokeInTab("xhrHandler"); + // wait for a bit to make sure the debugger do not pause + await wait(100); + assertNotPaused(dbg); + + invokeInTab("timerHandler"); + // wait for a bit to make sure the debugger do not pause + await wait(100); + assertNotPaused(dbg); + + // Cleanup - unblackbox the source + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); +}); + +add_task(async function checkUnavailableEvents() { + await pushPref("apz.scrollend-event.content.enabled", false); + + const dbg = await initDebugger( + "doc-event-breakpoints.html", + "event-breakpoints.js" + ); + await selectSource(dbg, "event-breakpoints.js"); + await waitForSelectedSource(dbg, "event-breakpoints.js"); + + is( + await getEventBreakpointCheckbox(dbg, "Control", "event.control.scrollend"), + null, + `"scrollend" item is not displayed when "apz.scrollend-event.content.enabled" is false` + ); +}); + +function getEventListenersPanel(dbg) { + return findElementWithSelector(dbg, ".event-listeners-pane .event-listeners"); +} + +async function toggleEventBreakpoint( + dbg, + eventBreakpointGroup, + eventBreakpointName +) { + const eventCheckbox = await getEventBreakpointCheckbox( + dbg, + eventBreakpointGroup, + eventBreakpointName + ); + eventCheckbox.scrollIntoView(); + info(`Toggle ${eventBreakpointName} breakpoint`); + const onEventListenersUpdate = waitForDispatch( + dbg.store, + "UPDATE_EVENT_LISTENERS" + ); + eventCheckbox.click(); + await onEventListenersUpdate; +} + +async function getEventBreakpointCheckbox( + dbg, + eventBreakpointGroup, + eventBreakpointName +) { + if (!getEventListenersPanel(dbg)) { + // Event listeners panel is collapse, expand it + findElementWithSelector( + dbg, + `.event-listeners-pane ._header .arrow` + ).click(); + await waitFor(() => getEventListenersPanel(dbg)); + } + + const groupCheckbox = findElementWithSelector( + dbg, + `input[value="${eventBreakpointGroup}"]` + ); + const groupEl = groupCheckbox.closest(".event-listener-group"); + let groupEventsUl = groupEl.querySelector("ul"); + if (!groupEventsUl) { + info( + `Expand ${eventBreakpointGroup} and wait for the sub list to be displayed` + ); + groupEl.querySelector(".event-listener-expand").click(); + groupEventsUl = await waitFor(() => groupEl.querySelector("ul")); + } + + return findElementWithSelector(dbg, `input[value="${eventBreakpointName}"]`); +} + +async function invokeOnElement(selector, action) { + await SpecialPowers.focus(gBrowser.selectedBrowser); + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [selector, action], + (_selector, _action) => { + content.document.querySelector(_selector)[_action](); + } + ); +} + +// TODO: Enable this function when you fix bug 1466596 or bug 1690827 +/* +function invokeComposition() { + const string = "ex"; + EventUtils.synthesizeCompositionChange({ + composition: { + string, + clauses: [ + { + length: string.length, + attr: Ci.nsITextInputProcessor.ATTR_RAW_CLAUSE, + }, + ], + }, + caret: { start: string.length, length: 0 }, + key: { key: string[string.length - 1] }, + }); +} +*/ diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-handler.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-handler.js new file mode 100644 index 0000000000..10acc9f118 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-event-handler.js @@ -0,0 +1,18 @@ +/* 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 . */ + +// Test that pausing within an event handler on an element does *not* show the +// HTML page containing that element. It should show a sources tab containing +// just the handler's text instead. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-event-handler.html"); + + invokeInTab("synthesizeClick"); + await waitForPaused(dbg); + const source = dbg.selectors.getSelectedSource(); + ok(!source.url, "Selected source should not have a URL"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-expressions-error.js b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-error.js new file mode 100644 index 0000000000..af54bc1a92 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-error.js @@ -0,0 +1,51 @@ +/* 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 . */ + +/** + * test pausing on an errored watch expression + * assert that you can: + * 1. resume + * 2. still evalutate expressions + * 3. expand properties + */ + +"use strict"; + +const EXPRESSION_SELECTORS = { + plusIcon: ".watch-expressions-pane button.plus", + input: "input.input-expression", +}; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + await togglePauseOnExceptions(dbg, true, true); + + // add a good expression, 2 bad expressions, and another good one + info(`Adding location`); + await addExpression(dbg, "location"); + await addExpression(dbg, "foo.bar"); + await addExpression(dbg, "foo.batt"); + await addExpression(dbg, "2"); + // check the value of + is(getWatchExpressionValue(dbg, 2), "(unavailable)"); + is(getWatchExpressionValue(dbg, 3), "(unavailable)"); + is(getWatchExpressionValue(dbg, 4), "2"); + + await toggleExpressionNode(dbg, 1); + is(findAllElements(dbg, "expressionNodes").length, 37); +}); + +async function addExpression(dbg, input) { + const plusIcon = findElementWithSelector(dbg, EXPRESSION_SELECTORS.plusIcon); + if (plusIcon) { + plusIcon.click(); + } + + const evaluation = waitForDispatch(dbg.store, "EVALUATE_EXPRESSION"); + findElementWithSelector(dbg, EXPRESSION_SELECTORS.input).focus(); + type(dbg, input); + pressKey(dbg, "Enter"); + await evaluation; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-expressions-focus.js b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-focus.js new file mode 100644 index 0000000000..07054fd80f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-focus.js @@ -0,0 +1,30 @@ +/* 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 . */ + +// Ensures the input is displayed and focused when "+" is clicked + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + info(">> Close the panel"); + clickElementWithSelector(dbg, ".watch-expressions-pane ._header"); + + info(">> Click + to add the new expression"); + await waitForElementWithSelector( + dbg, + ".watch-expressions-pane ._header .plus" + ); + clickElementWithSelector(dbg, ".watch-expressions-pane ._header .plus"); + + info(">> Ensure element gets focused"); + await waitForElementWithSelector(dbg, ".expression-input-container.focused"); + + info(">> Ensure the element is focused"); + is( + dbg.win.document.activeElement.classList.contains("input-expression"), + true + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-expressions-thread.js b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-thread.js new file mode 100644 index 0000000000..2d4dc94c9a --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-thread.js @@ -0,0 +1,97 @@ +/* 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 . */ + +/** + * Test the watch expressions update when selecting a different thread in the thread panel. + */ + +"use strict"; + +const TEST_COM_URI = `${URL_ROOT_COM_SSL}examples/doc_dbg-fission-frame-sources.html`; +const TEST_ORG_IFRAME_URI = `${URL_ROOT_ORG_SSL}examples/doc_dbg-fission-frame-sources-frame.html`; +const DATA_URI = "data:text/html,foo"; + +add_task(async function () { + // Load a test page with a remote frame and wait for both sources to be visible. + // simple1.js is imported by the main page. simple2.js comes from the frame. + const dbg = await initDebuggerWithAbsoluteURL( + TEST_COM_URI, + "simple1.js", + "simple2.js" + ); + + const threadsPaneEl = await waitForElementWithSelector( + dbg, + ".threads-pane .header-label" + ); + + await waitForElement(dbg, "threadsPaneItems"); + const threadsEl = findAllElements(dbg, "threadsPaneItems"); + is(threadsEl.length, 2, "There are two threads in the thread panel"); + const [mainThreadEl, remoteThreadEl] = threadsEl; + is( + mainThreadEl.textContent, + "Main Thread", + "first thread displayed is the main thread" + ); + is( + remoteThreadEl.textContent, + "Test remote frame sources", + "second thread displayed is the remote thread" + ); + + await addExpression(dbg, "document.location.href"); + + is( + getWatchExpressionValue(dbg, 1), + JSON.stringify(TEST_COM_URI), + "expression is evaluated on the expected thread" + ); + + info( + "Select the remote frame thread and check that the expression is updated" + ); + let onExpressionsEvaluated = waitForDispatch( + dbg.store, + "EVALUATE_EXPRESSIONS" + ); + remoteThreadEl.click(); + await onExpressionsEvaluated; + + is( + getWatchExpressionValue(dbg, 1), + JSON.stringify(TEST_ORG_IFRAME_URI), + "expression is evaluated on the remote origin thread" + ); + + info("Select the main thread again and check that the expression is updated"); + onExpressionsEvaluated = waitForDispatch(dbg.store, "EVALUATE_EXPRESSIONS"); + mainThreadEl.click(); + await onExpressionsEvaluated; + + is( + getWatchExpressionValue(dbg, 1), + JSON.stringify(TEST_COM_URI), + "expression is evaluated on the main thread again" + ); + + // close the threads pane so following test don't have it open + threadsPaneEl.click(); + + await navigateToAbsoluteURL(dbg, DATA_URI); + + is( + getWatchExpressionValue(dbg, 1), + JSON.stringify(DATA_URI), + "The location.host expression is updated after a navigaiton" + ); + + await addExpression(dbg, "document.title"); + + is( + getWatchExpressionValue(dbg, 2), + `"foo"`, + "We can add expressions after a navigation" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-expressions-watch.js b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-watch.js new file mode 100644 index 0000000000..5f2c6aba40 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-expressions-watch.js @@ -0,0 +1,91 @@ +/* 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 . */ + +/** + * Test the watch expressions "refresh" button: + * - hidden when no expression is available + * - visible with one or more expressions + * - updates expressions values after clicking on it + * - disappears when all expressions are removed + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + invokeInTab("firstCall"); + await waitForPaused(dbg); + + ok( + !getRefreshExpressionsElement(dbg), + "No refresh button is displayed when there are no watch expressions" + ); + + await addExpression(dbg, "someVariable"); + + ok( + getRefreshExpressionsElement(dbg), + "Refresh button is displayed after adding a watch expression" + ); + + is( + getWatchExpressionLabel(dbg, 1), + "someVariable", + "Watch expression was added" + ); + is( + getWatchExpressionValue(dbg, 1), + "(unavailable)", + "Watch expression has no value" + ); + + info("Switch to the console and update the value of the watched variable"); + const { hud } = await dbg.toolbox.selectTool("webconsole"); + await evaluateExpressionInConsole(hud, "var someVariable = 1"); + + info("Switch back to the debugger"); + await dbg.toolbox.selectTool("jsdebugger"); + + is( + getWatchExpressionLabel(dbg, 1), + "someVariable", + "Watch expression is still available" + ); + is( + getWatchExpressionValue(dbg, 1), + "(unavailable)", + "Watch expression still has no value" + ); + + info( + "Click on the watch expression refresh button and wait for the " + + "expression to update." + ); + const refreshed = waitForDispatch(dbg.store, "EVALUATE_EXPRESSIONS"); + await clickElement(dbg, "expressionRefresh"); + await refreshed; + + is( + getWatchExpressionLabel(dbg, 1), + "someVariable", + "Watch expression is still available" + ); + is( + getWatchExpressionValue(dbg, 1), + "1", + "Watch expression value has been updated" + ); + + await deleteExpression(dbg, "someVariable"); + + ok( + !getRefreshExpressionsElement(dbg), + "The refresh button is no longer displayed after removing watch expressions" + ); +}); + +function getRefreshExpressionsElement(dbg) { + return findElement(dbg, "expressionRefresh", 1); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-expressions.js b/devtools/client/debugger/test/mochitest/browser_dbg-expressions.js new file mode 100644 index 0000000000..44dcd81f43 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-expressions.js @@ -0,0 +1,73 @@ +/* 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 . */ + +/** + * tests the watch expressions component + * 1. add watch expressions + * 2. edit watch expressions + * 3. delete watch expressions + * 4. expanding properties when not paused + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + invokeInTab("firstCall"); + await waitForPaused(dbg); + + await addExpression(dbg, "f"); + is(getWatchExpressionLabel(dbg, 1), "f"); + is(getWatchExpressionValue(dbg, 1), "(unavailable)"); + + await editExpression(dbg, "oo"); + is(getWatchExpressionLabel(dbg, 1), "foo()"); + + // There is no "value" element for functions. + assertEmptyValue(dbg, 1); + + await addExpression(dbg, "location"); + is(getWatchExpressionLabel(dbg, 2), "location"); + ok(getWatchExpressionValue(dbg, 2).includes("Location"), "has a value"); + + // can expand an expression + await toggleExpressionNode(dbg, 2); + + is(findAllElements(dbg, "expressionNodes").length, 35); + is(dbg.selectors.getExpressions(dbg.store.getState()).length, 2); + + await deleteExpression(dbg, "foo"); + await deleteExpression(dbg, "location"); + is(findAllElements(dbg, "expressionNodes").length, 0); + is(dbg.selectors.getExpressions(dbg.store.getState()).length, 0); + + // Test expanding properties when the debuggee is active + // Wait for full evaluation of the expressions in order to avoid having + // mixed up code between the location being removed and the one being re-added + const evaluated = waitForDispatch(dbg.store, "EVALUATE_EXPRESSIONS"); + await resume(dbg); + await evaluated; + + await addExpression(dbg, "location"); + is(dbg.selectors.getExpressions(dbg.store.getState()).length, 1); + + is(findAllElements(dbg, "expressionNodes").length, 1); + + await toggleExpressionNode(dbg, 1); + is(findAllElements(dbg, "expressionNodes").length, 34); + + await deleteExpression(dbg, "location"); + is(findAllElements(dbg, "expressionNodes").length, 0); +}); + +function assertEmptyValue(dbg, index) { + const value = findElement(dbg, "expressionValue", index); + if (value) { + is(value.innerText, ""); + return; + } + + is(value, null); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-extension-inspectedWindow-debugger-statement.js b/devtools/client/debugger/test/mochitest/browser_dbg-extension-inspectedWindow-debugger-statement.js new file mode 100644 index 0000000000..790b0366b9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-extension-inspectedWindow-debugger-statement.js @@ -0,0 +1,88 @@ +/* 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 . */ + +// Test that web extensions' inspectedWindow.eval() doesn't break debugger/console + +"use strict"; + +// Test debugger statement in page, with devtools opened to debugger panel +add_task(async function () { + const extension = await installAndStartExtension(); + + const dbg = await initDebugger("doc-scripts.html"); + + await extension.awaitMessage("loaded"); + + info("Evaluating debugger statement in page"); + const evalFinished = invokeInTab("nestedC"); + await waitForPaused(dbg); + + info("resuming once"); + await resume(dbg); + + // bug 1728290: WebExtension target used to trigger the thread actor and also pause a second time on the debugger statement. + // This would prevent the evaluation from completing. + info("waiting for invoked function to complete"); + await evalFinished; + + await closeTabAndToolbox(); + await extension.unload(); +}); + +// Test debugger statement in webconsole +add_task(async function () { + const extension = await installAndStartExtension(); + + // Test again with debugger panel closed + const toolbox = await openNewTabAndToolbox( + EXAMPLE_URL + "doc-scripts.html", + "webconsole" + ); + await extension.awaitMessage("loaded"); + + info("Evaluating debugger statement in console"); + const onSelected = toolbox.once("jsdebugger-selected"); + const evalFinished = invokeInTab("nestedC"); + + await onSelected; + const dbg = createDebuggerContext(toolbox); + await waitForPaused(dbg); + + info("resuming once"); + await resume(dbg); + + await evalFinished; + + await closeTabAndToolbox(); + await extension.unload(); +}); + +async function installAndStartExtension() { + async function devtools_page() { + await globalThis.browser.devtools.inspectedWindow.eval(""); + globalThis.browser.test.sendMessage("loaded"); + } + + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + devtools_page: "devtools_page.html", + }, + files: { + "devtools_page.html": ` + + + + + + + + `, + "devtools_page.js": devtools_page, + }, + }); + + await extension.startup(); + + return extension; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-asm.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-asm.js new file mode 100644 index 0000000000..15c7737072 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-asm.js @@ -0,0 +1,92 @@ +/* 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 . */ + +/** + * This test covers all specifics of debugging ASM.js files. + * + * ASM.js is a subset of the Javascript syntax. + * Thanks to these limitations, the JS engine is able to compile this code + * into machine instructions and execute it faster. + * + * When the DevTools are opened, ThreadConfiguration's `observeAsmJS` is set to true, + * which sets DebuggerAPI's `allowUnobservedAsmJS` to false, + * which disables the compilation of ASM.js files and make them run as regular JS code. + * Thus, allowing to debug them as regular JS code. + * + * This behavior introduces some limitations when opening the debugger against + * and already loaded page. The ASM.js file won't be debuggable. + */ + +"use strict"; + +add_task(async function () { + // Load the test page before opening the debugger + // and also force a GC before opening the debugger + // so that ASM.js is fully garbaged collected. + // Otherwise on debug builds, the thread actor is able to intermittently + // retrieve the ASM.js sources and retrieve the breakable lines. + const tab = await addTab(EXAMPLE_URL + "doc-asm.html"); + await SpecialPowers.spawn(tab.linkedBrowser, [], function () { + Cu.forceGC(); + }); + const toolbox = await openToolboxForTab(tab, "jsdebugger"); + const dbg = createDebuggerContext(toolbox); + + // There is the legit and the spurious wasm source (see following comment) + await waitForSourcesInSourceTree(dbg, ["doc-asm.html", "asm.js", "asm.js"]); + is(dbg.selectors.getSourceCount(), 3, "There are only three sources"); + + const legitSource = findSource(dbg, EXAMPLE_URL + "asm.js"); + ok( + legitSource.url.startsWith("https://"), + "We got the legit source that works, not the spurious WASM one" + ); + is(legitSource.isWasm, false, "ASM.js sources are *not* flagged as WASM"); + + // XXX Bug 1759573 - There is a spurious wasm source reported which is broken + // and ideally shouldn't exists at all in UI. + // The Thread Actor is notified by the Debugger API about a WASM + // source when calling Debugger.findSources in ThreadActor.addAllSources. + const wasmUrl = "wasm:" + legitSource.url; + ok( + sourceExists(dbg, wasmUrl), + `There is a spurious wasm:// source displayed: ${wasmUrl}` + ); + + await selectSource(dbg, legitSource); + + assertTextContentOnLine(dbg, 7, "return 1 | 0;"); + + info( + "Before reloading, ThreadConfiguration's 'observedAsmJS' was false while the page was loading" + ); + info( + "So that we miss info about the ASM sources and lines are not breakables" + ); + assertLineIsBreakable(dbg, legitSource.url, 7, false); + + info("Reload and assert that ASM.js file are then debuggable"); + await reload(dbg, "doc-asm.html", "asm.js"); + + info("After reloading, ASM lines are breakable"); + // Ensure selecting the source before asserting breakable lines + // otherwise the gutter may not be yet updated + await selectSource(dbg, "asm.js"); + assertLineIsBreakable(dbg, legitSource.url, 7, true); + + await waitForSourcesInSourceTree(dbg, ["doc-asm.html", "asm.js"]); + is(dbg.selectors.getSourceCount(), 2, "There is only the two sources"); + + assertTextContentOnLine(dbg, 7, "return 1 | 0;"); + + await addBreakpoint(dbg, "asm.js", 7); + invokeInTab("runAsm"); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "asm.js").id, 7); + await assertBreakpoint(dbg, 7); + + await removeBreakpoint(dbg, findSource(dbg, "asm.js").id, 7); + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-lines.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-lines.js new file mode 100644 index 0000000000..872530025f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-lines.js @@ -0,0 +1,92 @@ +/* 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 . */ + +"use strict"; + +const testServer = createVersionizedHttpTestServer( + "examples/sourcemaps-reload-uncompressed" +); +const TEST_URL = testServer.urlFor("index.html"); + +// Assert the behavior of the gutter that grays out non-breakable lines +add_task(async function testBreakableLinesOverReloads() { + const dbg = await initDebuggerWithAbsoluteURL( + TEST_URL, + "index.html", + "script.js", + "original.js" + ); + + info("Assert breakable lines of the first html page load"); + await assertBreakableLines(dbg, "index.html", 78, [ + ...getRange(16, 17), + 21, + ...getRange(24, 25), + 30, + 36, + ]); + + info("Assert breakable lines of the first original source file, original.js"); + // The length of original.js is longer than the test file + // because the sourcemap replaces the content of the original file + // and appends a few lines with a "WEBPACK FOOTER" comment + // All the appended lines are empty lines or comments, so none of them are breakable. + await assertBreakableLines(dbg, "original.js", 15, [ + ...getRange(1, 3), + 5, + ...getRange(8, 10), + ]); + + info("Assert breakable lines of the simple first load of script.js"); + await assertBreakableLines(dbg, "script.js", 9, [1, 5, 7, 8, 9]); + + info("Assert breakable lines of the first iframe page load"); + await assertBreakableLines(dbg, "iframe.html", 30, [ + ...getRange(16, 17), + ...getRange(22, 23), + ]); + + info( + "Reload the page, wait for sources and assert that breakable lines get updated" + ); + testServer.switchToNextVersion(); + await reload(dbg, "index.html", "script.js", "original.js", "iframe.html"); + + // Wait for previously selected source to be re-selected after reload + // otherwise it may overlap with the next source selected by assertBreakableLines. + await waitForSelectedSource(dbg, "iframe.html"); + + info("Assert breakable lines of the more complex second load of script.js"); + await assertBreakableLines(dbg, "script.js", 23, [2, ...getRange(13, 23)]); + + info("Assert breakable lines of the second html page load"); + await assertBreakableLines(dbg, "index.html", 33, [25, 27]); + + info("Assert breakable lines of the second orignal file"); + // See first assertion about original.js, + // the size of original.js doesn't match the size of the test file + await assertBreakableLines(dbg, "original.js", 18, [ + ...getRange(1, 3), + ...getRange(8, 11), + 13, + ]); + + await selectSource(dbg, "iframe.html"); + // When EFT is disabled, iframe.html is a regular source and the right content is displayed + if (isEveryFrameTargetEnabled()) { + is( + getCM(dbg).getValue(), + `Error: Incorrect contents fetched, please reload.` + ); + } + /** + * Bug 1762381 - Can't assert breakable lines yet, because the iframe page content fails loading + + info("Assert breakable lines of the second iframe page load"); + await assertBreakableLines(dbg, "iframe.html", 27, [ + ...getRange(15, 17), + ...getRange(21, 23), + ]); + */ +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-positions.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-positions.js new file mode 100644 index 0000000000..b1e13f6cd7 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-breakable-positions.js @@ -0,0 +1,284 @@ +/* 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 . */ + +"use strict"; + +const testServer = createVersionizedHttpTestServer( + "examples/sourcemaps-reload-uncompressed" +); +const TEST_URL = testServer.urlFor("index.html"); + +// getTokenFromPosition pauses 0.5s for each line, +// so this test is quite slow to complete +requestLongerTimeout(4); + +/** + * Cover the breakpoints positions/columns: + * - assert that the UI displayed markers in CodeMirror next to each breakable columns, + * - assert the data in the reducers about the breakable columns. + * + * Note that it doesn't assert that the breakpoint can be hit. + * It only verify data integrity and the UI. + */ +add_task(async function testBreakableLinesOverReloads() { + const dbg = await initDebuggerWithAbsoluteURL( + TEST_URL, + "index.html", + "script.js", + "original.js" + ); + + info("Assert breakable lines of the first html page load"); + await assertBreakablePositions(dbg, "index.html", 78, [ + { line: 16, columns: [6, 14] }, + { line: 17, columns: [] }, + { line: 21, columns: [12, 20, 48] }, + { line: 24, columns: [12, 20] }, + { line: 25, columns: [] }, + { line: 30, columns: [] }, + { line: 36, columns: [] }, + ]); + + info("Pretty print first html page load and assert breakable lines"); + await prettyPrint(dbg); + await assertBreakablePositions(dbg, "index.html:formatted", 87, [ + { line: 16, columns: [0, 8] }, + { line: 22, columns: [0, 8, 35] }, + { line: 27, columns: [0, 8] }, + { line: 28, columns: [] }, + { line: 36, columns: [] }, + ]); + await closeTab(dbg, "index.html:formatted"); + + info("Assert breakable lines of the first original source file, original.js"); + // The length of original.js is longer than the test file + // because the sourcemap replaces the content of the original file + // and appends a few lines with a "WEBPACK FOOTER" comment + // All the appended lines are empty lines or comments, so none of them are breakable. + await assertBreakablePositions(dbg, "original.js", 15, [ + { line: 1, columns: [] }, + { line: 2, columns: [2, 9, 32] }, + { line: 3, columns: [] }, + { line: 5, columns: [] }, + { line: 8, columns: [2, 8] }, + { line: 9, columns: [2, 10] }, + { line: 10, columns: [] }, + ]); + + info("Assert breakable lines of the simple first load of script.js"); + await assertBreakablePositions(dbg, "script.js", 9, [ + { line: 1, columns: [0, 8] }, + { line: 5, columns: [2, 10] }, + { line: 7, columns: [2, 9] }, + { line: 8, columns: [] }, + { line: 9, columns: [] }, + ]); + + info("Pretty print first load of script.js and assert breakable lines"); + await prettyPrint(dbg); + await assertBreakablePositions(dbg, "script.js:formatted", 8, [ + { line: 1, columns: [0, 8] }, + { line: 4, columns: [2, 10] }, + { line: 6, columns: [2, 9] }, + { line: 7, columns: [] }, + ]); + await closeTab(dbg, "script.js:formatted"); + + info( + "Reload the page, wait for sources and assert that breakable lines get updated" + ); + testServer.switchToNextVersion(); + await reload(dbg, "index.html", "script.js", "original.js"); + + info("Assert breakable lines of the more complex second load of script.js"); + await assertBreakablePositions(dbg, "script.js", 23, [ + { line: 2, columns: [0, 8] }, + { line: 13, columns: [4, 12] }, + { line: 14, columns: [] }, + { line: 15, columns: [] }, + { line: 16, columns: [] }, + { line: 17, columns: [] }, + { line: 18, columns: [2, 10] }, + { line: 19, columns: [] }, + { line: 20, columns: [] }, + { line: 21, columns: [] }, + { line: 22, columns: [] }, + { line: 23, columns: [] }, + ]); + + info("Pretty print first load of script.js and assert breakable lines"); + await prettyPrint(dbg); + await assertBreakablePositions(dbg, "script.js:formatted", 23, [ + { line: 2, columns: [0, 8] }, + { line: 13, columns: [4, 12] }, + { line: 14, columns: [] }, + { line: 15, columns: [] }, + { line: 16, columns: [] }, + { line: 17, columns: [] }, + { line: 18, columns: [2, 10] }, + { line: 19, columns: [] }, + { line: 20, columns: [] }, + { line: 21, columns: [] }, + { line: 22, columns: [] }, + ]); + await closeTab(dbg, "script.js:formatted"); + + info("Assert breakable lines of the second html page load"); + await assertBreakablePositions(dbg, "index.html", 33, [ + { line: 25, columns: [6, 14] }, + { line: 27, columns: [] }, + ]); + + info("Pretty print second html page load and assert breakable lines"); + await prettyPrint(dbg); + await assertBreakablePositions(dbg, "index.html:formatted", 33, [ + { line: 25, columns: [0, 8] }, + ]); + await closeTab(dbg, "index.html:formatted"); + + info("Assert breakable lines of the second orignal file"); + // See first assertion about original.js, + // the size of original.js doesn't match the size of the test file + await assertBreakablePositions(dbg, "original.js", 18, [ + { line: 1, columns: [] }, + { line: 2, columns: [2, 9, 32] }, + { line: 3, columns: [] }, + { line: 8, columns: [] }, + { line: 9, columns: [2, 8] }, + { line: 10, columns: [2, 10] }, + { line: 11, columns: [] }, + { line: 13, columns: [] }, + ]); +}); + +async function assertBreakablePositions( + dbg, + file, + numberOfLines, + breakablePositions +) { + await selectSource(dbg, file); + is( + getCM(dbg).lineCount(), + numberOfLines, + `We show the expected number of lines in CodeMirror for ${file}` + ); + for (let line = 1; line <= numberOfLines; line++) { + info(`Asserting line #${line}`); + const positions = breakablePositions.find( + position => position.line == line + ); + // If we don't have any position, only assert that the line isn't breakable + if (!positions) { + assertLineIsBreakable(dbg, file, line, false); + continue; + } + const { columns } = positions; + // Otherwise, set a breakpoint on the line to ensure we force fetching the breakable columns per line + // (this is only fetch on-demand) + await addBreakpointViaGutter(dbg, line); + await assertBreakpoint(dbg, line); + const source = findSource(dbg, file); + + // If there is no column breakpoint, skip all further assertions + // Last lines of inline script are reported as breakable lines and selectors reports + // one breakable column, but, we don't report any available column breakpoint for them. + if (!columns.length) { + // So, only ensure that the really is no marker on this line + const lineElement = await getTokenFromPosition(dbg, { line, ch: -1 }); + const columnMarkers = lineElement.querySelectorAll(".column-breakpoint"); + is( + columnMarkers.length, + 0, + `There is no breakable columns on line ${line}` + ); + await removeBreakpoint(dbg, source.id, line); + continue; + } + + const selectorPositions = dbg.selectors.getBreakpointPositionsForSource( + source.id + ); + ok(selectorPositions, "Selector returned positions"); + const selectorPositionsForLine = selectorPositions[line]; + ok(selectorPositionsForLine, "Selector returned positions for the line"); + is( + selectorPositionsForLine.length, + columns.length, + "Selector has the expected number of breakable columns" + ); + for (const selPos of selectorPositionsForLine) { + is( + selPos.location.line, + line, + "Selector breakable column has the right line" + ); + ok( + columns.includes(selPos.location.column), + `Selector breakable column has an expected column (${ + selPos.location.column + } in ${JSON.stringify(columns)}) for line ${line}` + ); + is( + selPos.location.sourceId, + source.id, + "Selector breakable column has the right sourceId" + ); + is( + selPos.location.sourceUrl, + source.url, + "Selector breakable column has the right sourceUrl" + ); + } + + const tokenElement = await getTokenFromPosition(dbg, { line, ch: -1 }); + const lineElement = tokenElement.closest(".CodeMirror-line"); + // Those are the breakpoint chevron we click on to set a breakpoint on a given column + const columnMarkers = [ + ...lineElement.querySelectorAll(".column-breakpoint"), + ]; + is( + columnMarkers.length, + columns.length, + "Got the expeced number of column markers" + ); + + // The first breakable column received the line breakpoint when calling addBreakpoint() + const firstColumn = columns.shift(); + ok( + findColumnBreakpoint(dbg, file, line, firstColumn), + `The first column ${firstColumn} has a breakpoint automatically` + ); + columnMarkers.shift(); + + for (const column of columns) { + ok( + !findColumnBreakpoint(dbg, file, line, column), + `Before clicking on the marker, the column ${column} was not having a breakpoint` + ); + const marker = columnMarkers.shift(); + const onSetBreakpoint = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + marker.click(); + await onSetBreakpoint; + ok( + findColumnBreakpoint(dbg, file, line, column), + `Was able to set column breakpoint for ${file} @ ${line}:${column}` + ); + + const onRemoveBreakpoint = waitForDispatch( + dbg.store, + "REMOVE_BREAKPOINT" + ); + marker.click(); + await onRemoveBreakpoint; + + ok( + !findColumnBreakpoint(dbg, file, line, column), + `Removed the just-added column breakpoint` + ); + } + + await removeBreakpoint(dbg, source.id, line); + } +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-breakpoints.js new file mode 100644 index 0000000000..6cd3354cff --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-breakpoints.js @@ -0,0 +1,72 @@ +/* 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 . */ + +"use strict"; + +/** + * Assert that breakpoints and stepping works in various conditions + */ + +const testServer = createVersionizedHttpTestServer( + "examples/sourcemaps-reload-uncompressed" +); +const TEST_URL = testServer.urlFor("index.html"); + +add_task( + async function testSteppingFromOriginalToGeneratedAndAnotherOriginal() { + const dbg = await initDebuggerWithAbsoluteURL( + TEST_URL, + "index.html", + "script.js", + "original.js" + ); + + await selectSource(dbg, "original.js"); + await addBreakpoint(dbg, "original.js", 8); + assertBreakpointSnippet(dbg, 1, "await nonSourceMappedFunction();"); + + info("Test pausing on an original source"); + invokeInTab("foo"); + await waitForPaused(dbg, "original.js"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "original.js").id, 8); + + info("Then stepping into a generated source"); + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "script.js").id, 5); + + info("Stepping another time within the same generated source"); + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "script.js").id, 7); + + info("And finally stepping into another original source"); + await stepIn(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "removed-original.js").id, + 4 + ); + + info("Walk up the stack backward, until we resume execution"); + await stepIn(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "removed-original.js").id, + 5 + ); + + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "script.js").id, 8); + + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "original.js").id, 9); + + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "original.js").id, 10); + + // We can't use the `stepIn` helper as this last step will resume + // and the helper is expecting to pause again + await dbg.actions.stepIn(getThreadContext(dbg)); + await assertNotPaused(dbg); + } +); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-browser-toolbox-source-tree.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-browser-toolbox-source-tree.js new file mode 100644 index 0000000000..fa321f5f43 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-browser-toolbox-source-tree.js @@ -0,0 +1,122 @@ +/* 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 . */ + +/** + * This test focuses on the SourceTree component, within the browser toolbox. + */ + +"use strict"; + +requestLongerTimeout(2); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/framework/browser-toolbox/test/helpers-browser-toolbox.js", + this +); + +// Test that the Web extension name is shown in source tree rather than +// the extensions internal UUID. This checks both the web toolbox and the +// browser toolbox. +add_task(async function testSourceTreeNamesForWebExtensions() { + await pushPref("devtools.chrome.enabled", true); + await pushPref("devtools.browsertoolbox.scope", "everything"); + const extension = await installAndStartContentScriptExtension(); + + const dbg = await initDebugger("doc-content-script-sources.html"); + await waitForSourcesInSourceTree(dbg, [], { + noExpand: true, + }); + + is( + getSourceTreeLabel(dbg, 2), + "Test content script extension", + "Test content script extension is labeled properly" + ); + + await dbg.toolbox.closeToolbox(); + await extension.unload(); + + // Make sure the toolbox opens with the debugger selected. + await pushPref("devtools.browsertoolbox.panel", "jsdebugger"); + + const ToolboxTask = await initBrowserToolboxTask(); + await ToolboxTask.importFunctions({ + createDebuggerContext, + waitUntil, + findSourceNodeWithText, + findAllElements, + getSelector, + findAllElementsWithSelector, + assertSourceTreeNode, + }); + + await ToolboxTask.spawn(selectors, async _selectors => { + this.selectors = _selectors; + }); + + await ToolboxTask.spawn(null, async () => { + try { + /* global gToolbox */ + // Wait for the debugger to finish loading. + await gToolbox.getPanelWhenReady("jsdebugger"); + const dbgx = createDebuggerContext(gToolbox); + let rootNodeForExtensions = null; + await waitUntil(() => { + rootNodeForExtensions = findSourceNodeWithText(dbgx, "extension"); + return !!rootNodeForExtensions; + }); + // Find the root node for extensions and expand it if needed + if ( + !!rootNodeForExtensions && + !rootNodeForExtensions.querySelector(".arrow.expanded") + ) { + rootNodeForExtensions.querySelector(".arrow").click(); + } + + // Assert that extensions are displayed in the source tree + // with their extension name. + await assertSourceTreeNode(dbgx, "Picture-In-Picture"); + await assertSourceTreeNode(dbgx, "Form Autofill"); + + const threadLabels = [...findAllElements(dbgx, "sourceTreeThreads")].map( + el => { + return el.textContent; + } + ); + is( + threadLabels[0], + "Main Thread", + "The first thread is always the main thread" + ); + let lastPID = -1, + lastThreadLabel = ""; + for (let i = 1; i < threadLabels.length; i++) { + const label = threadLabels[i]; + if (label.startsWith("(pid ")) { + ok( + !lastThreadLabel, + "We should only have content process threads first after the main thread" + ); + const pid = parseInt(label.match(/pid (\d+)\)/)[1], 10); + ok( + pid >= lastPID, + `The content process threads are sorted by incremental PID ${pid} > ${lastPID}` + ); + lastPID = pid; + } else { + ok( + label.localeCompare(lastThreadLabel) >= 0, + `Worker thread labels are sorted alphabeticaly: ${label} vs ${lastThreadLabel}` + ); + lastThreadLabel = label; + } + } + } catch (e) { + console.log("Caught exception in spawn", e); + throw e; + } + }); + + await ToolboxTask.destroy(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-source-text-content.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-source-text-content.js new file mode 100644 index 0000000000..e8bb9875b3 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-source-text-content.js @@ -0,0 +1,565 @@ +/* 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 . */ + +/** + * This test focus on asserting the source content displayed in CodeMirror + * when we open a source from the SourceTree (or by any other means). + * + * The source content is being fetched from the server only on-demand. + * The main shortcoming is about sources being GC-ed. This only happens + * when we open the debugger on an already loaded page. + * When we (re)load a page while the debugger is opened, sources are never GC-ed. + * There are also specifics related to HTML page having inline scripts. + * Also, as this data is fetched on-demand, there is a loading prompt + * being displayed while the source is being fetched from the server. + */ + +"use strict"; + +const httpServer = createTestHTTPServer(); +const BASE_URL = `http://localhost:${httpServer.identity.primaryPort}/`; +const loadCounts = {}; + +/** + * Simple tests, asserting that we correctly display source text content in CodeMirror + */ +const NAMED_EVAL_CONTENT = `function namedEval() {}; console.log('eval script'); //# sourceURL=named-eval.js`; +const NEW_FUNCTION_CONTENT = + "console.log('new function'); //# sourceURL=new-function.js"; +const INDEX_PAGE_CONTENT = ` + + + + + + + + + + + + + `; +const IFRAME_CONTENT = ` + + + + + + + + + `; + +httpServer.registerPathHandler("/index.html", (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(INDEX_PAGE_CONTENT); +}); + +httpServer.registerPathHandler("/normal-script.js", (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.setHeader("Content-Type", "application/javascript"); + response.write(`console.log("normal script")`); +}); +httpServer.registerPathHandler( + "/slow-loading-script.js", + (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.processAsync(); + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(function () { + response.setHeader("Content-Type", "application/javascript"); + response.write(`console.log("slow loading script")`); + response.finish(); + }, 1000); + } +); +httpServer.registerPathHandler("/http-error-script.js", (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.setStatusLine(request.httpVersion, 404, "Not found"); + response.write(`console.log("http error")`); +}); +httpServer.registerPathHandler("/same-url.js", (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + const sameUrlLoadCount = loadCounts[request.path]; + // Prevents gecko from cache this request in order to force fetching + // a new, distinct content for each usage of this URL + response.setHeader("Cache-Control", "no-store"); + response.setHeader("Content-Type", "application/javascript"); + response.write(`console.log("same url #${sameUrlLoadCount}")`); +}); +httpServer.registerPathHandler("/iframe.html", (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.setHeader("Content-Type", "text/html"); + response.write(IFRAME_CONTENT); +}); +add_task(async function testSourceTextContent() { + const dbg = await initDebuggerWithAbsoluteURL("about:blank"); + + const waitForSources = [ + "index.html", + "normal-script.js", + "slow-loading-script.js", + "same-url.js", + "new-function.js", + ]; + + // With fission and EFT disabled, the structure of the source tree changes + // as there is no iframe thread and all the iframe sources are loaded under the + // Main thread, so nodes will be in different positions in the tree. + const noFissionNoEFT = !isFissionEnabled() && !isEveryFrameTargetEnabled(); + + if (noFissionNoEFT) { + waitForSources.push("iframe.html", "named-eval.js"); + } + + // Load the document *once* the debugger is opened + // in order to avoid having any source being GC-ed. + await navigateToAbsoluteURL(dbg, BASE_URL + "index.html", ...waitForSources); + + await selectSourceFromSourceTree( + dbg, + "new-function.js", + noFissionNoEFT ? 6 : 5, + "Select `new-function.js`" + ); + is( + getCM(dbg).getValue(), + `function anonymous(\n) {\n${NEW_FUNCTION_CONTENT}\n}` + ); + + await selectSourceFromSourceTree( + dbg, + "normal-script.js", + noFissionNoEFT ? 7 : 6, + "Select `normal-script.js`" + ); + is(getCM(dbg).getValue(), `console.log("normal script")`); + + await selectSourceFromSourceTree( + dbg, + "slow-loading-script.js", + noFissionNoEFT ? 9 : 8, + "Select `slow-loading-script.js`" + ); + is(getCM(dbg).getValue(), `console.log("slow loading script")`); + + await selectSourceFromSourceTree( + dbg, + "index.html", + noFissionNoEFT ? 4 : 3, + "Select `index.html`" + ); + is(getCM(dbg).getValue(), INDEX_PAGE_CONTENT); + + await selectSourceFromSourceTree( + dbg, + "named-eval.js", + noFissionNoEFT ? 5 : 4, + "Select `named-eval.js`" + ); + is(getCM(dbg).getValue(), NAMED_EVAL_CONTENT); + + await selectSourceFromSourceTree( + dbg, + "same-url.js", + noFissionNoEFT ? 8 : 7, + "Select `same-url.js` in the Main Thread" + ); + + is( + getCM(dbg).getValue(), + `console.log("same url #1")`, + "We get an arbitrary content for same-url, the first loaded one" + ); + + const sameUrlSource = findSource(dbg, "same-url.js"); + const sourceActors = dbg.selectors.getSourceActorsForSource(sameUrlSource.id); + + if (isFissionEnabled() || isEveryFrameTargetEnabled()) { + const mainThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == "Main Thread"); + + is( + sourceActors.filter(actor => actor.thread == mainThread.actor).length, + 3, + "same-url.js is loaded 3 times in the main thread" + ); + + info(`Close the same-url.js from Main Thread`); + await closeTab(dbg, "same-url.js"); + + info("Click on the iframe tree node to show sources in the iframe"); + await clickElement(dbg, "sourceDirectoryLabel", 9); + await waitForSourcesInSourceTree( + dbg, + [ + "index.html", + "named-eval.js", + "normal-script.js", + "slow-loading-script.js", + "same-url.js", + "iframe.html", + "same-url.js", + "new-function.js", + ], + { + noExpand: true, + } + ); + + await selectSourceFromSourceTree( + dbg, + "same-url.js", + 12, + "Select `same-url.js` in the iframe" + ); + + is( + getCM(dbg).getValue(), + `console.log("same url #3")`, + "We get the expected content for same-url.js in the iframe" + ); + + const iframeThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == `${BASE_URL}iframe.html`); + is( + sourceActors.filter(actor => actor.thread == iframeThread.actor).length, + 1, + "same-url.js is loaded one time in the iframe thread" + ); + } else { + // There is no iframe thread when fission is off + const mainThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == "Main Thread"); + + is( + sourceActors.filter(actor => actor.thread == mainThread.actor).length, + 4, + "same-url.js is loaded 4 times in the main thread without fission" + ); + } + + info(`Close the same-url.js from the iframe`); + await closeTab(dbg, "same-url.js"); + + info("Click on the worker tree node to show sources in the worker"); + + await clickElement(dbg, "sourceDirectoryLabel", noFissionNoEFT ? 10 : 13); + + const workerSources = [ + "index.html", + "named-eval.js", + "normal-script.js", + "slow-loading-script.js", + "same-url.js", + "iframe.html", + "same-url.js", + "new-function.js", + ]; + + if (!noFissionNoEFT) { + workerSources.push("same-url.js"); + } + + await waitForSourcesInSourceTree(dbg, workerSources, { + noExpand: true, + }); + + await selectSourceFromSourceTree( + dbg, + "same-url.js", + noFissionNoEFT ? 12 : 15, + "Select `same-url.js` in the worker" + ); + + is( + getCM(dbg).getValue(), + `console.log("same url #4")`, + "We get the expected content for same-url.js worker" + ); + + const workerThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == `${BASE_URL}same-url.js`); + + is( + sourceActors.filter(actor => actor.thread == workerThread.actor).length, + 1, + "same-url.js is loaded one time in the worker thread" + ); + + await selectSource(dbg, "iframe.html"); + is(getCM(dbg).getValue(), IFRAME_CONTENT); + + ok( + !sourceExists(dbg, "http-error-script.js"), + "scripts with HTTP error code do not appear in the source list" + ); + + const onNewSource = waitForDispatch(dbg.store, "ADD_SOURCES"); + invokeInTab("breakInNewFunction"); + await waitForPaused(dbg); + const { sources } = await onNewSource; + is(sources.length, 1, "Got a unique source related to new Function source"); + const newFunctionSource = sources[0]; + // We acknowledge the function header as well as the new line in the first argument + assertPausedAtSourceAndLine(dbg, newFunctionSource.id, 4, 0); + is(getCM(dbg).getValue(), "function anonymous(a\n,b\n) {\ndebugger;\n}"); + await resume(dbg); + + // As we are loading the page while the debugger is already opened, + // none of the resources are loaded twice. + is(loadCounts["/index.html"], 1, "We loaded index.html only once"); + is( + loadCounts["/normal-script.js"], + 1, + "We loaded normal-script.js only once" + ); + is( + loadCounts["/slow-loading-script.js"], + 1, + "We loaded slow-loading-script.js only once" + ); + is( + loadCounts["/same-url.js"], + 4, + "We loaded same-url.js in 4 distinct ways (the named eval doesn't count)" + ); + // For some reason external to the debugger, we issue two requests to scripts having http error codes. + // These two requests are done before opening the debugger. + is( + loadCounts["/http-error-script.js"], + 2, + "We loaded http-error-script.js twice, only before the debugger is opened" + ); +}); + +/** + * In this test, we force a GC before loading DevTools. + * So that Spidermonkey will no longer have access to the sources + * and another request should be issues to load the source text content. + */ +const GARBAGED_PAGE_CONTENT = ` + + + + + + `; + +httpServer.registerPathHandler( + "/garbaged-collected.html", + (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(GARBAGED_PAGE_CONTENT); + } +); + +httpServer.registerPathHandler("/garbaged-script.js", (request, response) => { + loadCounts[request.path] = (loadCounts[request.path] || 0) + 1; + response.setHeader("Content-Type", "application/javascript"); + response.write(`console.log("garbaged script ${loadCounts[request.path]}")`); +}); +add_task(async function testGarbageCollectedSourceTextContent() { + const tab = await addTab(BASE_URL + "garbaged-collected.html"); + is( + loadCounts["/garbaged-collected.html"], + 1, + "The HTML page is loaded once before opening the DevTools" + ); + is( + loadCounts["/garbaged-script.js"], + 1, + "The script is loaded once before opening the DevTools" + ); + + // Force freeing both the HTML page and script in memory + // so that the debugger has to fetch source content from http cache. + await SpecialPowers.spawn(tab.linkedBrowser, [], () => { + Cu.forceGC(); + }); + + const toolbox = await openToolboxForTab(tab, "jsdebugger"); + const dbg = createDebuggerContext(toolbox); + await waitForSources(dbg, "garbaged-collected.html", "garbaged-script.js"); + + await selectSource(dbg, "garbaged-script.js"); + // XXX Bug 1758454 - Source content of GC-ed script can be wrong! + // Even if we have to issue a new HTTP request for this source, + // we should be using HTTP cache and retrieve the first served version which + // is the one that actually runs in the page! + // We should be displaying `console.log("garbaged script 1")`, + // but instead, a new HTTP request is dispatched and we get a new content. + is(getCM(dbg).getValue(), `console.log("garbaged script 2")`); + + await selectSource(dbg, "garbaged-collected.html"); + is(getCM(dbg).getValue(), GARBAGED_PAGE_CONTENT); + + is( + loadCounts["/garbaged-collected.html"], + 2, + "We loaded the html page once as we haven't tried to display it in the debugger (2)" + ); + is( + loadCounts["/garbaged-script.js"], + 2, + "We loaded the garbaged script twice as we lost its content" + ); +}); + +/** + * Test failures when trying to open the source text content. + * + * In this test we load an html page + * - with inline source (so that it shows up in the debugger) + * - it first loads fine so that it shows up + * - initDebuggerWithAbsoluteURL will first load the document before the debugger + * - so the debugger will have to fetch the html page content via a network request + * - the test page will return a connection reset error on the second load attempt + */ +let loadCount = 0; +httpServer.registerPathHandler( + "/200-then-connection-reset.html", + (request, response) => { + loadCount++; + if (loadCount > 1) { + response.seizePower(); + response.bodyOutPutStream.close(); + response.finish(); + return; + } + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(``); + } +); +add_task(async function testFailingHtmlSource() { + info("Test failure in retrieving html page sources"); + + // initDebuggerWithAbsoluteURL will first load the document once before the debugger, + // then the debugger will have to fetch the html page content via a network request + // therefore the test page will encounter a connection reset error on the second load attempt + const dbg = await initDebuggerWithAbsoluteURL( + BASE_URL + "200-then-connection-reset.html", + "200-then-connection-reset.html" + ); + + // We can't select the HTML page as its source content isn't fetched + // (waitForSelectedSource doesn't resolve) + // Note that it is important to load the page *before* opening the page + // so that the thread actor has to request the page content and will fail + const source = findSource(dbg, "200-then-connection-reset.html"); + await dbg.actions.selectLocation( + getContext(dbg), + createLocation({ source }), + { keepContext: false } + ); + + ok( + getCM(dbg).getValue().includes("Could not load the source"), + "Display failure error" + ); +}); + +/** + * In this test we try to reproduce the "Loading..." message. + * This may happen when opening an HTML source that was loaded *before* + * opening DevTools. The thread actor will have to issue a new HTTP request + * to load the source content. + */ +let loadCount2 = 0; +let slowLoadingPageResolution = null; +httpServer.registerPathHandler( + "/slow-loading-page.html", + (request, response) => { + loadCount2++; + if (loadCount2 > 1) { + response.processAsync(); + slowLoadingPageResolution = function () { + response.write( + `` + ); + response.finish(); + }; + return; + } + response.write( + `` + ); + } +); +add_task(async function testLoadingHtmlSource() { + info("Test loading progress of html page sources"); + const dbg = await initDebuggerWithAbsoluteURL( + BASE_URL + "slow-loading-page.html", + "slow-loading-page.html" + ); + + const onSelected = selectSource(dbg, "slow-loading-page.html"); + await waitFor( + () => getCM(dbg).getValue() == `Loading…`, + "Wait for the source to be displayed as loading" + ); + + info("Wait for a second HTTP request to be made for the html page"); + await waitFor( + () => slowLoadingPageResolution, + "Wait for the html page to be queried a second time" + ); + is( + getCM(dbg).getValue(), + `Loading…`, + "The source is still loading until we release the network request" + ); + + slowLoadingPageResolution(); + info("Wait for the source to be fully selected and loaded"); + await onSelected; + + // Note that, even if the thread actor triggers a new HTTP request, + // it will use the HTTP cache and retrieve the first request content. + // This is actually relevant as that's the source that actually runs in the page! + // + // XXX Bug 1758458 - the source content is wrong. + // We should be seeing the whole HTML page content, + // whereas we only see the inline source text content. + is(getCM(dbg).getValue(), `console.log("slow-loading-page:first-load");`); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-source-tree.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-source-tree.js new file mode 100644 index 0000000000..320e157b70 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-source-tree.js @@ -0,0 +1,554 @@ +/* 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 . */ + +/** + * This test focuses on the SourceTree component, where we display all debuggable sources. + * + * The first two tests expand the tree via manual DOM events (first with clicks and second with keys). + * `waitForSourcesInSourceTree()` is a key assertion method. Passing `{noExpand: true}` + * is important to avoid automatically expand the source tree. + * + * The following tests depend on auto-expand and only assert all the sources possibly displayed + */ + +"use strict"; + +const testServer = createVersionizedHttpTestServer( + "examples/sourcemaps-reload-uncompressed" +); +const TEST_URL = testServer.urlFor("index.html"); + +/** + * This test opens the SourceTree manually via click events on the nested source, + * and then adds a source dynamically and asserts it is visible. + */ +add_task(async function testSimpleSourcesWithManualClickExpand() { + const dbg = await initDebugger( + "doc-sources.html", + "simple1.js", + "simple2.js", + "nested-source.js", + "long.js" + ); + + // Expand nodes and make sure more sources appear. + is( + getSourceTreeLabel(dbg, 1), + "Main Thread", + "Main thread is labeled properly" + ); + info("Before interacting with the source tree, no source are displayed"); + await waitForSourcesInSourceTree(dbg, [], { noExpand: true }); + await clickElement(dbg, "sourceDirectoryLabel", 3); + info( + "After clicking on the directory, all sources but the nested ones are displayed" + ); + await waitForSourcesInSourceTree( + dbg, + ["doc-sources.html", "simple1.js", "simple2.js", "long.js"], + { noExpand: true } + ); + + await clickElement(dbg, "sourceDirectoryLabel", 4); + info( + "After clicking on the nested directory, the nested source is also displayed" + ); + await waitForSourcesInSourceTree( + dbg, + [ + "doc-sources.html", + "simple1.js", + "simple2.js", + "long.js", + "nested-source.js", + ], + { noExpand: true } + ); + + const selected = waitForDispatch(dbg.store, "SET_SELECTED_LOCATION"); + await clickElement(dbg, "sourceNode", 5); + await selected; + await waitForSelectedSource(dbg, "nested-source.js"); + + // Ensure the source file clicked is now focused + await waitForElementWithSelector(dbg, ".sources-list .focused"); + + const selectedSource = dbg.selectors.getSelectedSource().url; + ok(selectedSource.includes("nested-source.js"), "nested-source is selected"); + await assertNodeIsFocused(dbg, 5); + + // Make sure new sources appear in the list. + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const script = content.document.createElement("script"); + script.src = "math.min.js"; + content.document.body.appendChild(script); + }); + + info("After adding math.min.js, we got a new source displayed"); + await waitForSourcesInSourceTree( + dbg, + [ + "doc-sources.html", + "simple1.js", + "simple2.js", + "long.js", + "nested-source.js", + "math.min.js", + ], + { noExpand: true } + ); + is( + getSourceNodeLabel(dbg, 8), + "math.min.js", + "math.min.js - The dynamic script exists" + ); + + info("Assert that nested-source.js is still the selected source"); + await assertNodeIsFocused(dbg, 5); + + info("Test the copy to clipboard context menu"); + const mathMinTreeNode = findSourceNodeWithText(dbg, "math.min.js"); + await triggerSourceTreeContextMenu( + dbg, + mathMinTreeNode, + "#node-menu-copy-source" + ); + const clipboardData = SpecialPowers.getClipboardData("text/plain"); + is( + clipboardData, + EXAMPLE_URL + "math.min.js", + "The clipboard content is the selected source URL" + ); + + info("Test the download file context menu"); + // Before trigerring the menu, mock the file picker + const MockFilePicker = SpecialPowers.MockFilePicker; + MockFilePicker.init(window); + const nsiFile = FileUtils.getFile("TmpD", [ + `export_source_content_${Date.now()}.log`, + ]); + MockFilePicker.setFiles([nsiFile]); + const path = nsiFile.path; + + await triggerSourceTreeContextMenu( + dbg, + mathMinTreeNode, + "#node-menu-download-file" + ); + + info("Wait for the downloaded file to be fully saved to disk"); + await BrowserTestUtils.waitForCondition(() => IOUtils.exists(path)); + await BrowserTestUtils.waitForCondition(async () => { + const { size } = await IOUtils.stat(path); + return size > 0; + }); + const buffer = await IOUtils.read(path); + const savedFileContent = new TextDecoder().decode(buffer); + + const mathMinRequest = await fetch(EXAMPLE_URL + "math.min.js"); + const mathMinContent = await mathMinRequest.text(); + + is( + savedFileContent, + mathMinContent, + "The downloaded file has the expected content" + ); + + dbg.toolbox.closeToolbox(); +}); + +/** + * Test keyboard arrow behaviour on the SourceTree with a nested folder + * that we manually expand/collapse via arrow keys. + */ +add_task(async function testSimpleSourcesWithManualKeyShortcutsExpand() { + const dbg = await initDebugger( + "doc-sources.html", + "simple1.js", + "simple2.js", + "nested-source.js", + "long.js" + ); + + // Before clicking on the source label, no source is displayed + await waitForSourcesInSourceTree(dbg, [], { noExpand: true }); + await clickElement(dbg, "sourceDirectoryLabel", 3); + // Right after, all sources, but the nested one are displayed + await waitForSourcesInSourceTree( + dbg, + ["doc-sources.html", "simple1.js", "simple2.js", "long.js"], + { noExpand: true } + ); + + // Right key on open dir + await pressKey(dbg, "Right"); + await assertNodeIsFocused(dbg, 3); + + // Right key on closed dir + await pressKey(dbg, "Right"); + await assertNodeIsFocused(dbg, 4); + + // Left key on a open dir + await pressKey(dbg, "Left"); + await assertNodeIsFocused(dbg, 4); + + // Down key on a closed dir + await pressKey(dbg, "Down"); + await assertNodeIsFocused(dbg, 4); + + // Right key on a source + // We are focused on the nested source and up to this point we still display only the 4 initial sources + await waitForSourcesInSourceTree( + dbg, + ["doc-sources.html", "simple1.js", "simple2.js", "long.js"], + { noExpand: true } + ); + await pressKey(dbg, "Right"); + await assertNodeIsFocused(dbg, 4); + // Now, the nested source is also displayed + await waitForSourcesInSourceTree( + dbg, + [ + "doc-sources.html", + "simple1.js", + "simple2.js", + "long.js", + "nested-source.js", + ], + { noExpand: true } + ); + + // Down key on a source + await pressKey(dbg, "Down"); + await assertNodeIsFocused(dbg, 5); + + // Go to bottom of tree and press down key + await pressKey(dbg, "Down"); + await pressKey(dbg, "Down"); + await assertNodeIsFocused(dbg, 6); + + // Up key on a source + await pressKey(dbg, "Up"); + await assertNodeIsFocused(dbg, 5); + + // Left key on a source + await pressKey(dbg, "Left"); + await assertNodeIsFocused(dbg, 4); + + // Left key on a closed dir + // We are about to close the nested folder, the nested source is about to disappear + await waitForSourcesInSourceTree( + dbg, + [ + "doc-sources.html", + "simple1.js", + "simple2.js", + "long.js", + "nested-source.js", + ], + { noExpand: true } + ); + await pressKey(dbg, "Left"); + // And it disappeared + await waitForSourcesInSourceTree( + dbg, + ["doc-sources.html", "simple1.js", "simple2.js", "long.js"], + { noExpand: true } + ); + await pressKey(dbg, "Left"); + await assertNodeIsFocused(dbg, 3); + + // Up Key at the top of the source tree + await pressKey(dbg, "Up"); + await assertNodeIsFocused(dbg, 2); + dbg.toolbox.closeToolbox(); +}); + +/** + * Tests that the source tree works with all the various types of sources + * coming from the integration test page. + * + * Also assert a few extra things on sources with query strings: + * - they can be pretty printed, + * - quick open matches them, + * - you can set breakpoint on them. + */ +add_task(async function testSourceTreeOnTheIntegrationTestPage() { + // We open against a blank page and only then navigate to the test page + // so that sources aren't GC-ed before opening the debugger. + // When we (re)load a page while the debugger is opened, the debugger + // will force all sources to be held in memory. + const dbg = await initDebuggerWithAbsoluteURL("about:blank"); + + await navigateToAbsoluteURL( + dbg, + TEST_URL, + "index.html", + "script.js", + "test-functions.js", + "query.js?x=1", + "query.js?x=2", + "query2.js?y=3", + "bundle.js", + "original.js", + "replaced-bundle.js", + "removed-original.js", + "named-eval.js" + ); + + info("Verify source tree content"); + await waitForSourcesInSourceTree(dbg, INTEGRATION_TEST_PAGE_SOURCES); + + info("Verify Thread Source Items"); + const mainThreadItem = findSourceTreeThreadByName(dbg, "Main Thread"); + ok(mainThreadItem, "Found the thread item for the main thread"); + ok( + mainThreadItem.querySelector("span.img.window"), + "The thread has the window icon" + ); + + info( + "Assert the number of sources and source actors for the same-url.sjs sources" + ); + const sameUrlSource = findSource(dbg, "same-url.sjs"); + ok(sameUrlSource, "Found same-url.js in the main thread"); + + const sourceActors = dbg.selectors.getSourceActorsForSource(sameUrlSource.id); + + const mainThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == "Main Thread"); + + is( + sourceActors.filter(actor => actor.thread == mainThread.actor).length, + // When EFT is disabled the iframe's source is meld into the main target + isEveryFrameTargetEnabled() ? 3 : 4, + "same-url.js is loaded 3 times in the main thread" + ); + + if (isEveryFrameTargetEnabled()) { + const iframeThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == testServer.urlFor("iframe.html")); + + is( + sourceActors.filter(actor => actor.thread == iframeThread.actor).length, + 1, + "same-url.js is loaded one time in the iframe thread" + ); + } + + const workerThread = dbg.selectors + .getAllThreads() + .find(thread => thread.name == testServer.urlFor("same-url.sjs")); + + is( + sourceActors.filter(actor => actor.thread == workerThread.actor).length, + 1, + "same-url.js is loaded one time in the worker thread" + ); + + const workerThreadItem = findSourceTreeThreadByName(dbg, "same-url.sjs"); + ok(workerThreadItem, "Found the thread item for the worker"); + ok( + workerThreadItem.querySelector("span.img.worker"), + "The thread has the worker icon" + ); + + info("Verify source icons"); + assertSourceIcon(dbg, "index.html", "file"); + assertSourceIcon(dbg, "script.js", "javascript"); + assertSourceIcon(dbg, "query.js?x=1", "javascript"); + assertSourceIcon(dbg, "original.js", "javascript"); + // Framework icons are only displayed when we parse the source, + // which happens when we select the source + assertSourceIcon(dbg, "react-component-module.js", "javascript"); + await selectSource(dbg, "react-component-module.js"); + assertSourceIcon(dbg, "react-component-module.js", "react"); + + info("Verify blackbox source icon"); + await selectSource(dbg, "script.js"); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + assertSourceIcon(dbg, "script.js", "blackBox"); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); + assertSourceIcon(dbg, "script.js", "javascript"); + + info("Assert the content of the named eval"); + await selectSource(dbg, "named-eval.js"); + assertTextContentOnLine(dbg, 3, `console.log("named-eval");`); + + info("Assert that nameless eval don't show up in the source tree"); + invokeInTab("breakInEval"); + await waitForPaused(dbg); + await waitForSourcesInSourceTree(dbg, INTEGRATION_TEST_PAGE_SOURCES); + await resume(dbg); + + info("Assert the content of sources with query string"); + await selectSource(dbg, "query.js?x=1"); + const tab = findElement(dbg, "activeTab"); + is(tab.innerText, "query.js?x=1", "Tab label is query.js?x=1"); + assertTextContentOnLine( + dbg, + 1, + `function query() {console.log("query x=1");}` + ); + await addBreakpoint(dbg, "query.js?x=1", 1); + assertBreakpointHeading(dbg, "query.js?x=1", 0); + + // pretty print the source and check the tab text + clickElement(dbg, "prettyPrintButton"); + await waitForSource(dbg, "query.js?x=1:formatted"); + await waitForSelectedSource(dbg, "query.js?x=1:formatted"); + assertSourceIcon(dbg, "query.js?x=1", "prettyPrint"); + + const prettyTab = findElement(dbg, "activeTab"); + is(prettyTab.innerText, "query.js?x=1", "Tab label is query.js?x=1"); + ok(prettyTab.querySelector(".img.prettyPrint")); + assertBreakpointHeading(dbg, "query.js?x=1", 0); + assertTextContentOnLine(dbg, 1, `function query() {`); + // Note the replacements of " by ' here: + assertTextContentOnLine(dbg, 2, `console.log('query x=1');`); + + // assert quick open works with queries + pressKey(dbg, "quickOpen"); + type(dbg, "query.js?x"); + + // There can be intermediate updates in the results, + // so wait for the final expected value + await waitFor(async () => { + const resultItem = findElement(dbg, "resultItems"); + if (!resultItem) { + return false; + } + return resultItem.innerText.includes("query.js?x=1"); + }, "Results include the source with the query string"); + dbg.toolbox.closeToolbox(); +}); + +/** + * Verify that Web Extension content scripts appear only when + * devtools.chrome.enabled is set to true and that they get + * automatically re-selected on page reload. + */ +add_task(async function testSourceTreeWithWebExtensionContentScript() { + const extension = await installAndStartContentScriptExtension(); + + info("Without the chrome preference, the content script doesn't show up"); + await pushPref("devtools.chrome.enabled", false); + let dbg = await initDebugger("doc-content-script-sources.html"); + // Let some time for unexpected source to appear + await wait(1000); + await waitForSourcesInSourceTree(dbg, []); + await dbg.toolbox.closeToolbox(); + + info("With the chrome preference, the content script shows up"); + await pushPref("devtools.chrome.enabled", true); + const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger"); + dbg = createDebuggerContext(toolbox); + await waitForSourcesInSourceTree(dbg, ["content_script.js"]); + await selectSource(dbg, "content_script.js"); + ok( + findElementWithSelector(dbg, ".sources-list .focused"), + "Source is focused" + ); + + const contentScriptGroupItem = findSourceNodeWithText( + dbg, + "Test content script extension" + ); + ok(contentScriptGroupItem, "Found the group item for the content script"); + ok( + contentScriptGroupItem.querySelector("span.img.extension"), + "The group has the extension icon" + ); + assertSourceIcon(dbg, "content_script.js", "javascript"); + + for (let i = 1; i < 3; i++) { + info( + `Reloading tab (${i} time), the content script should always be reselected` + ); + gBrowser.reloadTab(gBrowser.selectedTab); + await waitForSelectedSource(dbg, "content_script.js"); + ok( + findElementWithSelector(dbg, ".sources-list .focused"), + "Source is focused" + ); + } + await dbg.toolbox.closeToolbox(); + + await extension.unload(); +}); + +add_task(async function testSourceTreeWithEncodedPaths() { + const httpServer = createTestHTTPServer(); + httpServer.registerContentType("html", "text/html"); + httpServer.registerContentType("js", "application/javascript"); + + httpServer.registerPathHandler("/index.html", function (request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + + + + +

Encoded scripts paths

+ + `); + }); + httpServer.registerPathHandler( + encodeURI("/my folder/my file.js"), + function (request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "application/javascript", false); + response.write(`const x = 42`); + } + ); + httpServer.registerPathHandler( + "/malformedUri.js", + function (request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "application/javascript", false); + response.write(`const y = "malformed"`); + } + ); + const port = httpServer.identity.primaryPort; + + const dbg = await initDebuggerWithAbsoluteURL( + `http://localhost:${port}/index.html`, + "my file.js" + ); + + await waitForSourcesInSourceTree(dbg, ["my file.js", "malformedUri.js?%"]); + ok( + true, + "source name are decoded in the tree, and malformed uri source are displayed" + ); + is( + // We don't have any specific class on the folder item, so let's target the folder + // icon next sibling, which is the directory label. + findElementWithSelector(dbg, ".sources-panel .node .folder + .label") + .innerText, + "my folder", + "folder name is decoded in the tree" + ); +}); + +/** + * Assert the location displayed in the breakpoint list, in the right sidebar. + * + * @param {Object} dbg + * @param {String} label + * The expected displayed location + * @param {Number} index + * The position of the breakpoint in the list to verify + */ +function assertBreakpointHeading(dbg, label, index) { + const breakpointHeading = findAllElements(dbg, "breakpointHeadings")[index] + .innerText; + is(breakpointHeading, label, `Breakpoint heading is ${label}`); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-tabs.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-tabs.js new file mode 100644 index 0000000000..7aedb868e6 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-tabs.js @@ -0,0 +1,62 @@ +/* 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 . */ + +/** + * This test focuses on the Tabs component, where we display all opened sources. + */ + +"use strict"; + +const testServer = createVersionizedHttpTestServer( + "examples/sourcemaps-reload-uncompressed" +); +const TEST_URL = testServer.urlFor("index.html"); + +add_task(async function () { + // We open against a blank page and only then navigate to the test page + // so that sources aren't GC-ed before opening the debugger. + // When we (re)load a page while the debugger is opened, the debugger + // will force all sources to be held in memory. + const dbg = await initDebuggerWithAbsoluteURL("about:blank"); + + await navigateToAbsoluteURL(dbg, TEST_URL, ...INTEGRATION_TEST_PAGE_SOURCES); + + info("Wait for all sources to be displayed in the source tree"); + let displayedSources; + await waitFor(() => { + displayedSources = dbg.selectors.getDisplayedSourcesList(); + return displayedSources.length == INTEGRATION_TEST_PAGE_SOURCES.length; + }, "Got the expected number of sources from the selectors"); + + // Open each visible source in tabs + const uniqueUrls = new Set(); + for (const source of displayedSources) { + info(`Opening '${source.url}'`); + await selectSource(dbg, source); + uniqueUrls.add(source.url); + } + + // Some sources are loaded from the same URL and only one tab will be opened for them + is(countTabs(dbg), uniqueUrls.size, "Got a tab for each distinct source URL"); + + await reload(dbg, ...INTEGRATION_TEST_PAGE_SOURCES); + + await waitFor( + () => countTabs(dbg) == uniqueUrls.size, + "Wait for tab count to be fully restored" + ); + + is( + countTabs(dbg), + uniqueUrls.size, + "Still get the same number of tabs after reload" + ); + + for (const source of displayedSources) { + info(`Closing '${source.url}'`); + await closeTab(dbg, source); + } + + is(countTabs(dbg), 0, "All tabs are closed"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-features-wasm.js b/devtools/client/debugger/test/mochitest/browser_dbg-features-wasm.js new file mode 100644 index 0000000000..e7d4ee17d0 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-features-wasm.js @@ -0,0 +1,168 @@ +/* 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 . */ + +/** + * This test covers all specifics of debugging WASM/WebAssembly files. + * + * WebAssembly is a binary file format for the Web. + * The binary files are loaded by Gecko and machine code runs. + * In order to debug them ThreadConfiguration's `observeWasm` is set to true, + * only once the debugger is opened. This will set DebuggerAPI's `allowUnobservedWasm` to false. + * Then, the engine will compute a different machine code with debugging instruction. + * This will use a lot more memory, but allow debugger to set breakpoint and break on WASM sources. + * + * This behavior introduces some limitations when opening the debugger against + * and already loaded page. The WASM file won't be debuggable. + */ + +"use strict"; + +add_task(async function () { + // Load the test page before opening the debugger so that WASM are built + // without debugging instructions. Opening the console still doesn't enable debugging instructions. + const tab = await addTab(EXAMPLE_URL + "doc-wasm-sourcemaps.html"); + const toolbox = await openToolboxForTab(tab, "webconsole"); + + // Reload once again, while the console is opened. + // When opening the debugger, it will still miss the source content. + // To see the sources, we have to reload while the debugger has been opened. + await reloadBrowser(); + + await toolbox.selectTool("jsdebugger"); + const dbg = createDebuggerContext(toolbox); + + // When opening against an already loaded page, WASM source loading doesn't work + // And because of that we don't see sourcemap/original files. + await waitForSourcesInSourceTree(dbg, [ + "doc-wasm-sourcemaps.html", + "fib.wasm", + ]); + is(dbg.selectors.getSourceCount(), 2, "There are only these two sources"); + + const source = findSource(dbg, "fib.wasm"); + is(source.isWasm, true, "The original source is flagged as Wasm source"); + + // Note that there is no point in asserting breakable lines, + // as we aren't fetching any source. + await dbg.actions.selectLocation( + getContext(dbg), + createLocation({ source }), + { keepContext: false } + ); + is(getCM(dbg).getValue(), `Please refresh to debug this module`); + + info("Reload and assert that WASM files are then debuggable"); + await reload(dbg, "doc-wasm-sourcemaps.html", "fib.wasm", "fib.c"); + + info("After reloading, original file lines are breakable"); + // Ensure selecting the source before asserting breakable lines + // otherwise the gutter may not be yet updated + await selectSource(dbg, "fib.c"); + assertLineIsBreakable(dbg, source.url, 14, true); + + await waitForSourcesInSourceTree(dbg, [ + "doc-wasm-sourcemaps.html", + "fib.wasm", + "fib.c", + ]); + is(dbg.selectors.getSourceCount(), 3, "There is all these 3 sources"); + // (even if errno_location.c is still not functional) + + // The line in the original C file, where the for() loop starts + const breakpointLine = 12; + assertTextContentOnLine(dbg, breakpointLine, "for (i = 0; i < n; i++) {"); + + info("Register and trigger a breakpoint from the original source in C"); + await addBreakpoint(dbg, "fib.c", breakpointLine); + invokeInTab("runWasm"); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "fib.c").id, breakpointLine); + await assertBreakpoint(dbg, breakpointLine); + // Capture the generated location line, so that we can better report + // when the binary code changed later in this test + const frames = dbg.selectors.getCurrentThreadFrames(); + const generatedLine = frames[0].generatedLocation.line; + + assertFirstFrameTitleAndLocation(dbg, "(wasmcall)", "fib.c"); + + await removeBreakpoint(dbg, findSource(dbg, "fib.c").id, breakpointLine); + await resume(dbg); + + info( + "Now register and trigger the same breakpoint from the binary source file" + ); + const binarySource = findSource(dbg, "fib.wasm"); + + // There is two lines, the hexadecimal one is the "virtual line" displayed in the gutter. + // While the decimal one is the line where the line appear in CodeMirror. + // So while we set the breakpoint on the decimal line in CodeMirror gutter, + // internaly, the engine sets the breakpoint on the "virtual line". + const virtualBinaryLine = 0x11a; + is( + "0x" + virtualBinaryLine.toString(16), + "0x" + generatedLine.toString(16), + "The hardcoded binary line matches the mapped location when we set the breakpoint on the original line. If you rebuilt the binary, you may just need to update the virtualBinaryLine variable to the new location." + ); + const binaryLine = + dbg.wasmOffsetToLine(binarySource.id, virtualBinaryLine) + 1; + + // We can't use selectSource here because binary source won't have symbols loaded + // (getSymbols(source) selector will be false) + await dbg.actions.selectLocation( + getContext(dbg), + createLocation({ source: binarySource }), + { keepContext: false } + ); + + assertLineIsBreakable(dbg, binarySource.url, binaryLine, true); + + await addBreakpoint(dbg, binarySource, virtualBinaryLine); + invokeInTab("runWasm"); + + // We can't use waitForPaused test helper as the text content isn't displayed correctly + // so only assert that we are in paused state. + await waitForPaused(dbg); + // We don't try to assert paused line as there is two types of line in wasm + assertPausedAtSourceAndLine(dbg, binarySource.id, virtualBinaryLine); + + // Switch to original source + info( + "Manually switch to original C source as we set the breakpoint on binary source, we paused on it" + ); + await dbg.actions.jumpToMappedSelectedLocation(getContext(dbg)); + + // But once we switch to original source, we should have the original text content and be able + // to do all classic assertions for paused state. + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "fib.c").id, breakpointLine); + + info("Reselect the binary source"); + await dbg.actions.selectLocation( + getContext(dbg), + createLocation({ source: binarySource }), + { keepContext: false } + ); + + assertFirstFrameTitleAndLocation(dbg, "(wasmcall)", "fib.wasm"); + + // We can't use this method as it uses internaly the breakpoint line, which isn't the line in CodeMirror + // assertPausedAtSourceAndLine(dbg, binarySource.id, binaryLine); + await assertBreakpoint(dbg, binaryLine); + + await removeBreakpoint(dbg, binarySource.id, virtualBinaryLine); + await resume(dbg); +}); + +function assertFirstFrameTitleAndLocation(dbg, title, location) { + const frames = findAllElements(dbg, "frames"); + const firstFrameTitle = frames[0].querySelector(".title").textContent; + is(firstFrameTitle, title, "First frame title is the expected one"); + const firstFrameLocation = frames[0].querySelector(".location").textContent; + is( + firstFrameLocation.includes(location), + true, + `First frame location '${firstFrameLocation}' includes '${location}'` + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-breakpoint.js b/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-breakpoint.js new file mode 100644 index 0000000000..783b86f80c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-breakpoint.js @@ -0,0 +1,50 @@ +/* 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 . */ + +"use strict"; + +const TEST_COM_URI = `${URL_ROOT_COM_SSL}examples/doc_dbg-fission-frame-sources.html`; + +add_task(async function () { + // Load a test page with a remote frame: + // simple1.js is imported by the main page. simple2.js comes from the remote frame. + const dbg = await initDebuggerWithAbsoluteURL( + TEST_COM_URI, + "simple1.js", + "simple2.js" + ); + const { + selectors: { getSelectedSource }, + } = dbg; + + // Add breakpoint within the iframe, which is hit early on load + await selectSource(dbg, "simple2.js"); + await addBreakpoint(dbg, "simple2.js", 7); + + const onBreakpoint = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + info("Reload the page to hit the breakpoint on load"); + const onReloaded = reload(dbg); + await onBreakpoint; + await waitForSelectedSource(dbg, "simple2.js"); + + ok( + getSelectedSource().url.includes("simple2.js"), + "Selected source is simple2.js" + ); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "simple2.js").id, 7); + + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "simple2.js").id, 7); + + // We can't used `stepIn` helper as this last step will resume + // and the helper is expecting to pause again + await dbg.actions.stepIn(getThreadContext(dbg)); + assertNotPaused(dbg, "Stepping in two times resumes"); + + info("Wait for reload to complete after resume"); + await onReloaded; + + await dbg.toolbox.closeToolbox(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-pause-exceptions.js b/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-pause-exceptions.js new file mode 100644 index 0000000000..8a76831e8c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-pause-exceptions.js @@ -0,0 +1,54 @@ +/* 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 . */ + +"use strict"; + +const TEST_COM_URI = `${URL_ROOT_COM_SSL}examples/doc_dbg-fission-pause-exceptions.html`; +// Tests Pause on exceptions in remote iframes + +add_task(async function () { + // Load a test page with a remote iframe + const dbg = await initDebuggerWithAbsoluteURL(TEST_COM_URI); + + info("Test pause on exceptions ignoring caught exceptions"); + await togglePauseOnExceptions(dbg, true, false); + + let onReloaded = reload(dbg); + await waitForPaused(dbg); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc_dbg-fission-frame-pause-exceptions.html").id, + 17 + ); + + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; + + info("Test pause on exceptions including caught exceptions"); + await togglePauseOnExceptions(dbg, true, true); + + onReloaded = reload(dbg); + await waitForPaused(dbg); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc_dbg-fission-frame-pause-exceptions.html").id, + 13 + ); + + await resume(dbg); + await waitForPaused(dbg); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc_dbg-fission-frame-pause-exceptions.html").id, + 17 + ); + + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-sources.js b/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-sources.js new file mode 100644 index 0000000000..b40058a7dd --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-fission-frame-sources.js @@ -0,0 +1,47 @@ +/* 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 . */ + +"use strict"; + +const TEST_COM_URI = `${URL_ROOT_COM_SSL}examples/doc_dbg-fission-frame-sources.html`; + +add_task(async function () { + // Simply load a test page with a remote frame and wait for both sources to + // be visible. + // simple1.js is imported by the main page. simple2.js comes from the frame. + const dbg = await initDebuggerWithAbsoluteURL( + TEST_COM_URI, + "simple1.js", + "simple2.js" + ); + + const rootNodes = dbg.win.document.querySelectorAll( + selectors.sourceTreeRootNode + ); + + info("Expands the main root node"); + await expandAllSourceNodes(dbg, rootNodes[0]); + + // We need to assert the actual DOM nodes in the source tree, because the + // state can contain simple1.js and simple2.js, but only show one of them. + info("Waiting for simple1.js from example.com (parent page)"); + await waitUntil(() => findSourceNodeWithText(dbg, "simple1.js")); + + // If fission or EFT is enabled, the second source is under another root node. + if (isFissionEnabled() || isEveryFrameTargetEnabled()) { + is( + rootNodes.length, + 2, + "Found 2 sourceview root nodes when iframe has dedicated target" + ); + + info("Expands the remote frame root node"); + await expandAllSourceNodes(dbg, rootNodes[1]); + } + + info("Waiting for simple2.js from example.org (frame)"); + await waitUntil(() => findSourceNodeWithText(dbg, "simple2.js")); + + await dbg.toolbox.closeToolbox(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-fission-project-search.js b/devtools/client/debugger/test/mochitest/browser_dbg-fission-project-search.js new file mode 100644 index 0000000000..7a781c8451 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-fission-project-search.js @@ -0,0 +1,52 @@ +/* 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 . */ + +"use strict"; + +const TEST_COM_URI = `${URL_ROOT_COM_SSL}examples/doc_dbg-fission-frame-sources.html`; + +// Testing project search for remote frames. +add_task(async function () { + // Load page and wait for sources. simple.js is loaded from + // the top-level document in the dot com domain, while simple2.js + // is loaded from the remote frame in the dot org domain + const dbg = await initDebuggerWithAbsoluteURL( + TEST_COM_URI, + "simple1.js", + "simple2.js" + ); + + pressKey(dbg, "projectSearch"); + type(dbg, "foo"); + pressKey(dbg, "Enter"); + + await waitForState(dbg, state => state.projectTextSearch.status === "DONE"); + + const fileResults = findAllElements(dbg, "projectSearchFileResults"); + const matches = findAllElements(dbg, "projectSearchExpandedResults"); + + is(fileResults.length, 2, "Two results found"); + is(matches.length, 6, "Total no of matches found"); + + // Asserts that we find a matches in the js file included in the top-level document + assertFileResult("simple1.js", 5); + // Asserts that we find the match in the js file included + assertFileResult("simple2.js", 1); + + function assertFileResult(fileMatched, noOfMatches) { + // The results can be out of order so let find it from the collection + const match = [...fileResults].find(result => + result.querySelector(".file-path").innerText.includes(fileMatched) + ); + + ok(match, `Matches were found in ${fileMatched} file.`); + + const matchText = noOfMatches > 1 ? "matches" : "match"; + is( + match.querySelector(".matches-summary").innerText.trim(), + `(${noOfMatches} ${matchText})`, + `${noOfMatches} ${matchText} were found in ${fileMatched} file.` + ); + } +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-fission-switch-target.js b/devtools/client/debugger/test/mochitest/browser_dbg-fission-switch-target.js new file mode 100644 index 0000000000..6500942d0a --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-fission-switch-target.js @@ -0,0 +1,32 @@ +/* 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 . */ + +// Test switching for the top-level target. + +"use strict"; + +const PARENT_PROCESS_URI = "about:robots"; + +add_task(async function () { + // Start the debugger on a parent process URL + const dbg = await initDebuggerWithAbsoluteURL( + PARENT_PROCESS_URI, + "aboutRobots.js" + ); + + // Navigate to a content process URL and check that the sources tree updates + await navigate(dbg, "doc-scripts.html", "simple1.js"); + info("Wait for all sources to be in the store"); + await waitFor(() => dbg.selectors.getSourceCount() == 5); + is(dbg.selectors.getSourceCount(), 5, "5 sources are loaded."); + + // Check that you can still break after target switching. + await selectSource(dbg, "simple1.js"); + await addBreakpoint(dbg, "simple1.js", 4); + invokeInTab("main"); + await waitForPaused(dbg); + await waitForLoadedSource(dbg, "simple1.js"); + + await dbg.toolbox.closeToolbox(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-gc-breakpoint-positions.js b/devtools/client/debugger/test/mochitest/browser_dbg-gc-breakpoint-positions.js new file mode 100644 index 0000000000..7c46e7cacc --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-gc-breakpoint-positions.js @@ -0,0 +1,17 @@ +/* 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 . */ + +// Test that we can set breakpoints in scripts that have been GCed. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-gc-breakpoint-positions.html", + "doc-gc-breakpoint-positions.html" + ); + await selectSource(dbg, "doc-gc-breakpoint-positions.html"); + await addBreakpoint(dbg, "doc-gc-breakpoint-positions.html", 21); + ok(true, "Added breakpoint at GC'ed script location"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-gc-sources.js b/devtools/client/debugger/test/mochitest/browser_dbg-gc-sources.js new file mode 100644 index 0000000000..e4a6da536a --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-gc-sources.js @@ -0,0 +1,29 @@ +/* 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 . */ + +// Test that we can set breakpoints in scripts that have been GCed. +// Check that breakpoints can be set in GC'ed inline scripts, and in +// generated and original sources of scripts with source maps specified either +// inline or in their HTTP response headers. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-gc-sources.html", + "doc-gc-sources.html", + "collected-bundle.js", + "collected.js", + "collected2.js" + ); + await selectSource(dbg, "doc-gc-sources.html"); + await addBreakpoint(dbg, "doc-gc-sources.html", 21); + await selectSource(dbg, "collected-bundle.js"); + await addBreakpoint(dbg, "collected-bundle.js", 31); + await selectSource(dbg, "collected.js"); + await addBreakpoint(dbg, "collected.js", 2); + await selectSource(dbg, "collected2.js"); + await addBreakpoint(dbg, "collected2.js", 2); + ok(true, "Added breakpoint in GC'ed sources"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-go-to-line.js b/devtools/client/debugger/test/mochitest/browser_dbg-go-to-line.js new file mode 100644 index 0000000000..d151d03a51 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-go-to-line.js @@ -0,0 +1,64 @@ +/* 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 . */ + +// Test the "go to line" feature correctly responses to keyboard shortcuts. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "long.js"); + await selectSource(dbg, "long.js"); + await waitForSelectedSource(dbg, "long.js"); + + info("Test opening"); + pressKey(dbg, "goToLine"); + assertEnabled(dbg); + is( + dbg.win.document.activeElement.tagName, + "INPUT", + "The input area of 'go to line' box is focused" + ); + + info("Test closing by the same keyboard shortcut"); + pressKey(dbg, "goToLine"); + assertDisabled(dbg); + is(findElement(dbg, "searchField"), null, "The 'go to line' box is closed"); + + info("Test closing by escape"); + pressKey(dbg, "goToLine"); + assertEnabled(dbg); + + pressKey(dbg, "Escape"); + assertDisabled(dbg); + is(findElement(dbg, "searchField"), null, "The 'go to line' box is closed"); + + info("Test going to the correct line"); + pressKey(dbg, "goToLine"); + await waitForGoToLineBoxFocus(dbg); + type(dbg, "66"); + pressKey(dbg, "Enter"); + await assertLine(dbg, 66); +}); + +function assertEnabled(dbg) { + is(dbg.selectors.getQuickOpenEnabled(), true, "quickOpen enabled"); +} + +function assertDisabled(dbg) { + is(dbg.selectors.getQuickOpenEnabled(), false, "quickOpen disabled"); +} + +async function waitForGoToLineBoxFocus(dbg) { + await waitFor(() => dbg.win.document.activeElement.tagName === "INPUT"); +} + +async function assertLine(dbg, lineNumber) { + // Wait for the line to be set + await waitUntil(() => !!dbg.selectors.getSelectedLocation().line); + is( + dbg.selectors.getSelectedLocation().line, + lineNumber, + `goto line is ${lineNumber}` + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-highlights-calls.js b/devtools/client/debugger/test/mochitest/browser_dbg-highlights-calls.js new file mode 100644 index 0000000000..98319cda51 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-highlights-calls.js @@ -0,0 +1,31 @@ +/* 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 . */ + +// This test checks to see if command button highlights and unhighlights +// calls when debugger is paused. + +"use strict"; + +add_task(async function () { + await pushPref("devtools.debugger.features.command-click", true); + const dbg = await initDebugger("doc-command-click.html", "simple4.js"); + + const source = findSource(dbg, "simple4.js"); + + await selectSource(dbg, source); + + invokeInTab("funcA"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + + pressKey(dbg, "commandKeyDown"); + await waitForDispatch(dbg.store, "HIGHLIGHT_CALLS"); + const calls = dbg.win.document.querySelectorAll(".highlight-function-calls"); + is(calls.length, 2); + pressKey(dbg, "commandKeyUp"); + const nocalls = dbg.win.document.querySelectorAll( + ".highlight-function-calls" + ); + is(nocalls.length, 0); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-html-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-html-breakpoints.js new file mode 100644 index 0000000000..aafea0d650 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-html-breakpoints.js @@ -0,0 +1,54 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-html-breakpoints.html"); + + await selectSource(dbg, "doc-html-breakpoints.html"); + + // Reload the page so that we know the debugger is already open and + // functional before the page loads and we start getting notifications + // about new actors being created in the page content. + await reload(dbg, "doc-html-breakpoints.html"); + + await waitForBreakableLine(dbg, "doc-html-breakpoints.html", 8); + await addBreakpoint(dbg, "doc-html-breakpoints.html", 8); + + // Ensure that the breakpoints get added once the later scripts load. + await waitForBreakableLine(dbg, "doc-html-breakpoints.html", 15); + await addBreakpoint(dbg, "doc-html-breakpoints.html", 15); + await waitForBreakableLine(dbg, "doc-html-breakpoints.html", 20); + await addBreakpoint(dbg, "doc-html-breakpoints.html", 20); + + await reload(dbg, "doc-html-breakpoints.html", "simple2.js"); + + invokeInTab("test1"); + await waitForPaused(dbg); + + const htmlSource = findSource(dbg, "doc-html-breakpoints.html"); + + is(htmlSource.isHTML, true, "The html page is flagged as an html source"); + is( + findSource(dbg, "simple2.js").isHTML, + false, + "The js source is not flagged as an html source" + ); + + assertPausedAtSourceAndLine(dbg, htmlSource.id, 8); + await resume(dbg); + + await waitForBreakableLine(dbg, "doc-html-breakpoints.html", 15); + invokeInTab("test3"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, htmlSource.id, 15); + await resume(dbg); + + await waitForBreakableLine(dbg, "doc-html-breakpoints.html", 20); + invokeInTab("test4"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, htmlSource.id, 20); + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-idb-run-to-completion.js b/devtools/client/debugger/test/mochitest/browser_dbg-idb-run-to-completion.js new file mode 100644 index 0000000000..dcd4a11463 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-idb-run-to-completion.js @@ -0,0 +1,15 @@ +/* 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 . */ + +// Test that IDB transactions are not processed at microtask checkpoints +// introduced by debugger hooks. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-idb-run-to-completion.html"); + invokeInTab("test", "doc-xhr-run-to-completion.html"); + await waitForPaused(dbg); + ok(true, "paused after successfully processing IDB transaction"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-iframes.js b/devtools/client/debugger/test/mochitest/browser_dbg-iframes.js new file mode 100644 index 0000000000..0bcc0e020f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-iframes.js @@ -0,0 +1,60 @@ +/* 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 . */ + +// Test is taking too much time to complete on some hardware since +// release at https://bugzilla.mozilla.org/show_bug.cgi?id=1423158 + +"use strict"; + +requestLongerTimeout(3); + +/** + * Test debugging a page with iframes + * 1. pause in the main thread + * 2. pause in the iframe + */ +add_task(async function () { + const dbg = await initDebugger("doc-iframes.html"); + + // test pausing in the main thread + const onReloaded = reload(dbg); + await waitForPaused(dbg); + await waitForLoadedSource(dbg, "doc-iframes.html"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "doc-iframes.html").id, 11); + + // test pausing in the iframe + await resume(dbg); + await waitForPaused(dbg); + await waitForLoadedSource(dbg, "doc-debugger-statements.html"); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 11 + ); + + // test pausing in the iframe + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-debugger-statements.html").id, + 16 + ); + await waitFor(() => dbg.toolbox.isHighlighted("jsdebugger")); + ok(true, "Debugger is highlighted when paused"); + + if (isFissionEnabled() || isEveryFrameTargetEnabled()) { + info("Remove the iframe and wait for resume"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + const iframe = content.document.querySelector("iframe"); + iframe.remove(); + }); + await waitForResumed(dbg); + await waitFor(() => !dbg.toolbox.isHighlighted("jsdebugger")); + ok(true, "Debugger is no longer highlighted when resumed"); + + info("Wait for reload to complete after resume"); + await onReloaded; + } +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-inline-cache.js b/devtools/client/debugger/test/mochitest/browser_dbg-inline-cache.js new file mode 100644 index 0000000000..f579ddb1b9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-inline-cache.js @@ -0,0 +1,146 @@ +/* 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 . */ + +/* + * Test loading inline scripts from cache: + * - Load document with inline script + * - Reload inside debugger with toolbox caching disabled + * - Reload inside debugger with toolbox caching enabled + */ + +"use strict"; + +// Breakpoint position calculations can throw when interrupted by a navigation. +PromiseTestUtils.allowMatchingRejectionsGlobally(/Resource .*? does not exist/); + +const server = createTestHTTPServer(); + +let docValue = 1; +server.registerPathHandler("/inline-cache.html", (request, response) => { + response.setHeader("Content-Type", "text/html"); + // HTTP should assume cacheable by default. + // Toolbox defaults to disabling cache for subsequent loads when open. + response.setHeader("Cache-Control", "public, max-age=3600"); + response.write(` + + + + + + `); +}); + +const SOURCE_URL = `http://localhost:${server.identity.primaryPort}/inline-cache.html`; + +add_task(async function () { + info("Load document with inline script"); + const tab = await addTab(SOURCE_URL); + info("Open debugger"); + clearDebuggerPreferences(); + const toolbox = await openToolboxForTab(tab, "jsdebugger"); + const dbg = createDebuggerContext(toolbox); + await waitForSource(dbg, "inline-cache.html"); + info("Reload tab to ensure debugger finds script"); + await reloadBrowser(); + let pageValue = await getPageValue(tab); + is(pageValue, "let x = 1;", "Content loads from network, has doc value 1"); + await waitForSource(dbg, "inline-cache.html"); + await selectSource(dbg, "inline-cache.html"); + await waitForLoadedSource(dbg, "inline-cache.html"); + let dbgValue = findSourceContent(dbg, "inline-cache.html"); + info(`Debugger text: ${dbgValue.value}`); + ok( + dbgValue.value.includes(pageValue), + "Debugger loads from cache, gets value 1 like page" + ); + + info("Disable HTTP cache for page"); + await toolbox.commands.targetConfigurationCommand.updateConfiguration({ + cacheDisabled: true, + }); + makeChanges(); + + info("Reload inside debugger with toolbox caching disabled (attempt 1)"); + await reloadBrowser(); + pageValue = await getPageValue(tab); + is(pageValue, "let x = 2;", "Content loads from network, has doc value 2"); + await waitForLoadedSource(dbg, "inline-cache.html"); + dbgValue = findSourceContent(dbg, "inline-cache.html"); + + info(`Debugger text: ${dbgValue.value}`); + ok( + dbgValue.value.includes(pageValue), + "Debugger loads from network, gets value 2 like page" + ); + + makeChanges(); + + info("Reload inside debugger with toolbox caching disabled (attempt 2)"); + await reloadBrowser(); + pageValue = await getPageValue(tab); + is(pageValue, "let x = 3;", "Content loads from network, has doc value 3"); + await waitForLoadedSource(dbg, "inline-cache.html"); + dbgValue = findSourceContent(dbg, "inline-cache.html"); + info(`Debugger text: ${dbgValue.value}`); + ok( + dbgValue.value.includes(pageValue), + "Debugger loads from network, gets value 3 like page" + ); + + info("Enable HTTP cache for page"); + await toolbox.commands.targetConfigurationCommand.updateConfiguration({ + cacheDisabled: false, + }); + makeChanges(); + + // Even though the HTTP cache is now enabled, Gecko sets the VALIDATE_ALWAYS flag when + // reloading the page. So, it will always make a request to the server for the main + // document contents. + + info("Reload inside debugger with toolbox caching enabled (attempt 1)"); + await reloadBrowser(); + pageValue = await getPageValue(tab); + is(pageValue, "let x = 4;", "Content loads from network, has doc value 4"); + await waitForLoadedSource(dbg, "inline-cache.html"); + dbgValue = findSourceContent(dbg, "inline-cache.html"); + info(`Debugger text: ${dbgValue.value}`); + ok( + dbgValue.value.includes(pageValue), + "Debugger loads from cache, gets value 4 like page" + ); + + makeChanges(); + + info("Reload inside debugger with toolbox caching enabled (attempt 2)"); + await reloadBrowser(); + pageValue = await getPageValue(tab); + is(pageValue, "let x = 5;", "Content loads from network, has doc value 5"); + await waitForLoadedSource(dbg, "inline-cache.html"); + await waitForSelectedSource(dbg, "inline-cache.html"); + dbgValue = findSourceContent(dbg, "inline-cache.html"); + info(`Debugger text: ${dbgValue.value}`); + ok( + dbgValue.value.includes(pageValue), + "Debugger loads from cache, gets value 5 like page" + ); + + await toolbox.destroy(); + await removeTab(tab); +}); + +/** + * This is meant to simulate the developer editing the inline source and saving. + * Effectively, we change the source during the test at specific controlled points. + */ +function makeChanges() { + docValue++; +} + +function getPageValue(tab) { + return SpecialPowers.spawn(tab.linkedBrowser, [], function () { + return content.document.querySelector("script").textContent.trim(); + }); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-inline-exceptions.js b/devtools/client/debugger/test/mochitest/browser_dbg-inline-exceptions.js new file mode 100644 index 0000000000..a7ae02cb61 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-inline-exceptions.js @@ -0,0 +1,28 @@ +/* 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 . */ + +// This test checks the appearance of an inline exception +// and the content of the exception tooltip. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-exceptions.html"); + await selectSource(dbg, "exceptions.js"); + + info("Hovers over the inline exception mark text."); + await assertPreviewTextValue(dbg, 85, 10, { + text: 'TypeError: "abc".push is not a function', + }); + await closePreviewAtPos(dbg, 85, 10); + + const excLineEls = findAllElementsWithSelector(dbg, ".line-exception"); + const excTextMarkEls = findAllElementsWithSelector( + dbg, + ".mark-text-exception" + ); + + is(excLineEls.length, 1, "The editor has one exception line"); + is(excTextMarkEls.length, 1, "One token is marked as an exception."); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-inline-preview.js b/devtools/client/debugger/test/mochitest/browser_dbg-inline-preview.js new file mode 100644 index 0000000000..54946f2109 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-inline-preview.js @@ -0,0 +1,111 @@ +/* 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 . */ + +// Test checking inline preview feature + +"use strict"; + +add_task(async function () { + await pushPref("devtools.debugger.features.inline-preview", true); + + const dbg = await initDebugger( + "doc-inline-preview.html", + "inline-preview.js" + ); + await selectSource(dbg, "inline-preview.js"); + + await checkInlinePreview(dbg, "checkValues", [ + { identifier: "a:", value: '""' }, + { identifier: "b:", value: "false" }, + { identifier: "c:", value: "undefined" }, + { identifier: "d:", value: "null" }, + { identifier: "e:", value: "Array []" }, + { identifier: "f:", value: "Object { }" }, + { identifier: "obj:", value: "Object { foo: 1 }" }, + { + identifier: "bs:", + value: "Array(101) [ {…}, {…}, {…}, … ]", + }, + ]); + + await checkInlinePreview(dbg, "columnWise", [ + { identifier: "c:", value: '"c"' }, + { identifier: "a:", value: '"a"' }, + { identifier: "b:", value: '"b"' }, + ]); + + // Check that referencing an object property previews the property, not the + // object (bug 1599917) + await checkInlinePreview(dbg, "objectProperties", [ + { identifier: "obj:", value: 'Object { hello: "world", a: {…} }' }, + { identifier: "obj.hello:", value: '"world"' }, + { identifier: "obj.a.b:", value: '"c"' }, + ]); + + await checkInlinePreview(dbg, "classProperties", [ + { identifier: "i:", value: "2" }, + { identifier: "self:", value: `Object { x: 1, #privateVar: 2 }` }, + ]); + + // Checks that open in inspector button works in inline preview + invokeInTab("btnClick"); + await checkInspectorIcon(dbg); + + const { toolbox } = dbg; + await toolbox.selectTool("jsdebugger"); + + await waitForPaused(dbg); + + // Check preview of event ( event.target should be clickable ) + // onBtnClick function in inline-preview.js + await checkInspectorIcon(dbg); +}); + +async function checkInlinePreview(dbg, fnName, inlinePreviews) { + invokeInTab(fnName); + + await waitForAllElements(dbg, "inlinePreviewLabels", inlinePreviews.length); + + const labels = findAllElements(dbg, "inlinePreviewLabels"); + const values = findAllElements(dbg, "inlinePreviewValues"); + + inlinePreviews.forEach((inlinePreview, index) => { + const { identifier, value } = inlinePreview; + is( + labels[index].innerText, + identifier, + `${identifier} in ${fnName} has correct inline preview label` + ); + is( + values[index].innerText, + value, + `${identifier} in ${fnName} has correct inline preview value` + ); + }); + + await resume(dbg); +} + +async function checkInspectorIcon(dbg) { + await waitForElement(dbg, "inlinePreviewOpenInspector"); + + const { toolbox } = dbg; + const node = findElement(dbg, "inlinePreviewOpenInspector"); + + // Ensure hovering over button highlights the node in content pane + const view = node.ownerDocument.defaultView; + const onNodeHighlight = toolbox.getHighlighter().waitForHighlighterShown(); + + EventUtils.synthesizeMouseAtCenter(node, { type: "mousemove" }, view); + + const { nodeFront } = await onNodeHighlight; + is(nodeFront.displayName, "button", "The correct node was highlighted"); + + // Ensure panel changes when button is clicked + const onInspectorPanelLoad = waitForInspectorPanelChange(dbg); + node.click(); + await onInspectorPanelLoad; + + await resume(dbg); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-inline-script-offset.js b/devtools/client/debugger/test/mochitest/browser_dbg-inline-script-offset.js new file mode 100644 index 0000000000..b0473d21e1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-inline-script-offset.js @@ -0,0 +1,40 @@ +/* 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 . */ + +// Test that breakpoints work when set in inline scripts that do not start at column 0. + +"use strict"; + +const TEST_PAGE = "doc-inline-script-offset.html"; + +add_task(async function () { + const dbg = await initDebugger(TEST_PAGE); + await selectSource(dbg, TEST_PAGE); + + // Ensure that breakable lines are correct when loading against an already loaded page + await assertBreakableLines(dbg, TEST_PAGE, 16, [ + ...getRange(3, 5), + ...getRange(11, 13), + 15, + ]); + + await reload(dbg, TEST_PAGE); + + // Also verify they are fine after reload + await assertBreakableLines(dbg, TEST_PAGE, 16, [ + ...getRange(3, 5), + ...getRange(11, 13), + 15, + ]); + + await addBreakpoint(dbg, "doc-inline-script-offset.html", 15, 66); + + const onReloaded = reload(dbg); + await waitForPaused(dbg); + ok(true, "paused after reloading at column breakpoint"); + + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-inspector-integration.js b/devtools/client/debugger/test/mochitest/browser_dbg-inspector-integration.js new file mode 100644 index 0000000000..e9f2464bc6 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-inspector-integration.js @@ -0,0 +1,140 @@ +/* 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 . */ + +// Tests that clicking the DOM node button in any ObjectInspect +// opens the Inspector panel + +"use strict"; + +add_task(async function () { + // Ensures the end panel is wide enough to show the inspector icon + await pushPref("devtools.debugger.end-panel-size", 600); + // Disable 3-pane inspector as it might trigger unwanted server communication. + await pushPref("devtools.inspector.three-pane-enabled", false); + + const dbg = await initDebugger("doc-script-switching.html"); + const { toolbox } = dbg; + const highlighterTestFront = await getHighlighterTestFront(toolbox); + const highlighter = toolbox.getHighlighter(); + + // Bug 1562165: the WhyPaused element is displayed for a few hundred ms when adding an + // expression, which can break synthesizeMouseAtCenter. So here we wait for the + // whyPaused element to be displayed then hidden before testing the highlight feature. + const onWhyPausedDisplayed = waitUntil(() => + dbg.win.document.querySelector(".why-paused") + ); + await addExpression(dbg, "window.document.querySelector('button')"); + // TODO: Remove when Bug 1562165 lands. + await onWhyPausedDisplayed; + // TODO: Remove when Bug 1562165 lands. + await waitUntil(() => !dbg.win.document.querySelector(".why-paused")); + + info( + "Check that hovering over DOM element highlights the node in content panel" + ); + let onNodeHighlight = highlighter.waitForHighlighterShown(); + + info("Mouseover the open in inspector button"); + const inspectorNode = await waitFor(() => findElement(dbg, "openInspector")); + const view = inspectorNode.ownerDocument.defaultView; + EventUtils.synthesizeMouseAtCenter( + inspectorNode, + { type: "mouseover" }, + view + ); + + info("Wait for highligther to be shown"); + const { nodeFront } = await onNodeHighlight; + is(nodeFront.displayName, "button", "The correct node was highlighted"); + + info("Check that moving the mouse away from the node hides the highlighter"); + let onNodeUnhighlight = highlighter.waitForHighlighterHidden(); + const nonHighlightEl = inspectorNode.closest(".object-node"); + EventUtils.synthesizeMouseAtCenter( + nonHighlightEl, + { type: "mouseover" }, + view + ); + + await onNodeUnhighlight; + isVisible = await highlighterTestFront.isHighlighting(); + is(isVisible, false, "The highlighter is not displayed anymore"); + + info("Check we don't have zombie highlighters when briefly hovering a node"); + onNodeHighlight = highlighter.waitForHighlighterShown(); + onNodeUnhighlight = highlighter.waitForHighlighterHidden(); + + // Move hover the node and then, right after, move out. + EventUtils.synthesizeMouseAtCenter( + inspectorNode, + { type: "mousemove" }, + view + ); + EventUtils.synthesizeMouseAtCenter( + nonHighlightEl, + { type: "mousemove" }, + view + ); + + await Promise.all([onNodeHighlight, onNodeUnhighlight]); + isVisible = await highlighterTestFront.isHighlighting(); + is(isVisible, false, "The highlighter is not displayed anymore - no zombie"); + + info("Ensure panel changes when button is clicked"); + // Loading the inspector panel at first, to make it possible to listen for + // new node selections + const inspector = await toolbox.loadTool("inspector"); + const onInspectorSelected = toolbox.once("inspector-selected"); + const onInspectorUpdated = inspector.once("inspector-updated"); + const onNewNode = toolbox.selection.once("new-node-front"); + + inspectorNode.click(); + + await onInspectorSelected; + await onInspectorUpdated; + const inspectorNodeFront = await onNewNode; + + ok(true, "Inspector selected and new node got selected"); + is( + inspectorNodeFront.displayName, + "button", + "The expected node was selected" + ); +}); + +add_task(async function () { + // Disable 3-pane inspector as it might trigger unwanted server communication. + await pushPref("devtools.inspector.three-pane-enabled", false); + + const dbg = await initDebugger("doc-event-handler.html"); + const { toolbox } = dbg; + + invokeInTab("synthesizeClick"); + await waitForPaused(dbg); + + findElement(dbg, "frame", 2).focus(); + await clickElement(dbg, "frame", 2); + + // Hover over the token to launch preview popup + await tryHovering(dbg, 5, 8, "popup"); + + // Click the first inspector buttom to view node in inspector + await waitForElement(dbg, "openInspector"); + + // Loading the inspector panel at first, to make it possible to listen for + // new node selections + const inspector = await toolbox.loadTool("inspector"); + + const onInspectorSelected = toolbox.once("inspector-selected"); + const onInspectorUpdated = inspector.once("inspector-updated"); + const onNewNode = toolbox.selection.once("new-node-front"); + + findElement(dbg, "openInspector").click(); + + await onInspectorSelected; + await onInspectorUpdated; + await onNewNode; + + ok(true, "Inspector selected and new node got selected"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-compressed-sourcemaps.js b/devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-compressed-sourcemaps.js new file mode 100644 index 0000000000..0569d36071 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-compressed-sourcemaps.js @@ -0,0 +1,22 @@ +/* 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 . */ + +/** + * This runs all integration tests against a test using sources maps + * whose generated files (bundles) are compressed. + * i.e. bundles are made of a unique line with all sources compressed into one line. + */ + +"use strict"; + +requestLongerTimeout(10); + +add_task(async function () { + const testFolder = "sourcemaps-reload-compressed"; + const isCompressed = true; + + await runAllIntegrationTests(testFolder, { + isCompressed, + }); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-uncompressed-sourcemaps.js b/devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-uncompressed-sourcemaps.js new file mode 100644 index 0000000000..df07f0dc43 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-integration-reloading-uncompressed-sourcemaps.js @@ -0,0 +1,22 @@ +/* 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 . */ + +/** + * This runs all integration tests against a test using sources maps + * whose generated files (bundles) are uncompressed. + * i.e. bundles are keeping the same format as original files. + */ + +"use strict"; + +requestLongerTimeout(10); + +add_task(async function () { + const testFolder = "sourcemaps-reload-uncompressed"; + const isCompressed = false; + + await runAllIntegrationTests(testFolder, { + isCompressed, + }); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js b/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js new file mode 100644 index 0000000000..535321aa37 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js @@ -0,0 +1,226 @@ +/* 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 . */ + +// Tests the Javascript Tracing feature. + +"use strict"; + +add_task(async function () { + // This is preffed off for now, so ensure turning it on + await pushPref("devtools.debugger.features.javascript-tracing", true); + + const dbg = await initDebugger("doc-scripts.html"); + + info("Enable the tracing"); + await clickElement(dbg, "trace"); + + const topLevelThread = + dbg.toolbox.commands.targetCommand.targetFront.threadFront.actorID; + info("Wait for tracing to be enabled"); + await waitForState(dbg, state => { + return dbg.selectors.getIsThreadCurrentlyTracing(topLevelThread); + }); + + ok( + dbg.toolbox.splitConsole, + "Split console is automatically opened when tracing to the console" + ); + + invokeInTab("main"); + + info("Wait for console messages for the whole trace"); + // `main` calls `foo` which calls `bar` + await hasConsoleMessage(dbg, "λ main"); + await hasConsoleMessage(dbg, "λ foo"); + await hasConsoleMessage(dbg, "λ bar"); + + const traceMessages = await findConsoleMessages(dbg.toolbox, "λ main"); + is(traceMessages.length, 1, "We got a unique trace for 'main' function call"); + const sourceLink = traceMessages[0].querySelector(".frame-link-source"); + sourceLink.click(); + info("Wait for the main function to be highlighted in the debugger"); + await waitForSelectedSource(dbg, "simple1.js"); + await waitForSelectedLocation(dbg, 1, 16); + + // Test Blackboxing + info("Clear the console from previous traces"); + const { hud } = await dbg.toolbox.getPanel("webconsole"); + hud.ui.clearOutput(); + await waitFor( + async () => !(await findConsoleMessages(dbg.toolbox, "λ main")).length, + "Wait for console to be cleared" + ); + + info( + "Now blackbox only the source where main function is (simple1.js), but foo and bar are in another module" + ); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + + info("Trigger some code from simple1 and simple2"); + invokeInTab("main"); + + info("Only methods from simple2 are logged"); + await hasConsoleMessage(dbg, "λ foo"); + await hasConsoleMessage(dbg, "λ bar"); + is( + (await findConsoleMessages(dbg.toolbox, "λ main")).length, + 0, + "Traces from simple1.js, related to main function are not logged" + ); + + info("Revert blackboxing"); + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); + + // Test Disabling tracing + info("Disable the tracing"); + await clickElement(dbg, "trace"); + info("Wait for tracing to be disabled"); + await waitForState(dbg, state => { + return !dbg.selectors.getIsThreadCurrentlyTracing(topLevelThread); + }); + + invokeInTab("inline_script2"); + + // Let some time for the tracer to appear if we failed disabling the tracing + await wait(1000); + + const messages = await findConsoleMessages(dbg.toolbox, "inline_script2"); + is( + messages.length, + 0, + "We stopped recording traces, an the function call isn't logged in the console" + ); + + // Test Navigations + await navigate(dbg, "doc-sourcemaps2.html", "main.js", "main.min.js"); + + info("Re-enable the tracing after navigation"); + await clickElement(dbg, "trace"); + + const newTopLevelThread = + dbg.toolbox.commands.targetCommand.targetFront.threadFront.actorID; + info("Wait for tracing to be re-enabled"); + await waitForState(dbg, state => { + return dbg.selectors.getIsThreadCurrentlyTracing(newTopLevelThread); + }); + + invokeInTab("logMessage"); + + await hasConsoleMessage(dbg, "λ logMessage"); + + const traceMessages2 = await findConsoleMessages(dbg.toolbox, "λ logMessage"); + is( + traceMessages2.length, + 1, + "We got a unique trace for 'logMessage' function call" + ); + const sourceLink2 = traceMessages2[0].querySelector(".frame-link-source"); + sourceLink2.click(); + + info("Wait for the 'logMessage' function to be highlighted in the debugger"); + await waitForSelectedSource(dbg, "main.js"); + await waitForSelectedLocation(dbg, 4, 2); + ok(true, "The selected source and location is on the original file"); +}); + +add_task(async function testPersitentLogMethod() { + let dbg = await initDebugger("doc-scripts.html"); + is( + dbg.selectors.getJavascriptTracingLogMethod(), + "console", + "By default traces are logged to the console" + ); + + info("Change the log method to stdout"); + dbg.actions.setJavascriptTracingLogMethod("stdout"); + + await dbg.toolbox.closeToolbox(); + + dbg = await initDebugger("doc-scripts.html"); + is( + dbg.selectors.getJavascriptTracingLogMethod(), + "stdout", + "The new setting has been persisted" + ); + + info("Reset back to the default value"); + dbg.actions.setJavascriptTracingLogMethod("console"); +}); + +add_task(async function testPageKeyShortcut() { + // Ensures that the key shortcut emitted in the content process bubbles up to the parent process + await pushPref("test.events.async.enabled", true); + + // Fake DevTools being opened by a real user interaction. + // Tests are bypassing DevToolsStartup to open the tools by calling gDevTools directly. + // By doing so DevToolsStartup considers itself as uninitialized, + // whereas we want it to handle the key shortcut we trigger in this test. + const DevToolsStartup = Cc["@mozilla.org/devtools/startup-clh;1"].getService( + Ci.nsISupports + ).wrappedJSObject; + DevToolsStartup.initialized = true; + registerCleanupFunction(() => { + DevToolsStartup.initialized = false; + }); + + const dbg = await initDebugger("data:text/html,key-shortcut"); + + const topLevelThread = + dbg.toolbox.commands.targetCommand.targetFront.threadFront.actorID; + ok( + !dbg.selectors.getIsThreadCurrentlyTracing(topLevelThread), + "Tracing is disabled on debugger opening" + ); + + info( + "Focus the page in order to assert that the page keeps the focus when enabling the tracer" + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + content.focus(); + }); + await waitFor( + () => Services.focus.focusedElement == gBrowser.selectedBrowser + ); + is( + Services.focus.focusedElement, + gBrowser.selectedBrowser, + "The tab is still focused before enabling tracing" + ); + + info("Toggle ON the tracing via the key shortcut from the web page"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + EventUtils.synthesizeKey( + "VK_5", + { ctrlKey: true, shiftKey: true }, + content + ); + }); + + info("Wait for tracing to be enabled"); + await waitForState(dbg, state => { + return dbg.selectors.getIsThreadCurrentlyTracing(topLevelThread); + }); + + is( + Services.focus.focusedElement, + gBrowser.selectedBrowser, + "The tab is still focused after enabling tracing" + ); + + info("Toggle it back off, wit the same shortcut"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + EventUtils.synthesizeKey( + "VK_5", + { ctrlKey: true, shiftKey: true }, + content + ); + }); + + info("Wait for tracing to be disabled"); + await waitForState(dbg, state => { + return !dbg.selectors.getIsThreadCurrentlyTracing(topLevelThread); + }); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-navigation.js b/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-navigation.js new file mode 100644 index 0000000000..21d911a2cd --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-navigation.js @@ -0,0 +1,41 @@ +/* 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 . */ + +// Tests that keyboard navigation into and out of debugger code editor + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple2.js"); + const doc = dbg.win.document; + + await selectSource(dbg, "simple2.js"); + + await waitForElementWithSelector(dbg, ".CodeMirror"); + findElementWithSelector(dbg, ".CodeMirror").focus(); + + // Enter code editor + pressKey(dbg, "Enter"); + is( + findElementWithSelector(dbg, "textarea"), + doc.activeElement, + "Editor is enabled" + ); + + // Exit code editor and focus on container + pressKey(dbg, "Escape"); + is( + findElementWithSelector(dbg, ".CodeMirror"), + doc.activeElement, + "Focused on container" + ); + + // Enter code editor + pressKey(dbg, "Tab"); + is( + findElementWithSelector(dbg, "textarea"), + doc.activeElement, + "Editor is enabled" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts-modal.js b/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts-modal.js new file mode 100644 index 0000000000..d293d3d56f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts-modal.js @@ -0,0 +1,49 @@ +/* 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 . */ + +/** + * Test the keyboard shortcuts modal + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebuggerWithAbsoluteURL( + "data:text/html,Test keyboard shortcuts modal" + ); + + ok(!getShortcutsModal(dbg), "The shortcuts modal is hidden by default"); + + info("Open the modal with the shortcut"); + pressKeyboardShortcut(dbg); + + const el = await waitFor(() => getShortcutsModal(dbg)); + const sections = [...el.querySelectorAll(".shortcuts-section h2")].map( + h2 => h2.textContent + ); + is( + JSON.stringify(sections), + JSON.stringify(["Editor", "Stepping", "Search"]), + "The modal has the expected sections" + ); + + info("Close the modal with the shortcut"); + pressKeyboardShortcut(dbg); + await waitFor(() => !getShortcutsModal(dbg)); + ok(true, "The modal was closed"); +}); + +function getShortcutsModal(dbg) { + return findElementWithSelector(dbg, ".shortcuts-modal"); +} + +function pressKeyboardShortcut(dbg) { + EventUtils.synthesizeKey( + "/", + { + [Services.appinfo.OS === "Darwin" ? "metaKey" : "ctrlKey"]: true, + }, + dbg.win + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts.js b/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts.js new file mode 100644 index 0000000000..67fa1d397e --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-keyboard-shortcuts.js @@ -0,0 +1,58 @@ +/* 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 . */ + +/** + * Test keyboard shortcuts. + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-debugger-statements.html"); + + const onReloaded = reload(dbg); + await waitForPaused(dbg); + await waitForLoadedSource(dbg, "doc-debugger-statements.html"); + const source = findSource(dbg, "doc-debugger-statements.html"); + assertPausedAtSourceAndLine(dbg, source.id, 11); + + await pressResume(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 16); + + await pressStepOver(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 17); + + await pressStepIn(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 22); + + await pressStepOut(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 18); + + await pressStepOver(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 18); + + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; +}); + +function pressResume(dbg) { + pressKey(dbg, "resumeKey"); + return waitForPaused(dbg); +} + +function pressStepOver(dbg) { + pressKey(dbg, "stepOverKey"); + return waitForPaused(dbg); +} + +function pressStepIn(dbg) { + pressKey(dbg, "stepInKey"); + return waitForPaused(dbg); +} + +function pressStepOut(dbg) { + pressKey(dbg, "stepOutKey"); + return waitForPaused(dbg); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-layout-changes.js b/devtools/client/debugger/test/mochitest/browser_dbg-layout-changes.js new file mode 100644 index 0000000000..609879c7b1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-layout-changes.js @@ -0,0 +1,94 @@ +/* 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 . */ + +/** + * This if the debugger's layout is correctly modified when the toolbox's + * host changes. + */ + +"use strict"; + +requestLongerTimeout(2); + +let gDefaultHostType = Services.prefs.getCharPref("devtools.toolbox.host"); + +add_setup(async function () { + // Disable window occlusion. See bug 1733955 / bug 1779559. + if (navigator.platform.indexOf("Win") == 0) { + await SpecialPowers.pushPrefEnv({ + set: [["widget.windows.window_occlusion_tracking.enabled", false]], + }); + } +}); + +add_task(async function () { + // test is too slow on some platforms due to the number of test cases + const dbg = await initDebugger("doc-iframes.html"); + + const layouts = [ + ["vertical", "window:small"], + ["horizontal", "bottom"], + ["vertical", "right"], + ["horizontal", "window:big"], + ]; + + for (const layout of layouts) { + const [orientation, host] = layout; + await testLayout(dbg, orientation, host); + } + + ok(true, "Orientations are correct"); +}); + +async function testLayout(dbg, orientation, host) { + info(`Switching to ${host} ${orientation}.`); + + await switchHost(dbg, host); + await resizeToolboxWindow(dbg, host); + return waitForState( + dbg, + state => dbg.selectors.getOrientation() == orientation + ); +} + +function getHost(host) { + if (host.indexOf("window") == 0) { + return "window"; + } + return host; +} + +async function switchHost(dbg, hostType) { + const { toolbox } = dbg; + await toolbox.switchHost(getHost(hostType)); +} + +function resizeToolboxWindow(dbg, host) { + const { toolbox } = dbg; + const sizeOption = host.split(":")[1]; + if (!sizeOption) { + return; + } + + const win = toolbox.win.parent; + + let breakpoint = 800; + if (sizeOption == "big" && win.outerWidth <= breakpoint) { + breakpoint += 300; + } else if (sizeOption == "small" && win.outerWidth >= breakpoint) { + breakpoint -= 300; + } + resizeWindow(dbg, breakpoint); +} + +function resizeWindow(dbg, width) { + const { toolbox } = dbg; + const win = toolbox.win.parent; + win.resizeTo(width, window.screen.availHeight); +} + +registerCleanupFunction(function () { + Services.prefs.setCharPref("devtools.toolbox.host", gDefaultHostType); + gDefaultHostType = null; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-link-reload.js b/devtools/client/debugger/test/mochitest/browser_dbg-link-reload.js new file mode 100644 index 0000000000..c374fc6bba --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-link-reload.js @@ -0,0 +1,65 @@ +/* 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 . */ + +/** + * Test reload via an href link, which refers to the same document. + * It seems to cause different codepath compared to F5. + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-reload-link.html", + "doc-reload-link.html" + ); + + info("Add a breakpoint that will be hit on reload"); + await addBreakpoint(dbg, "doc-reload-link.html", 3); + + for (let i = 0; i < 5; i++) { + const onReloaded = waitForReload(dbg.commands); + + info( + "Reload via a link, this causes special race condition different from F5" + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + const reloadLink = content.document.querySelector("a"); + reloadLink.click(); + }); + + info("Wait for paused\n"); + await waitForPaused(dbg); + + info("Check paused location\n"); + const source = findSource(dbg, "doc-reload-link.html"); + assertPausedAtSourceAndLine(dbg, source.id, 3); + + await resume(dbg); + + info("Wait for completion of the page load"); + // This help ensure that the page loaded correctly and prevent pending request at teardown + await onReloaded; + } +}); + +async function waitForReload(commands) { + let resolve; + const onReloaded = new Promise(r => (resolve = r)); + const { resourceCommand } = commands; + const { DOCUMENT_EVENT } = resourceCommand.TYPES; + const onAvailable = resources => { + if (resources.find(resource => resource.name == "dom-complete")) { + resourceCommand.unwatchResources([DOCUMENT_EVENT], { onAvailable }); + resolve(); + } + }; + // Wait for watchResources completion before reloading, otherwise we might miss the dom-complete event + // if watchResources is still pending while the reload already started and finished loading the document early. + await resourceCommand.watchResources([DOCUMENT_EVENT], { + onAvailable, + ignoreExistingResources: true, + }); + return onReloaded; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js new file mode 100644 index 0000000000..6ce0acbc77 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js @@ -0,0 +1,25 @@ +/* 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 . */ + +/* + * Tests that we can log event listeners calls + */ + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + const dbg = await initDebugger( + "doc-event-breakpoints.html", + "event-breakpoints.js" + ); + + await clickElement(dbg, "logEventsCheckbox"); + await dbg.actions.addEventListenerBreakpoints(["event.mouse.click"]); + clickElementInTab("#click-target"); + + await hasConsoleMessage(dbg, "click"); + await waitForRequestsToSettle(dbg); + ok(true); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js new file mode 100644 index 0000000000..f061bbbbc5 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js @@ -0,0 +1,44 @@ +/* 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 . */ + +/* + * Tests that expressions in log points are source mapped. + */ + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + + const dbg = await initDebugger("doc-sourcemaps3.html", "test.js"); + dbg.actions.toggleMapScopes(); + + const source = findSource(dbg, "test.js"); + await selectSource(dbg, "test.js"); + + await getDebuggerSplitConsole(dbg); + + await addBreakpoint(dbg, "test.js", 6); + await waitForBreakpoint(dbg, "test.js", 6); + + await dbg.actions.addBreakpoint( + getContext(dbg), + createLocation({ line: 5, source }), + { logValue: "`value: ${JSON.stringify(test)}`", requiresMapping: true } + ); + await waitForBreakpoint(dbg, "test.js", 5); + + invokeInTab("test"); + + await waitForPaused(dbg); + + await hasConsoleMessage(dbg, "value:"); + const { value } = await findConsoleMessage(dbg, "value:"); + is( + value, + 'value: ["b (30)","a","b (5)","z"]', + "Variables in logpoint expression should be mapped" + ); + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js new file mode 100644 index 0000000000..aa52904d31 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js @@ -0,0 +1,30 @@ +/* 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 . */ + +/* + * Tests that log points in a worker are correctly logged to the console + */ + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + + const dbg = await initDebugger("doc-windowless-workers.html"); + + await waitForSource(dbg, "simple-worker.js"); + await selectSource(dbg, "simple-worker.js"); + + await altClickElement(dbg, "gutter", 4); + await waitForBreakpoint(dbg, "simple-worker.js", 4); + + await getDebuggerSplitConsole(dbg); + await hasConsoleMessage(dbg, "timer"); + const { link } = await findConsoleMessage(dbg, "timer"); + is( + link, + "simple-worker.js:4:9", + "message should include the url and linenumber" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js new file mode 100644 index 0000000000..a025b58dab --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js @@ -0,0 +1,41 @@ +/* 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 . */ + +/* + * Tests that log points are correctly logged to the console + */ + +"use strict"; + +add_task(async function () { + Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + const dbg = await initDebugger( + "doc-script-switching.html", + "script-switching-01.js" + ); + + const source = findSource(dbg, "script-switching-01.js"); + await selectSource(dbg, "script-switching-01.js"); + + await getDebuggerSplitConsole(dbg); + + await altClickElement(dbg, "gutter", 7); + await waitForBreakpoint(dbg, "script-switching-01.js", 7); + + await dbg.actions.addBreakpoint( + getContext(dbg), + createLocation({ line: 8, source }), + { logValue: "'a', 'b', 'c'" } + ); + + invokeInTab("firstCall"); + await waitForPaused(dbg); + + await hasConsoleMessage(dbg, "a b c"); + await hasConsoleMessage(dbg, "firstCall"); + + const { link, value } = await findConsoleMessage(dbg, "a b c"); + is(link, "script-switching-01.js:8:2", "logs should have the relevant link"); + is(value, "a b c", "logs should have multiple values"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-many-breakpoints-same-line.js b/devtools/client/debugger/test/mochitest/browser_dbg-many-breakpoints-same-line.js new file mode 100644 index 0000000000..d6d1fc30c7 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-many-breakpoints-same-line.js @@ -0,0 +1,93 @@ +/* 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 . */ + +// Test settings multiple types of breakpoints on the same line +// Only the last should be used + +"use strict"; + +// Line where we set a breakpoint in simple2.js +const BREAKPOINT_LINE = 5; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple2.js"); + + await selectSource(dbg, "simple2.js"); + await waitForSelectedSource(dbg, "simple2.js"); + + await testSimpleAndLog(dbg); + + await testLogUpdates(dbg); +}); + +async function testSimpleAndLog(dbg) { + info("Add a simple breakpoint"); + await addBreakpoint(dbg, "simple2.js", BREAKPOINT_LINE); + + info("Add a log breakpoint, replacing the breakpoint into a logpoint"); + await setLogPoint(dbg, BREAKPOINT_LINE, "`log point ${x}`"); + await waitForLog(dbg, "`log point ${x}`"); + await assertLogBreakpoint(dbg, BREAKPOINT_LINE); + + const bp = findBreakpoint(dbg, "simple2.js", BREAKPOINT_LINE); + is( + bp.options.logValue, + "`log point ${x}`", + "log breakpoint value is correct" + ); + + info( + "Eval foo() and trigger the breakpoints. If this freeze here, it means that the log point has been ignored." + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.wrappedJSObject.foo(42); + }); + + info( + "Wait for the log-point message. Only log-point breakpoint should work." + ); + + await waitForMessageByType(dbg, "log point 42", ".logPoint"); + + const source = findSource(dbg, "simple2.js"); + await removeBreakpoint(dbg, source.id, BREAKPOINT_LINE); +} + +async function testLogUpdates(dbg) { + info("Add a log breakpoint"); + await setLogPoint(dbg, BREAKPOINT_LINE, "`log point`"); + await waitForLog(dbg, "`log point`"); + await assertLogBreakpoint(dbg, BREAKPOINT_LINE); + + const bp = findBreakpoint(dbg, "simple2.js", BREAKPOINT_LINE); + is(bp.options.logValue, "`log point`", "log breakpoint value is correct"); + + info("Edit the log breakpoint"); + await setLogPoint(dbg, BREAKPOINT_LINE, " + ` edited`"); + await waitForLog(dbg, "`log point` + ` edited`"); + await assertLogBreakpoint(dbg, BREAKPOINT_LINE); + + const bp2 = findBreakpoint(dbg, "simple2.js", BREAKPOINT_LINE); + is( + bp2.options.logValue, + "`log point` + ` edited`", + "log breakpoint value is correct" + ); + + info("Eval foo() and trigger the breakpoints"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.wrappedJSObject.foo(); + }); + + info("Wait for the log-point message. Only the edited one should appear"); + await waitForMessageByType(dbg, "log point edited", ".logPoint"); +} + +async function waitForMessageByType(dbg, msg, typeSelector) { + const webConsolePanel = await getDebuggerSplitConsole(dbg); + const hud = webConsolePanel.hud; + return waitFor( + async () => !!findMessagesByType(hud, msg, typeSelector).length + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-merge-scopes.js b/devtools/client/debugger/test/mochitest/browser_dbg-merge-scopes.js new file mode 100644 index 0000000000..7bed9fc9ea --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-merge-scopes.js @@ -0,0 +1,55 @@ +/* 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 . */ + +// Test that adjacent scopes are merged together as expected. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-merge-scopes.html"); + + invokeInTab("run"); + await waitForPaused(dbg, "doc-merge-scopes.html"); + + // Function body and function lexical scopes are merged together. + expectLabels(dbg, ["first", "", "arguments", "x", "y", "z"]); + + await resume(dbg); + await waitForPaused(dbg); + + // Function body and inner lexical scopes are not merged together. + await toggleScopeNode(dbg, 4); + expectLabels(dbg, ["Block", "", "y", "second", "arguments", "x"]); + + await resume(dbg); + await waitForPaused(dbg); + + // When there is a function body, function lexical, and inner lexical scope, + // the first two are merged together. + await toggleScopeNode(dbg, 4); + expectLabels(dbg, [ + "Block", + "", + "z", + "third", + "arguments", + "v", + "x", + "y", + ]); +}); + +function getLabel(dbg, index) { + return findElement(dbg, "scopeNode", index).innerText; +} + +function expectLabels(dbg, array) { + for (let i = 0; i < array.length; i++) { + is( + getLabel(dbg, i + 1), + array[i], + `Correct label ${array[i]} for index ${i + 1}` + ); + } +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-message-run-to-completion.js b/devtools/client/debugger/test/mochitest/browser_dbg-message-run-to-completion.js new file mode 100644 index 0000000000..a168c9233d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-message-run-to-completion.js @@ -0,0 +1,24 @@ +/* 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 . */ + +// Test that messages from postMessage calls are not delivered while paused in +// the debugger. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-message-run-to-completion.html"); + invokeInTab("test", "doc-message-run-to-completion.html"); + await waitForPaused(dbg); + let result = await dbg.client.evaluate("event.data"); + is(result.result, "first", "first message delivered in order"); + await resume(dbg); + await waitForPaused(dbg); + result = await dbg.client.evaluate("event.data"); + is(result.result, "second", "second message delivered in order"); + await resume(dbg); + await waitForPaused(dbg); + result = await dbg.client.evaluate("event.data"); + is(result.result, "third", "third message delivered in order"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-minified.js b/devtools/client/debugger/test/mochitest/browser_dbg-minified.js new file mode 100644 index 0000000000..8086dbbf46 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-minified.js @@ -0,0 +1,33 @@ +/* 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 . */ + +// Tests minfied + source maps. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-minified2.html", "sum.js"); + dbg.actions.toggleMapScopes(); + + await selectSource(dbg, "sum.js"); + await addBreakpoint(dbg, "sum.js", 2); + + invokeInTab("test"); + await waitForPaused(dbg); + + is(getScopeNodeLabel(dbg, 1), "sum", "check scope label"); + is(getScopeNodeLabel(dbg, 2), "first", "check scope label"); + is(getScopeNodeValue(dbg, 2), "40", "check scope value"); + is(getScopeNodeLabel(dbg, 3), "second", "check scope label"); + is(getScopeNodeValue(dbg, 3), "2", "check scope value"); + is(getScopeNodeLabel(dbg, 4), "Window", "check scope label"); +}); + +function getScopeNodeLabel(dbg, index) { + return findElement(dbg, "scopeNode", index).innerText; +} + +function getScopeNodeValue(dbg, index) { + return findElement(dbg, "scopeValue", index).innerText; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-navigation-when-paused.js b/devtools/client/debugger/test/mochitest/browser_dbg-navigation-when-paused.js new file mode 100644 index 0000000000..9af7228527 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-navigation-when-paused.js @@ -0,0 +1,42 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-navigation-when-paused.html"); + + await togglePauseOnExceptions(dbg, true, true); + + clickElementInTab("body"); + + await waitForPaused(dbg, "doc-navigation-when-paused.html"); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-navigation-when-paused.html").id, + 12 + ); + + await navigate( + dbg, + "doc-navigation-when-paused.html", + "doc-navigation-when-paused.html" + ); + + clickElementInTab("body"); + + await waitForPaused(dbg, "doc-navigation-when-paused.html"); + + // If breakpoints aren't properly ignored after navigation, this could + // potentially pause at line 9. This helper also ensures that the file + // source itself has loaded, which may not be the case if navigation cleared + // the source and nothing has sent it to the devtools client yet, as was + // the case in Bug 1581530. + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-navigation-when-paused.html").id, + 12 + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js b/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js new file mode 100644 index 0000000000..6cf5c0ec5f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-navigation.js @@ -0,0 +1,73 @@ +/* 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 . */ + +"use strict"; + +const SOURCES = [ + "simple1.js", + "simple2.js", + "simple3.js", + "long.js", + "doc-scripts.html", +]; + +/** + * Test navigating + * navigating while paused will reset the pause state and sources + */ +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + const { + selectors: { getSelectedSource, getIsPaused, getCurrentThread }, + } = dbg; + ok(getCurrentThread(), "We get a thread selected on debugger opening"); + + info("Pause in the first document"); + invokeInTab("firstCall"); + await waitForPaused(dbg); + + info("Navigate while being paused in the first document"); + await navigate(dbg, "doc-scripts.html", "simple1.js"); + ok( + getCurrentThread(), + "The new top level thread is selected after navigation" + ); + + info("Set a breakpoint on the second document and pause on it"); + await selectSource(dbg, "simple1.js"); + await addBreakpoint(dbg, "simple1.js", 4); + invokeInTab("main"); + await waitForPaused(dbg); + const source = findSource(dbg, "simple1.js"); + assertPausedAtSourceAndLine(dbg, source.id, 4); + is(countSources(dbg), 5, "5 sources are loaded."); + + await waitForRequestsToSettle(dbg); + let onBreakpoint = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + await navigate(dbg, "doc-scripts.html", ...SOURCES); + await onBreakpoint; + is(countSources(dbg), 5, "5 sources are loaded."); + ok(!getIsPaused(getCurrentThread()), "Is not paused"); + + await waitForRequestsToSettle(dbg); + onBreakpoint = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + await navigate(dbg, "doc-scripts.html", ...SOURCES); + await onBreakpoint; + is(countSources(dbg), 5, "5 sources are loaded."); + + info("Test that the current selected source persists across reloads"); + await selectSource(dbg, "long.js"); + + await waitForRequestsToSettle(dbg); + onBreakpoint = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + await reload(dbg, "long.js"); + await onBreakpoint; + await waitForSelectedSource(dbg, "long.js"); + + ok(getSelectedSource().url.includes("long.js"), "Selected source is long.js"); +}); + +function countSources(dbg) { + return dbg.selectors.getSourceCount(); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-no-duplicate-breakpoints-on-frame-reload.js b/devtools/client/debugger/test/mochitest/browser_dbg-no-duplicate-breakpoints-on-frame-reload.js new file mode 100644 index 0000000000..97d4eeb273 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-no-duplicate-breakpoints-on-frame-reload.js @@ -0,0 +1,101 @@ +/* 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 . */ + +// This tests makes sure that on reload of a remote iframe, there +// are no duplicated breakpoints. The test case relates to a specific +// issue in Bug 1728587 +"use strict"; + +add_task(async function () { + // With fission and EFT disabled all the sources are going to be under the Main Thread + // and this sceanrio becomes irrelevant + if (!isFissionEnabled() && !isEveryFrameTargetEnabled()) { + return; + } + + const dbg = await initDebugger( + "doc_dbg-fission-frame-sources.html", + "simple2.js" + ); + + info( + "Open a tab for a source in the top document to assert it doesn't disappear" + ); + await selectSource(dbg, "simple1.js"); + + info("Add breakpoint to the source (simple2.js) in the remote frame"); + await selectSource(dbg, "simple2.js"); + await addBreakpoint(dbg, "simple2.js", 3); + + is(dbg.selectors.getBreakpointCount(), 1, "Only one breakpoint exists"); + + const source = findSource(dbg, "simple2.js"); + assertBreakpointsList(dbg, source); + + is( + countTabs(dbg), + 2, + "We see the tabs for the sources of both top and iframe documents" + ); + + const onBreakpointSet = waitForDispatch(dbg.store, "SET_BREAKPOINT"); + + info("reload the iframe"); + const iframeBrowsingContext = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + function () { + const iframe = content.document.querySelector("iframe"); + return iframe.browsingContext; + } + ); + await SpecialPowers.spawn(iframeBrowsingContext, [], () => { + content.location.reload(); + }); + + await onBreakpointSet; + + await waitForSelectedSource(dbg, "simple2.js"); + + is(dbg.selectors.getBreakpointCount(), 1, "Only one breakpoint still exists"); + + const sourceAfterReload = findSource(dbg, "simple2.js"); + assertBreakpointsList(dbg, sourceAfterReload); + + is(countTabs(dbg), 2, "We still see the tabs for the two sources"); + + await removeBreakpoint(dbg, sourceAfterReload.id, 3); +}); + +function assertBreakpointsList(dbg, source) { + const breakpointHeadings = findAllElements(dbg, "breakpointHeadings"); + const breakpointItems = findAllElements(dbg, "breakpointItems"); + + is( + breakpointHeadings.length, + 1, + "The breakpoint list show one breakpoint source" + ); + is( + breakpointItems.length, + 1, + "The breakpoint list shows only one breakpoint" + ); + + is( + breakpointHeadings[0].title, + source.url, + "The info displayed for the breakpoint tooltip of the 1st breakpoint is correct" + ); + is( + breakpointHeadings[0].textContent, + "simple2.js", + "The info displayed for the breakpoint heading of the 1st breakpoint is correct" + ); + is( + breakpointItems[0].textContent, + "return x + y;3:4", + "The info displayed for the 1st breakpoint is correct" + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js b/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js new file mode 100644 index 0000000000..ad8ee830b0 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-old-breakpoint.js @@ -0,0 +1,111 @@ +/* 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 . */ + +// Test that we show a breakpoint in the UI when there is an old pending +// breakpoint with an invalid original location. + +"use strict"; + +add_task(async function () { + clearDebuggerPreferences(); + + const pending = { + bp1: { + location: { + sourceId: "", + sourceUrl: `${EXAMPLE_URL}nowhere2.js`, + line: 5, + column: 0, + }, + generatedLocation: { + sourceUrl: `${EXAMPLE_URL}simple1.js`, + line: 4, + column: 0, + }, + options: {}, + disabled: false, + }, + bp2: { + location: { + sourceId: "", + sourceUrl: `${EXAMPLE_URL}nowhere.js`, + line: 5, + column: 0, + }, + generatedLocation: { + sourceUrl: `${EXAMPLE_URL}simple3.js`, + line: 2, + column: 0, + }, + options: {}, + disabled: false, + }, + }; + asyncStorage.setItem("debugger.pending-breakpoints", pending); + + const toolbox = await openNewTabAndToolbox( + `${EXAMPLE_URL}doc-scripts.html`, + "jsdebugger" + ); + const dbg = createDebuggerContext(toolbox); + const onBreakpoint = waitForDispatch(dbg.store, "SET_BREAKPOINT", 2); + + // Pending breakpoints are installed asynchronously, keep invoking the entry + // function until the debugger pauses. + await waitUntil(() => { + invokeInTab("main"); + return isPaused(dbg); + }); + await onBreakpoint; + + ok(true, "paused at unmapped breakpoint"); + await waitForState( + dbg, + state => dbg.selectors.getBreakpointCount(state) == 2 + ); + ok(true, "unmapped breakpoints shown in UI"); +}); + +// Test that if we show a breakpoint with an old generated location, it is +// removed after we load the original source and find the new generated +// location. +add_task(async function () { + clearDebuggerPreferences(); + + const pending = { + bp1: { + location: { + sourceId: "", + sourceUrl: "webpack:///entry.js", + line: 15, + column: 0, + }, + generatedLocation: { + sourceUrl: `${EXAMPLE_URL}sourcemaps/bundle.js`, + line: 47, + column: 16, + }, + options: {}, + disabled: false, + }, + }; + asyncStorage.setItem("debugger.pending-breakpoints", pending); + + const toolbox = await openNewTabAndToolbox( + `${EXAMPLE_URL}doc-sourcemaps.html`, + "jsdebugger" + ); + const dbg = createDebuggerContext(toolbox); + + await waitForState(dbg, state => { + const bps = dbg.selectors.getBreakpointsList(state); + return ( + bps.length == 1 && + bps[0].location.sourceUrl.includes("entry.js") && + bps[0].location.line == 15 + ); + }); + ok(true, "removed old breakpoint during sync"); + await waitForRequestsToSettle(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-outline-filter.js b/devtools/client/debugger/test/mochitest/browser_dbg-outline-filter.js new file mode 100644 index 0000000000..3ce605311c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-outline-filter.js @@ -0,0 +1,82 @@ +/* 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 . */ + +// Tests the outline pane fuzzy filtering of outline items + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "long.js"); + await selectSource(dbg, "long.js", 1); + findElementWithSelector(dbg, ".outline-tab").click(); + + // turn off alphetical sort if active + const alphabetizeButton = findElementWithSelector( + dbg, + ".outline-footer button" + ); + if (alphabetizeButton.className === "active") { + alphabetizeButton.click(); + } + + const outlineFilter = findElementWithSelector(dbg, ".outline-filter-input"); + ok(outlineFilter, "Outline filter is present"); + + outlineFilter.blur(); + ok( + !findElementWithSelector(dbg, ".outline-filter-input.focused"), + "does not have class focused when not focused" + ); + + outlineFilter.focus(); + ok( + findElementWithSelector(dbg, ".outline-filter-input.focused"), + "has class focused when focused" + ); + + is(getItems(dbg).length, 9, "9 items in the unfiltered list"); + + // filter all items starting with t + type(dbg, "t"); + is(getItems(dbg).length, 4, "4 items in the list after 't' filter"); + ok(getItems(dbg)[0].textContent.includes("TodoModel(key)"), "item TodoModel"); + ok( + getItems(dbg)[1].textContent.includes("toggleAll(checked)"), + "item toggleAll" + ); + ok( + getItems(dbg)[2].textContent.includes("toggle(todoToToggle)"), + "item toggle" + ); + ok(getItems(dbg)[3].textContent.includes("testModel()"), "item testModel"); + + // filter using term 'tog' + type(dbg, "og"); + is(getItems(dbg).length, 2, "2 items in the list after 'tog' filter"); + ok( + getItems(dbg)[0].textContent.includes("toggleAll(checked)"), + "item toggleAll" + ); + ok( + getItems(dbg)[1].textContent.includes("toggle(todoToToggle)"), + "item toggle" + ); + + pressKey(dbg, "Escape"); + is(getItems(dbg).length, 9, "9 items in the list after escape pressed"); + + // Ensure no action is taken when Enter key is pressed + pressKey(dbg, "Enter"); + is(getItems(dbg).length, 9, "9 items in the list after enter pressed"); + + // check that the term 'todo' includes items with todo + type(dbg, "todo"); + is(getItems(dbg).length, 2, "2 items in the list after 'todo' filter"); + ok(getItems(dbg)[0].textContent.includes("TodoModel(key)"), "item TodoModel"); + ok(getItems(dbg)[1].textContent.includes("addTodo(title)"), "item addTodo"); +}); + +function getItems(dbg) { + return findAllElements(dbg, "outlineItems"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-outline-focus.js b/devtools/client/debugger/test/mochitest/browser_dbg-outline-focus.js new file mode 100644 index 0000000000..f04a28b732 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-outline-focus.js @@ -0,0 +1,39 @@ +/* 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 . */ + +// Tests that after clicking a function in edtior, outline focuses that function + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-sources.html", "long.js"); + + await selectSource(dbg, "long.js", 1); + findElementWithSelector(dbg, ".outline-tab").click(); + is(getItems(dbg).length, 9, "9 items in the outline list"); + + info("Clicking inside a function in editor should focus the outline"); + clickAtPos(dbg, { line: 15, ch: 2 }); + await waitForElementWithSelector(dbg, ".outline-list__element.focused"); + ok( + getFocusedFunction(dbg).includes("addTodo"), + "The right function is focused" + ); + + info("Clicking an empty line in the editor should unfocus the outline"); + await clickAtPos(dbg, { line: 13, ch: 2 }); + is(getFocusedNode(dbg), null, "should not exist"); +}); + +function getItems(dbg) { + return findAllElements(dbg, "outlineItems"); +} + +function getFocusedNode(dbg) { + return findElementWithSelector(dbg, ".outline-list__element.focused"); +} + +function getFocusedFunction(dbg) { + return getFocusedNode(dbg).innerText; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-outline-pretty.js b/devtools/client/debugger/test/mochitest/browser_dbg-outline-pretty.js new file mode 100644 index 0000000000..ca0d5c65c4 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-outline-pretty.js @@ -0,0 +1,30 @@ +/* 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 . */ + +// Tests that the length of outline functions for original and pretty printed source matches + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + + await selectSource(dbg, "simple1.js"); + findElementWithSelector(dbg, ".outline-tab").click(); + const originalSource = getItems(dbg); + + clickElement(dbg, "prettyPrintButton"); + await waitForLoadedSource(dbg, "simple1.js:formatted"); + await waitForElementWithSelector(dbg, ".outline-list"); + const prettySource = getItems(dbg); + + is( + originalSource.length, + prettySource.length, + "Length of outline functions for both prettyPrint and originalSource same" + ); +}); + +function getItems(dbg) { + return findAllElements(dbg, "outlineItems"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-outline.js b/devtools/client/debugger/test/mochitest/browser_dbg-outline.js new file mode 100644 index 0000000000..825fdc1508 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-outline.js @@ -0,0 +1,89 @@ +/* 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 . */ + +// Tests that clicking a function in outline panel, the editor highlights the correct location. +// Tests that outline panel can sort functions alphabetically. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + + await selectSource(dbg, "simple1.js", 1); + + findElementWithSelector(dbg, ".outline-tab").click(); + + assertOutlineItems(dbg, [ + "λmain()", + "λdoEval()", + "λevaledFunc()", + "λdoNamedEval()", + // evaledFunc is set twice + "λevaledFunc()", + "class MyClass", + "λconstructor(a, b)", + "λtest()", + "λ#privateFunc(a, b)", + "class Klass", + "λconstructor()", + "λtest()", + ]); + + info("Click an item in outline panel"); + const item = getNthItem(dbg, 3); + item.click(); + await waitForLoadedSource(dbg, "simple1.js"); + assertHighlightLocation(dbg, "simple1.js", 15); + ok( + item.parentNode.classList.contains("focused"), + "The clicked item li is focused" + ); + + info("Sort the list"); + findElementWithSelector(dbg, ".outline-footer button").click(); + // Button becomes active to show alphabetization + is( + findElementWithSelector(dbg, ".outline-footer button").className, + "active", + "Alphabetize button is highlighted when active" + ); + + info("Check that the list was sorted as expected"); + assertOutlineItems(dbg, [ + "λdoEval()", + "λdoNamedEval()", + // evaledFunc is set twice + "λevaledFunc()", + "λevaledFunc()", + "λmain()", + "class Klass", + "λconstructor()", + "λtest()", + "class MyClass", + "λ#privateFunc(a, b)", + "λconstructor(a, b)", + "λtest()", + ]); +}); + +function assertOutlineItems(dbg, expectedItems) { + SimpleTest.isDeeply( + getItems(dbg).map(i => i.innerText.trim()), + expectedItems, + "The expected items are displayed in the outline panel" + ); +} + +function getItems(dbg) { + return Array.from( + findAllElementsWithSelector( + dbg, + ".outline-list h2, .outline-list .outline-list__element" + ) + ); +} + +function getNthItem(dbg, index) { + return findElement(dbg, "outlineItem", index); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-overrides.js b/devtools/client/debugger/test/mochitest/browser_dbg-overrides.js new file mode 100644 index 0000000000..136a11c497 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-overrides.js @@ -0,0 +1,128 @@ +/* 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 . */ + +/** + * Testing the script overrides feature + */ + +"use strict"; + +const httpServer = createTestHTTPServer(); +const BASE_URL = `http://localhost:${httpServer.identity.primaryPort}/`; + +httpServer.registerContentType("html", "text/html"); +httpServer.registerContentType("js", "application/javascript"); + +const testSourceContent = `function foo() { + console.log("original test script"); +}`; + +const testOverrideSourceContent = `function foo() { + console.log("overriden test script"); +} +foo(); +`; + +httpServer.registerPathHandler("/index.html", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + + + + + + `); +}); + +httpServer.registerPathHandler("/test.js", (request, response) => { + response.setHeader("Content-Type", "application/javascript"); + response.write(testSourceContent); +}); + +add_task(async function () { + const dbg = await initDebuggerWithAbsoluteURL( + BASE_URL + "index.html", + "test.js" + ); + + await waitForSourcesInSourceTree(dbg, ["test.js"], { + noExpand: false, + }); + + info("Load and assert the content of the test.js script"); + await selectSourceFromSourceTree( + dbg, + "test.js", + 3, + "Select the `test.js` script for the tree" + ); + is( + getCM(dbg).getValue(), + testSourceContent, + "The test.js is the original source content" + ); + + info("Add a breakpoint"); + await addBreakpoint(dbg, "test.js", 2); + + info("Select test.js tree node, and add override"); + const MockFilePicker = SpecialPowers.MockFilePicker; + MockFilePicker.init(window); + const nsiFile = FileUtils.getFile("TmpD", [`test.js`]); + MockFilePicker.setFiles([nsiFile]); + const path = nsiFile.path; + + await triggerSourceTreeContextMenu( + dbg, + findSourceNodeWithText(dbg, "test.js"), + "#node-menu-overrides" + ); + + info("Wait for `test.js` to be saved to disk and re-write it"); + await BrowserTestUtils.waitForCondition(() => IOUtils.exists(path)); + await BrowserTestUtils.waitForCondition(async () => { + const { size } = await IOUtils.stat(path); + return size > 0; + }); + + await IOUtils.write( + path, + new TextEncoder().encode(testOverrideSourceContent) + ); + + info("Reload and assert `test.js` pause and it overriden source content"); + const onReloaded = reload(dbg, "test.js"); + await waitForPaused(dbg); + + assertPausedAtSourceAndLine(dbg, findSource(dbg, "test.js").id, 2); + is( + getCM(dbg).getValue(), + testOverrideSourceContent, + "The test.js is the overridden source content" + ); + + await resume(dbg); + await onReloaded; + + info("Remove override and test"); + const removed = waitForDispatch(dbg.store, "REMOVE_OVERRIDE"); + await triggerSourceTreeContextMenu( + dbg, + findSourceNodeWithText(dbg, "test.js"), + "#node-menu-overrides" + ); + await removed; + + info("Reload and assert `test.js`'s original source content"); + await reload(dbg, "test.js"); + await waitForSelectedSource(dbg, "test.js"); + + is( + getCM(dbg).getValue(), + testSourceContent, + "The test.js is the original source content" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pause-exceptions.js b/devtools/client/debugger/test/mochitest/browser_dbg-pause-exceptions.js new file mode 100644 index 0000000000..eeebb1be3d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pause-exceptions.js @@ -0,0 +1,139 @@ +/* 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 . */ + +/* + Tests Pausing on exception + 1. skip an uncaught exception + 2. pause on an uncaught exception + 3. pause on a caught error + 4. skip a caught error +*/ + +"use strict"; + +const { PromiseTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PromiseTestUtils.sys.mjs" +); +// This is step 9 +PromiseTestUtils.allowMatchingRejectionsGlobally(/doesntExists is not defined/); + +add_task(async function () { + const dbg = await initDebugger("doc-exceptions.html", "exceptions.js"); + const source = findSource(dbg, "exceptions.js"); + + info("1. test skipping an uncaught exception"); + await uncaughtException(); + assertNotPaused(dbg); + + info("2. Test pausing on an uncaught exception"); + await togglePauseOnExceptions(dbg, true, true); + uncaughtException(); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + + const whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is(whyPaused, `Paused on exception\nunreachable`); + + await resume(dbg); + + info("2.b Test throwing the same uncaught exception pauses again"); + await togglePauseOnExceptions(dbg, true, true); + uncaughtException(); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 2); + await resume(dbg); + + info("3. Test pausing on a caught Error"); + caughtException(); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 7); + + info("3.b Test pausing in the catch statement"); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 9); + await resume(dbg); + + info("4. Test skipping a caught error"); + await togglePauseOnExceptions(dbg, true, false); + caughtException(); + + info("4.b Test pausing in the catch statement"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 9); + await resume(dbg); + + await togglePauseOnExceptions(dbg, true, true); + + info("5. Only pause once when throwing deep in the stack"); + invokeInTab("deepError"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 16); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 22); + await resume(dbg); + + info("6. Only pause once on an exception when pausing in a finally block"); + invokeInTab("deepErrorFinally"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 34); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 31); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 40); + await resume(dbg); + + info("7. Only pause once on an exception when it is rethrown from a catch"); + invokeInTab("deepErrorCatch"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 53); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 49); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 59); + await resume(dbg); + + info("8. Pause on each exception thrown while unwinding"); + invokeInTab("deepErrorThrowDifferent"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 71); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 68); + await resume(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, source.id, 77); + await resume(dbg); + + info("9. Pause in throwing new Function argument"); + const onNewSource = waitForDispatch(dbg.store, "ADD_SOURCES"); + invokeInTab("throwInNewFunctionArgument"); + await waitForPaused(dbg); + const { sources } = await onNewSource; + is(sources.length, 1, "Got a unique source related to new Function source"); + const newFunctionSource = sources[0]; + is( + newFunctionSource.url, + null, + "This new source looks like the new function one as it has no url" + ); + assertPausedAtSourceAndLine(dbg, newFunctionSource.id, 1, 0); + assertTextContentOnLine(dbg, 1, "function anonymous(f=doesntExists()"); + await resume(dbg); +}); + +function uncaughtException() { + return invokeInTab("uncaughtException").catch(() => {}); +} + +function caughtException() { + return invokeInTab("caughtException"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pause-on-next.js b/devtools/client/debugger/test/mochitest/browser_dbg-pause-on-next.js new file mode 100644 index 0000000000..27ae30983f --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pause-on-next.js @@ -0,0 +1,21 @@ +/* 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 . */ + +// Tests that when pause on next is selected, we pause on the next execution + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + const { + selectors: { getIsWaitingOnBreak, getCurrentThread }, + } = dbg; + + clickElement(dbg, "pause"); + await waitForState(dbg, state => getIsWaitingOnBreak(getCurrentThread())); + invokeInTab("simple"); + + await waitForPaused(dbg, "simple3"); + assertPaused(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pause-points.js b/devtools/client/debugger/test/mochitest/browser_dbg-pause-points.js new file mode 100644 index 0000000000..4ebfe08f15 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pause-points.js @@ -0,0 +1,97 @@ +/* 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 . */ + +"use strict"; + +requestLongerTimeout(2); + +async function testCase(dbg, { name, steps }) { + info(` ### Execute testCase "${name}"`); + + const { + selectors: { getTopFrame, getCurrentThread }, + } = dbg; + const locations = []; + + const recordFrame = state => { + const { line, column } = getTopFrame(getCurrentThread()).location; + locations.push([line, column]); + info(`Break on ${line}:${column}`); + }; + + info("Trigger the expected debugger statement"); + const onPaused = waitForPaused(dbg); + invokeInTab(name); + await onPaused; + recordFrame(); + + info("Start stepping over"); + for (let i = 0; i < steps.length - 1; i++) { + await dbg.actions.stepOver(getThreadContext(dbg)); + await waitForPaused(dbg); + recordFrame(); + } + + is(formatSteps(locations), formatSteps(steps), name); + + await resume(dbg); +} + +add_task(async function test() { + const dbg = await initDebugger("doc-pause-points.html", "pause-points.js"); + + await selectSource(dbg, "pause-points.js"); + await testCase(dbg, { + name: "statements", + steps: [ + [9, 2], + [10, 4], + [10, 13], + [11, 2], + [11, 21], + [12, 2], + [12, 12], + [13, 0], + ], + }); + + await testCase(dbg, { + name: "expressions", + steps: [ + [40, 2], + [41, 2], + [42, 12], + [43, 0], + ], + }); + + await testCase(dbg, { + name: "sequences", + steps: [ + [23, 2], + [25, 12], + [29, 12], + [34, 2], + [37, 0], + ], + }); + + await testCase(dbg, { + name: "flow", + steps: [ + [16, 2], + [17, 12], + [18, 10], + [19, 8], + [19, 17], + [19, 8], + [19, 17], + [19, 8], + ], + }); +}); + +function formatSteps(steps) { + return steps.map(loc => `(${loc.join(",")})`).join(", "); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pause-ux.js b/devtools/client/debugger/test/mochitest/browser_dbg-pause-ux.js new file mode 100644 index 0000000000..a8ca4b74bf --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pause-ux.js @@ -0,0 +1,52 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + + // Make sure that we can set a breakpoint on a line out of the + // viewport, and that pausing there scrolls the editor to it. + const longSrc = findSource(dbg, "long.js"); + await selectSource(dbg, "long.js"); + await addBreakpoint(dbg, longSrc, 66); + invokeInTab("testModel"); + await waitForPaused(dbg, "long.js"); + + const pauseScrollTop = getScrollTop(dbg); + + info("1. adding a breakpoint should not scroll the editor"); + getCM(dbg).scrollTo(0, 0); + await addBreakpoint(dbg, longSrc, 11); + is(getScrollTop(dbg), 0, "scroll position"); + + info("2. searching should jump to the match"); + pressKey(dbg, "fileSearch"); + type(dbg, "check"); + + const cm = getCM(dbg); + await waitFor( + () => cm.getSelection() == "check", + "Wait for actual selection in CodeMirror" + ); + is( + cm.getCursor().line, + 26, + "The line of first check occurence in long.js is selected (this is zero-based)" + ); + // The column is the end of "check", so after 'k' + is( + cm.getCursor().ch, + 51, + "The column of first check occurence in long.js is selected (this is zero-based)" + ); + + const matchScrollTop = getScrollTop(dbg); + ok(pauseScrollTop != matchScrollTop, "did not jump to debug line"); +}); + +function getScrollTop(dbg) { + return getCM(dbg).doc.scrollTop; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-iframe.js b/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-iframe.js new file mode 100644 index 0000000000..5f8bcd972c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-iframe.js @@ -0,0 +1,76 @@ +/* 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 . */ + +// Tests the paused overlay in a remote frame + +"use strict"; + +const TEST_COM_URI = `${URL_ROOT_COM_SSL}examples/doc_dbg-fission-frame-sources.html`; + +add_task(async function () { + info("Load a test page with a remote frame"); + // simple1.js is imported by the main page. simple2.js comes from the remote frame. + const dbg = await initDebuggerWithAbsoluteURL( + TEST_COM_URI, + "simple1.js", + "simple2.js" + ); + const { + selectors: { getSelectedSource }, + commands, + } = dbg; + + info("Add breakpoint within the iframe, in `foo` function"); + await selectSource(dbg, "simple2.js"); + await addBreakpoint(dbg, "simple2.js", 5); + + info("Call `foo` in the iframe"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + const iframe = content.document.querySelector("iframe"); + SpecialPowers.spawn(iframe, [], () => { + return content.wrappedJSObject.foo(); + }); + }); + await waitForPaused(dbg); + ok(true, "debugger is paused"); + + let highlighterTestFront; + if (isFissionEnabled() || isEveryFrameTargetEnabled()) { + // We need to retrieve the highlighterTestFront for the frame target. + const iframeTarget = commands.targetCommand + .getAllTargets([commands.targetCommand.TYPES.FRAME]) + .find(target => target.url.includes("example.org")); + highlighterTestFront = await iframeTarget.getFront("highlighterTest"); + } else { + // When fission is disabled, we don't have a dedicated target for the remote frame. + // In this case, the overlay is displayed by the top-level target anyway, so we can + // get the corresponding highlighter test front. + highlighterTestFront = await getHighlighterTestFront(dbg.toolbox); + } + + info("Check that the paused overlay is displayed"); + await waitFor(() => highlighterTestFront.isPausedDebuggerOverlayVisible()); + ok(true, "Paused debugger overlay is visible"); + + ok( + getSelectedSource().url.includes("simple2.js"), + "Selected source is simple2.js" + ); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "simple2.js").id, 5); + + info("Test clicking the resume button"); + await highlighterTestFront.clickPausedDebuggerOverlayButton( + "paused-dbg-resume-button" + ); + + await waitForResumed(dbg); + ok("The debugger isn't paused after clicking on the resume button"); + + await waitFor(async () => { + const visible = await highlighterTestFront.isPausedDebuggerOverlayVisible(); + return !visible; + }); + + ok(true, "The overlay is now hidden"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-loading.js b/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-loading.js new file mode 100644 index 0000000000..0601a66a9c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay-loading.js @@ -0,0 +1,48 @@ +/* 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 . */ + +// Tests that the paused overlay isn't visible after resuming if the debugger paused +// while the DOM was still loading (Bug 1678636). + +"use strict"; + +add_task(async function () { + const dbg = await initDebuggerWithAbsoluteURL( + "data:text/html," + ); + + info("Reload the page to hit the debugger statement while loading"); + const onReloaded = reload(dbg); + await waitForPaused(dbg); + ok(true, "We're paused"); + + info("Check that the paused overlay is displayed"); + const highlighterTestFront = await dbg.toolbox.target.getFront( + "highlighterTest" + ); + + await waitFor(async () => { + const visible = await highlighterTestFront.isPausedDebuggerOverlayVisible(); + return visible; + }); + ok(true, "Paused debugger overlay is visible"); + + info("Click the resume button"); + await highlighterTestFront.clickPausedDebuggerOverlayButton( + "paused-dbg-resume-button" + ); + + await waitForResumed(dbg); + ok("The debugger isn't paused after clicking on the resume button"); + + await waitFor(async () => { + const visible = await highlighterTestFront.isPausedDebuggerOverlayVisible(); + return !visible; + }); + + ok(true, "The overlay is now hidden"); + + info("Wait for reload to complete after resume"); + await onReloaded; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay.js b/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay.js new file mode 100644 index 0000000000..1d442c5ca5 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-paused-overlay.js @@ -0,0 +1,73 @@ +/* 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 . */ + +// Tests the paused overlay + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + + // Sanity check + const highlighterTestFront = await getHighlighterTestFront(dbg.toolbox); + let isPausedOverlayVisible = + await highlighterTestFront.isPausedDebuggerOverlayVisible(); + is( + isPausedOverlayVisible, + false, + "The pause overlay is not visible in the beginning" + ); + + info("Create an eval script that pauses itself."); + invokeInTab("doEval"); + await waitForPaused(dbg); + + info("Check that the paused overlay is displayed"); + await waitFor(() => highlighterTestFront.isPausedDebuggerOverlayVisible()); + ok(true, "Paused debugger overlay is visible"); + + const pauseLine = getVisibleSelectedFrameLine(dbg); + is(pauseLine, 2, "We're paused at the expected location"); + + info("Test clicking the step over button"); + await highlighterTestFront.clickPausedDebuggerOverlayButton( + "paused-dbg-step-button" + ); + await waitFor(() => isPaused(dbg) && getVisibleSelectedFrameLine(dbg) === 4); + ok(true, "We're paused at the expected location after stepping"); + + isPausedOverlayVisible = + await highlighterTestFront.isPausedDebuggerOverlayVisible(); + is(isPausedOverlayVisible, true, "The pause overlay is still visible"); + + info("Test clicking the highlighter resume button"); + await highlighterTestFront.clickPausedDebuggerOverlayButton( + "paused-dbg-resume-button" + ); + + await waitForResumed(dbg); + ok("The debugger isn't paused after clicking on the resume button"); + + await waitFor(async () => { + const visible = await highlighterTestFront.isPausedDebuggerOverlayVisible(); + return !visible; + }); + ok(true, "The overlay is now hidden"); + + info( + "Check that the highlighter is removed when clicking on the debugger resume button" + ); + invokeInTab("doEval"); + await waitFor(() => highlighterTestFront.isPausedDebuggerOverlayVisible()); + ok(true, "Paused debugger overlay is visible again"); + + info("Click debugger UI resume button"); + const resumeButton = await waitFor(() => findElement(dbg, "resume")); + resumeButton.click(); + await waitFor(async () => { + const visible = await highlighterTestFront.isPausedDebuggerOverlayVisible(); + return !visible; + }); + ok(true, "The overlay is hidden after clicking on the resume button"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-columns.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-columns.js new file mode 100644 index 0000000000..e5dcfc4f4c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-columns.js @@ -0,0 +1,119 @@ +/* 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 . */ + +// Tests that breakpoints set in the pretty printed files display and paused +// correctly. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await selectSource(dbg, "pretty.js"); + await prettyPrint(dbg); + + info( + "Add breakpoint in funcWithMultipleBreakableColumns, on the for-loop line" + ); + const LINE_INDEX_TO_BREAK_ON = 15; + const prettySourceFileName = "pretty.js:formatted"; + await addBreakpoint(dbg, prettySourceFileName, LINE_INDEX_TO_BREAK_ON); + const prettySource = findSource(dbg, prettySourceFileName); + + info("Check that multiple column breakpoints can be set"); + const columnBreakpointMarkers = await waitForAllElements( + dbg, + "columnBreakpoints" + ); + /* + * We're pausing on the following line, which should have those breakpoints (marked with ➤) + * + * for( ➤let i=0; ➤i < items.length; ➤i++ ) { + * + */ + is( + columnBreakpointMarkers.length, + 3, + "We have the expected numbers of possible column breakpoints" + ); + + info("Enable the second column breakpoint"); + columnBreakpointMarkers[1].click(); + await waitForBreakpointCount(dbg, 2); + await waitForAllElements(dbg, "breakpointItems", 2); + + info("Check that we do pause at expected locations"); + invokeInTab("funcWithMultipleBreakableColumns"); + + info("We pause on the first column breakpoint (before `i` init)"); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + await assertPausedAtSourceAndLine( + dbg, + prettySource.id, + LINE_INDEX_TO_BREAK_ON, + 15 + ); + await resume(dbg); + + info( + "We pause at the second column breakpoint, before the first loop iteration" + ); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + await assertPausedAtSourceAndLine( + dbg, + prettySource.id, + LINE_INDEX_TO_BREAK_ON, + 18 + ); + const assertScopesForSecondColumnBreakpoint = topBlockItems => + assertScopes(dbg, [ + "Block", + ["", "Window"], + ...topBlockItems, + "funcWithMultipleBreakableColumns", + ["arguments", "Arguments"], + ["items", "(2) […]"], + ]); + await assertScopesForSecondColumnBreakpoint([["i", "0"]]); + await resume(dbg); + + info( + "We pause at the second column breakpoint, before the second loop iteration" + ); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + await assertPausedAtSourceAndLine( + dbg, + prettySource.id, + LINE_INDEX_TO_BREAK_ON, + 18 + ); + await assertScopesForSecondColumnBreakpoint([["i", "1"]]); + await resume(dbg); + + info( + "We pause at the second column breakpoint, before we exit the loop (`items.length` is 2, so the condition will fail)" + ); + await waitForPaused(dbg); + await waitForInlinePreviews(dbg); + await assertPausedAtSourceAndLine( + dbg, + prettySource.id, + LINE_INDEX_TO_BREAK_ON, + 18 + ); + await assertScopesForSecondColumnBreakpoint([["i", "2"]]); + await resume(dbg); + + info("Remove all breakpoints"); + await clickGutter(dbg, LINE_INDEX_TO_BREAK_ON); + await waitForBreakpointCount(dbg, 0); + + ok( + !findAllElements(dbg, "columnBreakpoints").length, + "There is no column breakpoints anymore after clicking on the gutter" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-delete.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-delete.js new file mode 100644 index 0000000000..38798f2b50 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints-delete.js @@ -0,0 +1,113 @@ +/* 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 . */ + +// Tests that breakpoints in pretty printed files +// are properly removed when the user deletes them on either the generated or original files. + +"use strict"; + +add_task(async function () { + info( + "Test removing the breakpoint from the minified file (generated source) works" + ); + + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await selectSource(dbg, "pretty.js"); + + await prettyPrint(dbg); + + await addBreakpointToPrettyPrintedFile(dbg); + + info( + `Close the pretty-printed source, so it is not automatically reopened on reload` + ); + await closeTab(dbg, "pretty.js:formatted"); + + await waitForSelectedSource(dbg, "pretty.js"); + + info( + "Assert that a equivalent breakpoint was set in pretty.js (generated source)" + ); + await assertBreakpoint(dbg, 4); + + info(`Remove the breakpoint from pretty.js (generated source)`); + await clickGutter(dbg, 4); + + await waitForBreakpointCount(dbg, 0); + + await reloadAndCheckNoBreakpointExists(dbg); +}); + +add_task(async function () { + info( + "Test removing the breakpoint from the pretty printed (original source) works" + ); + + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await selectSource(dbg, "pretty.js"); + + await prettyPrint(dbg); + + await addBreakpointToPrettyPrintedFile(dbg); + + info("Check that breakpoint gets added to pretty.js (generated source)"); + await selectSource(dbg, "pretty.js"); + await assertBreakpoint(dbg, 4); + + info("Close the pretty.js (generated source)"); + await closeTab(dbg, "pretty.js"); + + await waitForSelectedSource(dbg, "pretty.js:formatted"); + + info(`Remove the breakpoint from pretty.js:formatted (original source)`); + await clickGutter(dbg, 5); + + await waitForBreakpointCount(dbg, 0); + + info( + `Close the pretty-printed source, so it is not automatically reopened on reload` + ); + await closeTab(dbg, "pretty.js:formatted"); + + await reloadAndCheckNoBreakpointExists(dbg); +}); + +async function addBreakpointToPrettyPrintedFile(dbg) { + // This breakpoint would be set before the debugger statement + // and should be hit first. + info("Add breakpoint to pretty.js:formatted (original source)"); + await addBreakpoint(dbg, "pretty.js:formatted", 5); + await waitForBreakpointCount(dbg, 1); +} + +async function reloadAndCheckNoBreakpointExists(dbg) { + info("Reload and pretty print pretty.js"); + await reload(dbg, "pretty.js"); + await selectSource(dbg, "pretty.js"); + await prettyPrint(dbg); + info("Check that we do not pause on the removed breakpoint"); + invokeInTab("stuff"); + await waitForPaused(dbg); + + const sourcePretty = findSource(dbg, "pretty.js:formatted"); + info( + "Assert pause at the debugger statement in pretty.js:formatted (original source) and not the removed breakpoint" + ); + assertPausedAtSourceAndLine(dbg, sourcePretty.id, 8); + + await selectSource(dbg, "pretty.js"); + const source = findSource(dbg, "pretty.js"); + + info( + "Assert pause at the debugger statement in pretty.js (generated source) and not the removed breakpoint" + ); + assertPausedAtSourceAndLine(dbg, source.id, 6); + + info(`Confirm that pretty.js:formatted does not have any breakpoints`); + is(dbg.selectors.getBreakpointCount(), 0, "Breakpoints should be cleared"); + + await resume(dbg); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints.js new file mode 100644 index 0000000000..1c25d3e0c8 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-breakpoints.js @@ -0,0 +1,126 @@ +/* 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 . */ + +// Tests that breakpoints set in the pretty printed files display and paused +// correctly. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await selectSource(dbg, "pretty.js"); + await prettyPrint(dbg); + + await addBreakpoint(dbg, "pretty.js:formatted", 5); + + is(dbg.selectors.getBreakpointCount(), 1, "Only one breakpoint is created"); + + invokeInTab("stuff"); + await waitForPaused(dbg); + + await assertBreakpointsInNonPrettyAndPrettySources(dbg); + + is( + dbg.selectors.getBreakpointCount(), + 1, + "Only one breakpoint still exists after pause " + ); + + await resume(dbg); + + info("Wait for another pause because of a debugger statement on line 8"); + await waitForPaused(dbg); + + await resume(dbg); + assertNotPaused(dbg); + + await closeTab(dbg, "pretty.js"); + await closeTab(dbg, "pretty.js:formatted"); +}); + +// Tests that breakpoints appear and work when you reload a page +// with pretty-printed files. +add_task(async function () { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await selectSource(dbg, "pretty.js"); + await prettyPrint(dbg); + + await addBreakpoint(dbg, "pretty.js:formatted", 5); + + info("Check that equivalent breakpoint to pretty.js (generated source)"); + await selectSource(dbg, "pretty.js"); + await assertBreakpoint(dbg, 4); + + await selectSource(dbg, "pretty.js:formatted"); + + is(dbg.selectors.getBreakpointCount(), 1, "Only one breakpoint exists"); + + await reload(dbg, "pretty.js", "pretty.js:formatted"); + + await waitForSelectedSource(dbg, "pretty.js:formatted"); + + invokeInTab("stuff"); + await waitForPaused(dbg); + + await assertBreakpointsInNonPrettyAndPrettySources(dbg); + + is( + dbg.selectors.getBreakpointCount(), + 1, + "Only one breakpoint still exists after reload and pause " + ); +}); + +// Test that breakpoints appear and work when set in the minified source +add_task(async function () { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await selectSource(dbg, "pretty.js"); + + info("Add breakpoint to pretty.js (generated source)"); + await addBreakpoint(dbg, "pretty.js", 4, 7); + + await prettyPrint(dbg); + + info( + "Check that equivalent breakpoint is added to pretty.js:formatted (original source)" + ); + await selectSource(dbg, "pretty.js:formatted"); + await assertBreakpoint(dbg, 5); + + is(dbg.selectors.getBreakpointCount(), 1, "Only one breakpoint created"); + + await reload(dbg, "pretty.js", "pretty.js:formatted"); + + await waitForSelectedSource(dbg, "pretty.js:formatted"); + + invokeInTab("stuff"); + await waitForPaused(dbg); + + await assertBreakpointsInNonPrettyAndPrettySources(dbg); + + is( + dbg.selectors.getBreakpointCount(), + 1, + "Only one breakpoint still exists after reload and pause " + ); +}); + +async function assertBreakpointsInNonPrettyAndPrettySources(dbg) { + info( + "Asserts breakpoint pause and display on the correct line in the pretty printed source" + ); + const prettyPrintedSource = findSource(dbg, "pretty.js:formatted"); + await assertPausedAtSourceAndLine(dbg, prettyPrintedSource.id, 5); + await assertBreakpoint(dbg, 5); + + await selectSource(dbg, "pretty.js"); + + info("Assert pause and display on the correct line in the minified source"); + const minifiedSource = findSource(dbg, "pretty.js"); + await assertPausedAtSourceAndLine(dbg, minifiedSource.id, 4, 7); + await assertBreakpoint(dbg, 4); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-console.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-console.js new file mode 100644 index 0000000000..e6aa266b26 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-console.js @@ -0,0 +1,66 @@ +/* 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 . */ + +// The test has a lot of interactions between debugger and console panels which +// might take more than 30s to complete on a slow machine. + +"use strict"; + +requestLongerTimeout(2); + +// Tests that pretty-printing updates console messages. +add_task(async function () { + const dbg = await initDebugger("doc-minified.html"); + invokeInTab("arithmetic"); + + info("Switch to console and check message"); + const minifiedLink = await waitForConsoleLink( + dbg, + "arithmetic", + "math.min.js:3:73" + ); + + info("Click on the link to open the debugger"); + minifiedLink.click(); + await waitForSelectedSource(dbg, "math.min.js"); + await waitForSelectedLocation(dbg, 3); + + info("Click on pretty print button and wait for the file to be formatted"); + clickElement(dbg, "prettyPrintButton"); + await waitForSelectedSource(dbg, "math.min.js:formatted"); + + info("Switch back to console and check message"); + const formattedLink = await waitForConsoleLink( + dbg, + "arithmetic", + "math.min.js:formatted:31:24" + ); + ok(true, "Message location was updated as expected"); + + info( + "Click on the link again and check the debugger opens in formatted file" + ); + formattedLink.click(); + await selectSource(dbg, "math.min.js:formatted"); + await waitForSelectedLocation(dbg, 31); +}); + +async function waitForConsoleLink(dbg, messageText, linkText) { + const { toolbox } = dbg; + await toolbox.selectTool("webconsole"); + + return waitFor(async () => { + // Wait until the message updates. + const [message] = await findConsoleMessages(toolbox, messageText); + if (!message) { + return false; + } + const linkEl = message.querySelector(".frame-link-source"); + if (!linkEl) { + return false; + } + + return linkEl.textContent == linkText ? linkEl : false; + }); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-flow.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-flow.js new file mode 100644 index 0000000000..eb8db0b25e --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-flow.js @@ -0,0 +1,78 @@ +/* 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 . */ + +// Tests that loader and new tab appear when pretty printing, +// and the selected location is mapped afterwards + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + const scriptEl = content.document.createElement("script"); + scriptEl.innerText = `(function callInPretty() { debugger; funcWithMultipleBreakableColumns() })()`; + content.document.body.append(scriptEl); + }); + + await waitForPaused(dbg); + const scriptSource = dbg.selectors.getSelectedSource(); + + info( + "Step-in to navigate to pretty.js `funcWithMultipleBreakableColumns` function" + ); + await stepOver(dbg); + await stepIn(dbg); + await waitForSelectedSource(dbg, "pretty.js"); + await waitForSelectedLocation(dbg, 9); + + is( + countTabs(dbg), + 2, + "Two tabs are opened, one for the dynamically inserted script and one for minified pretty.js" + ); + is( + dbg.selectors.getSourceCount(), + 2, + "There are 2 sources before pretty printing" + ); + await prettyPrint(dbg); + + info("Wait for the pretty printed source to be selected on a different line"); + await waitForSelectedLocation(dbg, 11); + + is(countTabs(dbg), 3, "A new tab was opened for the prettified source"); + is( + dbg.selectors.getSourceCount(), + 3, + "There are three sources after pretty printing" + ); + + info("Navigate to previous frame in call stack"); + clickElement(dbg, "frame", 2); + await waitForSelectedSource(dbg, scriptSource); + + info("Navigate back to `funcWithMultipleBreakableColumns` frame"); + clickElement(dbg, "frame", 1); + await waitForSelectedLocation(dbg, 11); + await waitForSelectedSource(dbg, "pretty.js:formatted"); + ok(true, "pretty-printed source was selected"); + + await resume(dbg); + + info("Re select the minified version"); + await selectSource(dbg, "pretty.js", 4, 8); + info("Re toggle pretty print from the minified source"); + await prettyPrint(dbg); + is(countTabs(dbg), 3, "There are stil three tabs"); + info( + "Wait for re-selecting the mapped location in the pretty printed source" + ); + await waitForSelectedLocation(dbg, 5); + is( + dbg.selectors.getSourceCount(), + 3, + "There are still 3 sources after retrying to pretty print the same source" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-inline-scripts.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-inline-scripts.js new file mode 100644 index 0000000000..2b263ad0a9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-inline-scripts.js @@ -0,0 +1,256 @@ +/* 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 . */ + +// Tests pretty-printing of HTML file and its inner scripts. + +"use strict"; + +const TEST_FILENAME = `doc-pretty-print-inline-scripts.html`; +const PRETTY_PRINTED_FILENAME = `${TEST_FILENAME}:formatted`; + +const BREAKABLE_LINE_HINT_CHAR = `➤`; + +// Import helpers for the inspector +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js", + this +); + +add_task(async function () { + const dbg = await initDebugger(TEST_FILENAME); + + await selectSource(dbg, TEST_FILENAME); + clickElement(dbg, "prettyPrintButton"); + + await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); + const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_FILENAME); + + ok(prettyPrintedSource, "Pretty-printed source exists"); + + info("Check that the HTML file was pretty-printed as expected"); + const expectedPrettyHtml = getExpectedPrettyPrintedHtml(); + is( + prettyPrintedSource.value, + // ➤ is used to indicate breakable lines, we remove them to assert the actual + // content of the editor. + expectedPrettyHtml.replaceAll(BREAKABLE_LINE_HINT_CHAR, ""), + "HTML file is pretty printed as expected" + ); + + info("Check breakable lines"); + const htmlLines = expectedPrettyHtml.split("\n"); + const expectedBreakableLines = []; + htmlLines.forEach((line, index) => { + if (line.startsWith(BREAKABLE_LINE_HINT_CHAR)) { + // Lines in the debugger are 1-based + expectedBreakableLines.push(index + 1); + } + }); + + await assertBreakableLines( + dbg, + PRETTY_PRINTED_FILENAME, + htmlLines.length, + expectedBreakableLines + ); + + info("Check that console messages are pointing to pretty-printed file"); + const { toolbox } = dbg; + await toolbox.selectTool("webconsole"); + + const { hud } = await dbg.toolbox.getPanel("webconsole"); + info("Wait until messages are sourcemapped"); + await waitFor( + () => !!findMessageByType(hud, PRETTY_PRINTED_FILENAME, ".log") + ); + + const firstMessage = await waitFor(() => + findMessageByType(hud, "User information", ".log") + ); + const firstMessageLink = firstMessage.querySelector(".frame-link-source"); + is( + firstMessageLink.innerText, + `${PRETTY_PRINTED_FILENAME}:18:8`, + "first console message has expected location" + ); + info( + "Click on the link of the first message to open the file in the debugger" + ); + firstMessageLink.click(); + await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); + ok(true, "pretty printed file was selected in debugger…"); + await waitForSelectedLocation(dbg, 18); + ok(true, "…at the expected location"); + + info("Go back to the console to check an other message"); + await toolbox.selectTool("webconsole"); + + const secondMessage = await waitFor(() => + findMessageByType(hud, "42 yay", ".log") + ); + const secondMessageLink = secondMessage.querySelector(".frame-link-source"); + is( + secondMessageLink.innerText, + `${PRETTY_PRINTED_FILENAME}:41:12`, + "second console message has expected location" + ); + info( + "Click on the link of the second message to open the file in the debugger" + ); + secondMessageLink.click(); + await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); + ok(true, "pretty printed file was selected in debugger…"); + await waitForSelectedLocation(dbg, 41); + ok(true, "…at the expected location"); + + info("Check that event listener popup is pointing to pretty-printed file"); + const inspector = await toolbox.selectTool("inspector"); + + const nodeFront = await getNodeFront("h1", inspector); + const markupContainer = inspector.markup.getContainer(nodeFront); + const evHolder = markupContainer.elt.querySelector( + ".inspector-badge.interactive[data-event]" + ); + const eventTooltip = inspector.markup.eventDetailsTooltip; + + info("Open event tooltip."); + EventUtils.synthesizeMouseAtCenter( + evHolder, + {}, + inspector.markup.doc.defaultView + ); + await eventTooltip.once("shown"); + ok(true, "event tooltip opened"); + // wait for filename to be sourcemapped + await waitFor(() => + eventTooltip.panel + .querySelector(".event-header") + ?.innerText.includes(PRETTY_PRINTED_FILENAME) + ); + const header = eventTooltip.panel.querySelector(".event-header"); + const headerFilename = header.querySelector( + ".event-tooltip-filename" + ).innerText; + ok( + headerFilename.endsWith(`${PRETTY_PRINTED_FILENAME}:51`), + `Location in event tooltip is the pretty printed one (${headerFilename})` + ); + info( + "Check that clicking on open debugger icon selects the pretty printed file" + ); + header.querySelector(".event-tooltip-debugger-icon").click(); + await waitForSelectedSource(dbg, PRETTY_PRINTED_FILENAME); + ok(true, "pretty printed file was selected in debugger…"); + await waitForSelectedLocation(dbg, 51); + ok(true, "…at the expected location"); +}); + +add_task(async function prettyPrintSingleLineDataUrl() { + const TEST_URL = `data:text/html,`; + const PRETTY_PRINTED_URL = `${TEST_URL}:formatted`; + const dbg = await initDebuggerWithAbsoluteURL(TEST_URL); + + await selectSource(dbg, TEST_URL); + clickElement(dbg, "prettyPrintButton"); + + const prettySource = await waitForSource(dbg, PRETTY_PRINTED_URL); + await waitForSelectedSource(dbg, prettySource); + const prettyPrintedSource = findSourceContent(dbg, PRETTY_PRINTED_URL); + + ok(prettyPrintedSource, "Pretty-printed source exists"); + + info("Check that the HTML file was pretty-printed as expected"); + const expectedPrettyHtml = ``; + is( + prettyPrintedSource.value, + expectedPrettyHtml, + "HTML file is pretty printed as expected" + ); +}); + +/** + * Return the expected pretty-printed HTML. Lines starting with ➤ indicate breakable + * lines for easier maintenance. + * + * @returns {String} + */ +function getExpectedPrettyPrintedHtml() { + return ` + + + + + + Debugger test page + + + + + + + + +

Minified

+ + + + + + +

+ + +`; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js new file mode 100644 index 0000000000..8d3771cae9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js @@ -0,0 +1,148 @@ +/* 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 . */ + +// Tests that pretty-printing a file with no URL works while paused. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-minified.html"); + + info("Evaluate an expression with scriptCommand.execute"); + const debuggerDone = dbg.commands.scriptCommand.execute( + `debugger; var foo; document.addEventListener("click", e => { debugger; }, {once: true})` + ); + await waitForPaused(dbg); + const evaluatedSourceId = dbg.selectors.getSelectedSourceId(); + + // This will throw if things fail to pretty-print and render properly. + info("Pretty print the source created by the evaluated expression"); + await prettyPrint(dbg); + + const prettyEvaluatedSourceFilename = + evaluatedSourceId.split("/").at(-1) + ":formatted"; + await waitForSource(dbg, prettyEvaluatedSourceFilename); + const prettySource = findSource(dbg, prettyEvaluatedSourceFilename); + + info("Check that the script was pretty-printed as expected"); + const { value: prettySourceValue } = findSourceContent(dbg, prettySource); + + is( + prettySourceValue.trim(), + `debugger; +var foo; +document.addEventListener('click', e => { + debugger; +}, { + once: true +}) +`.trim(), + "script was pretty printed as expected" + ); + + await resume(dbg); + await debuggerDone; + + info("Check if we can pause inside the pretty-printed source"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.body.click(); + }); + await waitForPaused(dbg); + await assertPausedAtSourceAndLine(dbg, prettySource.id, 4); + await resume(dbg); + + info("Check that pretty printing works in `eval`'d source"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.eval( + `setTimeout(() => {debugger;document.addEventListener("click", e => { debugger; }, {once: true})}, 100)` + ); + }); + await waitForPaused(dbg); + const evalSourceId = dbg.selectors.getSelectedSourceId(); + + // This will throw if things fail to pretty-print and render properly. + info("Pretty print the source created by the `eval` expression"); + await prettyPrint(dbg); + + const prettyEvalSourceFilename = + evalSourceId.split("/").at(-1) + ":formatted"; + await waitForSource(dbg, prettyEvalSourceFilename); + const prettyEvalSource = findSource(dbg, prettyEvalSourceFilename); + + info("Check that the script was pretty-printed as expected"); + const { value: prettyEvalSourceValue } = findSourceContent( + dbg, + prettyEvalSource + ); + + is( + prettyEvalSourceValue.trim(), + ` +setTimeout( + () => { + debugger; + document.addEventListener('click', e => { + debugger; + }, { + once: true + }) + }, + 100 +)`.trim(), + "script was pretty printed as expected" + ); + await resume(dbg); + + info("Check if we can pause inside the pretty-printed eval source"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.body.click(); + }); + await waitForPaused(dbg); + await assertPausedAtSourceAndLine(dbg, prettyEvalSource.id, 5); + await resume(dbg); + + info("Check that pretty printing works in `new Function` source"); + invokeInTab("breakInNewFunction"); + await waitForPaused(dbg); + const newFunctionSourceId = dbg.selectors.getSelectedSourceId(); + + // This will throw if things fail to pretty-print and render properly. + info("Pretty print the source created with `new Function`"); + await prettyPrint(dbg); + + const prettyNewFunctionSourceFilename = + newFunctionSourceId.split("/").at(-1) + ":formatted"; + await waitForSource(dbg, prettyNewFunctionSourceFilename); + const prettyNewFunctionSource = findSource( + dbg, + prettyNewFunctionSourceFilename + ); + + info("Check that the script was pretty-printed as expected"); + const { value: prettyNewFunctionSourceValue } = findSourceContent( + dbg, + prettyNewFunctionSource + ); + + is( + prettyNewFunctionSourceValue.trim(), + `function anonymous() { + debugger; + document.addEventListener('click', function () { + debugger; + }) +} +`.trim(), + "script was pretty printed as expected" + ); + await resume(dbg); + + info("Check if we can pause inside the pretty-printed eval source"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.body.click(); + }); + await waitForPaused(dbg); + await assertPausedAtSourceAndLine(dbg, prettyNewFunctionSource.id, 4); + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused.js new file mode 100644 index 0000000000..a5b9260af2 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused.js @@ -0,0 +1,35 @@ +/* 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 . */ + +// Tests pretty-printing a source that is currently paused. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-minified.html", "math.min.js"); + const thread = dbg.selectors.getCurrentThread(); + + await selectSource(dbg, "math.min.js"); + await addBreakpoint(dbg, "math.min.js", 2); + + invokeInTab("arithmetic"); + await waitForPaused(dbg, "math.min.js"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "math.min.js").id, 2); + + clickElement(dbg, "prettyPrintButton"); + await waitForSelectedSource(dbg, "math.min.js:formatted"); + await waitForState( + dbg, + state => dbg.selectors.getSelectedFrame(thread).location.line == 18 + ); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "math.min.js:formatted").id, + 18 + ); + await waitForBreakpoint(dbg, "math.min.js:formatted", 18); + await assertBreakpoint(dbg, 18); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-sourcemap.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-sourcemap.js new file mode 100644 index 0000000000..25d3712d32 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-sourcemap.js @@ -0,0 +1,157 @@ +/* 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 . */ + +// This tests pretty-printing for sourcemapped files. + +"use strict"; + +const { LocalizationHelper } = require("resource://devtools/shared/l10n.js"); +const L10N = new LocalizationHelper( + "devtools/client/locales/debugger.properties" +); + +const httpServer = createTestHTTPServer(); +httpServer.registerContentType("html", "text/html"); +httpServer.registerContentType("js", "application/javascript"); + +httpServer.registerPathHandler( + "/doc-prettyprint-sourcemap.html", + (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + + + + + + `); + } +); + +function sourceHandler(request, response) { + response.setHeader("Content-Type", "text/javascript"); + console.log(request.path.substring(1)); + response.write(`function add(a,b,k){var result=a+b;return k(result)}function sub(a,b,k){var result=a-b;return k(result)}function mul(a,b,k){var result=a*b;return k(result)}function div(a,b,k){var result=a/b;return k(result)}function arithmetic(){add(4,4,function(a){sub(a,2,function(b){mul(b,3,function(c){div(c,2,function(d){console.log("arithmetic",d)})})})})}; +//# sourceMappingURL=${request.path.substring(1)}.map`); +} + +httpServer.registerPathHandler("/js1.min.js", sourceHandler); +httpServer.registerPathHandler("/js2.min.js", sourceHandler); + +// This returns no sourcemap +httpServer.registerPathHandler("/js1.min.js.map", (request, response) => { + response.setHeader("Content-Type", "application/javascript"); + response.setStatusLine(request.httpVersion, 404, "Not found"); + response.write(`console.log("http error")`); +}); + +// This returns a sourcemap without any original source +httpServer.registerPathHandler("/js2.min.js.map", (request, response) => { + response.setHeader("Content-Type", "application/javascript"); + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write( + `{"version":3,"sources":[],"names":["message","document","getElementById","logMessage","console","log","value","addEventListener","logMessageButton"],"mappings":"AAAA,GAAIA,SAAUC,SAASC,eAAe,UAEtC,SAASC,cACPC,QAAQC,IAAI,eAAiBL,QAAQM,OAGvCN,QAAQO,iBAAiB,QAAS,WAChCP,QAAQM,MAAQ,IACf,MAEH,IAAIE,kBAAmBP,SAASC,eAAe,cAE/CM,kBAAiBD,iBAAiB,QAAS,WACzCJ,cACC"}` + ); +}); + +const BASE_URL = `http://localhost:${httpServer.identity.primaryPort}/`; + +// Tests that the pretty-print icon is visible for source files with a sourceMappingURL +// but the linked sourcemap is not found or no original files exist. +add_task(async () => { + const dbg = await initDebuggerWithAbsoluteURL( + `${BASE_URL}doc-prettyprint-sourcemap.html`, + "doc-prettyprint-sourcemap.html", + "js1.min.js", + "js2.min.js" + ); + + info(" - Test HTML source"); + const htmlSource = findSource(dbg, "doc-prettyprint-sourcemap.html"); + await selectSource(dbg, htmlSource); + assertPrettyPrintButton(dbg, L10N.getStr("sourceTabs.prettyPrint"), false); + + info(" - Test source with sourceMappingURL but sourcemap does not exist"); + const source1 = findSource(dbg, "js1.min.js"); + await selectSource(dbg, source1); + + // The source may be reported as pretty printable while we are fetching the sourcemap, + // but once the sourcemap is reported to be failing, we no longer report to be pretty printable. + await waitFor( + () => !dbg.selectors.isSourceWithMap(source1.id), + "Wait for the selector to report the source to be source-map less" + ); + + assertPrettyPrintButton(dbg, L10N.getStr("sourceTabs.prettyPrint"), false); + + info( + " - Test source with sourceMappingURL and sourcemap but no original files" + ); + const source2 = findSource(dbg, "js2.min.js"); + await selectSource(dbg, source2); + + assertPrettyPrintButton(dbg, L10N.getStr("sourceTabs.prettyPrint"), false); + + info(" - Test an already pretty-printed source"); + info("Pretty print the script"); + clickElement(dbg, "prettyPrintButton"); + + await waitForSelectedSource(dbg, "js2.min.js:formatted"); + assertPrettyPrintButton( + dbg, + L10N.getStr("sourceFooter.prettyPrint.isPrettyPrintedMessage"), + true + ); +}); + +add_task(async () => { + const dbg = await initDebugger( + "doc-sourcemaps2.html", + "main.min.js", + "main.js" + ); + + info( + " - Test source with sourceMappingURL, sourcemap and original files exist" + ); + const source = findSource(dbg, "main.min.js"); + await selectSource(dbg, source); + + assertPrettyPrintButton( + dbg, + L10N.getStr("sourceFooter.prettyPrint.hasSourceMapMessage"), + true + ); + + info(" - Test an original source"); + const originalSource = findSource(dbg, "main.js"); + await selectSource(dbg, originalSource); + + assertPrettyPrintButton( + dbg, + L10N.getStr("sourceFooter.prettyPrint.isOriginalMessage"), + true + ); +}); + +function assertPrettyPrintButton(dbg, expectedTitle, shouldBeDisabled) { + const prettyPrintButton = findElement(dbg, "prettyPrintButton"); + ok(prettyPrintButton, "Pretty Print Button is visible"); + is( + prettyPrintButton.title, + expectedTitle, + "The pretty print button title should be correct" + ); + ok( + shouldBeDisabled ? prettyPrintButton.disabled : !prettyPrintButton.disabled, + `The pretty print button should be ${ + shouldBeDisabled ? "disabled" : "enabled" + }` + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print.js new file mode 100644 index 0000000000..293cabb75c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print.js @@ -0,0 +1,134 @@ +/* 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 . */ + +// Tests basic pretty-printing functionality. + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + const dbg = await initDebugger("doc-minified.html", "math.min.js"); + + await selectSource(dbg, "math.min.js", 2); + clickElement(dbg, "prettyPrintButton"); + + await waitForSelectedSource(dbg, "math.min.js:formatted"); + const ppSrc = findSource(dbg, "math.min.js:formatted"); + + ok(ppSrc, "Pretty-printed source exists"); + + // 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"); + await waitForPaused(dbg); + + assertPausedAtSourceAndLine(dbg, ppSrc.id, 18); + + await stepOver(dbg); + + assertPausedAtSourceAndLine(dbg, ppSrc.id, 39); + + await resume(dbg); + + // The pretty-print button should be disabled in the pretty-printed source. + ok( + findElement(dbg, "prettyPrintButton").disabled, + "Pretty Print Button should be disabled" + ); + + await selectSource(dbg, "math.min.js"); + await waitForSelectedSource(dbg, "math.min.js"); + + ok( + !findElement(dbg, "prettyPrintButton").disabled, + "Pretty Print Button should be enabled" + ); +}); + +add_task(async function testPrivateFields() { + // Create a source containing a class with private fields + const httpServer = createTestHTTPServer(); + httpServer.registerContentType("html", "text/html"); + httpServer.registerContentType("js", "application/javascript"); + + httpServer.registerPathHandler(`/`, function (request, response) { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + Test pretty-printing class with private fields + + `); + }); + + httpServer.registerPathHandler( + "/class-with-private-fields.js", + function (request, response) { + response.setHeader("Content-Type", "application/javascript"); + response.write(` + class MyClass { + constructor(a) { + this.#a = a;this.#b = Math.random();this.ab = this.#getAB(); + } + #a + #b = "default value" + static #someStaticPrivate + #getA() { + return this.#a; + } + #getAB() { + return this.#getA()+this. + #b + } + } + `); + } + ); + const port = httpServer.identity.primaryPort; + const TEST_URL = `http://localhost:${port}/`; + + info("Open toolbox"); + const dbg = await initDebuggerWithAbsoluteURL(TEST_URL); + + info("Select script with private fields"); + await selectSource(dbg, "class-with-private-fields.js", 2); + + info("Pretty print the script"); + clickElement(dbg, "prettyPrintButton"); + + info("Wait until the script is pretty-printed"); + await waitForSelectedSource(dbg, "class-with-private-fields.js:formatted"); + + info("Check that the script was pretty-printed as expected"); + const prettyPrintedSource = await findSourceContent( + dbg, + "class-with-private-fields.js:formatted" + ); + + is( + prettyPrintedSource.value.trim(), + ` +class MyClass { + constructor(a) { + this.#a = a; + this.#b = Math.random(); + this.ab = this.#getAB(); + } + #a + #b = 'default value' + static #someStaticPrivate + #getA() { + return this.#a; + } + #getAB() { + return this.#getA() + this.#b + } +} + `.trim(), + "script was pretty printed as expected" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview-frame.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview-frame.js new file mode 100644 index 0000000000..8ca20bce53 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview-frame.js @@ -0,0 +1,47 @@ +/* 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 . */ + +// Test hovering in a selected frame + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + const found = findElement(dbg, "callStackBody"); + is(found, null, "Call stack is hidden"); + + invokeInTab("firstCall"); + await waitForPaused(dbg); + + info("Preview a variable in the second frame"); + clickElement(dbg, "frame", 2); + await waitForSelectedFrame(dbg, "firstCall"); + await assertFunctionPreview(dbg, 8, 4, "secondCall()"); + + info("Preview should still work after selecting different locations"); + const frame = dbg.selectors.getVisibleSelectedFrame(); + const inScopeLines = dbg.selectors.getInScopeLines(frame.location); + await selectSource(dbg, "script-switching-01.js"); + await assertFunctionPreview(dbg, 8, 4, "secondCall()"); + is( + dbg.selectors.getInScopeLines(frame.location), + inScopeLines, + "In scope lines" + ); +}); + +function waitForSelectedFrame(dbg, displayName) { + const { getInScopeLines, getVisibleSelectedFrame } = dbg.selectors; + return waitForState(dbg, state => { + const frame = getVisibleSelectedFrame(); + + return frame?.displayName == displayName && getInScopeLines(frame.location); + }); +} + +async function assertFunctionPreview(dbg, line, column, displayName) { + const previewEl = await tryHovering(dbg, line, column, "tooltip"); + is(previewEl.innerText, displayName); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview-getter.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview-getter.js new file mode 100644 index 0000000000..88b85b8292 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview-getter.js @@ -0,0 +1,56 @@ +/* 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 . */ + +// This test checks if 'executing getter' in a preview correctly displays values. +// Bug 1456441 - 'execute getter' not working in Preview + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-preview-getter.html", + "preview-getter.js" + ); + + await loadAndAddBreakpoint(dbg, "preview-getter.js", 5, 4); + invokeInTab("funcA"); + await waitForPaused(dbg); + + info("Hovers over 'this' token to display the preview."); + await tryHovering(dbg, 5, 8, "previewPopup"); + + info("Invokes getter and waits for the element to be rendered"); + await clickElement(dbg, "previewPopupInvokeGetterButton"); + await waitForAllElements(dbg, "previewPopupObjectNumber", 2); + + await clickElement(dbg, "previewPopupInvokeGetterButton"); + await waitForAllElements(dbg, "previewPopupObjectObject", 3); + + const getterButtonEls = findAllElements( + dbg, + "previewPopupInvokeGetterButton" + ); + const previewObjectNumberEls = findAllElements( + dbg, + "previewPopupObjectNumber" + ); + const previewObjectObjectEls = findAllElements( + dbg, + "previewPopupObjectObject" + ); + + is(getterButtonEls.length, 0, "There are no getter buttons in the preview."); + is( + previewObjectNumberEls.length, + 2, + "Getters were invoked and two numerical values are displayed as expected." + ); + is( + previewObjectObjectEls.length, + 3, + "Getters were invoked and three object values are displayed as expected." + ); + + await closePreviewAtPos(dbg, 5, 8); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview-module.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview-module.js new file mode 100644 index 0000000000..be51ec88a4 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview-module.js @@ -0,0 +1,36 @@ +/* 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 . */ + +// Test hovering in a script that is paused on load +// and doesn't have functions. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html"); + + const onNavigated = navigate(dbg, "doc-on-load.html"); + + // wait for `top-level.js` to load and to pause at a debugger statement + await waitForSelectedSource(dbg, "top-level.js"); + await waitForPaused(dbg); + + await assertPreviews(dbg, [ + { + line: 1, + column: 6, + expression: "obj", + fields: [ + ["foo", "1"], + ["bar", "2"], + ], + }, + ]); + + await assertPreviewTooltip(dbg, 2, 7, { result: "3", expression: "func" }); + + info("Resume and wait for full navigation of the tab"); + await resume(dbg); + await onNavigated; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview-source-maps.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview-source-maps.js new file mode 100644 index 0000000000..ddf9945ef5 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview-source-maps.js @@ -0,0 +1,45 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-sourcemaps.html", + "entry.js", + "output.js", + "times2.js", + "opts.js" + ); + + await selectSource(dbg, "times2.js"); + await addBreakpoint(dbg, "times2.js", 2); + + invokeInTab("keepMeAlive"); + await waitForPaused(dbg); + await waitForSelectedSource(dbg, "times2.js"); + + info("Test previewing in the original location"); + await assertPreviews(dbg, [ + { line: 2, column: 10, result: 4, expression: "x" }, + ]); + + info("Test previewing in the generated location"); + await dbg.actions.jumpToMappedSelectedLocation(getContext(dbg)); + await waitForSelectedSource(dbg, "bundle.js"); + await assertPreviews(dbg, [ + { line: 70, column: 11, result: 4, expression: "x" }, + ]); + + info("Test that you can not preview in another original file"); + await selectSource(dbg, "output.js"); + await hoverAtPos(dbg, { line: 2, ch: 16 }); + await assertNoTooltip(dbg); +}); + +async function assertNoTooltip(dbg) { + await wait(200); + const el = findElement(dbg, "tooltip"); + is(el, null, "Tooltip should not exist"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview.js new file mode 100644 index 0000000000..3a5fe4ec5d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview.js @@ -0,0 +1,123 @@ +/* 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 . */ + +// Test hovering on an object, which will show a popup and on a +// simple value, which will show a tooltip. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-preview.html", "preview.js"); + + await testPreviews(dbg, "testInline", [ + { line: 17, column: 16, expression: "obj?.prop", result: 2 }, + ]); + + await selectSource(dbg, "preview.js"); + await testBucketedArray(dbg); + + await testPreviews(dbg, "empties", [ + { line: 6, column: 9, expression: "a", result: '""' }, + { line: 7, column: 9, expression: "b", result: "false" }, + { line: 8, column: 9, expression: "c", result: "undefined" }, + { line: 9, column: 9, expression: "d", result: "null" }, + ]); + + await testPreviews(dbg, "objects", [ + { line: 27, column: 10, expression: "empty", result: "No properties" }, + { line: 28, column: 22, expression: "obj?.foo", result: 1 }, + ]); + + await testPreviews(dbg, "smalls", [ + { line: 14, column: 9, expression: "a", result: '"..."' }, + { line: 15, column: 9, expression: "b", result: "true" }, + { line: 16, column: 9, expression: "c", result: "1" }, + { + line: 17, + column: 9, + expression: "d", + fields: [["length", "0"]], + }, + ]); + + await testPreviews(dbg, "classPreview", [ + { line: 50, column: 20, expression: "this.x", result: 1 }, + { line: 50, column: 29, expression: "this.#privateVar", result: 2 }, + { + line: 50, + column: 47, + expression: "Foo.#privateStatic", + fields: [ + ["first", `"a"`], + ["second", `"b"`], + ], + }, + { + line: 51, + column: 26, + expression: "this", + fields: [ + ["x", "1"], + ["#privateVar", "2"], + ], + }, + { line: 51, column: 39, expression: "this.#privateVar", result: 2 }, + ]); + + info( + "Check that closing the preview tooltip doesn't release the underlying object actor" + ); + invokeInTab("classPreview"); + await waitForPaused(dbg); + info("Display popup a first time and hide it"); + await assertPreviews(dbg, [ + { + line: 60, + column: 7, + expression: "y", + fields: [["hello", "{…}"]], + }, + ]); + await closePreviewAtPos(dbg, 60, 7); + + info("Display the popup again and try to expand a property"); + const popupEl = await tryHovering(dbg, 60, 7, "popup"); + const nodes = popupEl.querySelectorAll(".preview-popup .node"); + const initialNodesLength = nodes.length; + nodes[0].querySelector(".arrow").click(); + await waitFor( + () => + popupEl.querySelectorAll(".preview-popup .node").length > + initialNodesLength + ); + ok(true, `"hello" was expanded`); + await resume(dbg); +}); + +async function testPreviews(dbg, fnName, previews) { + invokeInTab(fnName); + await waitForPaused(dbg); + + await assertPreviews(dbg, previews); + await resume(dbg); + + info(`Ran tests for ${fnName}`); +} + +async function testBucketedArray(dbg) { + invokeInTab("largeArray"); + await waitForPaused(dbg); + await tryHovering(dbg, 34, 10, "popup"); + const preview = dbg.selectors.getPreview(); + + is( + preview.properties.map(p => p.name).join(" "), + "[0…99] [100…100] length ", + "Popup properties are bucketed" + ); + + is(preview.properties[0].meta.endIndex, 99, "first bucket ends at 99"); + is(preview.properties[2].contents.value, 101, "length is 101"); + await resume(dbg); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-project-root.js b/devtools/client/debugger/test/mochitest/browser_dbg-project-root.js new file mode 100644 index 0000000000..e1e3c4a9ad --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-project-root.js @@ -0,0 +1,119 @@ +/* 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 . */ + +// This test covers changing to a distinct "project root" +// i.e. displaying only one particular thread, domain, or directory in the Source Tree. + +"use strict"; + +const httpServer = createTestHTTPServer(); +const HOST = `localhost:${httpServer.identity.primaryPort}`; +const BASE_URL = `http://${HOST}/`; + +const INDEX_PAGE_CONTENT = ` + + + + + + + + `; + +httpServer.registerPathHandler("/index.html", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(INDEX_PAGE_CONTENT); +}); +httpServer.registerPathHandler("/root-script.js", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("console.log('root script')"); +}); +httpServer.registerPathHandler( + "/folder/folder-script.js", + (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("console.log('folder script')"); + } +); +httpServer.registerPathHandler( + "/folder/sub-folder/sub-folder-script.js", + (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write("console.log('sub folder script')"); + } +); + +const ALL_SCRIPTS = [ + "root-script.js", + "folder-script.js", + "sub-folder-script.js", +]; + +add_task(async function testProjectRoot() { + const dbg = await initDebuggerWithAbsoluteURL( + BASE_URL + "index.html", + ...ALL_SCRIPTS + ); + + await waitForSourcesInSourceTree(dbg, ALL_SCRIPTS); + + info("Select the Main Thread as project root"); + const threadItem = findSourceNodeWithText(dbg, "Main Thread"); + await setProjectRoot(dbg, threadItem); + assertRootLabel(dbg, "Main Thread"); + await waitForSourcesInSourceTree(dbg, ALL_SCRIPTS); + + info("Select the host as project root"); + const hostItem = findSourceNodeWithText(dbg, HOST); + await setProjectRoot(dbg, hostItem); + assertRootLabel(dbg, HOST); + await waitForSourcesInSourceTree(dbg, ALL_SCRIPTS); + + info("Select 'folder' as project root"); + const folderItem = findSourceNodeWithText(dbg, "folder"); + await setProjectRoot(dbg, folderItem); + assertRootLabel(dbg, "folder"); + await waitForSourcesInSourceTree(dbg, [ + "folder-script.js", + "sub-folder-script.js", + ]); + + info("Reload and see if project root is preserved"); + await reload(dbg, "folder-script.js", "sub-folder-script.js"); + assertRootLabel(dbg, "folder"); + await waitForSourcesInSourceTree(dbg, [ + "folder-script.js", + "sub-folder-script.js", + ]); + + info("Select 'sub-folder' as project root"); + const subFolderItem = findSourceNodeWithText(dbg, "sub-folder"); + await setProjectRoot(dbg, subFolderItem); + assertRootLabel(dbg, "sub-folder"); + await waitForSourcesInSourceTree(dbg, ["sub-folder-script.js"]); + + info("Clear project root"); + await clearProjectRoot(dbg); + await waitForSourcesInSourceTree(dbg, ALL_SCRIPTS); +}); + +async function setProjectRoot(dbg, treeNode) { + return triggerSourceTreeContextMenu( + dbg, + treeNode, + "#node-set-directory-root" + ); +} + +function assertRootLabel(dbg, label) { + const rootHeaderLabel = dbg.win.document.querySelector( + ".sources-clear-root-label" + ); + is(rootHeaderLabel.textContent, label); +} + +async function clearProjectRoot(dbg) { + const rootHeader = dbg.win.document.querySelector(".sources-clear-root"); + rootHeader.click(); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js b/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js new file mode 100644 index 0000000000..03ac9b740c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-project-search.js @@ -0,0 +1,258 @@ +/* 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 . */ + +// Testing various project search features + +"use strict"; + +requestLongerTimeout(3); + +add_task(async function testProjectSearchCloseOnNavigation() { + const dbg = await initDebugger( + "doc-script-switching.html", + "script-switching-01.js" + ); + + await selectSource(dbg, "script-switching-01.js"); + + await openProjectSearch(dbg); + await doProjectSearch(dbg, "first"); + + is(dbg.selectors.getActiveSearch(), "project"); + + await navigate(dbg, "doc-scripts.html"); + + // wait for the project search to close + await waitForState(dbg, state => !dbg.selectors.getActiveSearch()); +}); + +add_task(async function testSimpleProjectSearch() { + // Start with side panel collapsed so we can assert that the project search keyboard + // shortcut will open it. + await pushPref("devtools.debugger.start-panel-collapsed", true); + + const dbg = await initDebugger( + "doc-script-switching.html", + "script-switching-01.js" + ); + + await openProjectSearch(dbg); + + ok( + !!findElementWithSelector(dbg, ".project-text-search"), + "Project search is visible" + ); + + const searchTerm = "first"; + await doProjectSearch(dbg, searchTerm); + + const queryMatch = findElement(dbg, "fileMatch").querySelector( + ".query-match" + ); + is( + queryMatch.innerText, + searchTerm, + "The highlighted text matches the search term" + ); + + info("Select a result match to open the location in the source"); + await clickElement(dbg, "fileMatch"); + await waitForSelectedSource(dbg, "script-switching-01.js"); + + info("Close start sidebar"); + const startPanelToggleButtonEl = findElementWithSelector( + dbg, + ".toggle-button.start" + ); + startPanelToggleButtonEl.click(); + await waitFor(() => startPanelToggleButtonEl.classList.contains("collapsed")); + + info("Try to open project search again"); + await openProjectSearch(dbg); + ok( + !!findElementWithSelector(dbg, ".project-text-search"), + "Project search is visible" + ); +}); + +add_task(async function testMatchesForRegexSearches() { + const dbg = await initDebugger("doc-react.html", "App.js"); + await openProjectSearch(dbg); + + type(dbg, "import .* from 'react'"); + await clickElement(dbg, "projectSearchModifiersRegexMatch"); + + await waitForSearchResults(dbg, 2); + + const queryMatch = findAllElements(dbg, "fileMatch")[1].querySelector( + ".query-match" + ); + + is( + queryMatch.innerText, + "import React, { Component } from 'react'", + "The highlighted text matches the search term" + ); + + // Turn off the regex modifier so does not break tests below + await clickElement(dbg, "projectSearchModifiersRegexMatch"); +}); + +// Test expanding search results to reveal the search matches. +add_task(async function testExpandSearchResultsToShowMatches() { + const dbg = await initDebugger("doc-react.html", "App.js"); + + await openProjectSearch(dbg); + await doProjectSearch(dbg, "we"); + + is(getExpandedResultsCount(dbg), 159); + + const collapsedNodes = findAllElements(dbg, "projectSearchCollapsed"); + is(collapsedNodes.length, 1); + + collapsedNodes[0].click(); + + is(getExpandedResultsCount(dbg), 367); +}); + +add_task(async function testSearchModifiers() { + const dbg = await initDebugger("doc-react.html", "App.js"); + + await openProjectSearch(dbg); + + await assertProjectSearchModifier( + dbg, + "projectSearchModifiersCaseSensitive", + "FIELDS", + "case sensitive", + { resultWithModifierOn: 0, resultWithModifierOff: 2 } + ); + await assertProjectSearchModifier( + dbg, + "projectSearchModifiersRegexMatch", + `\\*`, + "regex match", + { resultWithModifierOn: 12, resultWithModifierOff: 0 } + ); + await assertProjectSearchModifier( + dbg, + "projectSearchModifiersWholeWordMatch", + "so", + "whole word match", + { resultWithModifierOn: 6, resultWithModifierOff: 16 } + ); +}); + +add_task(async function testSearchExcludePatterns() { + const dbg = await initDebugger("doc-react.html", "App.js"); + + info("Search across all files"); + await openProjectSearch(dbg); + let fileResults = await doProjectSearch(dbg, "console"); + + is(fileResults.length, 5, "5 results were found"); + + let resultsFromNodeModules = [...fileResults].filter(result => + result.innerText.includes("node_modules") + ); + + is( + resultsFromNodeModules.length, + 3, + "3 results were found from node_modules" + ); + + info("Excludes search results based on multiple search patterns"); + + await clickElement(dbg, "excludePatternsInput"); + type(dbg, "App.js, main.js"); + pressKey(dbg, "Enter"); + + fileResults = await waitForSearchResults(dbg, 3); + + const resultsFromAppJS = [...fileResults].filter(result => + result.innerText.includes("App.js") + ); + + is(resultsFromAppJS.length, 0, "None of the results is from the App.js file"); + + const resultsFromMainJS = [...fileResults].filter(result => + result.innerText.includes("main.js") + ); + + is( + resultsFromMainJS.length, + 0, + "None of the results is from the main.js file" + ); + + info("Excludes search results from node modules files"); + + await clearElement(dbg, "excludePatternsInput"); + type(dbg, "**/node_modules/**"); + pressKey(dbg, "Enter"); + + fileResults = await waitForSearchResults(dbg, 2); + + resultsFromNodeModules = [...fileResults].filter(result => + result.innerText.includes("node_modules") + ); + + is( + resultsFromNodeModules.length, + 0, + "None of the results is from the node modules files" + ); + + info("Assert that the exclude pattern is persisted across reloads"); + await reloadBrowser(); + await openProjectSearch(dbg); + + const excludePatternsInputElement = await waitForElement( + dbg, + "excludePatternsInput" + ); + + is( + excludePatternsInputElement.value, + "**/node_modules/**", + "The exclude pattern for node modules is persisted accross reloads" + ); + + // Clear the fields so that it does not impact on the subsequent tests + await clearElement(dbg, "projectSearchSearchInput"); + await clearElement(dbg, "excludePatternsInput"); + pressKey(dbg, "Enter"); +}); + +async function assertProjectSearchModifier( + dbg, + searchModifierBtn, + searchTerm, + title, + expected +) { + info(`Assert ${title} search modifier`); + + type(dbg, searchTerm); + info(`Turn on the ${title} search modifier option`); + await clickElement(dbg, searchModifierBtn); + let results = await waitForSearchResults(dbg, expected.resultWithModifierOn); + is( + results.length, + expected.resultWithModifierOn, + `${results.length} results where found` + ); + + info(`Turn off the ${title} search modifier`); + await clickElement(dbg, searchModifierBtn); + + results = await waitForSearchResults(dbg, expected.resultWithModifierOff); + is( + results.length, + expected.resultWithModifierOff, + `${results.length} results where found` + ); + await clearElement(dbg, "projectSearchSearchInput"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js b/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js new file mode 100644 index 0000000000..1cbf02c616 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-quick-open.js @@ -0,0 +1,166 @@ +/* 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 . */ + +// Testing quick open + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + // Inject lots of sources to go beyond the maximum limit of displayed sources (set to 100) + const injectedSources = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + function () { + const sources = []; + for (let i = 1; i <= 200; i++) { + const value = String(i).padStart(3, "0"); + content.eval( + `function evalSource() {}; //# sourceURL=eval-source-${value}.js` + ); + sources.push(`eval-source-${value}.js`); + } + return sources; + } + ); + await waitForSources(dbg, ...injectedSources); + + info("test opening and closing"); + await quickOpen(dbg, ""); + pressKey(dbg, "Escape"); + assertDisabled(dbg); + + info("Testing the number of results for source search"); + await quickOpen(dbg, "sw"); + await waitForResults(dbg, [undefined, undefined]); + is(resultCount(dbg), 2, "two file results"); + pressKey(dbg, "Escape"); + + // We ensure that sources after maxResult limit are visible + info("Test that first and last eval source are visible"); + await quickOpen(dbg, "eval-source-001.js"); + await waitForResults(dbg, ["eval-source-001.js"]); + is(resultCount(dbg), 1, "one file result"); + pressKey(dbg, "Escape"); + await quickOpen(dbg, "eval-source-200.js"); + await waitForResults(dbg, ["eval-source-200.js"]); + is(resultCount(dbg), 1, "one file result"); + pressKey(dbg, "Escape"); + + info("Testing source search and check to see if source is selected"); + await waitForSource(dbg, "script-switching-01.js"); + await quickOpen(dbg, "sw1"); + await waitForResults(dbg, ["script-switching-01.js"]); + is(resultCount(dbg), 1, "one file results"); + pressKey(dbg, "Enter"); + await waitForSelectedSource(dbg, "script-switching-01.js"); + + info("Test that results show tab icons"); + await quickOpen(dbg, "sw1"); + await waitForResults(dbg, ["script-switching-01.js"]); + await assertResultIsTab(dbg, 1); + pressKey(dbg, "Tab"); + + info( + "Testing arrow keys in source search and check to see if source is selected" + ); + await quickOpen(dbg, "sw2"); + await waitForResults(dbg, ["script-switching-02.js"]); + is(resultCount(dbg), 1, "one file results"); + pressKey(dbg, "Down"); + pressKey(dbg, "Enter"); + await waitForSelectedSource(dbg, "script-switching-02.js"); + + info("Testing tab closes the search"); + await quickOpen(dbg, "sw"); + await waitForResults(dbg, [undefined, undefined]); + pressKey(dbg, "Tab"); + assertDisabled(dbg); + + info("Testing function search"); + await quickOpen(dbg, "", "quickOpenFunc"); + await waitForResults(dbg, ["secondCall", "foo"]); + is(resultCount(dbg), 2, "two function results"); + + type(dbg, "@x"); + await waitForResults(dbg, []); + is(resultCount(dbg), 0, "no functions with 'x' in name"); + + pressKey(dbg, "Escape"); + assertDisabled(dbg); + + info("Testing goto line:column"); + assertLine(dbg, 0); + assertColumn(dbg, null); + await quickOpen(dbg, ":7:12"); + await waitForResults(dbg, [undefined, undefined]); + pressKey(dbg, "Enter"); + await waitForSelectedSource(dbg, "script-switching-02.js"); + assertLine(dbg, 7); + assertColumn(dbg, 12); + + info("Testing gotoSource"); + await quickOpen(dbg, "sw1:5"); + await waitForResults(dbg, ["script-switching-01.js"]); + pressKey(dbg, "Enter"); + await waitForSelectedSource(dbg, "script-switching-01.js"); + assertLine(dbg, 5); +}); + +function assertEnabled(dbg) { + is(dbg.selectors.getQuickOpenEnabled(), true, "quickOpen enabled"); +} + +function assertDisabled(dbg) { + is(dbg.selectors.getQuickOpenEnabled(), false, "quickOpen disabled"); +} + +function assertLine(dbg, lineNumber) { + is( + dbg.selectors.getSelectedLocation().line, + lineNumber, + `goto line is ${lineNumber}` + ); +} + +function assertColumn(dbg, columnNumber) { + let value = dbg.selectors.getSelectedLocation().column; + if (value === undefined) { + value = null; + } + is(value, columnNumber, `goto column is ${columnNumber}`); +} + +function resultCount(dbg) { + return findAllElements(dbg, "resultItems").length; +} + +async function quickOpen(dbg, query, shortcut = "quickOpen") { + pressKey(dbg, shortcut); + assertEnabled(dbg); + query !== "" && type(dbg, query); +} + +async function waitForResults(dbg, results) { + await waitForAllElements(dbg, "resultItems", results.length, true); + + for (let i = 0; i < results.length; ++i) { + if (results[i] !== undefined) { + await waitForElement(dbg, "resultItemName", results[i], i + 1); + } + } +} + +function findResultEl(dbg, index = 1) { + return waitForElementWithSelector(dbg, `.result-item:nth-child(${index})`); +} + +async function assertResultIsTab(dbg, index) { + const el = await findResultEl(dbg, index); + ok( + el && !!el.querySelector(".tab.result-item-icon"), + "Result should be a tab" + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-react-app.js b/devtools/client/debugger/test/mochitest/browser_dbg-react-app.js new file mode 100644 index 0000000000..16d1e361c1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-react-app.js @@ -0,0 +1,34 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-react.html", "App.js"); + dbg.actions.toggleMapScopes(); + + await selectSource(dbg, "App.js"); + await addBreakpoint(dbg, "App.js", 11); + + info("Test previewing an immutable Map inside of a react component"); + invokeInTab("clickButton"); + await waitForPaused(dbg); + + await waitForState(dbg, state => + dbg.selectors.getSelectedScopeMappings(dbg.selectors.getCurrentThread()) + ); + + await assertPreviewTextValue(dbg, 10, 22, { + text: "size: 1", + expression: "_this.fields;", + }); + + info("Verify that the file is flagged as a React module"); + const sourceTab = findElementWithSelector(dbg, ".source-tab.active"); + ok( + sourceTab.querySelector(".source-icon.react"), + "Source tab has a React icon" + ); + assertSourceIcon(dbg, "App.js", "react"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-react-jsx.js b/devtools/client/debugger/test/mochitest/browser_dbg-react-jsx.js new file mode 100644 index 0000000000..9e9f6417bd --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-react-jsx.js @@ -0,0 +1,20 @@ +/* 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 . */ + +// Test that dynamically generated + `); +}); + +httpServer.registerPathHandler("/test.js", function (request, response) { + response.setHeader("Content-Type", "application/javascript"); + response.write(` + document.addEventListener("click", function onClick(e) { + var sharedValue = {hello: "world"}; + var x = { + a: sharedValue, + b: sharedValue, + c: sharedValue, + d: { + e: { + g: sharedValue + }, + f: { + h: sharedValue + } + } + }; + debugger; + }); + `); +}); +const port = httpServer.identity.primaryPort; +const TEST_URL = `http://localhost:${port}/`; + +add_task(async function () { + const dbg = await initDebuggerWithAbsoluteURL(TEST_URL); + + const ready = Promise.all([ + waitForPaused(dbg), + waitForLoadedSource(dbg, "test.js"), + ]); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.querySelector("button.pause").click(); + }); + + await ready; + + checkScopesLabels( + dbg, + ` + | e + | sharedValue + | x + `, + { startIndex: 3 } + ); + + info("Expand `x` node"); + await toggleScopeNode(dbg, 6); + checkScopesLabels( + dbg, + ` + | x + | | a + | | b + | | c + | | d + `, + { startIndex: 5 } + ); + + info("Expand node `d`"); + await toggleScopeNode(dbg, 10); + checkScopesLabels( + dbg, + ` + | | d + | | | e + | | | f + `, + { startIndex: 9 } + ); + + info("Expand `f` and `e` nodes"); + await toggleScopeNode(dbg, 12); + await toggleScopeNode(dbg, 11); + checkScopesLabels( + dbg, + ` + | | d + | | | e + | | | | g + | | | | + | | | f + | | | | h + | | | | + `, + { startIndex: 9 } + ); + + info("Expand `h`, `g`, `e`, `c`, `b` and `a` nodes"); + await toggleScopeNode(dbg, 15); + await toggleScopeNode(dbg, 12); + await toggleScopeNode(dbg, 9); + await toggleScopeNode(dbg, 8); + await toggleScopeNode(dbg, 7); + + checkScopesLabels( + dbg, + ` + | x + | | a + | | | hello + | | | + | | b + | | | hello + | | | + | | c + | | | hello + | | | + | | d + | | | e + | | | | g + | | | | | hello + | | | | | + | | | | + | | | f + | | | | h + | | | | | hello + | | | | | + | | | | + `, + { startIndex: 5 } + ); + + info("Expand `e`"); + await toggleScopeNode(dbg, 4); + + info("Expand the `target` node"); + let nodes = getAllLabels(dbg); + const originalNodesCount = nodes.length; + const targetNodeIndex = nodes.indexOf("target"); + ok(targetNodeIndex > -1, "Found the target node"); + await toggleScopeNode(dbg, targetNodeIndex); + nodes = getAllLabels(dbg); + ok(nodes.length > originalNodesCount, "the target node was expanded"); + ok(nodes.includes("classList"), "classList is displayed"); + + await resume(dbg); +}); + +function getAllLabels(dbg, withIndent = false) { + return Array.from(findAllElements(dbg, "scopeNodes")).map(el => { + let text = el.innerText; + if (withIndent) { + const node = el.closest(".tree-node"); + const level = Number(node.getAttribute("aria-level")); + if (!Number.isNaN(level)) { + text = `${"| ".repeat(level - 1)}${text}`.trim(); + } + } + return text; + }); +} + +function checkScopesLabels(dbg, expected, { startIndex = 0 } = {}) { + const lines = expected + .trim() + .split("\n") + .map(line => line.trim()); + + const labels = getAllLabels(dbg, true).slice( + startIndex, + startIndex + lines.length + ); + + const format = arr => `\n${arr.join("\n")}\n`; + is(format(labels), format(lines), "got expected scope labels"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-scopes-mutations.js b/devtools/client/debugger/test/mochitest/browser_dbg-scopes-mutations.js new file mode 100644 index 0000000000..d0cb3b6035 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-scopes-mutations.js @@ -0,0 +1,93 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-mutate.html"); + + const onPaused = waitForPaused(dbg); + invokeInTab("mutate"); + await onPaused; + await waitForSelectedSource(dbg, "script-mutate"); + await waitForDispatch(dbg.store, "ADD_INLINE_PREVIEW"); + + is( + getScopeNodeLabel(dbg, 2), + "", + 'The second element in the scope panel is ""' + ); + is( + getScopeNodeLabel(dbg, 4), + "phonebook", + 'The fourth element in the scope panel is "phonebook"' + ); + + info("Expand `phonebook`"); + await expandNode(dbg, 4); + is( + getScopeNodeLabel(dbg, 5), + "S", + 'The fifth element in the scope panel is "S"' + ); + + info("Expand `S`"); + await expandNode(dbg, 5); + is( + getScopeNodeLabel(dbg, 6), + "sarah", + 'The sixth element in the scope panel is "sarah"' + ); + is( + getScopeNodeLabel(dbg, 7), + "serena", + 'The seventh element in the scope panel is "serena"' + ); + + info("Expand `sarah`"); + await expandNode(dbg, 6); + is( + getScopeNodeLabel(dbg, 7), + "lastName", + 'The seventh element in the scope panel is now "lastName"' + ); + is( + getScopeNodeValue(dbg, 7), + '"Doe"', + 'The "lastName" element has the expected "Doe" value' + ); + + await resume(dbg); + await waitForPaused(dbg); + await waitForDispatch(dbg.store, "ADD_INLINE_PREVIEW"); + + is( + getScopeNodeLabel(dbg, 2), + "", + 'The second element in the scope panel is ""' + ); + is( + getScopeNodeLabel(dbg, 4), + "phonebook", + 'The fourth element in the scope panel is "phonebook"' + ); +}); + +function getScopeNodeLabel(dbg, index) { + return findElement(dbg, "scopeNode", index).innerText; +} + +function getScopeNodeValue(dbg, index) { + return findElement(dbg, "scopeValue", index).innerText; +} + +function expandNode(dbg, index) { + const node = findElement(dbg, "scopeNode", index); + const objectInspector = node.closest(".object-inspector"); + const properties = objectInspector.querySelectorAll(".node").length; + findElement(dbg, "scopeNode", index).click(); + return waitUntil( + () => objectInspector.querySelectorAll(".node").length !== properties + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-scopes-xrays.js b/devtools/client/debugger/test/mochitest/browser_dbg-scopes-xrays.js new file mode 100644 index 0000000000..43ec77d5b3 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-scopes-xrays.js @@ -0,0 +1,67 @@ +/* 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 . */ + +// Test that xrays do not interfere with examining objects in the scopes pane. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scopes-xrays.html"); + + invokeInTab("start"); + await waitForPaused(dbg, "doc-scopes-xrays.html"); + + await toggleNode(dbg, "set"); + await toggleNode(dbg, ""); + await checkObjectNode(dbg, "0", "1"); + await toggleNode(dbg, "set"); + + await toggleNode(dbg, "weakset"); + await toggleNode(dbg, ""); + await checkObjectNode(dbg, "0", "2"); + await toggleNode(dbg, "weakset"); + + await toggleNode(dbg, "map"); + await toggleNode(dbg, ""); + await toggleNode(dbg, "0"); + await checkObjectNode(dbg, "", "3"); + await toggleNode(dbg, ""); + await checkObjectNode(dbg, "", "4"); + await toggleNode(dbg, "map"); + + await toggleNode(dbg, "weakmap"); + await toggleNode(dbg, ""); + await toggleNode(dbg, "0"); + await checkObjectNode(dbg, "", "5"); + await toggleNode(dbg, ""); + await checkObjectNode(dbg, "", "6"); + await toggleNode(dbg, "weakmap"); +}); + +function findNode(dbg, text) { + for (let index = 0; ; index++) { + const elem = findElement(dbg, "scopeNode", index); + if (elem?.innerText == text) { + return elem; + } + } +} + +function toggleNode(dbg, text) { + return toggleObjectInspectorNode(findNode(dbg, text)); +} + +function findNodeValue(dbg, text) { + for (let index = 0; ; index++) { + const elem = findElement(dbg, "scopeNode", index); + if (elem?.innerText == text) { + return findElement(dbg, "scopeValue", index).innerText; + } + } +} + +async function checkObjectNode(dbg, text, value) { + await toggleNode(dbg, text); + ok(findNodeValue(dbg, "a") == value, "object value"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-scopes.js b/devtools/client/debugger/test/mochitest/browser_dbg-scopes.js new file mode 100644 index 0000000000..0de1e32baa --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-scopes.js @@ -0,0 +1,34 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-script-switching.html"); + + const ready = Promise.all([ + waitForPaused(dbg), + waitForLoadedSource(dbg, "script-switching-02.js"), + + // MAP_FRAMES triggers a new Scopes panel render cycle, which introduces + // a race condition with the click event on the foo node. + waitForDispatch(dbg.store, "MAP_FRAMES"), + ]); + invokeInTab("firstCall"); + await ready; + + is(getLabel(dbg, 1), "secondCall"); + is(getLabel(dbg, 2), ""); + is(getLabel(dbg, 4), "foo()"); + await toggleScopeNode(dbg, 4); + is(getLabel(dbg, 5), "arguments"); + + await stepOver(dbg); + is(getLabel(dbg, 4), "foo()"); + is(getLabel(dbg, 5), "Window"); +}); + +function getLabel(dbg, index) { + return findElement(dbg, "scopeNode", index).innerText; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-scroll-run-to-completion.js b/devtools/client/debugger/test/mochitest/browser_dbg-scroll-run-to-completion.js new file mode 100644 index 0000000000..c42841f135 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-scroll-run-to-completion.js @@ -0,0 +1,25 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scroll-run-to-completion.html"); + invokeInTab("pauseOnce", "doc-scroll-run-to-completion.html"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-scroll-run-to-completion.html").id, + 20 + ); + + await checkEvaluateInTopFrame(dbg, "window.scrollBy(0, 10);", undefined); + + // checkEvaluateInTopFrame does an implicit resume for some reason. + await waitForPaused(dbg); + + const onTestPassed = once(Services.ppmm, "test passed"); + await resume(dbg); + await onTestPassed; +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-search-file-paused.js b/devtools/client/debugger/test/mochitest/browser_dbg-search-file-paused.js new file mode 100644 index 0000000000..54a493fc2d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-search-file-paused.js @@ -0,0 +1,71 @@ +/* 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 . */ + +// Tests the search bar correctly responds to queries, enter, shift enter + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-scripts.html", + "simple1.js", + "simple2.js" + ); + + info("Add a breakpoint, wait for pause"); + const source = findSource(dbg, "simple2.js"); + await selectSource(dbg, source); + await addBreakpoint(dbg, source, 5); + invokeInTab("main"); + await waitForPaused(dbg); + + info("Starting a search for 'bar'"); + const cm = getCM(dbg); + pressKey(dbg, "fileSearch"); + is(dbg.selectors.getActiveSearch(), "file"); + const el = getFocusedEl(dbg); + type(dbg, "bar"); + await waitForSearchState(dbg); + + info("Ensuring 'bar' matches are highlighted"); + pressKey(dbg, "Enter"); + is(cm.state.search.posFrom.line, 1); + pressKey(dbg, "Enter"); + is(cm.state.search.posFrom.line, 4); + + info("Switching files via frame click"); + const frames = findAllElements(dbg, "frames"); + pressMouseDown(dbg, frames[1]); + + // Ensure that the debug line is in view, and not the first "bar" instance, + // which the user would have to scroll down for + const { top } = cm.getScrollInfo(); + is(top, 0, "First search term is not in view"); + + // Change the search term and go back to the first source in stack + info("Switching to paused file via frame click"); + pressKey(dbg, "fileSearch"); + el.value = ""; + type(dbg, "func"); + await waitForSearchState(dbg); + pressMouseDown(dbg, frames[0]); + await waitFor(() => cm.state.search.query === "func"); + + // Ensure there is a match for the new term + pressKey(dbg, "Enter"); + await waitFor(() => cm.state.search.posFrom.line === 0); + is(cm.state.search.posFrom.line, 0); + pressKey(dbg, "Enter"); + await waitFor(() => cm.state.search.posFrom.line === 1); + is(cm.state.search.posFrom.line, 1); +}); + +function getFocusedEl(dbg) { + const doc = dbg.win.document; + return doc.activeElement; +} + +function pressMouseDown(dbg, node) { + EventUtils.sendMouseEvent({ type: "mousedown" }, node, dbg.win); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-search-file-retains-query.js b/devtools/client/debugger/test/mochitest/browser_dbg-search-file-retains-query.js new file mode 100644 index 0000000000..53e9861d14 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-search-file-retains-query.js @@ -0,0 +1,40 @@ +/* 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 . */ + +// Tests the search bar retains previous query on re-opening. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + const { + selectors: { getActiveSearch }, + } = dbg; + await selectSource(dbg, "simple1.js"); + + // Open search bar + pressKey(dbg, "fileSearch"); + await waitFor(() => getActiveSearch() === "file"); + is(getActiveSearch(), "file"); + + // Type a search query + type(dbg, "con"); + await waitForSearchState(dbg); + is(findElement(dbg, "fileSearchInput").value, "con"); + is(getCM(dbg).state.search.query, "con"); + + // Close the search bar + pressKey(dbg, "Escape"); + await waitFor(() => getActiveSearch() === null); + is(getActiveSearch(), null); + + // Re-open search bar + pressKey(dbg, "fileSearch"); + await waitFor(() => getActiveSearch() === "file"); + is(getActiveSearch(), "file"); + + // Test for the retained query + is(getCM(dbg).state.search.query, "con"); + is(findElement(dbg, "fileSearchInput").value, "con"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js b/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js new file mode 100644 index 0000000000..0bf438fba4 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-search-file.js @@ -0,0 +1,146 @@ +/* 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 . */ + +// Tests the search bar correctly responds to queries, enter, shift enter + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + await selectSource(dbg, "simple1.js"); + + pressKey(dbg, "fileSearch"); + is(dbg.selectors.getActiveSearch(), "file", "The search UI was opened"); + + info("Test closing and re-opening the search UI"); + pressKey(dbg, "Escape"); + is( + dbg.selectors.getActiveSearch(), + null, + "The search UI was closed when hitting Escape" + ); + + pressKey(dbg, "fileSearch"); + is(dbg.selectors.getActiveSearch(), "file", "The search UI was opened again"); + + info("Search for `con` in the script"); + type(dbg, "con"); + await waitForSearchState(dbg); + + // All the lines in the script that include `con` + const linesWithResults = [ + // const func + 4, + // const result + 5, + // constructor (in MyClass) + 42, + // constructor (in Klass) + 55, + // console.log + 62, + ]; + + await waitFor( + () => getCursorPositionLine(dbg) === linesWithResults[0], + `typing in the search input did not set the search state in expected state` + ); + + is( + getCursorPositionLine(dbg), + linesWithResults[0], + "typing in the search input initialize the search" + ); + + info("Check that pressing Enter navigates forward through the results"); + await navigateWithKey( + dbg, + "Enter", + linesWithResults[1], + "Enter moves forward in the search results" + ); + + await navigateWithKey( + dbg, + "Enter", + linesWithResults[2], + "Enter moves forward in the search results" + ); + + info( + "Check that pressing Shift+Enter navigates backward through the results" + ); + await navigateWithKey( + dbg, + "ShiftEnter", + linesWithResults[1], + "Shift+Enter moves backward in the search results" + ); + + await navigateWithKey( + dbg, + "ShiftEnter", + linesWithResults[0], + "Shift+Enter moves backward in the search results" + ); + + info( + "Check that navigating backward goes to the last result when we were at the first one" + ); + await navigateWithKey( + dbg, + "ShiftEnter", + linesWithResults.at(-1), + "Shift+Enter cycles back through the results" + ); + + info( + "Check that navigating forward goes to the first result when we were at the last one" + ); + await navigateWithKey( + dbg, + "Enter", + linesWithResults[0], + "Enter cycles forward through the results" + ); + + info("Check that changing the search term works"); + pressKey(dbg, "fileSearch"); + type(dbg, "doEval"); + + await waitFor( + () => getCursorPositionLine(dbg) === 9, + "The UI navigates to the new search results" + ); + + // selecting another source keeps search open + await selectSource(dbg, "simple2.js"); + ok(findElement(dbg, "searchField"), "Search field is still visible"); + + // search is always focused regardless of when or how it was opened + pressKey(dbg, "fileSearch"); + await clickElement(dbg, "codeMirror"); + pressKey(dbg, "fileSearch"); + is(dbg.win.document.activeElement.tagName, "INPUT", "Search field focused"); +}); + +async function navigateWithKey(dbg, key, expectedLine, assertionMessage) { + const currentLine = getCursorPositionLine(dbg); + pressKey(dbg, key); + await waitFor( + () => currentLine !== getCursorPositionLine(dbg), + `Pressing "${key}" did not change the position` + ); + + is(getCursorPositionLine(dbg), expectedLine, assertionMessage); +} + +function getCursorPositionLine(dbg) { + const cursorPosition = findElementWithSelector(dbg, ".cursor-position"); + const { innerText } = cursorPosition; + // Cursor position text has the following shape: (L, C) + // where N is the line number, and C the column number + const line = innerText.substring(1, innerText.indexOf(",")); + return parseInt(line, 10); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-settings-disable-javascript.js b/devtools/client/debugger/test/mochitest/browser_dbg-settings-disable-javascript.js new file mode 100644 index 0000000000..9b244ca4b7 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-settings-disable-javascript.js @@ -0,0 +1,49 @@ +/* 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 . */ + +"use strict"; + +requestLongerTimeout(2); + +// Tests that using the Settings menu to enable and disable JavaScript +// updates the pref properly +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple1.js"); + const menuItemClassName = ".debugger-settings-menu-item-disable-javascript"; + + info("Waiting for source to load"); + await waitForSource(dbg, "simple1.js"); + + const waitForDevToolsReload = await watchForDevToolsReload( + gBrowser.selectedBrowser + ); + info("Clicking the disable javascript button in the settings menu"); + await toggleDebbuggerSettingsMenuItem(dbg, { + className: menuItemClassName, + isChecked: false, + }); + + info("Waiting for reload triggered by disabling javascript"); + await waitForSourcesInSourceTree(dbg, [], { noExpand: true }); + + info("Wait for DevTools to be reloaded"); + await waitForDevToolsReload(); + + info( + "Clicking the disable javascript button in the settings menu to reenable JavaScript" + ); + await toggleDebbuggerSettingsMenuItem(dbg, { + className: menuItemClassName, + isChecked: true, + }); + is( + Services.prefs.getBoolPref("javascript.enabled"), + true, + "JavaScript is enabled" + ); + + info("Reloading page to ensure there are sources"); + await reload(dbg); + await waitForSource(dbg, "simple1.js"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-slow-script.js b/devtools/client/debugger/test/mochitest/browser_dbg-slow-script.js new file mode 100644 index 0000000000..d5d60c7994 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-slow-script.js @@ -0,0 +1,91 @@ +/* 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 . */ + +"use strict"; + +// Tests the slow script warning + +add_task(async function openDebuggerFirst() { + // In mochitest, the timeout is disable, so set it to a short, but non zero duration + await pushPref("dom.max_script_run_time", 1); + // Prevents having to click on the page to have the dialog to appear + await pushPref("dom.max_script_run_time.require_critical_input", false); + + const dbg = await initDebugger("doc-slow-script.html"); + + const alert = BrowserTestUtils.waitForGlobalNotificationBar( + window, + "process-hang" + ); + + info("Execute an infinite loop"); + invokeInTab("infiniteLoop"); + + info("Wait for the slow script warning"); + const notification = await alert; + + info("Click on the debug script button"); + const buttons = notification.buttonContainer.getElementsByTagName("button"); + // The first button is "stop", the second is "debug script" + buttons[1].click(); + + info("Waiting for the debugger to be paused"); + await waitForPaused(dbg); + const source = findSource(dbg, "doc-slow-script.html"); + assertPausedAtSourceAndLine(dbg, source.id, 14); + + info("Close toolbox and tab"); + await dbg.toolbox.closeToolbox(); + await removeTab(gBrowser.selectedTab); +}); + +add_task(async function openDebuggerFromDialog() { + const tab = await addTab(EXAMPLE_URL + "doc-slow-script.html"); + + const alert = BrowserTestUtils.waitForGlobalNotificationBar( + window, + "process-hang" + ); + + // /!\ Hack this attribute in order to force showing the "debug script" button + // on all channels. Otherwise it is only displayed in dev edition. + tab.linkedBrowser.browsingContext.watchedByDevTools = true; + + info("Execute an infinite loop"); + // Note that spawn will return a promise that may be rejected because of the infinite loop + // And mochitest may consider this as an error. So ignore any rejection. + SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.wrappedJSObject.infiniteLoop(); + }).catch(e => {}); + + info("Wait for the slow script warning"); + const notification = await alert; + + info("Click on the debug script button"); + const buttons = notification.buttonContainer.getElementsByTagName("button"); + // The first button is "stop", the second is "debug script" + buttons[1].click(); + + info("Wait for the toolbox to appear and have the debugger initialized"); + await waitFor(async () => { + const tb = await gDevTools.getToolboxForTab(gBrowser.selectedTab); + if (tb) { + await tb.getPanelWhenReady("jsdebugger"); + return true; + } + return false; + }); + const toolbox = await gDevTools.getToolboxForTab(gBrowser.selectedTab); + ok(toolbox, "Got a toolbox"); + const dbg = createDebuggerContext(toolbox); + + info("Waiting for the debugger to be paused"); + await waitForPaused(dbg); + const source = findSource(dbg, "doc-slow-script.html"); + assertPausedAtSourceAndLine(dbg, source.id, 14); + + info("Close toolbox and tab"); + await dbg.toolbox.closeToolbox(); + await removeTab(gBrowser.selectedTab); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js b/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js new file mode 100644 index 0000000000..edca4e2b80 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js @@ -0,0 +1,26 @@ +/* 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 . */ + +"use strict"; + +add_task(async function () { + // Disable handling of //# source(Mapping)URL= comments. + await SpecialPowers.pushPrefEnv({ + set: [["javascript.options.source_pragmas", false]], + }); + + const dbg = await initDebugger("doc-source-pragma.html"); + + // The sourceURL pragma didn't rename the source + await waitForSource(dbg, "source-pragma.js"); + const source = findSource(dbg, "source-pragma.js"); + const actors = dbg.selectors.getSourceActorsForSource(source.id); + + is(actors.length, 1, "have a single actor"); + ok( + actors[0].url.endsWith("/source-pragma.js"), + "source url was not rewritten" + ); + is(actors[0].sourceMapURL, null, "source map was not exposed"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourceURL-breakpoint.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourceURL-breakpoint.js new file mode 100644 index 0000000000..a054d4fcac --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourceURL-breakpoint.js @@ -0,0 +1,18 @@ +/* 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 . */ + +// Test that breakpoints are hit in eval'ed sources with a sourceURL property. + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-sourceURL-breakpoint.html", "my-foo.js"); + await selectSource(dbg, "my-foo.js"); + await addBreakpoint(dbg, "my-foo.js", 2); + + invokeInTab("foo"); + await waitForPaused(dbg); + + ok(true, "paused at breakpoint"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-breakpoint-console.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-breakpoint-console.js new file mode 100644 index 0000000000..2bb4d853fe --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-breakpoint-console.js @@ -0,0 +1,86 @@ +/* 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 . */ + +"use strict"; + +// This test can be really slow on debug platforms and should be split. +requestLongerTimeout(3); + +add_task(async function () { + const dbg = await initDebugger("doc-sourcemapped.html"); + dbg.actions.toggleMapScopes(); + + await evalInConsoleAtPoint( + dbg, + "webpack3-babel6", + "eval-maps", + { line: 14, column: 4 }, + ["one === 1", "two === 4", "three === 5"] + ); + + await evalInConsoleAtPoint( + dbg, + "webpack3-babel6", + "esmodules-cjs", + { line: 18, column: 2 }, + [ + `aDefault === "a-default"`, + `anAliased === "an-original"`, + `aNamed === "a-named"`, + `aDefault2 === "a-default2"`, + `anAliased2 === "an-original2"`, + `aNamed2 === "a-named2"`, + `aDefault3 === "a-default3"`, + `anAliased3 === "an-original3"`, + `aNamed3 === "a-named3"`, + ] + ); + + await evalInConsoleAtPoint( + dbg, + "webpack3-babel6", + "shadowed-vars", + { line: 18, column: 6 }, + [`aVar === "var3"`, `aLet === "let3"`, `aConst === "const3"`] + ); + + await evalInConsoleAtPoint( + dbg, + "webpack3-babel6", + "babel-classes", + { line: 8, column: 16 }, + [`this.hasOwnProperty("bound")`] + ); +}); + +async function evalInConsoleAtPoint( + dbg, + target, + fixture, + { line, column }, + statements +) { + const url = `${target}://./${fixture}/input.js`; + const fnName = `${target}-${fixture}`.replace(/-([a-z])/g, (s, c) => + c.toUpperCase() + ); + + await invokeWithBreakpoint(dbg, fnName, url, { line, column }, async () => { + await assertConsoleEval(dbg, statements); + }); + + ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`); +} + +async function assertConsoleEval(dbg, statements) { + const { hud } = await dbg.toolbox.selectTool("webconsole"); + + for (const statement of statements.values()) { + await dbg.client.evaluate(`window.TEST_RESULT = false;`); + await evaluateExpressionInConsole(hud, `TEST_RESULT = ${statement};`); + + const result = await dbg.client.evaluate(`window.TEST_RESULT`); + is(result.result, true, `'${statement}' evaluates to true`); + } +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-preview.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-preview.js new file mode 100644 index 0000000000..5034761549 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-preview.js @@ -0,0 +1,206 @@ +/* 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 . */ + +"use strict"; + +// Tests for preview through Babel's compile output. +requestLongerTimeout(3); + +add_task(async function () { + const dbg = await initDebugger("doc-sourcemapped.html"); + dbg.actions.toggleMapScopes(); + + await testForOf(dbg); + await testShadowing(dbg); + await testImportedBindings(dbg); +}); + +async function breakpointPreviews( + dbg, + target, + fixture, + { line, column }, + previews +) { + const url = `${target}://./${fixture}/input.js`; + const fnName = `${target}-${fixture}`.replace(/-([a-z])/g, (s, c) => + c.toUpperCase() + ); + + info(`Starting ${fixture} tests`); + + await invokeWithBreakpoint(dbg, fnName, url, { line, column }, async () => { + await assertPreviews(dbg, previews); + }); + + ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`); +} + +function testForOf(dbg) { + return breakpointPreviews( + dbg, + "webpack3-babel6", + "for-of", + { line: 5, column: 4 }, + [ + { + line: 5, + column: 7, + expression: "doThing", + result: "doThing(arg)", + }, + { + line: 5, + column: 13, + expression: "x", + result: "1", + }, + { + line: 8, + column: 16, + expression: "doThing", + result: "doThing(arg)", + }, + ] + ); +} + +function testShadowing(dbg) { + return breakpointPreviews( + dbg, + "webpack3-babel6", + "shadowed-vars", + { line: 18, column: 6 }, + [ + // These aren't what the user would expect, but we test them anyway since + // they reflect what this actually returns. These shadowed bindings read + // the binding closest to the current frame's scope even though their + // actual value is different. + { + line: 2, + column: 9, + expression: "aVar", + result: '"var3"', + }, + { + line: 3, + column: 9, + expression: "_aLet2;", + result: '"let3"', + }, + { + line: 4, + column: 11, + expression: "_aConst2;", + result: '"const3"', + }, + { + line: 10, + column: 11, + expression: "aVar", + result: '"var3"', + }, + { + line: 11, + column: 11, + expression: "_aLet2;", + result: '"let3"', + }, + { + line: 12, + column: 13, + expression: "_aConst2;", + result: '"const3"', + }, + + // These actually result in the values the user would expect. + { + line: 14, + column: 13, + expression: "aVar", + result: '"var3"', + }, + { + line: 15, + column: 13, + expression: "_aLet2;", + result: '"let3"', + }, + { + line: 16, + column: 13, + expression: "_aConst2;", + result: '"const3"', + }, + ] + ); +} + +function testImportedBindings(dbg) { + return breakpointPreviews( + dbg, + "webpack3-babel6", + "esmodules-cjs", + { line: 20, column: 2 }, + [ + { + line: 20, + column: 16, + expression: "_mod2.default;", + result: '"a-default"', + }, + { + line: 21, + column: 16, + expression: "_mod4.original;", + result: '"an-original"', + }, + { + line: 22, + column: 16, + expression: "_mod3.aNamed;", + result: '"a-named"', + }, + { + line: 23, + column: 16, + expression: "_mod3.aNamed;", + result: '"a-named"', + }, + { + line: 24, + column: 16, + expression: "aNamespace", + fields: [ + ["aNamed", '"a-named"'], + ["default", '"a-default"'], + ], + }, + { + line: 29, + column: 20, + expression: "_mod7.default;", + result: '"a-default2"', + }, + { + line: 30, + column: 20, + expression: "_mod9.original;", + result: '"an-original2"', + }, + { + line: 31, + column: 20, + expression: "_mod8.aNamed2;", + result: '"a-named2"', + }, + { + line: 32, + column: 20, + expression: "_mod8.aNamed2;", + result: '"a-named2"', + }, + ] + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-scopes.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-scopes.js new file mode 100644 index 0000000000..d3029a683e --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-scopes.js @@ -0,0 +1,1646 @@ +/* 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 . */ + +"use strict"; + +// This test can be really slow on debug platforms and should be split. +requestLongerTimeout(30); + +// Tests loading sourcemapped sources for Babel's compile output. + +/* eslint-disable no-inline-comments */ + +const ACTIVE_TARGETS = new Set([ + // "webpack3", + "webpack3-babel6", + // "webpack3-babel7", + // "webpack4", + // "webpack4-babel6", + // "webpack4-babel7", + "rollup", + // "rollup-babel6", + // "rollup-babel7", + "parcel", +]); + +const ACTIVE_FIXTURES = [ + testBabelBindingsWithFlow, + testBabelFlowtypeBindings, + testEvalMaps, + testForOf, + testShadowedVars, + testLineStartBindingsES6, + testThisArgumentsBindings, + testClasses, + testForLoops, + testFunctions, + testSwitches, + testTryCatches, + testLexAndNonlex, + testTypescriptClasses, + testTypeModule, + testTypeScriptCJS, + testOutOfOrderDeclarationsCJS, + testModulesCJS, + testWebpackLineMappings, + testWebpackFunctions, + testESModules, + testESModulesCJS, + testESModulesES6, +]; + +async function breakpointScopes( + dbg, + target, + fixture, + { line, column }, + scopes +) { + if (!ACTIVE_TARGETS.has(target)) { + return; + } + + const extension = fixture == "typescript-classes" ? "ts" : "js"; + const url = `${target}://./${fixture}/input.${extension}`; + const fnName = pairToFnName(target, fixture); + + await invokeWithBreakpoint(dbg, fnName, url, { line, column }, async () => { + await assertScopes(dbg, scopes); + }); + + ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`); +} + +add_task(async function () { + const dbg = await initDebugger("doc-sourcemapped.html"); + dbg.actions.toggleMapScopes(); + + for (const fixture of ACTIVE_FIXTURES) { + await fixture(dbg); + } +}); + +function targetToFlags(target) { + const isRollup = target.startsWith("rollup"); + const isWebpack = target.startsWith("webpack"); + const isParcel = target.startsWith("parcel"); + const isWebpack4 = target.startsWith("webpack4"); + + // Rollup removes lots of things as dead code, so they are marked as optimized out. + const rollupOptimized = isRollup ? "(optimized away)" : null; + const webpackImportGetter = isWebpack ? "Getter" : null; + const webpack4ImportGetter = isWebpack4 ? "Getter" : null; + const maybeLineStart = col => col; + const defaultExport = isWebpack4 + ? name => `${name}()` + : name => [name, "(optimized away)"]; + + return { + isRollup, + isWebpack, + isParcel, + rollupOptimized, + webpackImportGetter, + webpack4ImportGetter, + maybeLineStart, + defaultExport, + }; +} +function pairToFnName(target, fixture) { + return `${target}-${fixture}`.replace(/-([a-z])/g, (s, c) => c.toUpperCase()); +} + +function runtimeFunctionName(target, fixture) { + // Webpack 4 appears to output it's bundles in such a way that Spidermonkey + if (target === "webpack4") { + return "js"; + } + + return pairToFnName(target, fixture); +} + +function webpackModule(target, fixture, optimizedOut) { + return [ + runtimeFunctionName(target, fixture), + ["__webpack_exports__", optimizedOut ? "(optimized away)" : "{\u2026}"], + optimizedOut + ? ["__webpack_require__", "(optimized away)"] + : "__webpack_require__()", + ["arguments", optimizedOut ? "(unavailable)" : "Arguments"], + ]; +} + +async function testBabelBindingsWithFlow(dbg) { + // Flow is not available on the non-babel builds. + for (const target of [ + "parcel", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { webpackImportGetter } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "babel-bindings-with-flow", + { line: 9, column: 2 }, + [ + "root", + ["value", '"a-named"'], + "Module", + ["aNamed", webpackImportGetter || '"a-named"'], + "root()", + ] + ); + } +} + +async function testBabelFlowtypeBindings(dbg) { + // Flow is not available on the non-babel builds. + for (const target of [ + "parcel", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { webpackImportGetter } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "babel-flowtype-bindings", + { line: 9, column: 2 }, + [ + "Module", + ["aConst", '"a-const"'], + ["Four", webpackImportGetter || '"one"'], + "root()", + ] + ); + } +} + +async function testEvalMaps(dbg) { + // At times, this test has been a bit flakey due to the inlined source map + // never loading. I'm not sure what causes that. If we observe flakiness in CI, + // we should consider disabling this test for now. + + for (const target of ["webpack3", "webpack4"]) { + const { defaultExport } = targetToFlags(target); + + await breakpointScopes(dbg, target, "eval-maps", { line: 14, column: 4 }, [ + "Block", + ["", "Window"], + ["three", "5"], + ["two", "4"], + "Block", + ["three", "3"], + ["two", "2"], + "Block", + ["arguments", "Arguments"], + ["one", "1"], + ...webpackModule(target, "eval-maps", true /* optimized out */), + ["module", "(optimized away)"], + defaultExport("root"), + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { defaultExport, rollupOptimized, maybeLineStart } = + targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "eval-maps", + { line: 14, column: maybeLineStart(4) }, + [ + "Block", + ["three", "5"], + ["two", "4"], + "Function Body", + ["three", rollupOptimized || "3"], + ["two", rollupOptimized || "2"], + "root", + ["one", "1"], + "Module", + defaultExport("root"), + ] + ); + } +} + +async function testForOf(dbg) { + for (const target of ["webpack3", "webpack4"]) { + const { defaultExport } = targetToFlags(target); + + await breakpointScopes(dbg, target, "for-of", { line: 5, column: 0 }, [ + "Block", + ["", "Window"], + ["x", "1"], + "Block", + ["arguments", "Arguments"], + "class doThing", + "Block", + "mod", + ...webpackModule(target, "for-of", true /* optimizedOut */), + defaultExport("forOf"), + "module", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { defaultExport, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "for-of", + { line: 5, column: maybeLineStart(4) }, + [ + "For", + ["x", "1"], + "forOf", + "doThing(arg)", + "Module", + defaultExport("forOf"), + "mod", + ] + ); + } +} + +async function testShadowedVars(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes( + dbg, + target, + "shadowed-vars", + { line: 18, column: 0 }, + [ + "Block", + ["", "Window"], + ["aConst", '"const3"'], + ["aLet", '"let3"'], + + "Block", + ["aConst", '"const2"'], + ["aLet", '"let2"'], + "class Outer", + + "Block", + ["aConst", '"const1"'], + ["aLet", '"let1"'], + "class Outer", + + "Block", + ["arguments", "Arguments"], + ["aVar", '"var3"'], + + ...webpackModule(target, "shadowed-vars", true /* optimizedOut */), + ["module", "(optimized away)"], + ] + ); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { isParcel, rollupOptimized, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "shadowed-vars", + { line: 18, column: maybeLineStart(6) }, + [ + "Block", + ["aConst", rollupOptimized || '"const3"'], + ["aLet", rollupOptimized || '"let3"'], + "Block", + ["aConst", rollupOptimized || '"const2"'], + ["aLet", rollupOptimized || '"let2"'], + // eslint-disable-next-line no-nested-ternary + isParcel + ? "Outer()" + : rollupOptimized + ? ["Outer", rollupOptimized] + : "Outer:_Outer()", + "Function Body", + ["aConst", rollupOptimized || '"const1"'], + ["aLet", rollupOptimized || '"let1"'], + // eslint-disable-next-line no-nested-ternary + rollupOptimized + ? ["Outer", rollupOptimized] + : isParcel + ? "class Outer" + : "Outer()", + "default", + ["aVar", rollupOptimized || '"var3"'], + ] + ); + } +} + +async function testLineStartBindingsES6(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes( + dbg, + target, + "line-start-bindings-es6", + { line: 19, column: 0 }, + [ + "Block", + ["", "{\u2026}"], + ["one", "1"], + ["two", "2"], + "Block", + ["arguments", "Arguments"], + + "Block", + ["aFunc", "(optimized away)"], + ["arguments", "(unavailable)"], + + ...webpackModule( + target, + "line-start-bindings-es6", + true /* optimizedOut */ + ), + ["module", "(optimized away)"], + "root()", + ] + ); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { rollupOptimized, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "line-start-bindings-es6", + { line: 19, column: maybeLineStart(4) }, + [ + "Function Body", + ["", "{\u2026}"], + ["one", rollupOptimized || "1"], + ["two", rollupOptimized || "2"], + "root", + ["aFunc", "(optimized away)"], + "Module", + "root()", + ] + ); + } +} + +async function testThisArgumentsBindings(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes( + dbg, + target, + "this-arguments-bindings", + { line: 4, column: 0 }, + [ + "Block", + ["", '"this-value"'], + ["arrow", "(uninitialized)"], + "fn", + ["arg", '"arg-value"'], + ["arguments", "Arguments"], + "root", + ["arguments", "Arguments"], + "fn()", + ...webpackModule( + target, + "this-arguments-bindings", + true /* optimizedOut */ + ), + ["module", "(optimized away)"], + "root()", + ] + ); + + await breakpointScopes( + dbg, + target, + "this-arguments-bindings", + { line: 8, column: 0 }, + [ + "Block", + ["", '"this-value"'], + ["argArrow", '"arrow-arg"'], + ["arguments", "Arguments"], + "Block", + ["arrow", "(optimized away)"], + "fn", + ["arg", '"arg-value"'], + ["arguments", "Arguments"], + "root", + ["arguments", "Arguments"], + "fn()", + ...webpackModule( + target, + "this-arguments-bindings", + true /* optimizedOut */ + ), + ["module", "(optimized away)"], + "root()", + ] + ); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { isParcel, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "this-arguments-bindings", + { line: 4, column: maybeLineStart(4) }, + [ + "Function Body", + ["", '"this-value"'], + [ + "arrow", + target === "rollup" || isParcel ? "(uninitialized)" : "undefined", + ], + "fn", + ["arg", '"arg-value"'], + ["arguments", "Arguments"], + "root", + "fn(arg)", + "Module", + "root()", + ] + ); + + await breakpointScopes( + dbg, + target, + "this-arguments-bindings", + { line: 8, column: maybeLineStart(6) }, + [ + "arrow", + ["", '"this-value"'], + ["argArrow", '"arrow-arg"'], + "Function Body", + target === "rollup" || isParcel + ? ["arrow", "(optimized away)"] + : "arrow(argArrow)", + "fn", + ["arg", '"arg-value"'], + ["arguments", "Arguments"], + "root", + "fn(arg)", + "Module", + "root()", + ] + ); + } +} + +async function testClasses(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "classes", { line: 6, column: 0 }, [ + "Block", + ["", "{}"], + ["arguments", "Arguments"], + "Block", + ["Thing", "(optimized away)"], + "Block", + "Another()", + ["one", "1"], + "Thing()", + "Block", + ["arguments", "(unavailable)"], + ...webpackModule(target, "classes", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + + await breakpointScopes(dbg, target, "classes", { line: 16, column: 0 }, [ + "Block", + ["", "{}"], + ["three", "3"], + ["two", "2"], + "Block", + ["arguments", "Arguments"], + "Block", + "Another()", + "Block", + "Another()", + ["one", "1"], + "Thing()", + "Block", + ["arguments", "(unavailable)"], + ...webpackModule(target, "classes", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { isParcel, rollupOptimized, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "classes", + { line: 6, column: maybeLineStart(6) }, + [ + "Class", + target === "rollup" || isParcel + ? ["Thing", "(optimized away)"] + : "Thing()", + "Function Body", + target === "webpack3-babel6" ? "Another()" : "class Another", + "one", + target === "rollup" || isParcel + ? ["Thing", "(optimized away)"] + : "Thing()", + "Module", + "root()", + ] + ); + + await breakpointScopes( + dbg, + target, + "classes", + { line: 16, column: maybeLineStart(6) }, + [ + "Function Body", + ["three", rollupOptimized || "3"], + ["two", rollupOptimized || "2"], + "Class", + target === "webpack3-babel6" ? "Another()" : "class Another", + "Function Body", + target === "webpack3-babel6" ? "Another()" : "class Another", + ["one", "1"], + target === "webpack3-babel6" ? "Thing()" : "class Thing", + "Module", + "root()", + ] + ); + } +} + +async function testForLoops(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "for-loops", { line: 5, column: 0 }, [ + "Block", + ["", "Window"], + ["i", "1"], + "Block", + ["i", "0"], + "Block", + ["arguments", "Arguments"], + ...webpackModule(target, "for-loops", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + await breakpointScopes(dbg, target, "for-loops", { line: 9, column: 0 }, [ + "Block", + ["", "Window"], + ["i", '"2"'], + "Block", + ["i", "0"], + "Block", + ["arguments", "Arguments"], + ...webpackModule(target, "for-loops", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + await breakpointScopes(dbg, target, "for-loops", { line: 13, column: 0 }, [ + "Block", + ["", "Window"], + ["i", "3"], + "Block", + ["i", "0"], + "Block", + ["arguments", "Arguments"], + ...webpackModule(target, "for-loops", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { rollupOptimized, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "for-loops", + { line: 5, column: maybeLineStart(4) }, + [ + "For", + ["i", "1"], + "Function Body", + ["i", rollupOptimized || "0"], + "Module", + "root()", + ] + ); + await breakpointScopes( + dbg, + target, + "for-loops", + { line: 9, column: maybeLineStart(4) }, + [ + "For", + ["i", '"2"'], + "Function Body", + ["i", rollupOptimized || "0"], + "Module", + "root()", + ] + ); + await breakpointScopes( + dbg, + target, + "for-loops", + { line: 13, column: maybeLineStart(4) }, + [ + "For", + ["i", target === "rollup" ? "3" : rollupOptimized || "3"], + "Function Body", + ["i", rollupOptimized || "0"], + "Module", + "root()", + ] + ); + } +} + +async function testFunctions(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "functions", { line: 6, column: 0 }, [ + "Block", + ["", "(optimized away)"], + ["arguments", "Arguments"], + ["p3", "undefined"], + "Block", + "arrow()", + "inner", + ["arguments", "Arguments"], + ["p2", "undefined"], + "Block", + "inner(p2)", + "Block", + ["inner", "(optimized away)"], + "decl", + ["arguments", "Arguments"], + ["p1", "undefined"], + "root", + ["arguments", "Arguments"], + "decl()", + ...webpackModule(target, "functions", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { isParcel, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "functions", + { line: 6, column: maybeLineStart(8) }, + [ + "arrow", + ["p3", "undefined"], + "Function Body", + "arrow(p3)", + "inner", + ["p2", "undefined"], + "Function Expression", + "inner(p2)", + "Function Body", + target === "rollup" || isParcel + ? ["inner", "(optimized away)"] + : "inner(p2)", + "decl", + ["p1", "undefined"], + "root", + "decl(p1)", + "Module", + "root()", + ] + ); + } +} + +async function testSwitches(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "switches", { line: 7, column: 0 }, [ + "Block", + ["", "Window"], + ["val", "2"], + "Block", + ["val", "1"], + "Block", + ["arguments", "Arguments"], + ...webpackModule(target, "switches", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + + await breakpointScopes(dbg, target, "switches", { line: 10, column: 0 }, [ + "Block", + ["", "Window"], + ["val", "3"], + "Block", + ["val", "2"], + "Block", + ["val", "1"], + "Block", + ["arguments", "Arguments"], + ...webpackModule(target, "switches", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { rollupOptimized, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "switches", + { line: 7, column: maybeLineStart(6) }, + [ + "Switch", + ["val", rollupOptimized || "2"], + "Function Body", + ["val", rollupOptimized || "1"], + "Module", + "root()", + ] + ); + + await breakpointScopes( + dbg, + target, + "switches", + { line: 10, column: maybeLineStart(6) }, + [ + "Block", + ["val", rollupOptimized || "3"], + "Switch", + ["val", rollupOptimized || "2"], + "Function Body", + ["val", rollupOptimized || "1"], + "Module", + "root()", + ] + ); + } +} + +async function testTryCatches(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "try-catches", { line: 8, column: 0 }, [ + "Block", + ["", "Window"], + ["two", "2"], + "Block", + ["err", '"AnError"'], + "Block", + ["one", "1"], + "Block", + ["arguments", "Arguments"], + ...webpackModule(target, "try-catches", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { rollupOptimized, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "try-catches", + { line: 8, column: maybeLineStart(4) }, + [ + "Block", + ["two", rollupOptimized || "2"], + "Catch", + ["err", '"AnError"'], + "Function Body", + ["one", rollupOptimized || "1"], + "Module", + "root()", + ] + ); + } +} + +async function testLexAndNonlex(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes( + dbg, + target, + "lex-and-nonlex", + { line: 3, column: 0 }, + [ + "Block", + ["", "undefined"], + ["arguments", "Arguments"], + "Block", + "class Thing", + "Block", + ["arguments", "(unavailable)"], + ["someHelper", "(optimized away)"], + ...webpackModule(target, "lex-and-nonlex", true /* optimizedOut */), + ["module", "(optimized away)"], + "root()", + ] + ); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { isParcel, maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "lex-and-nonlex", + { line: 3, column: maybeLineStart(4) }, + [ + "Function Body", + target === "rollup" || target === "parcel" ? "class Thing" : "Thing()", + "root", + target === "rollup" || isParcel + ? ["someHelper", "(optimized away)"] + : "someHelper()", + "Module", + "root()", + ] + ); + } +} + +async function testTypescriptClasses(dbg) { + // Typescript is not available on the Babel builds. + for (const target of ["parcel", "webpack3", "webpack4", "rollup"]) { + const { isRollup, isParcel, rollupOptimized } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "typescript-classes", + { line: 50, column: 2 }, + [ + "Module", + "AnotherThing()", + "AppComponent()", + "decoratorFactory(opts)", + rollupOptimized ? ["def", rollupOptimized] : "def()", + rollupOptimized + ? ["ExportedOther", rollupOptimized] + : "ExportedOther()", + rollupOptimized + ? ["ExpressionClass", rollupOptimized] + : "ExpressionClass:Foo()", + "fn(arg)", + // Rollup optimizes out the 'ns' reference here, but when it does, it leave a mapping + // pointed at a location that is super weird, so it ends up being unmapped instead + // be "(optimized out)". + // Parcel converts the "ns;" mapping into a single full-line mapping, for some reason. + // That may have to do with https://github.com/parcel-bundler/parcel/pull/1755#discussion_r205584159 + // though it's not 100% clear. + ["ns", isRollup || isParcel ? "(unmapped)" : "{\u2026}"], + "SubDecl()", + "SubVar:SubExpr()", + ] + ); + } +} + +async function testTypeModule(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "type-module", { line: 7, column: 0 }, [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + "Block", + ["alsoModuleScoped", "2"], + ...webpackModule(target, "type-module", true /* optimizedOut */), + ["module", "(optimized away)"], + ["moduleScoped", "1"], + "thirdModuleScoped()", + ]); + } + + for (const target of [ + "parcel", + "rollup", + "rollup-babel6", + "rollup-babel7", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { maybeLineStart } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "type-module", + { line: 7, column: maybeLineStart(2) }, + [ + "Module", + ["alsoModuleScoped", "2"], + ["moduleScoped", "1"], + "thirdModuleScoped()", + ] + ); + } +} + +async function testTypeScriptCJS(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes( + dbg, + target, + "type-script-cjs", + { line: 7, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + "Block", + "alsoModuleScopes", + + runtimeFunctionName(target, "type-script-cjs"), + ["arguments", "(unavailable)"], + ["exports", "(optimized away)"], + ["module", "(optimized away)"], + "moduleScoped", + "nonModules", + "thirdModuleScoped", + ] + ); + } + + // CJS does not work on Rollup. + for (const target of [ + "parcel", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + await breakpointScopes( + dbg, + target, + "type-script-cjs", + { line: 7, column: 2 }, + [ + "Module", + "alsoModuleScopes", + "moduleScoped", + "nonModules", + "thirdModuleScoped", + ] + ); + } +} + +async function testOutOfOrderDeclarationsCJS(dbg) { + // CJS does not work on Rollup. + for (const target of [ + "parcel", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + await breakpointScopes( + dbg, + target, + "out-of-order-declarations-cjs", + { line: 8, column: 4 }, + [ + "callback", + "fn(inner)", + ["val", "undefined"], + "root", + ["callback", "(optimized away)"], + ["fn", "(optimized away)"], + ["val", "(optimized away)"], + "Module", + + // This value is currently optimized away, which isn't 100% accurate. + // Because import declarations is the last thing in the file, our current + // logic doesn't cover _both_ 'var' statements that it generates, + // making us use the first, optimized-out binding. Given that imports + // are almost never the last thing in a file though, this is probably not + // a huge deal for now. + [ + "aDefault", + target.match(/webpack(3|4)-babel7/) + ? '"a-default"' + : "(optimized away)", + ], + ["root", "(optimized away)"], + ["val", "(optimized away)"], + ] + ); + } +} + +async function testModulesCJS(dbg) { + for (const target of ["webpack3", "webpack4"]) { + await breakpointScopes(dbg, target, "modules-cjs", { line: 7, column: 0 }, [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + "Block", + ["alsoModuleScoped", "2"], + runtimeFunctionName(target, "modules-cjs"), + ["arguments", "(unavailable)"], + ["exports", "(optimized away)"], + ["module", "(optimized away)"], + ["moduleScoped", "1"], + "thirdModuleScoped()", + ]); + } + + // CJS does not work on Rollup. + for (const target of [ + "parcel", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + await breakpointScopes(dbg, target, "modules-cjs", { line: 7, column: 2 }, [ + "Module", + ["alsoModuleScoped", "2"], + ["moduleScoped", "1"], + "thirdModuleScoped()", + ]); + } +} + +async function testWebpackLineMappings(dbg) { + await breakpointScopes( + dbg, + "webpack3", + "webpack-line-mappings", + { line: 11, column: 0 }, + [ + "Block", + ["", '"this-value"'], + ["arg", '"arg-value"'], + ["arguments", "Arguments"], + ["inner", "undefined"], + "Block", + ["someName", "(optimized away)"], + "Block", + ["two", "2"], + "Block", + ["one", "1"], + "root", + ["arguments", "Arguments"], + "fn:someName()", + runtimeFunctionName("webpack3", "webpack-line-mappings"), + ["__webpack_exports__", "(optimized away)"], + ["__WEBPACK_IMPORTED_MODULE_0__src_mod1__", "{\u2026}"], + ["__webpack_require__", "(optimized away)"], + ["arguments", "(unavailable)"], + ["module", "(optimized away)"], + "root()", + ] + ); + + await breakpointScopes( + dbg, + "webpack4", + "webpack-line-mappings", + { line: 11, column: 0 }, + [ + "Block", + ["", '"this-value"'], + ["arg", '"arg-value"'], + ["arguments", "Arguments"], + ["inner", "undefined"], + "Block", + ["someName", "(optimized away)"], + "Block", + ["two", "2"], + "Block", + ["one", "1"], + "root", + ["arguments", "Arguments"], + "fn:someName()", + runtimeFunctionName("webpack4", "webpack-line-mappings"), + ["__webpack_exports__", "(optimized away)"], + ["__webpack_require__", "(optimized away)"], + ["_src_mod1__WEBPACK_IMPORTED_MODULE_0__", "{\u2026}"], + ["arguments", "(unavailable)"], + ["module", "(optimized away)"], + "root()", + ] + ); +} + +async function testWebpackFunctions(dbg) { + for (const target of ["webpack3", "webpack4"]) { + const { defaultExport } = targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "webpack-functions", + { line: 4, column: 0 }, + [ + "Block", + ["", "{\u2026}"], + ["arguments", "Arguments"], + ["x", "4"], + runtimeFunctionName(target, "webpack-functions"), + ["__webpack_exports__", "(optimized away)"], + ["__webpack_require__", "(optimized away)"], + ["arguments", "(unavailable)"], + ["module", "{\u2026}"], + defaultExport("root"), + ] + ); + } +} + +async function testESModules(dbg) { + await breakpointScopes( + dbg, + "webpack3", + "esmodules", + { line: 20, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName("webpack3", "esmodules"), + "__webpack_exports__", + "__WEBPACK_IMPORTED_MODULE_0__src_mod1__", + "__WEBPACK_IMPORTED_MODULE_1__src_mod2__", + "__WEBPACK_IMPORTED_MODULE_10__src_optimized_out__", + "__WEBPACK_IMPORTED_MODULE_2__src_mod3__", + "__WEBPACK_IMPORTED_MODULE_3__src_mod4__", + "__WEBPACK_IMPORTED_MODULE_4__src_mod5__", + "__WEBPACK_IMPORTED_MODULE_5__src_mod6__", + "__WEBPACK_IMPORTED_MODULE_6__src_mod7__", + "__WEBPACK_IMPORTED_MODULE_7__src_mod9__", + "__WEBPACK_IMPORTED_MODULE_8__src_mod10__", + "__WEBPACK_IMPORTED_MODULE_9__src_mod11__", + "__webpack_require__", + "arguments", + "example", + "module", + "root()", + ] + ); + + await breakpointScopes( + dbg, + "webpack4", + "esmodules", + { line: 20, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName("webpack4", "esmodules"), + "__webpack_exports__", + "__webpack_require__", + "_src_mod1__WEBPACK_IMPORTED_MODULE_0__", + "_src_mod10__WEBPACK_IMPORTED_MODULE_8__", + "_src_mod11__WEBPACK_IMPORTED_MODULE_9__", + "_src_mod2__WEBPACK_IMPORTED_MODULE_1__", + "_src_mod3__WEBPACK_IMPORTED_MODULE_2__", + "_src_mod4__WEBPACK_IMPORTED_MODULE_3__", + "_src_mod5__WEBPACK_IMPORTED_MODULE_4__", + "_src_mod6__WEBPACK_IMPORTED_MODULE_5__", + "_src_mod7__WEBPACK_IMPORTED_MODULE_6__", + "_src_mod9__WEBPACK_IMPORTED_MODULE_7__", + "_src_optimized_out__WEBPACK_IMPORTED_MODULE_10__", + "arguments", + "example()", + "module", + "root()", + ] + ); + + for (const target of [ + "parcel", + "rollup", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { defaultExport, webpackImportGetter, maybeLineStart } = + targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "esmodules", + { line: 20, column: maybeLineStart(2) }, + [ + "Module", + ["aDefault", '"a-default"'], + ["aDefault2", '"a-default2"'], + ["aDefault3", '"a-default3"'], + ["anAliased", webpackImportGetter || '"an-original"'], + ["anAliased2", webpackImportGetter || '"an-original2"'], + ["anAliased3", webpackImportGetter || '"an-original3"'], + ["aNamed", webpackImportGetter || '"a-named"'], + ["aNamed2", webpackImportGetter || '"a-named2"'], + ["aNamed3", webpackImportGetter || '"a-named3"'], + ["aNamespace", "{\u2026}"], + ["anotherNamed", webpackImportGetter || '"a-named"'], + ["anotherNamed2", webpackImportGetter || '"a-named2"'], + ["anotherNamed3", webpackImportGetter || '"a-named3"'], + defaultExport("example"), + ["optimizedOut", "(optimized away)"], + "root()", + ] + ); + } + + for (const target of ["rollup-babel6", "rollup-babel7"]) { + // This test currently bails out because Babel does not map function calls + // fully and includes the () of the call in the range of the identifier. + // this means that Rollup, has to map locations for calls to imports, + // it can fail. This will be addressed in Babel eventually. + await breakpointScopes(dbg, target, "esmodules", { line: 20, column: 2 }, [ + "root", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName(target, "esmodules"), + ["aDefault", '"a-default"'], + ["aDefault2", '"a-default2"'], + ["aDefault3", '"a-default3"'], + ["aNamed", '"a-named"'], + ["aNamed$1", "(optimized away)"], + ["aNamed2", '"a-named2"'], + ["aNamed3", '"a-named3"'], + ["aNamespace", "{\u2026}"], + ["arguments", "(unavailable)"], + ["mod4", "(optimized away)"], + ["original", '"an-original"'], + ["original$1", '"an-original2"'], + ["original$2", '"an-original3"'], + "root()", + ]); + } +} + +async function testESModulesCJS(dbg) { + await breakpointScopes( + dbg, + "webpack3", + "esmodules-cjs", + { line: 20, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName("webpack3", "esmodules-cjs"), + "__webpack_exports__", + "__WEBPACK_IMPORTED_MODULE_0__src_mod1__", + "__WEBPACK_IMPORTED_MODULE_1__src_mod2__", + "__WEBPACK_IMPORTED_MODULE_10__src_optimized_out__", + "__WEBPACK_IMPORTED_MODULE_2__src_mod3__", + "__WEBPACK_IMPORTED_MODULE_3__src_mod4__", + "__WEBPACK_IMPORTED_MODULE_4__src_mod5__", + "__WEBPACK_IMPORTED_MODULE_5__src_mod6__", + "__WEBPACK_IMPORTED_MODULE_6__src_mod7__", + "__WEBPACK_IMPORTED_MODULE_7__src_mod9__", + "__WEBPACK_IMPORTED_MODULE_8__src_mod10__", + "__WEBPACK_IMPORTED_MODULE_9__src_mod11__", + "__webpack_require__", + "arguments", + "example", + "module", + "root()", + ] + ); + + await breakpointScopes( + dbg, + "webpack4", + "esmodules-cjs", + { line: 20, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName("webpack4", "esmodules-cjs"), + "__webpack_exports__", + "__webpack_require__", + "_src_mod1__WEBPACK_IMPORTED_MODULE_0__", + "_src_mod10__WEBPACK_IMPORTED_MODULE_8__", + "_src_mod11__WEBPACK_IMPORTED_MODULE_9__", + "_src_mod2__WEBPACK_IMPORTED_MODULE_1__", + "_src_mod3__WEBPACK_IMPORTED_MODULE_2__", + "_src_mod4__WEBPACK_IMPORTED_MODULE_3__", + "_src_mod5__WEBPACK_IMPORTED_MODULE_4__", + "_src_mod6__WEBPACK_IMPORTED_MODULE_5__", + "_src_mod7__WEBPACK_IMPORTED_MODULE_6__", + "_src_mod9__WEBPACK_IMPORTED_MODULE_7__", + "_src_optimized_out__WEBPACK_IMPORTED_MODULE_10__", + "arguments", + "example()", + "module", + "root()", + ] + ); + + // CJS does not work on Rollup. + for (const target of [ + "parcel", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + await breakpointScopes( + dbg, + target, + "esmodules-cjs", + { line: 20, column: 2 }, + [ + "Module", + ["aDefault", '"a-default"'], + ["aDefault2", '"a-default2"'], + ["aDefault3", '"a-default3"'], + ["anAliased", '"an-original"'], + ["anAliased2", '"an-original2"'], + ["anAliased3", '"an-original3"'], + ["aNamed", '"a-named"'], + ["aNamed2", '"a-named2"'], + ["aNamed3", '"a-named3"'], + ["aNamespace", "{\u2026}"], + ["anotherNamed", '"a-named"'], + ["anotherNamed2", '"a-named2"'], + ["anotherNamed3", '"a-named3"'], + ["example", "(optimized away)"], + ["optimizedOut", "(optimized away)"], + "root()", + ] + ); + } +} + +async function testESModulesES6(dbg) { + await breakpointScopes( + dbg, + "webpack3", + "esmodules-es6", + { line: 20, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName("webpack3", "esmodules-es6"), + "__webpack_exports__", + "__WEBPACK_IMPORTED_MODULE_0__src_mod1__", + "__WEBPACK_IMPORTED_MODULE_1__src_mod2__", + "__WEBPACK_IMPORTED_MODULE_10__src_optimized_out__", + "__WEBPACK_IMPORTED_MODULE_2__src_mod3__", + "__WEBPACK_IMPORTED_MODULE_3__src_mod4__", + "__WEBPACK_IMPORTED_MODULE_4__src_mod5__", + "__WEBPACK_IMPORTED_MODULE_5__src_mod6__", + "__WEBPACK_IMPORTED_MODULE_6__src_mod7__", + "__WEBPACK_IMPORTED_MODULE_7__src_mod9__", + "__WEBPACK_IMPORTED_MODULE_8__src_mod10__", + "__WEBPACK_IMPORTED_MODULE_9__src_mod11__", + "__webpack_require__", + "arguments", + "example", + "module", + "root()", + ] + ); + + await breakpointScopes( + dbg, + "webpack4", + "esmodules-es6", + { line: 20, column: 0 }, + [ + "Block", + ["", "Window"], + ["arguments", "Arguments"], + runtimeFunctionName("webpack4", "esmodules-es6"), + "__webpack_exports__", + "__webpack_require__", + "_src_mod1__WEBPACK_IMPORTED_MODULE_0__", + "_src_mod10__WEBPACK_IMPORTED_MODULE_8__", + "_src_mod11__WEBPACK_IMPORTED_MODULE_9__", + "_src_mod2__WEBPACK_IMPORTED_MODULE_1__", + "_src_mod3__WEBPACK_IMPORTED_MODULE_2__", + "_src_mod4__WEBPACK_IMPORTED_MODULE_3__", + "_src_mod5__WEBPACK_IMPORTED_MODULE_4__", + "_src_mod6__WEBPACK_IMPORTED_MODULE_5__", + "_src_mod7__WEBPACK_IMPORTED_MODULE_6__", + "_src_mod9__WEBPACK_IMPORTED_MODULE_7__", + "_src_optimized_out__WEBPACK_IMPORTED_MODULE_10__", + "arguments", + "example()", + "module", + "root()", + ] + ); + + for (const target of [ + "parcel", + "rollup", + "webpack3-babel6", + "webpack3-babel7", + "webpack4-babel6", + "webpack4-babel7", + ]) { + const { defaultExport, webpack4ImportGetter, maybeLineStart } = + targetToFlags(target); + + await breakpointScopes( + dbg, + target, + "esmodules-es6", + { line: 20, column: maybeLineStart(2) }, + [ + "Module", + ["aDefault", '"a-default"'], + ["aDefault2", '"a-default2"'], + ["aDefault3", '"a-default3"'], + ["anAliased", webpack4ImportGetter || '"an-original"'], + ["anAliased2", webpack4ImportGetter || '"an-original2"'], + ["anAliased3", webpack4ImportGetter || '"an-original3"'], + ["aNamed", webpack4ImportGetter || '"a-named"'], + ["aNamed2", webpack4ImportGetter || '"a-named2"'], + ["aNamed3", webpack4ImportGetter || '"a-named3"'], + ["aNamespace", "{\u2026}"], + ["anotherNamed", webpack4ImportGetter || '"a-named"'], + ["anotherNamed2", webpack4ImportGetter || '"a-named2"'], + ["anotherNamed3", webpack4ImportGetter || '"a-named3"'], + defaultExport("example"), + ["optimizedOut", "(optimized away)"], + "root()", + ] + ); + } + + for (const target of ["rollup-babel6", "rollup-babel7"]) { + // This test currently bails out because Babel does not map function calls + // fully and includes the () of the call in the range of the identifier. + // this means that Rollup, has to map locations for calls to imports, + // it can fail. This will be addressed in Babel eventually. + await breakpointScopes( + dbg, + target, + "esmodules-es6", + { line: 20, column: 2 }, + [ + "root", + ["", "Window"], + ["arguments", "Arguments"], + + "Block", + ["aNamed", '"a-named"'], + ["aNamed$1", "undefined"], + ["aNamed2", '"a-named2"'], + ["aNamed3", '"a-named3"'], + ["original", '"an-original"'], + ["original$1", '"an-original2"'], + ["original$2", '"an-original3"'], + + runtimeFunctionName(target, "esmodules-es6"), + ["aDefault", '"a-default"'], + ["aDefault2", '"a-default2"'], + ["aDefault3", '"a-default3"'], + + ["aNamespace", "{\u2026}"], + ["arguments", "(unavailable)"], + ["mod4", "(optimized away)"], + "root()", + ] + ); + } +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-stepping.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-stepping.js new file mode 100644 index 0000000000..99d0dde458 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-stepping.js @@ -0,0 +1,158 @@ +/* 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 . */ + +"use strict"; + +// Tests for stepping through Babel's compile output. +requestLongerTimeout(4); + +add_task(async function () { + const dbg = await initDebugger("doc-sourcemapped.html"); + + await testStepOverForOf(dbg); + await testStepOverForOfArray(dbg); + await testStepOveForOfClosure(dbg); + await testStepOverForOfArrayClosure(dbg); + await testStepOverFunctionParams(dbg); + await testStepOverRegeneratorAwait(dbg); +}); + +async function breakpointSteps(dbg, target, fixture, { line, column }, steps) { + const filename = `${target}://./${fixture}/input.`; + const fnName = `${target}-${fixture}`.replace(/-([a-z])/g, (s, c) => + c.toUpperCase() + ); + + await invokeWithBreakpoint( + dbg, + fnName, + filename, + { line, column }, + async source => { + await runSteps(dbg, source, steps); + } + ); + + ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`); +} + +async function runSteps(dbg, source, steps) { + for (const [i, [type, position]] of steps.entries()) { + info(`Step ${i}`); + switch (type) { + case "stepOver": + await stepOver(dbg); + break; + case "stepIn": + await stepIn(dbg); + break; + default: + throw new Error("Unknown stepping type"); + } + + assertPausedAtSourceAndLine(dbg, source.id, position.line, position.column); + } +} + +function testStepOverForOf(dbg) { + return breakpointSteps( + dbg, + "webpack3-babel6", + "step-over-for-of", + { line: 4, column: 2 }, + [ + ["stepOver", { line: 6, column: 20 }], + ["stepOver", { line: 6, column: 2 }], + ["stepOver", { line: 7, column: 4 }], + ["stepOver", { line: 6, column: 2 }], + ["stepOver", { line: 7, column: 4 }], + ["stepOver", { line: 6, column: 2 }], + ["stepOver", { line: 10, column: 2 }], + ] + ); +} + +// This codifies the current behavior, but stepping twice over the for +// header isn't ideal. +function testStepOverForOfArray(dbg) { + return breakpointSteps( + dbg, + "webpack3-babel6", + "step-over-for-of-array", + { line: 3, column: 2 }, + [ + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 13 }], + ["stepOver", { line: 6, column: 4 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 13 }], + ["stepOver", { line: 6, column: 4 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 9, column: 2 }], + ] + ); +} + +// The closure means it isn't actually possible to step into the for body, +// and Babel doesn't map the _loop() call, so we step past it automatically. +function testStepOveForOfClosure(dbg) { + return breakpointSteps( + dbg, + "webpack3-babel6", + "step-over-for-of-closure", + { line: 6, column: 2 }, + [ + ["stepOver", { line: 8, column: 20 }], + ["stepOver", { line: 8, column: 2 }], + ["stepOver", { line: 12, column: 2 }], + ] + ); +} + +// Same as the previous, not possible to step into the body. The less +// complicated array logic makes it possible to step into the header at least, +// but this does end up double-visiting the for head. +function testStepOverForOfArrayClosure(dbg) { + return breakpointSteps( + dbg, + "webpack3-babel6", + "step-over-for-of-array-closure", + { line: 3, column: 2 }, + [ + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 13 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 5, column: 13 }], + ["stepOver", { line: 5, column: 2 }], + ["stepOver", { line: 9, column: 2 }], + ] + ); +} + +function testStepOverFunctionParams(dbg) { + return breakpointSteps( + dbg, + "webpack3-babel6", + "step-over-function-params", + { line: 6, column: 2 }, + [ + ["stepOver", { line: 7, column: 2 }], + ["stepIn", { line: 2, column: 2 }], + ] + ); +} + +function testStepOverRegeneratorAwait(dbg) { + return breakpointSteps( + dbg, + "webpack3-babel6", + "step-over-regenerator-await", + { line: 2, column: 2 }, + [ + // Won't work until a fix to regenerator lands and we rebuild. + // https://github.com/facebook/regenerator/issues/342 + // ["stepOver", { line: 4, column: 2 }], + ] + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-toggle.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-toggle.js new file mode 100644 index 0000000000..270375a87c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemapped-toggle.js @@ -0,0 +1,48 @@ +/* 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 . */ + +"use strict"; + +// Tests for preview through Babel's compile output. +requestLongerTimeout(5); + +// Test pausing with mapScopes enabled and disabled +add_task(async function () { + const dbg = await initDebugger("doc-sourcemapped.html"); + dbg.actions.toggleMapScopes(); + + info("1. Pause on line 20"); + const url = "webpack3-babel6://./esmodules-cjs/input.js"; + await waitForSources(dbg, url); + const source = findSource(dbg, url); + await selectSource(dbg, source); + await addBreakpoint(dbg, source, 20, 2); + invokeInTab("webpack3Babel6EsmodulesCjs"); + await waitForPaused(dbg); + + info("2. Hover on a token with mapScopes enabled"); + await previewToken(dbg, 20, 16, '"a-default"'); + ok(getOriginalScope(dbg) != null, "Scopes are mapped"); + + info("3. Hover on a token with mapScopes disabled"); + clickElement(dbg, "mapScopesCheckbox"); + await previewToken(dbg, 21, 16, "undefined"); + + info("4. StepOver with mapScopes disabled"); + await stepOver(dbg); + await previewToken(dbg, 20, 16, "undefined"); + ok(getOriginalScope(dbg) == null, "Scopes are not mapped"); +}); + +function getOriginalScope(dbg) { + return dbg.selectors.getSelectedOriginalScope( + dbg.selectors.getCurrentThread() + ); +} + +async function previewToken(dbg, line, column, value) { + const previewEl = await tryHovering(dbg, line, column, "previewPopup"); + is(previewEl.innerText, value); + dbg.actions.clearPreview(getContext(dbg)); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-bogus.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-bogus.js new file mode 100644 index 0000000000..b51056bba7 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-bogus.js @@ -0,0 +1,63 @@ +/* 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 . */ + +// Test that errors while loading sourcemap does not break debugging. + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + + // - non-existant-map.js has a reference to source map file which doesn't exists. + // There is no particular warning and no original file is displayed, only the generated file. + // - map-with-failed-original-request.js has a reference to a valid source map file, + // but the map doesn't inline source content and refers to a URL which fails loading. + // (this file is based on dom-mutation.js and related map) + const dbg = await initDebugger( + "doc-sourcemap-bogus.html", + "non-existant-map.js", + "map-with-failed-original-request.js", + "map-with-failed-original-request.original.js" + ); + // Make sure there is only the expected sources and we miss some original sources. + is(dbg.selectors.getSourceCount(), 3, "Only 3 source exists"); + + await selectSource(dbg, "non-existant-map.js"); + + // We should still be able to set breakpoints and pause in the + // generated source. + await addBreakpoint(dbg, "non-existant-map.js", 4); + invokeInTab("runCode"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "non-existant-map.js").id, + 4 + ); + await resume(dbg); + + await selectSource(dbg, "map-with-failed-original-request.js"); + await addBreakpoint(dbg, "map-with-failed-original-request.js", 7); + invokeInTab("changeStyleAttribute"); + await waitForPaused(dbg); + + // As the original file can't be loaded, the generated source is automatically selected + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "map-with-failed-original-request.js").id, + 7 + ); + + // The original file is visible in the source tree and can be selected, + // but its content can't be displayed + await selectSource(dbg, "map-with-failed-original-request.original.js"); + is( + getCM(dbg).getValue(), + `Error while fetching an original source: request failed with status 404\nSource URL: ${EXAMPLE_URL}map-with-failed-original-request.original.js` + ); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-breakpoints.js new file mode 100644 index 0000000000..b1ae737f35 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-breakpoints.js @@ -0,0 +1,35 @@ +/* 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 . */ + +// Tests setting breakpoints in an original file and +// removing it in the generated file. + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger("doc-sourcemaps.html", "entry.js"); + + ok(true, "Original sources exist"); + + await selectSource(dbg, "entry.js"); + + await clickGutter(dbg, 9); + await waitForBreakpointCount(dbg, 1); + await assertBreakpoint(dbg, 9); + assertBreakpointSnippet(dbg, 3, "output(times2(3));"); + + await selectSource(dbg, "bundle.js"); + await assertBreakpoint(dbg, 55); + assertBreakpointSnippet(dbg, 3, "output(times2(3));"); + + await clickGutter(dbg, 55); + await waitForBreakpointCount(dbg, 0); + await assertNoBreakpoint(dbg, 55); + + await selectSource(dbg, "entry.js"); + await assertNoBreakpoint(dbg, 9); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-disabled.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-disabled.js new file mode 100644 index 0000000000..93440dc9e1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-disabled.js @@ -0,0 +1,23 @@ +/* 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 . */ + +// Tests loading and pretty printing bundles with sourcemaps disabled + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + await pushPref("devtools.source-map.client-service.enabled", false); + const dbg = await initDebugger("doc-sourcemaps.html"); + + await waitForSources(dbg, "bundle.js"); + const bundleSrc = findSource(dbg, "bundle.js"); + + info("Pretty print the bundle"); + await selectSource(dbg, bundleSrc); + clickElement(dbg, "prettyPrintButton"); + await waitForSelectedSource(dbg, "bundle.js:formatted"); + ok(true, "everything finished"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-ignorelist.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-ignorelist.js new file mode 100644 index 0000000000..9278ccf08e --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-ignorelist.js @@ -0,0 +1,75 @@ +/* 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 . */ + +// Tests that sources on the sourcemaps ignore list are ignored on debugger load +// when the 'Ignore Known Third-party Scripts' setting is enabled. + +"use strict"; + +add_task(async function () { + const sources = [ + "bundle.js", + "original-1.js", + "original-2.js", + "original-3.js", + "original-4.js", + "original-5.js", + ]; + const dbg = await initDebugger("doc-sourcemaps-ignorelist.html", ...sources); + + info("Click the settings menu to ignore the third party scripts"); + await toggleDebbuggerSettingsMenuItem(dbg, { + className: ".debugger-settings-menu-item-enable-sourcemap-ignore-list", + isChecked: false, + }); + await waitForDispatch(dbg.store, "ENABLE_SOURCEMAP_IGNORELIST"); + + info( + "Reload to hit breakpoints in the original-2.js and original-3.js files" + ); + const onReloaded = reload(dbg, ...sources); + await waitForPaused(dbg); + + info("Assert paused in original-2.js as original-1.js is ignored"); + const original2Source = findSource(dbg, "original-2.js"); + assertPausedAtSourceAndLine(dbg, original2Source.id, 2); + + await resume(dbg); + await waitForPaused(dbg); + + info("Assert paused in original-4.js as original-3.js is ignored"); + const original4Source = findSource(dbg, "original-4.js"); + assertPausedAtSourceAndLine(dbg, original4Source.id, 2); + + await resume(dbg); + await onReloaded; + + info("Click the settings menu to stop ignoring the third party scripts"); + await toggleDebbuggerSettingsMenuItem(dbg, { + className: ".debugger-settings-menu-item-enable-sourcemap-ignore-list", + isChecked: true, + }); + await waitForDispatch(dbg.store, "ENABLE_SOURCEMAP_IGNORELIST"); + + info("Reload to hit breakpoints in all the original-[x].js files"); + const onReloaded2 = reload(dbg, "original-1.js"); + await waitForPaused(dbg); + + info("Assert paused in original-1.js as it is no longer ignored"); + const original1Source = findSource(dbg, "original-1.js"); + assertPausedAtSourceAndLine(dbg, original1Source.id, 2); + + const originalSources = ["original-2.js", "original-3.js", "original-4.js"]; + for (const fileName of originalSources) { + await resume(dbg); + await waitForPaused(dbg); + + const originalSource = findSource(dbg, fileName); + assertPausedAtSourceAndLine(dbg, originalSource.id, 2); + } + await resume(dbg); + + await onReloaded2; + await dbg.toolbox.closeToolbox(); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-indexed.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-indexed.js new file mode 100644 index 0000000000..1fad8696c1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-indexed.js @@ -0,0 +1,51 @@ +/* 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 . */ + +// Tests loading sourcemapped sources, setting breakpoints, and +// stepping in them. + +"use strict"; + +requestLongerTimeout(2); + +// This source map does not have source contents, so it's fetched separately +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger( + "doc-sourcemaps-indexed.html", + "main.js", + "main.min.js" + ); + const { + selectors: { getBreakpoint, getBreakpointCount }, + } = dbg; + + ok(true, "Original sources exist"); + const mainSrc = findSource(dbg, "main.js"); + + await selectSource(dbg, mainSrc); + + // Test that breakpoint is not off by a line. + await addBreakpoint(dbg, mainSrc, 4, 2); + is(getBreakpointCount(), 1, "One breakpoint exists"); + ok( + getBreakpoint(createLocation({ source: mainSrc, line: 4, column: 2 })), + "Breakpoint has correct line" + ); + + await assertBreakpoint(dbg, 4); + invokeInTab("logMessage"); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, mainSrc.id, 4, 2); + + // Tests the existence of the sourcemap link in the original source. + ok(findElement(dbg, "sourceMapLink"), "Sourcemap link in original source"); + await selectSource(dbg, "main.min.js"); + + ok( + !findElement(dbg, "sourceMapLink"), + "No Sourcemap link exists in generated source" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-redirect.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-redirect.js new file mode 100644 index 0000000000..0efa6b25f1 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-redirect.js @@ -0,0 +1,54 @@ +/* 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 . */ + +// Test the redirects on the sourceMappingURL are blocked and not followed. + +"use strict"; + +const httpServer = createTestHTTPServer(); +const BASE_URL = `http://localhost:${httpServer.identity.primaryPort}`; + +httpServer.registerContentType("html", "text/html"); +httpServer.registerContentType("js", "application/javascript"); + +httpServer.registerPathHandler("/index.html", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write(` + + + + + + `); +}); + +httpServer.registerPathHandler("/redirect", (request, response) => { + response.setStatusLine(request.httpVersion, 301, "Moved Permanently"); + response.setHeader("Location", `${BASE_URL}/evil`); +}); + +httpServer.registerPathHandler("/evil", (request, response) => { + response.setStatusLine(request.httpVersion, 200, "OK"); + response.write( + `{"version":3,"sources":["evil.original.js"],"names":[], "mappings": ""}` + ); +}); + +add_task(async function () { + const dbg = await initDebuggerWithAbsoluteURL(`${BASE_URL}/index.html`); + + await getDebuggerSplitConsole(dbg); + await hasConsoleMessage(dbg, "Source map error"); + const { value } = await findConsoleMessage(dbg, "Source map error"); + + is( + value, + `Source map error: Error: NetworkError when attempting to fetch resource.\nResource URL: ${BASE_URL}/index.html\nSource Map URL: ${BASE_URL}/redirect[Learn More]`, + "A source map error message is logged indicating the redirect failed" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading-quickly.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading-quickly.js new file mode 100644 index 0000000000..ed2a3ceeac --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading-quickly.js @@ -0,0 +1,28 @@ +/* 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 . */ + +/* + * Test reloading an original file while the sourcemap is loading. + * The test passes when the selected source is visible after two reloads. + */ + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-sourcemaps.html"); + + await waitForSources(dbg, "entry.js"); + await selectSource(dbg, "entry.js"); + + await reload(dbg); + await waitForSources(dbg, "bundle.js"); + + await reload(dbg); + await waitForLoadedSource(dbg, "entry.js"); + + ok( + getCM(dbg).getValue().includes("window.keepMeAlive"), + "Original source text loaded correctly" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading.js new file mode 100644 index 0000000000..2931087d1d --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps-reloading.js @@ -0,0 +1,62 @@ +/* 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 . */ + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger("doc-sourcemaps.html"); + const { + selectors: { getBreakpoint, getBreakpointCount }, + } = dbg; + + await waitForSources(dbg, "entry.js", "output.js", "times2.js", "opts.js"); + ok(true, "Original sources exist"); + const entrySrc = findSource(dbg, "entry.js"); + + await selectSource(dbg, entrySrc); + ok( + getCM(dbg).getValue().includes("window.keepMeAlive"), + "Original source text loaded correctly" + ); + + await addBreakpoint(dbg, entrySrc, 5); + await addBreakpoint(dbg, entrySrc, 15, 0); + await disableBreakpoint(dbg, entrySrc, 15, 0); + + // Test reloading the debugger + const onReloaded = reload(dbg, "opts.js"); + await waitForDispatch(dbg.store, "LOAD_ORIGINAL_SOURCE_TEXT"); + + await waitForPaused(dbg); + await waitForDispatch(dbg.store, "ADD_INLINE_PREVIEW"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "entry.js").id, 5); + + await waitForBreakpointCount(dbg, 2); + is(getBreakpointCount(), 2, "Two breakpoints exist"); + + const bp = getBreakpoint( + createLocation({ + source: entrySrc, + line: 15, + column: 0, + }) + ); + ok(bp, "Breakpoint is on the correct line"); + ok(bp.disabled, "Breakpoint is disabled"); + await assertBreakpoint(dbg, 15); + + await resume(dbg); + info("Wait for reload to complete after resume"); + await onReloaded; +}); + +async function waitForBreakpointCount(dbg, count) { + return waitForState( + dbg, + state => dbg.selectors.getBreakpointCount() === count + ); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js new file mode 100644 index 0000000000..610cfd88f3 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js @@ -0,0 +1,124 @@ +/* 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 . */ + +// Tests loading sourcemapped sources, setting breakpoints, and +// stepping in them. + +"use strict"; + +requestLongerTimeout(2); + +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger( + "doc-sourcemaps.html", + "entry.js", + "output.js", + "times2.js", + "opts.js" + ); + const { + selectors: { getBreakpointCount }, + } = dbg; + + // Check that the original sources appear in the source tree + info("Before opening the page directory, no source are displayed"); + await waitForSourcesInSourceTree(dbg, [], { noExpand: true }); + await clickElement(dbg, "sourceDirectoryLabel", 4); + info( + "After opening the page directory, only all original sources (entry, output, time2, opts). (bundle is still hidden)" + ); + await waitForSourcesInSourceTree( + dbg, + ["entry.js", "output.js", "times2.js", "opts.js"], + { noExpand: true } + ); + info("Expand the page folder and assert that the bundle appears"); + await clickElement(dbg, "sourceDirectoryLabel", 3); + await clickElement(dbg, "sourceDirectoryLabel", 4); + await waitForSourcesInSourceTree( + dbg, + ["entry.js", "output.js", "times2.js", "opts.js", "bundle.js"], + { noExpand: true } + ); + + const bundleSrc = findSource(dbg, "bundle.js"); + await selectSource(dbg, bundleSrc); + + await clickGutter(dbg, 70); + await waitForBreakpointCount(dbg, 1); + await assertBreakpoint(dbg, 70); + + await clickGutter(dbg, 70); + await waitForBreakpointCount(dbg, 0); + + const entrySrc = findSource(dbg, "entry.js"); + + await selectSource(dbg, entrySrc); + ok( + getCM(dbg).getValue().includes("window.keepMeAlive"), + "Original source text loaded correctly" + ); + + // Bug 1824375 - pending location shouldn't be location and include only url, line and column attributes + let pendingSelectedLocation = Services.prefs.getStringPref( + "devtools.debugger.pending-selected-location" + ); + is( + pendingSelectedLocation, + JSON.stringify({ url: entrySrc.url, line: 0, column: undefined }), + "Pending selected location is the expected one" + ); + + // Test breaking on a breakpoint + await addBreakpoint(dbg, "entry.js", 15); + is(getBreakpointCount(), 1, "One breakpoint exists"); + assertBreakpointExists(dbg, entrySrc, 15); + + invokeInTab("keepMeAlive"); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, entrySrc.id, 15); + + await stepIn(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "times2.js").id, 2); + + await stepOver(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "times2.js").id, 3); + + await stepOut(dbg); + assertPausedAtSourceAndLine(dbg, entrySrc.id, 16); + + pendingSelectedLocation = Services.prefs.getStringPref( + "devtools.debugger.pending-selected-location" + ); + is( + pendingSelectedLocation, + JSON.stringify({ url: entrySrc.url, line: 16, column: 0 }), + "Pending selected location is the expected one" + ); + + info("Click on jump to generated source link from editor's footer"); + findElement(dbg, "sourceMapLink").click(); + + await waitForSelectedSource(dbg, bundleSrc); + assertPausedAtSourceAndLine(dbg, bundleSrc.id, 62); +}); + +function assertBreakpointExists(dbg, source, line) { + const { + selectors: { getBreakpoint }, + } = dbg; + + ok( + getBreakpoint(createLocation({ source, line })), + "Breakpoint has correct line" + ); +} + +async function waitForBreakpointCount(dbg, count) { + const { + selectors: { getBreakpointCount }, + } = dbg; + await waitForState(dbg, state => getBreakpointCount() == count); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps2.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps2.js new file mode 100644 index 0000000000..e1e05ba11a --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps2.js @@ -0,0 +1,51 @@ +/* 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 . */ + +// Tests loading sourcemapped sources, setting breakpoints, and +// stepping in them. + +"use strict"; + +requestLongerTimeout(2); + +// This source map does not have source contents, so it's fetched separately +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger( + "doc-sourcemaps2.html", + "main.js", + "main.min.js" + ); + const { + selectors: { getBreakpoint, getBreakpointCount }, + } = dbg; + + ok(true, "Original sources exist"); + const mainSrc = findSource(dbg, "main.js"); + + await selectSource(dbg, mainSrc); + + // Test that breakpoint is not off by a line. + await addBreakpoint(dbg, mainSrc, 4, 2); + is(getBreakpointCount(), 1, "One breakpoint exists"); + ok( + getBreakpoint(createLocation({ source: mainSrc, line: 4, column: 2 })), + "Breakpoint has correct line" + ); + + await assertBreakpoint(dbg, 4); + invokeInTab("logMessage"); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, mainSrc.id, 4); + + // Tests the existence of the sourcemap link in the original source. + ok(findElement(dbg, "sourceMapLink"), "Sourcemap link in original source"); + await selectSource(dbg, "main.min.js"); + + ok( + !findElement(dbg, "sourceMapLink"), + "No Sourcemap link exists in generated source" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps3.js b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps3.js new file mode 100644 index 0000000000..1ab8ea849c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps3.js @@ -0,0 +1,65 @@ +/* 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 . */ + +"use strict"; + +// Tests loading sourcemapped sources, setting breakpoints, and +// inspecting restored scopes. +requestLongerTimeout(2); + +// This source map does not have source contents, so it's fetched separately +add_task(async function () { + // NOTE: the CORS call makes the test run times inconsistent + const dbg = await initDebugger( + "doc-sourcemaps3.html", + "bundle.js", + "sorted.js", + "test.js" + ); + dbg.actions.toggleMapScopes(); + + ok(true, "Original sources exist"); + const sortedSrc = findSource(dbg, "sorted.js"); + + await selectSource(dbg, sortedSrc); + + // Test that breakpoint is not off by a line. + await addBreakpoint(dbg, sortedSrc, 9, 4); + is(dbg.selectors.getBreakpointCount(), 1, "One breakpoint exists"); + ok( + dbg.selectors.getBreakpoint( + createLocation({ source: sortedSrc, line: 9, column: 4 }) + ), + "Breakpoint has correct line" + ); + + invokeInTab("test"); + + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, sortedSrc.id, 9, 4); + + is(getScopeLabel(dbg, 1), "Block"); + is(getScopeLabel(dbg, 2), "na"); + is(getScopeLabel(dbg, 3), "nb"); + + is(getScopeLabel(dbg, 4), "Function Body"); + + await toggleScopeNode(dbg, 4); + + is(getScopeLabel(dbg, 5), "ma"); + is(getScopeLabel(dbg, 6), "mb"); + + await toggleScopeNode(dbg, 7); + + is(getScopeLabel(dbg, 8), "a"); + is(getScopeLabel(dbg, 9), "b"); + + is(getScopeLabel(dbg, 10), "Module"); + + await toggleScopeNode(dbg, 10); + + is(getScopeLabel(dbg, 11), "binaryLookup:o(n, e, r)"); + is(getScopeLabel(dbg, 12), "comparer:t(n, e)"); + is(getScopeLabel(dbg, 13), "fancySort"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-sources-project-search.js b/devtools/client/debugger/test/mochitest/browser_dbg-sources-project-search.js new file mode 100644 index 0000000000..c5733d5a97 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-sources-project-search.js @@ -0,0 +1,134 @@ +/* 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 . */ + +// Testing project search for various types of sources scenarios + +"use strict"; + +requestLongerTimeout(3); + +// Tests for searching in dynamic (without urls) sources +add_task(async function testSearchDynamicScripts() { + const dbg = await initDebugger("doc-minified.html"); + + const executeComplete = dbg.commands.scriptCommand.execute( + `const foo = 5; debugger; console.log(foo)` + ); + await waitForPaused(dbg); + + await openProjectSearch(dbg); + const fileResults = await doProjectSearch(dbg, "foo"); + ok( + /source\d+/g.test(fileResults[0].innerText), + "The search result was found in the eval script." + ); + + await resume(dbg); + await executeComplete; +}); + +// Tests that minified sources are ignored when the prettyfied versions +// exist. +add_task(async function testIgnoreMinifiedSourceForPrettySource() { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + + await openProjectSearch(dbg); + let fileResults = await doProjectSearch(dbg, "stuff"); + + is(fileResults.length, 1, "Only the result was found"); + ok( + fileResults[0].innerText.includes("pretty.js\n(1 match)"), + "The search result was found in the minified (pretty.js) source" + ); + + await selectSource(dbg, "pretty.js"); + await waitForSelectedSource(dbg, "pretty.js"); + + info("Pretty print the source"); + await prettyPrint(dbg); + + fileResults = await doProjectSearch(dbg, "stuff"); + + is( + fileResults.length, + 2, + "Two results were found form both the pretty and minified sources" + ); + ok( + fileResults[0].innerText.includes("pretty.js:formatted\n(1 match)"), + "The first search result was found in the prettyified (pretty.js:formatted) source" + ); + + ok( + fileResults[1].innerText.includes("pretty.js\n(1 match)"), + "The second search result was found in the minified (pretty.js) source" + ); +}); + +// Test the prioritization of files. Original files should be prioritized over +// generated files and third-party files should be deprioritized and load after the others. +add_task(async function testFilesPrioritization() { + const dbg = await initDebugger("doc-react.html", "App.js"); + + await openProjectSearch(dbg); + const fileResults = await doProjectSearch(dbg, "componentDidMount"); + + is(getExpandedResultsCount(dbg), 13); + + ok( + fileResults[0].innerText.includes( + "browser/devtools/client/debugger/test/mochitest/examples/react/build/App.js" + ), + "The first item should be the original (prettified) file" + ); + + ok( + findAllElements(dbg, "projectSearchExpandedResults")[0].innerText.endsWith( + "componentDidMount() {" + ), + "The first result match in the original file is correct" + ); + + const firstNodeModulesResultFound = [...fileResults].findIndex(el => + el.innerText.includes("node_modules") + ); + is( + firstNodeModulesResultFound, + 2, + "The node_modules (third-party) file is the last result in the list" + ); +}); + +add_task(async function testBlackBoxedSources() { + const dbg = await initDebugger("doc-pretty.html", "pretty.js"); + await selectSource(dbg, "pretty.js"); + + info("Blackbox pretty.js"); + await toggleBlackbox(dbg); + + await openProjectSearch(dbg); + let fileResults = await doProjectSearch(dbg, "stuff"); + + is(fileResults.length, 0, "No results were found as pretty.js is blackboxed"); + + info("Unblackbox pretty.js"); + await toggleBlackbox(dbg); + + await openProjectSearch(dbg); + fileResults = await doProjectSearch(dbg, "stuff"); + + is( + fileResults.length, + 1, + "One result was found as pretty.js is no longer blackboxed" + ); +}); + +async function toggleBlackbox(dbg) { + await clickElement(dbg, "blackbox"); + await Promise.any([ + waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"), + waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"), + ]); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js b/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js new file mode 100644 index 0000000000..0dc81605c9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js @@ -0,0 +1,164 @@ +/* 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 . */ + +"use strict"; + +// Tests that breakpoint panels open when their relevant breakpoint is hit +add_task(async function testBreakpointsPaneOpenOnPause() { + const dbg = await initDebugger("doc-sources.html", "simple1.js"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + info("Confirm the breakpoints pane is closed"); + is(getPaneElements(dbg).length, 1); + + await selectSource(dbg, "simple1.js"); + await addBreakpoint(dbg, "simple1.js", 4); + + info("Trigger the breakpoint ane ensure we're paused"); + invokeInTab("main"); + await waitForPaused(dbg, "simple1.js"); + + info("Confirm the breakpoints pane is open"); + is(getPaneElements(dbg).length, 2); + + await resume(dbg); + + info("Confirm the breakpoints pane is closed again"); + is(getPaneElements(dbg).length, 1); +}); + +// Tests that the breakpoint pane remains closed on stepping +add_task(async function testBreakpointsPanePersistOnStepping() { + const dbg = await initDebugger("doc-scripts.html", "simple3.js"); + + await selectSource(dbg, "simple3.js"); + await addBreakpoint(dbg, "simple3.js", 9); + await waitForBreakpoint(dbg, "simple3.js", 9); + + invokeInTab("nestedA"); + await waitForPaused(dbg, "simple3.js"); + + is(getPaneElements(dbg).length, 2, "Breakpoints pane is open"); + + info("Close breakpoints pane"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is closed"); + + info("Step into nestedB"); + + await stepIn(dbg); + + ok(isFrameSelected(dbg, 1, "nestedB"), "nestedB frame is selected"); + + is(getPaneElements(dbg).length, 1, "Breakpoints pane is still closed"); +}); + +// Tests that the breakpoint pane remains closed on call-stack frame selection +add_task(async function testBreakpointsPanePersistOnFrameSelection() { + const dbg = await initDebugger("doc-scripts.html", "simple3.js"); + + info("Close breakpoints pane"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is closed"); + + await selectSource(dbg, "simple3.js"); + await addBreakpoint(dbg, "simple3.js", 13); + await waitForBreakpoint(dbg, "simple3.js", 13); + + invokeInTab("nestedA"); + await waitForPaused(dbg, "simple3.js"); + + is(getPaneElements(dbg).length, 2, "Breakpoints pane is open"); + + info("Close breakpoints pane"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is closed"); + + is(getFrameList(dbg).length, 2, "Call stack has two frames"); + + info("Click on second call stack frame"); + + await clickElement(dbg, "frame", 2); + + ok(isFrameSelected(dbg, 2, "nestedA"), "Second frame is selected"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is still closed"); +}); + +// Tests that the breakpoint pane persists when toggled during pause +add_task(async function testBreakpointsPanePersistOnPauseToggle() { + const dbg = await initDebugger("doc-scripts.html", "simple3.js"); + + await selectSource(dbg, "simple3.js"); + await addBreakpoint(dbg, "simple3.js", 9); + await waitForBreakpoint(dbg, "simple3.js", 9); + + info("Close breakpoint pane"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is closed"); + + info("Trigger breakpoint"); + + invokeInTab("nestedA"); + await waitForPaused(dbg, "simple3.js"); + + is(getPaneElements(dbg).length, 2, "Breakpoints pane is open"); + + info("Close breakpoint pane and reopen it"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 1, "Breakpoints pane is closed"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 2, "Breakpoints pane is open"); + + await resume(dbg); + + is(getPaneElements(dbg).length, 2, "Breakpoints pane is still open"); +}); + +// Tests that the breakpoint pane remains closed when event breakpoints log is toggled +add_task(async function testBreakpointsPanePersistOnPauseToggle() { + const dbg = await initDebugger("doc-scripts.html", "simple3.js"); + + await selectSource(dbg, "simple3.js"); + await addBreakpoint(dbg, "simple3.js", 9); + await waitForBreakpoint(dbg, "simple3.js", 9); + + info("Trigger breakpoint"); + + invokeInTab("nestedA"); + await waitForPaused(dbg, "simple3.js"); + + info("Close breakpoint pane"); + + clickElementWithSelector(dbg, ".breakpoints-pane h2"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is closed"); + + info("Check event listener breakpoints log box"); + + await clickElement(dbg, "logEventsCheckbox"); + + is(getPaneElements(dbg).length, 1, "Breakpoint pane is still closed"); +}); + +function getPaneElements(dbg) { + return findElementWithSelector(dbg, ".breakpoints-pane").childNodes; +} + +function getFrameList(dbg) { + return dbg.win.document.querySelectorAll(".call-stack-pane .frame"); +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js b/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js new file mode 100644 index 0000000000..51127b5a83 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js @@ -0,0 +1,34 @@ +/* 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 . */ + +// Tests that Step In is cancelled when navigating to another page + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "simple3.js", "long.js"); + + // With the debugger stopped at a breakpoint, blackbox the current source and step in + await selectSource(dbg, "simple3.js"); + await addBreakpoint(dbg, "simple3.js", 5); + invokeInTab("simple"); + await waitForPaused(dbg, "simple3"); + + await clickElement(dbg, "blackbox"); + await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); + await dbg.actions.stepIn(getThreadContext(dbg)); + + // We should stop at this breakpoint, rather than the first executed script + await selectSource(dbg, "long.js"); + await addBreakpoint(dbg, "long.js", 1); + + // Navigation should clear the stepping state + const reloaded = reload(dbg); + await waitForPaused(dbg); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "long.js").id, 1); + + await resume(dbg); + await reloaded; + await waitForSource(dbg, "simple3.js"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-step-in-uninitialized.js b/devtools/client/debugger/test/mochitest/browser_dbg-step-in-uninitialized.js new file mode 100644 index 0000000000..41065295d9 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-step-in-uninitialized.js @@ -0,0 +1,47 @@ +/* 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 . */ + +// When stepping into a function, 'let' variables should show as uninitialized +// instead of undefined. + +"use strict"; + +add_task(async function test() { + const dbg = await initDebugger("doc-step-in-uninitialized.html"); + invokeInTab("main"); + await waitForPaused(dbg, "doc-step-in-uninitialized.html"); + + await stepOver(dbg); + await stepIn(dbg); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-step-in-uninitialized.html").id, + 8 + ); + + // We step past the 'let x' at the start of the function because it is not + // a breakpoint position. + ok(findNodeValue(dbg, "x") == "undefined", "x undefined"); + ok(findNodeValue(dbg, "y") == "(uninitialized)", "y uninitialized"); + + await stepOver(dbg); + + assertPausedAtSourceAndLine( + dbg, + findSource(dbg, "doc-step-in-uninitialized.html").id, + 9 + ); + + ok(findNodeValue(dbg, "y") == "3", "y initialized"); +}); + +function findNodeValue(dbg, text) { + for (let index = 0; ; index++) { + const elem = findElement(dbg, "scopeNode", index); + if (elem?.innerText == text) { + return findElement(dbg, "scopeValue", index).innerText; + } + } +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-stepping.js b/devtools/client/debugger/test/mochitest/browser_dbg-stepping.js new file mode 100644 index 0000000000..2821075e73 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-stepping.js @@ -0,0 +1,47 @@ +/* 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 . */ + +"use strict"; + +// This test can be really slow on debug platforms +requestLongerTimeout(5); + +add_task(async function test() { + const dbg = await initDebugger( + "big-sourcemap.html", + "bundle.js", + "step-in-test.js" + ); + invokeInTab("hitDebugStatement"); + // Bug 1829860 - The sourcemap for this file is broken and we couldn't resolve the paused location + // to the related original file. The debugger fallbacks to the generated source, + // but that highlights a bug in the example page. + await waitForPaused(dbg, "bundle.js"); + assertPausedAtSourceAndLine(dbg, findSource(dbg, "bundle.js").id, 52411); + + await stepIn(dbg); + + const whyPaused = await waitFor( + () => dbg.win.document.querySelector(".why-paused")?.innerText + ); + is(whyPaused, `Paused while stepping`); + + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + await stepIn(dbg); + + // Note that we are asserting against an original source here, + // See earlier comment about paused in bundle.js + assertPausedAtSourceAndLine(dbg, findSource(dbg, "step-in-test.js").id, 7679); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-tabs-keyboard.js b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-keyboard.js new file mode 100644 index 0000000000..23c8960d34 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-keyboard.js @@ -0,0 +1,29 @@ +/* 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 . */ + +// Tests removing tabs with keyboard shortcuts + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-scripts.html", + "simple1.js", + "simple2.js" + ); + + await selectSource(dbg, "simple1.js"); + await selectSource(dbg, "simple2.js"); + is(countTabs(dbg), 2, "Two tabs are open"); + + pressKey(dbg, "close"); + waitForDispatch(dbg.store, "CLOSE_TAB"); + is(countTabs(dbg), 1, "One tab is open"); + + await waitForSelectedSource(dbg, "simple1.js"); + + pressKey(dbg, "close"); + waitForDispatch(dbg.store, "CLOSE_TAB"); + is(countTabs(dbg), 0, "No tabs open"); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-tabs-pretty-print.js b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-pretty-print.js new file mode 100644 index 0000000000..f8bcdd0381 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-pretty-print.js @@ -0,0 +1,46 @@ +/* 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 . */ + +// Tests re-opening pretty printed tabs on load + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-minified.html", "math.min.js"); + + await selectSource(dbg, "math.min.js"); + clickElement(dbg, "prettyPrintButton"); + await waitForSource(dbg, "math.min.js:formatted"); + + await waitFor(() => findElement(dbg, "sourceTabs").children.length == 2); + const [prettyTab, originalTab] = findElement(dbg, "sourceTabs").children; + ok( + prettyTab.querySelector(".source-icon.img.prettyPrint"), + "Pretty printed tab has the pretty-print icon" + ); + ok( + !originalTab.querySelector(".source-icon.img.prettyPrint"), + "original tab does not have the pretty-print icon" + ); + + // Test reloading the debugger + await waitForSelectedSource(dbg, "math.min.js:formatted"); + await reload(dbg); + + await waitForSelectedSource(dbg, "math.min.js:formatted"); + ok(true, "Pretty printed source is selected on reload"); + + await selectSource(dbg, "math.min.js:formatted"); + const source = findSource(dbg, "math.min.js:formatted"); + dbg.actions.showSource(getContext(dbg), source.id); + const focusedTreeElement = findElementWithSelector( + dbg, + ".sources-list .focused .label" + ); + is( + focusedTreeElement.textContent.trim(), + "math.min.js", + "Pretty printed source is selected in tree" + ); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls-selected.js b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls-selected.js new file mode 100644 index 0000000000..fbfef690df --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls-selected.js @@ -0,0 +1,28 @@ +/* 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 . */ + +// Test that URL-less sources have tabs and selecting that location does not +// create a new tab for the same URL-less source + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-scripts.html", + "simple1.js", + "simple2.js" + ); + + // Create a URL-less source + invokeInTab("doEval"); + await waitForPaused(dbg); + + // Click a frame which shouldn't open a new source tab + await clickElement(dbg, "frame", 2); + + // Click the frame to select the same location and ensure there's only 1 tab + is(countTabs(dbg), 1); + + await resume(dbg); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls.js b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls.js new file mode 100644 index 0000000000..f6e223a657 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-tabs-without-urls.js @@ -0,0 +1,46 @@ +/* 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 . */ + +// Test that URL-less sources have tabs added to the UI, are named correctly +// and do not persist upon reload + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-scripts.html", + "simple1.js", + "simple2.js" + ); + + await selectSource(dbg, "simple1.js"); + is(countTabs(dbg), 1, "Only the `simple.js` source tab exists"); + + invokeInTab("doEval"); + await waitForPaused(dbg); + + is(countTabs(dbg), 2, "The new eval tab is now added"); + + info("Assert that the eval source the tab source name correctly"); + ok( + /source\d+/g.test(getTabContent(dbg, 0)), + "The tab name pattern is correct" + ); + + await resume(dbg); + + // Test reloading the debugger + await reload(dbg, "simple1.js", "simple2.js"); + is(countTabs(dbg), 1, "The eval source tab is no longer available"); +}); + +/* + * Get the tab content for the specific tab + * + * @param {Number} index - index of the tab to get the source content + */ +function getTabContent(dbg, index) { + const tabs = findElement(dbg, "sourceTabs").children; + return tabs[index]?.innerText || ""; +} diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-tabs.js b/devtools/client/debugger/test/mochitest/browser_dbg-tabs.js new file mode 100644 index 0000000000..08485beecc --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-tabs.js @@ -0,0 +1,46 @@ +/* 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 . */ + +// Tests adding and removing tabs + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger( + "doc-scripts.html", + "simple1.js", + "simple2.js" + ); + + await selectSource(dbg, "simple1.js"); + await selectSource(dbg, "simple2.js"); + is(countTabs(dbg), 2); + + info("Test reloading the debugger"); + await reload(dbg, "simple1.js", "simple2.js"); + await waitForSelectedSource(dbg, "simple2.js"); + is(countTabs(dbg), 2); + + info("Test reloading the debuggee a second time"); + await reload(dbg, "simple1.js", "simple2.js"); + await waitForSelectedSource(dbg, "simple2.js"); + is(countTabs(dbg), 2); +}); + +add_task(async function () { + const dbg = await initDebugger( + "doc-scripts.html", + "simple1.js", + "simple2.js" + ); + + await selectSource(dbg, "simple1.js"); + await selectSource(dbg, "simple2.js"); + await closeTab(dbg, "simple1.js"); + await closeTab(dbg, "simple2.js"); + + info("Test reloading the debugger"); + await reload(dbg, "simple1.js", "simple2.js"); + is(countTabs(dbg), 0); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-toggling-tools.js b/devtools/client/debugger/test/mochitest/browser_dbg-toggling-tools.js new file mode 100644 index 0000000000..7e6025869c --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-toggling-tools.js @@ -0,0 +1,19 @@ +/* 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 . */ + +// Tests that you can switch tools, without losing your editor position + +"use strict"; + +add_task(async function () { + const dbg = await initDebugger("doc-scripts.html", "long.js"); + + await selectSource(dbg, "long.js"); + getCM(dbg).scrollTo(0, 284); + + pressKey(dbg, "inspector"); + pressKey(dbg, "debugger"); + + is(getCM(dbg).getScrollInfo().top, 284); +}); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-ua-widgets.js b/devtools/client/debugger/test/mochitest/browser_dbg-ua-widgets.js new file mode 100644 index 0000000000..e563d52824 --- /dev/null +++ b/devtools/client/debugger/test/mochitest/browser_dbg-ua-widgets.js @@ -0,0 +1,47 @@ +/* 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 . */ + +// Test that you can debug User Agent widgets (like video controls) +// only when enabling "devtools.inspector.showAllAnonymousContent" pref. + +"use strict"; + +const TEST_URL = "data:text/html,