From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- devtools/client/inspector/test/browser.ini | 255 ++++ .../inspector/test/browser_inspector_addNode_01.js | 22 + .../inspector/test/browser_inspector_addNode_02.js | 76 + .../inspector/test/browser_inspector_addNode_03.js | 93 ++ .../test/browser_inspector_addSidebarTab.js | 63 + .../test/browser_inspector_breadcrumbs.js | 191 +++ ...rowser_inspector_breadcrumbs_highlight_hover.js | 69 + .../browser_inspector_breadcrumbs_keybinding.js | 82 ++ .../browser_inspector_breadcrumbs_keyboard_trap.js | 92 ++ .../browser_inspector_breadcrumbs_mutations.js | 282 ++++ .../browser_inspector_breadcrumbs_namespaced.js | 70 + .../browser_inspector_breadcrumbs_shadowdom.js | 107 ++ .../browser_inspector_breadcrumbs_visibility.js | 114 ++ .../browser_inspector_delete-selected-node-01.js | 29 + .../browser_inspector_delete-selected-node-02.js | 145 ++ .../browser_inspector_delete-selected-node-03.js | 24 + .../test/browser_inspector_delete_node_in_frame.js | 33 + .../browser_inspector_destroy-after-navigation.js | 23 + .../test/browser_inspector_destroy-before-ready.js | 26 + .../test/browser_inspector_expand-collapse.js | 68 + .../test/browser_inspector_eyedropper_ruleview.js | 50 + .../test/browser_inspector_fission_frame.js | 38 + .../browser_inspector_fission_frame_navigation.js | 163 +++ .../browser_inspector_fission_switch_target.js | 31 + .../test/browser_inspector_highlighter-01.js | 43 + .../test/browser_inspector_highlighter-02.js | 49 + .../test/browser_inspector_highlighter-03.js | 125 ++ .../test/browser_inspector_highlighter-04.js | 55 + .../test/browser_inspector_highlighter-05.js | 72 + .../test/browser_inspector_highlighter-06.js | 39 + .../test/browser_inspector_highlighter-07.js | 90 ++ .../test/browser_inspector_highlighter-08.js | 67 + ...ser_inspector_highlighter-autohide-config_01.js | 36 + ...ser_inspector_highlighter-autohide-config_02.js | 45 + ...ser_inspector_highlighter-autohide-config_03.js | 68 + .../test/browser_inspector_highlighter-autohide.js | 76 + .../test/browser_inspector_highlighter-by-type.js | 65 + .../test/browser_inspector_highlighter-cancel.js | 94 ++ .../test/browser_inspector_highlighter-comments.js | 119 ++ .../browser_inspector_highlighter-cssgrid_01.js | 91 ++ .../browser_inspector_highlighter-cssgrid_02.js | 51 + .../browser_inspector_highlighter-cssshape_01.js | 95 ++ .../browser_inspector_highlighter-cssshape_02.js | 157 +++ .../browser_inspector_highlighter-cssshape_03.js | 116 ++ .../browser_inspector_highlighter-cssshape_04.js | 453 ++++++ .../browser_inspector_highlighter-cssshape_05.js | 158 +++ ...wser_inspector_highlighter-cssshape_06-scale.js | 187 +++ ..._inspector_highlighter-cssshape_06-translate.js | 127 ++ .../browser_inspector_highlighter-cssshape_07.js | 179 +++ ...ser_inspector_highlighter-cssshape_iframe_01.js | 97 ++ ...rowser_inspector_highlighter-csstransform_01.js | 229 +++ ...rowser_inspector_highlighter-csstransform_02.js | 69 + ...browser_inspector_highlighter-custom-element.js | 30 + .../test/browser_inspector_highlighter-embed.js | 32 + ...r_inspector_highlighter-eyedropper-clipboard.js | 63 + ...browser_inspector_highlighter-eyedropper-csp.js | 39 + ...wser_inspector_highlighter-eyedropper-events.js | 184 +++ ...wser_inspector_highlighter-eyedropper-frames.js | 94 ++ ...owser_inspector_highlighter-eyedropper-image.js | 18 + ...owser_inspector_highlighter-eyedropper-label.js | 145 ++ ...r_inspector_highlighter-eyedropper-show-hide.js | 41 + ...browser_inspector_highlighter-eyedropper-xul.js | 74 + ...rowser_inspector_highlighter-eyedropper-zoom.js | 89 ++ .../browser_inspector_highlighter-geometry_01.js | 96 ++ .../browser_inspector_highlighter-geometry_02.js | 125 ++ .../browser_inspector_highlighter-geometry_03.js | 72 + .../browser_inspector_highlighter-geometry_04.js | 103 ++ .../browser_inspector_highlighter-geometry_05.js | 140 ++ .../browser_inspector_highlighter-geometry_06.js | 166 +++ ...tor_highlighter-geometry_hide_on_interaction.js | 80 ++ ...rowser_inspector_highlighter-geometry_iframe.js | 48 + .../test/browser_inspector_highlighter-hover_01.js | 34 + .../test/browser_inspector_highlighter-hover_02.js | 44 + .../test/browser_inspector_highlighter-hover_03.js | 60 + .../browser_inspector_highlighter-iframes_01.js | 90 ++ .../browser_inspector_highlighter-iframes_02.js | 80 ++ .../test/browser_inspector_highlighter-inline.js | 95 ++ .../browser_inspector_highlighter-keybinding_01.js | 71 + .../browser_inspector_highlighter-keybinding_02.js | 64 + .../browser_inspector_highlighter-keybinding_03.js | 69 + .../browser_inspector_highlighter-keybinding_04.js | 39 + ...ector_highlighter-keybinding_separate-window.js | 120 ++ .../browser_inspector_highlighter-measure_01.js | 92 ++ .../browser_inspector_highlighter-measure_02.js | 138 ++ .../browser_inspector_highlighter-measure_03.js | 114 ++ .../browser_inspector_highlighter-measure_04.js | 208 +++ .../test/browser_inspector_highlighter-options.js | 267 ++++ .../test/browser_inspector_highlighter-preview.js | 67 + ...inspector_highlighter-reduced-motion-message.js | 104 ++ ...browser_inspector_highlighter-reduced-motion.js | 89 ++ .../test/browser_inspector_highlighter-reload.js | 36 + .../browser_inspector_highlighter-rulers_01.js | 116 ++ .../browser_inspector_highlighter-rulers_02.js | 201 +++ .../browser_inspector_highlighter-rulers_03.js | 117 ++ .../browser_inspector_highlighter-selector_01.js | 80 ++ .../browser_inspector_highlighter-selector_02.js | 84 ++ .../test/browser_inspector_highlighter-zoom.js | 81 ++ .../test/browser_inspector_iframe-navigation.js | 58 + ...r_inspector_iframe-picker-bfcache-navigation.js | 121 ++ .../test/browser_inspector_iframe-picker.js | 131 ++ .../inspector/test/browser_inspector_infobar_01.js | 113 ++ .../inspector/test/browser_inspector_infobar_02.js | 53 + .../inspector/test/browser_inspector_infobar_03.js | 59 + .../inspector/test/browser_inspector_infobar_04.js | 45 + .../inspector/test/browser_inspector_infobar_05.js | 119 ++ .../test/browser_inspector_infobar_textnode.js | 53 + .../test/browser_inspector_initialization.js | 114 ++ .../browser_inspector_inspect-object-element.js | 18 + .../browser_inspector_inspect_loading_document.js | 168 +++ .../test/browser_inspector_inspect_mutated_node.js | 72 + .../browser_inspector_inspect_node_contextmenu.js | 140 ++ ...er_inspector_inspect_node_contextmenu_nested.js | 152 ++ ...rowser_inspector_inspect_parent_process_page.js | 29 + .../inspector/test/browser_inspector_invalidate.js | 44 + ..._inspector_keyboard-shortcuts-copy-outerhtml.js | 53 + .../test/browser_inspector_keyboard-shortcuts.js | 54 + .../test/browser_inspector_menu-01-sensitivity.js | 388 ++++++ .../browser_inspector_menu-03-paste-items-svg.js | 46 + .../test/browser_inspector_menu-03-paste-items.js | 166 +++ .../browser_inspector_menu-04-use-in-console.js | 57 + .../browser_inspector_menu-05-attribute-items.js | 124 ++ .../test/browser_inspector_menu-06-other.js | 160 +++ .../test/browser_inspector_navigate_to_errors.js | 69 + .../inspector/test/browser_inspector_navigation.js | 88 ++ .../test/browser_inspector_open_on_neterror.js | 41 + .../test/browser_inspector_pane-toggle-01.js | 36 + .../test/browser_inspector_pane-toggle-02.js | 85 ++ .../test/browser_inspector_pane-toggle-03.js | 60 + .../test/browser_inspector_pane-toggle-04.js | 55 + .../test/browser_inspector_pane-toggle-05.js | 106 ++ ...owser_inspector_pane-toggle-layout-invariant.js | 32 + .../test/browser_inspector_pane_state_restore.js | 75 + .../browser_inspector_picker-reset-reference.js | 69 + .../test/browser_inspector_picker-shift-key.js | 94 ++ .../browser_inspector_picker-stop-on-eyedropper.js | 46 + ...browser_inspector_picker-stop-on-tool-change.js | 27 + .../browser_inspector_picker-useragent-widget.js | 73 + .../test/browser_inspector_portrait_mode.js | 82 ++ .../test/browser_inspector_pseudoclass-lock.js | 235 ++++ .../test/browser_inspector_pseudoclass-menu.js | 60 + .../inspector/test/browser_inspector_reload-01.js | 30 + .../inspector/test/browser_inspector_reload-02.js | 47 + .../test/browser_inspector_reload_iframe.js | 48 + .../browser_inspector_reload_invalid_iframe.js | 63 + ...browser_inspector_reload_missing-iframe-node.js | 58 + .../test/browser_inspector_reload_nested_iframe.js | 50 + .../test/browser_inspector_reload_shadow_dom.js | 61 + .../inspector/test/browser_inspector_reload_xul.js | 50 + .../browser_inspector_remove-iframe-during-load.js | 83 ++ .../inspector/test/browser_inspector_search-01.js | 110 ++ .../inspector/test/browser_inspector_search-02.js | 155 +++ .../inspector/test/browser_inspector_search-03.js | 228 +++ .../inspector/test/browser_inspector_search-04.js | 115 ++ .../inspector/test/browser_inspector_search-05.js | 107 ++ .../inspector/test/browser_inspector_search-06.js | 103 ++ .../inspector/test/browser_inspector_search-07.js | 60 + .../inspector/test/browser_inspector_search-08.js | 75 + .../inspector/test/browser_inspector_search-09.js | 112 ++ .../inspector/test/browser_inspector_search-10.js | 51 + .../test/browser_inspector_search-clear.js | 59 + ...browser_inspector_search-filter_context-menu.js | 109 ++ .../test/browser_inspector_search-label.js | 33 + .../test/browser_inspector_search-navigation.js | 73 + .../test/browser_inspector_search-reserved.js | 137 ++ .../test/browser_inspector_search-selection.js | 83 ++ .../test/browser_inspector_search-sidebar.js | 87 ++ ...er_inspector_search-suggests-ids-and-classes.js | 154 +++ ..._inspector_search_keyboard_shortcut_conflict.js | 59 + .../test/browser_inspector_search_keyboard_trap.js | 95 ++ .../test/browser_inspector_select-last-selected.js | 75 + .../test/browser_inspector_sidebarstate.js | 135 ++ .../inspector/test/browser_inspector_startup.js | 84 ++ ...rowser_inspector_switch-to-inspector-on-pick.js | 123 ++ .../test/browser_inspector_textbox-menu.js | 103 ++ ...rowser_inspector_textbox-menu_reopen_toolbox.js | 51 + .../inspector/test/doc_inspector_add_node.html | 22 + .../inspector/test/doc_inspector_breadcrumbs.html | 75 + .../test/doc_inspector_breadcrumbs_visibility.html | 22 + .../client/inspector/test/doc_inspector_csp.html | 9 + .../inspector/test/doc_inspector_csp.html^headers^ | 2 + .../doc_inspector_delete-selected-node-01.html | 4 + .../doc_inspector_delete-selected-node-02.html | 20 + .../client/inspector/test/doc_inspector_embed.html | 6 + .../test/doc_inspector_eyedropper_disabled.xhtml | 3 + .../doc_inspector_fission_frame_navigation.html | 15 + .../doc_inspector_highlight_after_transition.html | 26 + .../test/doc_inspector_highlighter-comments.html | 19 + .../doc_inspector_highlighter-geometry_01.html | 90 ++ .../doc_inspector_highlighter-geometry_02.html | 120 ++ .../inspector/test/doc_inspector_highlighter.html | 40 + .../test/doc_inspector_highlighter_cssshapes.html | 89 ++ ...doc_inspector_highlighter_cssshapes_iframe.html | 11 + .../doc_inspector_highlighter_csstransform.html | 25 + .../doc_inspector_highlighter_custom_element.xhtml | 20 + .../test/doc_inspector_highlighter_dom.html | 20 + .../test/doc_inspector_highlighter_inline.html | 36 + .../test/doc_inspector_highlighter_rect.html | 22 + .../doc_inspector_highlighter_rect_iframe.html | 15 + .../test/doc_inspector_highlighter_scroll.html | 15 + .../inspector/test/doc_inspector_infobar.html | 43 + .../inspector/test/doc_inspector_infobar_01.html | 44 + .../inspector/test/doc_inspector_infobar_02.html | 34 + .../inspector/test/doc_inspector_infobar_03.html | 14 + .../inspector/test/doc_inspector_infobar_04.html | 41 + .../test/doc_inspector_infobar_textnode.html | 14 + .../inspector/test/doc_inspector_long-divs.html | 104 ++ .../client/inspector/test/doc_inspector_menu.html | 38 + .../inspector/test/doc_inspector_outerhtml.html | 11 + ...doc_inspector_pane-toggle-layout-invariant.html | 27 + .../inspector/test/doc_inspector_reload_xul.xhtml | 9 + .../doc_inspector_remove-iframe-during-load.html | 44 + .../test/doc_inspector_search-iframes.html | 13 + .../test/doc_inspector_search-reserved.html | 11 + .../test/doc_inspector_search-suggestions.html | 27 + .../inspector/test/doc_inspector_search-svg.html | 16 + .../inspector/test/doc_inspector_search.html | 26 + .../doc_inspector_select-last-selected-01.html | 21 + .../doc_inspector_select-last-selected-02.html | 10 + .../client/inspector/test/doc_inspector_svg.svg | 3 + devtools/client/inspector/test/head.js | 1459 ++++++++++++++++++++ ...wser_inspector_highlighter-eyedropper-image.png | Bin 0 -> 522 bytes devtools/client/inspector/test/shared-head.js | 1002 ++++++++++++++ .../inspector/test/sjs_slow-loading-image.sjs | 33 + .../client/inspector/test/style_inspector_csp.css | 3 + .../test/style_inspector_eyedropper_ruleview.css | 3 + 225 files changed, 20109 insertions(+) create mode 100644 devtools/client/inspector/test/browser.ini create mode 100644 devtools/client/inspector/test/browser_inspector_addNode_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_addNode_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_addNode_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_addSidebarTab.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_keybinding.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_keyboard_trap.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_namespaced.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js create mode 100644 devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js create mode 100644 devtools/client/inspector/test/browser_inspector_delete-selected-node-01.js create mode 100644 devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js create mode 100644 devtools/client/inspector/test/browser_inspector_delete-selected-node-03.js create mode 100644 devtools/client/inspector/test/browser_inspector_delete_node_in_frame.js create mode 100644 devtools/client/inspector/test/browser_inspector_destroy-after-navigation.js create mode 100644 devtools/client/inspector/test/browser_inspector_destroy-before-ready.js create mode 100644 devtools/client/inspector/test/browser_inspector_expand-collapse.js create mode 100644 devtools/client/inspector/test/browser_inspector_eyedropper_ruleview.js create mode 100644 devtools/client/inspector/test/browser_inspector_fission_frame.js create mode 100644 devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js create mode 100644 devtools/client/inspector/test/browser_inspector_fission_switch_target.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-04.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-05.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-06.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-07.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-08.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-autohide.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-by-type.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cancel.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-comments.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssgrid_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssgrid_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_04.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_05.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06-scale.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_06-translate.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_07.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-cssshape_iframe_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-csstransform_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-csstransform_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-custom-element.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-embed.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-clipboard.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-csp.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-events.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-frames.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-image.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-label.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-show-hide.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-xul.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-eyedropper-zoom.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_04.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_05.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_06.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_hide_on_interaction.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-geometry_iframe.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-hover_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-hover_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-hover_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-iframes_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-iframes_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-inline.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-keybinding_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-keybinding_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-keybinding_04.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-keybinding_separate-window.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-measure_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-measure_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-measure_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-measure_04.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-options.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-preview.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-reduced-motion-message.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-reduced-motion.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-reload.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-rulers_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-rulers_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-rulers_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-selector_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-selector_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_highlighter-zoom.js create mode 100644 devtools/client/inspector/test/browser_inspector_iframe-navigation.js create mode 100644 devtools/client/inspector/test/browser_inspector_iframe-picker-bfcache-navigation.js create mode 100644 devtools/client/inspector/test/browser_inspector_iframe-picker.js create mode 100644 devtools/client/inspector/test/browser_inspector_infobar_01.js create mode 100644 devtools/client/inspector/test/browser_inspector_infobar_02.js create mode 100644 devtools/client/inspector/test/browser_inspector_infobar_03.js create mode 100644 devtools/client/inspector/test/browser_inspector_infobar_04.js create mode 100644 devtools/client/inspector/test/browser_inspector_infobar_05.js create mode 100644 devtools/client/inspector/test/browser_inspector_infobar_textnode.js create mode 100644 devtools/client/inspector/test/browser_inspector_initialization.js create mode 100644 devtools/client/inspector/test/browser_inspector_inspect-object-element.js create mode 100644 devtools/client/inspector/test/browser_inspector_inspect_loading_document.js create mode 100644 devtools/client/inspector/test/browser_inspector_inspect_mutated_node.js create mode 100644 devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu.js create mode 100644 devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu_nested.js create mode 100644 devtools/client/inspector/test/browser_inspector_inspect_parent_process_page.js create mode 100644 devtools/client/inspector/test/browser_inspector_invalidate.js create mode 100644 devtools/client/inspector/test/browser_inspector_keyboard-shortcuts-copy-outerhtml.js create mode 100644 devtools/client/inspector/test/browser_inspector_keyboard-shortcuts.js create mode 100644 devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js create mode 100644 devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js create mode 100644 devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js create mode 100644 devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js create mode 100644 devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js create mode 100644 devtools/client/inspector/test/browser_inspector_menu-06-other.js create mode 100644 devtools/client/inspector/test/browser_inspector_navigate_to_errors.js create mode 100644 devtools/client/inspector/test/browser_inspector_navigation.js create mode 100644 devtools/client/inspector/test/browser_inspector_open_on_neterror.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane-toggle-01.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane-toggle-02.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane-toggle-03.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane-toggle-04.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane-toggle-05.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane-toggle-layout-invariant.js create mode 100644 devtools/client/inspector/test/browser_inspector_pane_state_restore.js create mode 100644 devtools/client/inspector/test/browser_inspector_picker-reset-reference.js create mode 100644 devtools/client/inspector/test/browser_inspector_picker-shift-key.js create mode 100644 devtools/client/inspector/test/browser_inspector_picker-stop-on-eyedropper.js create mode 100644 devtools/client/inspector/test/browser_inspector_picker-stop-on-tool-change.js create mode 100644 devtools/client/inspector/test/browser_inspector_picker-useragent-widget.js create mode 100644 devtools/client/inspector/test/browser_inspector_portrait_mode.js create mode 100644 devtools/client/inspector/test/browser_inspector_pseudoclass-lock.js create mode 100644 devtools/client/inspector/test/browser_inspector_pseudoclass-menu.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload-01.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload-02.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload_iframe.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload_invalid_iframe.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload_missing-iframe-node.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload_nested_iframe.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload_shadow_dom.js create mode 100644 devtools/client/inspector/test/browser_inspector_reload_xul.js create mode 100644 devtools/client/inspector/test/browser_inspector_remove-iframe-during-load.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-01.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-02.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-03.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-04.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-05.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-06.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-07.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-08.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-09.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-10.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-clear.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-label.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-navigation.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-reserved.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-selection.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-sidebar.js create mode 100644 devtools/client/inspector/test/browser_inspector_search-suggests-ids-and-classes.js create mode 100644 devtools/client/inspector/test/browser_inspector_search_keyboard_shortcut_conflict.js create mode 100644 devtools/client/inspector/test/browser_inspector_search_keyboard_trap.js create mode 100644 devtools/client/inspector/test/browser_inspector_select-last-selected.js create mode 100644 devtools/client/inspector/test/browser_inspector_sidebarstate.js create mode 100644 devtools/client/inspector/test/browser_inspector_startup.js create mode 100644 devtools/client/inspector/test/browser_inspector_switch-to-inspector-on-pick.js create mode 100644 devtools/client/inspector/test/browser_inspector_textbox-menu.js create mode 100644 devtools/client/inspector/test/browser_inspector_textbox-menu_reopen_toolbox.js create mode 100644 devtools/client/inspector/test/doc_inspector_add_node.html create mode 100644 devtools/client/inspector/test/doc_inspector_breadcrumbs.html create mode 100644 devtools/client/inspector/test/doc_inspector_breadcrumbs_visibility.html create mode 100644 devtools/client/inspector/test/doc_inspector_csp.html create mode 100644 devtools/client/inspector/test/doc_inspector_csp.html^headers^ create mode 100644 devtools/client/inspector/test/doc_inspector_delete-selected-node-01.html create mode 100644 devtools/client/inspector/test/doc_inspector_delete-selected-node-02.html create mode 100644 devtools/client/inspector/test/doc_inspector_embed.html create mode 100644 devtools/client/inspector/test/doc_inspector_eyedropper_disabled.xhtml create mode 100644 devtools/client/inspector/test/doc_inspector_fission_frame_navigation.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlight_after_transition.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter-comments.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter-geometry_01.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter-geometry_02.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_cssshapes.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_cssshapes_iframe.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_csstransform.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_custom_element.xhtml create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_dom.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_inline.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_rect.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_rect_iframe.html create mode 100644 devtools/client/inspector/test/doc_inspector_highlighter_scroll.html create mode 100644 devtools/client/inspector/test/doc_inspector_infobar.html create mode 100644 devtools/client/inspector/test/doc_inspector_infobar_01.html create mode 100644 devtools/client/inspector/test/doc_inspector_infobar_02.html create mode 100644 devtools/client/inspector/test/doc_inspector_infobar_03.html create mode 100644 devtools/client/inspector/test/doc_inspector_infobar_04.html create mode 100644 devtools/client/inspector/test/doc_inspector_infobar_textnode.html create mode 100644 devtools/client/inspector/test/doc_inspector_long-divs.html create mode 100644 devtools/client/inspector/test/doc_inspector_menu.html create mode 100644 devtools/client/inspector/test/doc_inspector_outerhtml.html create mode 100644 devtools/client/inspector/test/doc_inspector_pane-toggle-layout-invariant.html create mode 100644 devtools/client/inspector/test/doc_inspector_reload_xul.xhtml create mode 100644 devtools/client/inspector/test/doc_inspector_remove-iframe-during-load.html create mode 100644 devtools/client/inspector/test/doc_inspector_search-iframes.html create mode 100644 devtools/client/inspector/test/doc_inspector_search-reserved.html create mode 100644 devtools/client/inspector/test/doc_inspector_search-suggestions.html create mode 100644 devtools/client/inspector/test/doc_inspector_search-svg.html create mode 100644 devtools/client/inspector/test/doc_inspector_search.html create mode 100644 devtools/client/inspector/test/doc_inspector_select-last-selected-01.html create mode 100644 devtools/client/inspector/test/doc_inspector_select-last-selected-02.html create mode 100644 devtools/client/inspector/test/doc_inspector_svg.svg create mode 100644 devtools/client/inspector/test/head.js create mode 100644 devtools/client/inspector/test/img_browser_inspector_highlighter-eyedropper-image.png create mode 100644 devtools/client/inspector/test/shared-head.js create mode 100644 devtools/client/inspector/test/sjs_slow-loading-image.sjs create mode 100644 devtools/client/inspector/test/style_inspector_csp.css create mode 100644 devtools/client/inspector/test/style_inspector_eyedropper_ruleview.css (limited to 'devtools/client/inspector/test') diff --git a/devtools/client/inspector/test/browser.ini b/devtools/client/inspector/test/browser.ini new file mode 100644 index 0000000000..0fb65513f4 --- /dev/null +++ b/devtools/client/inspector/test/browser.ini @@ -0,0 +1,255 @@ +[DEFAULT] +tags = devtools +subsuite = devtools +support-files = + doc_inspector_add_node.html + doc_inspector_breadcrumbs.html + doc_inspector_breadcrumbs_visibility.html + doc_inspector_csp.html + doc_inspector_csp.html^headers^ + doc_inspector_delete-selected-node-01.html + doc_inspector_delete-selected-node-02.html + doc_inspector_embed.html + doc_inspector_eyedropper_disabled.xhtml + doc_inspector_fission_frame_navigation.html + doc_inspector_highlight_after_transition.html + doc_inspector_highlighter-comments.html + doc_inspector_highlighter-geometry_01.html + doc_inspector_highlighter-geometry_02.html + doc_inspector_highlighter_cssshapes.html + doc_inspector_highlighter_cssshapes_iframe.html + doc_inspector_highlighter_csstransform.html + doc_inspector_highlighter_dom.html + doc_inspector_highlighter_inline.html + doc_inspector_highlighter.html + doc_inspector_highlighter_rect.html + doc_inspector_highlighter_rect_iframe.html + doc_inspector_highlighter_scroll.html + doc_inspector_highlighter_custom_element.xhtml + doc_inspector_infobar_01.html + doc_inspector_infobar_02.html + doc_inspector_infobar_03.html + doc_inspector_infobar_04.html + doc_inspector_infobar_textnode.html + doc_inspector_long-divs.html + doc_inspector_menu.html + doc_inspector_outerhtml.html + doc_inspector_pane-toggle-layout-invariant.html + doc_inspector_reload_xul.xhtml + doc_inspector_remove-iframe-during-load.html + doc_inspector_search.html + doc_inspector_search-iframes.html + doc_inspector_search-reserved.html + doc_inspector_search-suggestions.html + doc_inspector_search-svg.html + doc_inspector_select-last-selected-01.html + doc_inspector_select-last-selected-02.html + doc_inspector_svg.svg + head.js + img_browser_inspector_highlighter-eyedropper-image.png + shared-head.js + sjs_slow-loading-image.sjs + style_inspector_eyedropper_ruleview.css + style_inspector_csp.css + !/devtools/client/shared/test/shared-head.js + !/devtools/client/shared/test/telemetry-test-helpers.js + !/devtools/client/debugger/test/mochitest/shared-head.js + !/devtools/client/shared/test/highlighter-test-actor.js + +[browser_inspector_addNode_01.js] +[browser_inspector_addNode_02.js] +[browser_inspector_addNode_03.js] +[browser_inspector_addSidebarTab.js] +[browser_inspector_breadcrumbs.js] +[browser_inspector_breadcrumbs_highlight_hover.js] +[browser_inspector_breadcrumbs_keybinding.js] +[browser_inspector_breadcrumbs_keyboard_trap.js] +skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences +[browser_inspector_breadcrumbs_mutations.js] +[browser_inspector_breadcrumbs_namespaced.js] +[browser_inspector_breadcrumbs_shadowdom.js] +[browser_inspector_breadcrumbs_visibility.js] +[browser_inspector_delete-selected-node-01.js] +[browser_inspector_delete-selected-node-02.js] +[browser_inspector_delete-selected-node-03.js] +[browser_inspector_delete_node_in_frame.js] +[browser_inspector_destroy-after-navigation.js] +[browser_inspector_destroy-before-ready.js] +[browser_inspector_expand-collapse.js] +[browser_inspector_eyedropper_ruleview.js] +[browser_inspector_fission_frame.js] +[browser_inspector_fission_frame_navigation.js] +[browser_inspector_fission_switch_target.js] +[browser_inspector_highlighter-01.js] +[browser_inspector_highlighter-02.js] +[browser_inspector_highlighter-03.js] +[browser_inspector_highlighter-04.js] +[browser_inspector_highlighter-05.js] +[browser_inspector_highlighter-06.js] +[browser_inspector_highlighter-07.js] +[browser_inspector_highlighter-08.js] +[browser_inspector_highlighter-autohide-config_01.js] +[browser_inspector_highlighter-autohide-config_02.js] +[browser_inspector_highlighter-autohide-config_03.js] +[browser_inspector_highlighter-autohide.js] +[browser_inspector_highlighter-by-type.js] +[browser_inspector_highlighter-cancel.js] +[browser_inspector_highlighter-comments.js] +[browser_inspector_highlighter-cssgrid_01.js] +[browser_inspector_highlighter-cssgrid_02.js] +[browser_inspector_highlighter-cssshape_01.js] +[browser_inspector_highlighter-cssshape_02.js] +[browser_inspector_highlighter-cssshape_03.js] +[browser_inspector_highlighter-cssshape_04.js] +skip-if = + os == "win" && asan # Bug 1453214 + os == "win" && debug # Bug 1453214 + os == "linux" # Bug 1453214 + os == "mac" && debug # high frequency intermittent, bug 1453214 +[browser_inspector_highlighter-cssshape_05.js] +fail-if = a11y_checks # bug 1687723 ruleview-shapeswatch is not accessible +[browser_inspector_highlighter-cssshape_06-scale.js] +fail-if = a11y_checks # bug 1687723 ruleview-shapeswatch is not accessible +[browser_inspector_highlighter-cssshape_06-translate.js] +fail-if = a11y_checks # bug 1687723 ruleview-shapeswatch is not accessible +[browser_inspector_highlighter-cssshape_07.js] +fail-if = a11y_checks # bug 1687723 ruleview-shapeswatch is not accessible +[browser_inspector_highlighter-cssshape_iframe_01.js] +skip-if = + verify && debug +fail-if = a11y_checks # bug 1687723 ruleview-shapeswatch is not accessible +[browser_inspector_highlighter-csstransform_01.js] +[browser_inspector_highlighter-csstransform_02.js] +[browser_inspector_highlighter-custom-element.js] +[browser_inspector_highlighter-embed.js] +[browser_inspector_highlighter-eyedropper-clipboard.js] +[browser_inspector_highlighter-eyedropper-csp.js] +[browser_inspector_highlighter-eyedropper-events.js] +skip-if = os == "win" # bug 1413442 +[browser_inspector_highlighter-eyedropper-frames.js] +[browser_inspector_highlighter-eyedropper-image.js] +[browser_inspector_highlighter-eyedropper-label.js] +[browser_inspector_highlighter-eyedropper-show-hide.js] +[browser_inspector_highlighter-eyedropper-xul.js] +[browser_inspector_highlighter-eyedropper-zoom.js] +[browser_inspector_highlighter-geometry_01.js] +[browser_inspector_highlighter-geometry_02.js] +[browser_inspector_highlighter-geometry_03.js] +[browser_inspector_highlighter-geometry_04.js] +[browser_inspector_highlighter-geometry_05.js] +[browser_inspector_highlighter-geometry_06.js] +[browser_inspector_highlighter-geometry_hide_on_interaction.js] +[browser_inspector_highlighter-geometry_iframe.js] +[browser_inspector_highlighter-hover_01.js] +[browser_inspector_highlighter-hover_02.js] +[browser_inspector_highlighter-hover_03.js] +[browser_inspector_highlighter-iframes_01.js] +[browser_inspector_highlighter-iframes_02.js] +[browser_inspector_highlighter-inline.js] +[browser_inspector_highlighter-keybinding_01.js] +[browser_inspector_highlighter-keybinding_02.js] +[browser_inspector_highlighter-keybinding_03.js] +[browser_inspector_highlighter-keybinding_04.js] +[browser_inspector_highlighter-keybinding_separate-window.js] +[browser_inspector_highlighter-measure_01.js] +[browser_inspector_highlighter-measure_02.js] +[browser_inspector_highlighter-measure_03.js] +[browser_inspector_highlighter-measure_04.js] +[browser_inspector_highlighter-options.js] +[browser_inspector_highlighter-preview.js] +[browser_inspector_highlighter-reduced-motion-message.js] +[browser_inspector_highlighter-reduced-motion.js] +[browser_inspector_highlighter-reload.js] +[browser_inspector_highlighter-rulers_01.js] +[browser_inspector_highlighter-rulers_02.js] +[browser_inspector_highlighter-rulers_03.js] +skip-if = + os == "win" && !debug # Bug 1449754 +[browser_inspector_highlighter-selector_01.js] +[browser_inspector_highlighter-selector_02.js] +[browser_inspector_highlighter-zoom.js] +[browser_inspector_iframe-navigation.js] +[browser_inspector_iframe-picker-bfcache-navigation.js] +[browser_inspector_iframe-picker.js] +[browser_inspector_infobar_01.js] +[browser_inspector_infobar_02.js] +[browser_inspector_infobar_03.js] +[browser_inspector_infobar_04.js] +[browser_inspector_infobar_05.js] +[browser_inspector_infobar_textnode.js] +[browser_inspector_initialization.js] +skip-if = debug # Bug 1250058 - Docshell leak on debug +[browser_inspector_inspect-object-element.js] +[browser_inspector_inspect_loading_document.js] +[browser_inspector_inspect_mutated_node.js] +skip-if = win10_2004 # Bug 1723573 +[browser_inspector_inspect_node_contextmenu.js] +[browser_inspector_inspect_node_contextmenu_nested.js] +[browser_inspector_inspect_parent_process_page.js] +[browser_inspector_invalidate.js] +[browser_inspector_keyboard-shortcuts-copy-outerhtml.js] +[browser_inspector_keyboard-shortcuts.js] +[browser_inspector_menu-01-sensitivity.js] +[browser_inspector_menu-03-paste-items-svg.js] +[browser_inspector_menu-03-paste-items.js] +[browser_inspector_menu-04-use-in-console.js] +[browser_inspector_menu-05-attribute-items.js] +[browser_inspector_menu-06-other.js] +[browser_inspector_navigate_to_errors.js] +skip-if = http3 # Bug 1829298 +[browser_inspector_navigation.js] +skip-if = http3 # Bug 1829298 +[browser_inspector_open_on_neterror.js] +[browser_inspector_pane-toggle-01.js] +[browser_inspector_pane-toggle-02.js] +[browser_inspector_pane-toggle-03.js] +[browser_inspector_pane-toggle-04.js] +[browser_inspector_pane-toggle-05.js] +[browser_inspector_pane-toggle-layout-invariant.js] +[browser_inspector_pane_state_restore.js] +[browser_inspector_picker-reset-reference.js] +[browser_inspector_picker-shift-key.js] +[browser_inspector_picker-stop-on-eyedropper.js] +[browser_inspector_picker-stop-on-tool-change.js] +[browser_inspector_picker-useragent-widget.js] +[browser_inspector_portrait_mode.js] +[browser_inspector_pseudoclass-lock.js] +[browser_inspector_pseudoclass-menu.js] +[browser_inspector_reload-01.js] +[browser_inspector_reload-02.js] +[browser_inspector_reload_iframe.js] +[browser_inspector_reload_invalid_iframe.js] +[browser_inspector_reload_missing-iframe-node.js] +[browser_inspector_reload_nested_iframe.js] +[browser_inspector_reload_shadow_dom.js] +[browser_inspector_reload_xul.js] +[browser_inspector_remove-iframe-during-load.js] +[browser_inspector_search-01.js] +[browser_inspector_search-02.js] +[browser_inspector_search-03.js] +[browser_inspector_search-04.js] +[browser_inspector_search-05.js] +[browser_inspector_search-06.js] +[browser_inspector_search-07.js] +[browser_inspector_search-08.js] +[browser_inspector_search-09.js] +[browser_inspector_search-10.js] +[browser_inspector_search-clear.js] +[browser_inspector_search-filter_context-menu.js] +[browser_inspector_search-label.js] +[browser_inspector_search-navigation.js] +[browser_inspector_search-reserved.js] +[browser_inspector_search-selection.js] +[browser_inspector_search-sidebar.js] +[browser_inspector_search-suggests-ids-and-classes.js] +[browser_inspector_search_keyboard_shortcut_conflict.js] +[browser_inspector_search_keyboard_trap.js] +[browser_inspector_select-last-selected.js] +[browser_inspector_sidebarstate.js] +[browser_inspector_startup.js] +[browser_inspector_switch-to-inspector-on-pick.js] +skip-if = + apple_catalina && !debug # Bug 1713158 +[browser_inspector_textbox-menu.js] +[browser_inspector_textbox-menu_reopen_toolbox.js] + diff --git a/devtools/client/inspector/test/browser_inspector_addNode_01.js b/devtools/client/inspector/test/browser_inspector_addNode_01.js new file mode 100644 index 0000000000..ef89950b9b --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_addNode_01.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the add node button and context menu items are present in the UI. + +const TEST_URL = "data:text/html;charset=utf-8,

Add node

"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + const { panelDoc } = inspector; + + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const menuItem = allMenuItems.find(item => item.id === "node-menu-add"); + ok(menuItem, "The item is in the menu"); + + const toolbarButton = panelDoc.querySelector( + "#inspector-toolbar #inspector-element-add-button" + ); + ok(toolbarButton, "The add button is in the toolbar"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_addNode_02.js b/devtools/client/inspector/test/browser_inspector_addNode_02.js new file mode 100644 index 0000000000..fcd9b00f18 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_addNode_02.js @@ -0,0 +1,76 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that the add node button and context menu items have the right state +// depending on the current selection. + +const TEST_URL = URL_ROOT + "doc_inspector_add_node.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + + info("Select the DOCTYPE element"); + let { nodes } = await inspector.walker.children(inspector.walker.rootNode); + await selectNode(nodes[0], inspector); + assertState(false, inspector, "The button and item are disabled on DOCTYPE"); + + info("Select the ::before pseudo-element"); + const body = await getNodeFront("body", inspector); + ({ nodes } = await inspector.walker.children(body)); + await selectNode(nodes[0], inspector); + assertState( + false, + inspector, + "The button and item are disabled on a pseudo-element" + ); + + info("Select the svg element"); + await selectNode("svg", inspector); + assertState( + false, + inspector, + "The button and item are disabled on a SVG element" + ); + + info("Select the div#foo element"); + await selectNode("#foo", inspector); + assertState( + true, + inspector, + "The button and item are enabled on a DIV element" + ); + + info("Select the documentElement element (html)"); + await selectNode("html", inspector); + assertState( + false, + inspector, + "The button and item are disabled on the documentElement" + ); + + info("Select the iframe element"); + await selectNode("iframe", inspector); + assertState( + false, + inspector, + "The button and item are disabled on an IFRAME element" + ); +}); + +function assertState(isEnabled, inspector, desc) { + const doc = inspector.panelDoc; + const btn = doc.querySelector("#inspector-element-add-button"); + + // Force an update of the context menu to make sure menu items are updated + // according to the current selection. This normally happens when the menu is + // opened, but for the sake of this test's simplicity, we directly call the + // private update function instead. + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const menuItem = allMenuItems.find(item => item.id === "node-menu-add"); + ok(menuItem, "The item is in the menu"); + is(!menuItem.disabled, isEnabled, desc); + + is(!btn.hasAttribute("disabled"), isEnabled, desc); +} diff --git a/devtools/client/inspector/test/browser_inspector_addNode_03.js b/devtools/client/inspector/test/browser_inspector_addNode_03.js new file mode 100644 index 0000000000..abc50d2969 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_addNode_03.js @@ -0,0 +1,93 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that adding nodes does work as expected: the parent node remains selected and the +// new node is created inside the parent. + +const TEST_URL = URL_ROOT + "doc_inspector_add_node.html"; +const PARENT_TREE_LEVEL = 3; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + + info("Adding a node in an element that has no children and is collapsed"); + let parentNode = await getNodeFront("#foo", inspector); + await selectNode(parentNode, inspector); + await testAddNode(parentNode, inspector); + + info( + "Adding a node in an element with children but that has not been expanded yet" + ); + parentNode = await getNodeFront("#bar", inspector); + await selectNode(parentNode, inspector); + await testAddNode(parentNode, inspector); + + info( + "Adding a node in an element with children that has been expanded then collapsed" + ); + // Select again #bar and collapse it. + parentNode = await getNodeFront("#bar", inspector); + await selectNode(parentNode, inspector); + collapseNode(parentNode, inspector); + await testAddNode(parentNode, inspector); + + info("Adding a node in an element with children that is expanded"); + parentNode = await getNodeFront("#bar", inspector); + await selectNode(parentNode, inspector); + await testAddNode(parentNode, inspector); +}); + +async function testAddNode(parentNode, inspector) { + const btn = inspector.panelDoc.querySelector("#inspector-element-add-button"); + const parentContainer = inspector.markup.getContainer(parentNode); + + is( + parseInt(parentContainer.tagLine.getAttribute("aria-level"), 10), + PARENT_TREE_LEVEL, + "The parent aria-level is up to date." + ); + + info( + "Clicking 'add node' and expecting a markup mutation and a new container" + ); + const onMutation = inspector.once("markupmutation"); + const onNewContainer = inspector.once("container-created"); + btn.click(); + let mutations = await onMutation; + await onNewContainer; + + // We are only interested in childList mutations here. Filter everything else out as + // there may be unrelated mutations (e.g. "events") grouped in. + mutations = mutations.filter(({ type }) => type === "childList"); + + is(mutations.length, 1, "There is one mutation only"); + is(mutations[0].added.length, 1, "There is one new node only"); + + const newNode = mutations[0].added[0]; + + is( + parentNode, + inspector.selection.nodeFront, + "The parent node is still selected" + ); + is( + newNode.parentNode(), + parentNode, + "The new node is inside the right parent" + ); + + const newNodeContainer = inspector.markup.getContainer(newNode); + + is( + parseInt(newNodeContainer.tagLine.getAttribute("aria-level"), 10), + PARENT_TREE_LEVEL + 1, + "The child aria-level is up to date." + ); +} + +function collapseNode(node, inspector) { + const container = inspector.markup.getContainer(node); + container.setExpanded(false); +} diff --git a/devtools/client/inspector/test/browser_inspector_addSidebarTab.js b/devtools/client/inspector/test/browser_inspector_addSidebarTab.js new file mode 100644 index 0000000000..d7c810d3c6 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_addSidebarTab.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const TEST_URI = + "data:text/html;charset=UTF-8," + "

browser_inspector_addtabbar.js

"; + +const CONTENT_TEXT = "Hello World!"; + +/** + * Verify InspectorPanel.addSidebarTab() API that can be consumed + * by DevTools extensions as well as DevTools code base. + */ +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + + const { Component, createFactory } = inspector.React; + const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js"); + const { div } = dom; + + info("Adding custom panel."); + + // Define custom side-panel. + class myTabPanel extends Component { + render() { + return div({ className: "my-tab-panel" }, CONTENT_TEXT); + } + } + let tabPanel = createFactory(myTabPanel); + + // Append custom panel (tab) into the Inspector panel and + // make sure it's selected by default (the last arg = true). + inspector.addSidebarTab("myPanel", "My Panel", tabPanel, true); + is( + inspector.sidebar.getCurrentTabID(), + "myPanel", + "My Panel is selected by default" + ); + + // Define another custom side-panel. + class myTabPanel2 extends Component { + render() { + return div({ className: "my-tab-panel2" }, "Another Content"); + } + } + tabPanel = createFactory(myTabPanel2); + + // Append second panel, but don't select it by default. + inspector.addSidebarTab("myPanel", "My Panel", tabPanel, false); + is( + inspector.sidebar.getCurrentTabID(), + "myPanel", + "My Panel is selected by default" + ); + + // Check the the panel content is properly rendered. + const tabPanelNode = inspector.panelDoc.querySelector(".my-tab-panel"); + is( + tabPanelNode.textContent, + CONTENT_TEXT, + "Side panel content has been rendered." + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs.js new file mode 100644 index 0000000000..01d53820ac --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs.js @@ -0,0 +1,191 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the breadcrumbs widget content is correct. + +const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html"; +const NODES = [ + { + selector: "#i1111", + ids: "i1 i11 i111 i1111", + nodeName: "div", + title: "div#i1111", + }, + { selector: "#i22", ids: "i2 i22", nodeName: "div", title: "div#i22" }, + { + selector: "#i2111", + ids: "i2 i21 i211 i2111", + nodeName: "div", + title: "div#i2111", + }, + { + selector: "#i21", + ids: "i2 i21 i211 i2111", + nodeName: "div", + title: "div#i21", + }, + { + selector: "#i22211", + ids: "i2 i22 i222 i2221 i22211", + nodeName: "div", + title: "div#i22211", + }, + { + selector: "#i22", + ids: "i2 i22 i222 i2221 i22211", + nodeName: "div", + title: "div#i22", + }, + { selector: "#i3", ids: "i3", nodeName: "article", title: "article#i3" }, + { + selector: "clipPath", + ids: "vector clip", + nodeName: "clipPath", + title: "clipPath#clip", + }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + const breadcrumbs = inspector.panelDoc.getElementById( + "inspector-breadcrumbs" + ); + const container = breadcrumbs.querySelector(".html-arrowscrollbox-inner"); + + for (const node of NODES) { + info("Testing node " + node.selector); + + info("Selecting node and waiting for breadcrumbs to update"); + const breadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + await selectNode(node.selector, inspector); + await breadcrumbsUpdated; + + info("Performing checks for node " + node.selector); + const buttonsLabelIds = node.ids.split(" "); + + // html > body > … + is( + container.childNodes.length, + buttonsLabelIds.length + 2, + "Node " + node.selector + ": Items count" + ); + + for (let i = 2; i < container.childNodes.length; i++) { + const expectedId = "#" + buttonsLabelIds[i - 2]; + const button = container.childNodes[i]; + const labelId = button.querySelector(".breadcrumbs-widget-item-id"); + is( + labelId.textContent, + expectedId, + "Node " + node.selector + ": button " + i + " matches" + ); + } + + const checkedButton = container.querySelector("button[checked]"); + const labelId = checkedButton.querySelector(".breadcrumbs-widget-item-id"); + const id = inspector.selection.nodeFront.id; + is( + labelId.textContent, + "#" + id, + "Node " + node.selector + ": selection matches" + ); + + const labelTag = checkedButton.querySelector( + ".breadcrumbs-widget-item-tag" + ); + is( + labelTag.textContent, + node.nodeName, + "Node " + node.selector + " has the expected tag name" + ); + + is( + checkedButton.getAttribute("title"), + node.title, + "Node " + node.selector + " has the expected tooltip" + ); + } + + await testPseudoElements(inspector, container); + await testComments(inspector, container); +}); + +async function testPseudoElements(inspector, container) { + info("Checking for pseudo elements"); + + const pseudoParent = await getNodeFront("#pseudo-container", inspector); + const children = await inspector.walker.children(pseudoParent); + is(children.nodes.length, 2, "Pseudo children returned from walker"); + + const beforeElement = children.nodes[0]; + let breadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + await selectNode(beforeElement, inspector); + await breadcrumbsUpdated; + is( + container.childNodes[3].textContent, + "::before", + "::before shows up in breadcrumb" + ); + + const afterElement = children.nodes[1]; + breadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + await selectNode(afterElement, inspector); + await breadcrumbsUpdated; + is( + container.childNodes[3].textContent, + "::after", + "::before shows up in breadcrumb" + ); +} + +async function testComments(inspector, container) { + info("Checking for comment elements"); + + const breadcrumbs = inspector.breadcrumbs; + const checkedButtonIndex = 2; + const button = container.childNodes[checkedButtonIndex]; + + let onBreadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + button.click(); + await onBreadcrumbsUpdated; + + is(breadcrumbs.currentIndex, checkedButtonIndex, "New button is selected"); + ok( + breadcrumbs.outer.hasAttribute("aria-activedescendant"), + "Active descendant must be set" + ); + + const comment = [...inspector.markup._containers].find( + ([node]) => node.nodeType === Node.COMMENT_NODE + )[0]; + + let onInspectorUpdated = inspector.once("inspector-updated"); + inspector.selection.setNodeFront(comment); + await onInspectorUpdated; + + is( + breadcrumbs.currentIndex, + -1, + "When comment is selected no breadcrumb should be checked" + ); + ok( + !breadcrumbs.outer.hasAttribute("aria-activedescendant"), + "Active descendant must not be set" + ); + + onInspectorUpdated = inspector.once("inspector-updated"); + onBreadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + button.click(); + await Promise.all([onInspectorUpdated, onBreadcrumbsUpdated]); + + is( + breadcrumbs.currentIndex, + checkedButtonIndex, + "Same button is selected again" + ); + ok( + breadcrumbs.outer.hasAttribute("aria-activedescendant"), + "Active descendant must be set again" + ); +} diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js new file mode 100644 index 0000000000..d94b640b6f --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_highlight_hover.js @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that hovering over nodes on the breadcrumb buttons in the inspector +// shows the highlighter over those nodes +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector, highlighterTestFront } = await openInspectorForURL( + "data:text/html;charset=utf-8,

foo

bar" + ); + const { waitForHighlighterTypeShown, waitForHighlighterTypeHidden } = + getHighlighterTestHelpers(inspector); + + info("Selecting the test node"); + await selectNode("span", inspector); + const bcButtons = inspector.breadcrumbs.container; + + let onNodeHighlighted = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + let button = bcButtons.childNodes[1]; + EventUtils.synthesizeMouseAtCenter( + button, + { type: "mousemove" }, + button.ownerDocument.defaultView + ); + await onNodeHighlighted; + + let isVisible = await highlighterTestFront.isHighlighting(); + ok(isVisible, "The highlighter is shown on a markup container hover"); + + ok( + await highlighterTestFront.assertHighlightedNode("body"), + "The highlighter highlights the right node" + ); + + const onNodeUnhighlighted = waitForHighlighterTypeHidden( + inspector.highlighters.TYPES.BOXMODEL + ); + // move outside of the breadcrumb trail to trigger unhighlight + EventUtils.synthesizeMouseAtCenter( + inspector.addNodeButton, + { type: "mousemove" }, + inspector.addNodeButton.ownerDocument.defaultView + ); + await onNodeUnhighlighted; + + onNodeHighlighted = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + button = bcButtons.childNodes[2]; + EventUtils.synthesizeMouseAtCenter( + button, + { type: "mousemove" }, + button.ownerDocument.defaultView + ); + await onNodeHighlighted; + + isVisible = await highlighterTestFront.isHighlighting(); + ok(isVisible, "The highlighter is shown on a markup container hover"); + + ok( + await highlighterTestFront.assertHighlightedNode("span"), + "The highlighter highlights the right node" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_keybinding.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_keybinding.js new file mode 100644 index 0000000000..5d795afdca --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_keybinding.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the breadcrumbs keybindings work. + +const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html"; +const TEST_DATA = [ + { + desc: "Pressing left should select the parent ", + key: "KEY_ArrowLeft", + newSelection: "body", + }, + { + desc: "Pressing left again should select the parent ", + key: "KEY_ArrowLeft", + newSelection: "html", + }, + { + desc: "Pressing left again should stay on , it's the first element", + key: "KEY_ArrowLeft", + newSelection: "html", + }, + { + desc: "Pressing right should go to ", + key: "KEY_ArrowRight", + newSelection: "body", + }, + { + desc: "Pressing right again should go to #i2", + key: "KEY_ArrowRight", + newSelection: "#i2", + }, + { + desc: "Pressing right again should stay on #i2, it's the last element", + key: "KEY_ArrowRight", + newSelection: "#i2", + }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + + info("Selecting the test node"); + await selectNode("#i2", inspector); + + info("Clicking on the corresponding breadcrumbs node to focus it"); + const container = inspector.panelDoc.getElementById("inspector-breadcrumbs"); + + const button = container.querySelector("button[checked]"); + button.click(); + + let currentSelection = "#id2"; + for (const { desc, key, newSelection } of TEST_DATA) { + info(desc); + + // If the selection will change, wait for the breadcrumb to update, + // otherwise continue. + let onUpdated = null; + if (newSelection !== currentSelection) { + info("Expecting a new node to be selected"); + onUpdated = inspector.once("breadcrumbs-updated"); + } + + EventUtils.synthesizeKey(key); + await onUpdated; + + const newNodeFront = await getNodeFront(newSelection, inspector); + is( + newNodeFront, + inspector.selection.nodeFront, + "The current selection is correct" + ); + is( + container.getAttribute("aria-activedescendant"), + container.querySelector("button[checked]").id, + "aria-activedescendant is set correctly" + ); + + currentSelection = newSelection; + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_keyboard_trap.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_keyboard_trap.js new file mode 100644 index 0000000000..e3046ce30e --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_keyboard_trap.js @@ -0,0 +1,92 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test ability to tab to and away from breadcrumbs using keyboard. + +const TEST_URL = URL_ROOT + "doc_inspector_breadcrumbs.html"; + +/** + * Test data has the format of: + * { + * desc {String} description for better logging + * focused {Boolean} flag, indicating if breadcrumbs contain focus + * key {String} key event's key + * options {?Object} optional event data such as shiftKey, etc + * } + */ +const TEST_DATA = [ + { + desc: "Move the focus away from breadcrumbs to a next focusable element", + focused: false, + key: "VK_TAB", + options: {}, + }, + { + desc: "Move the focus back to the breadcrumbs", + focused: true, + key: "VK_TAB", + options: { shiftKey: true }, + }, + { + desc: + "Move the focus back away from breadcrumbs to a previous focusable " + + "element", + focused: false, + key: "VK_TAB", + options: { shiftKey: true }, + }, + { + desc: "Move the focus back to the breadcrumbs", + focused: true, + key: "VK_TAB", + options: {}, + }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + const doc = inspector.panelDoc; + const { breadcrumbs } = inspector; + const { waitForHighlighterTypeShown } = getHighlighterTestHelpers(inspector); + + await selectNode("#i2", inspector); + + info("Clicking on the corresponding breadcrumbs node to focus it"); + const container = doc.getElementById("inspector-breadcrumbs"); + + const button = container.querySelector("button[checked]"); + const onHighlight = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + button.click(); + await onHighlight; + + // Ensure a breadcrumb is focused. + is(doc.activeElement, container, "Focus is on selected breadcrumb"); + is( + container.getAttribute("aria-activedescendant"), + button.id, + "aria-activedescendant is set correctly" + ); + + for (const { desc, focused, key, options } of TEST_DATA) { + info(desc); + + EventUtils.synthesizeKey(key, options); + // Wait until the keyPromise promise resolves. + await breadcrumbs.keyPromise; + + if (focused) { + is(doc.activeElement, container, "Focus is on selected breadcrumb"); + } else { + ok(!containsFocus(doc, container), "Focus is outside of breadcrumbs"); + } + is( + container.getAttribute("aria-activedescendant"), + button.id, + "aria-activedescendant is set correctly" + ); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js new file mode 100644 index 0000000000..91e3976907 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_mutations.js @@ -0,0 +1,282 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the breadcrumbs widget refreshes correctly when there are markup +// mutations (and that it doesn't refresh when those mutations don't change its +// output). + +const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html"; + +// Each item in the TEST_DATA array is a test case that should contain the +// following properties: +// - desc {String} A description of this test case (will be logged). +// - setup {Function*} A generator function (can yield promises) that sets up +// the test case. Useful for selecting a node before starting the test. +// - run {Function*} A generator function (can yield promises) that runs the +// actual test case, i.e, mutates the content DOM to cause the breadcrumbs +// to refresh, or not. +// - shouldRefresh {Boolean} Once the `run` function has completed, and the test +// has detected that the page has changed, this boolean instructs the test to +// verify if the breadcrumbs has refreshed or not. +// - output {Array} A list of strings for the text that should be found in each +// button after the test has run. +const TEST_DATA = [ + { + desc: "Adding a child at the end of the chain shouldn't change anything", + async setup(inspector) { + await selectNode("#i1111", inspector); + }, + async run({ walker, selection }) { + await walker.setInnerHTML(selection.nodeFront, "test"); + }, + shouldRefresh: false, + output: ["html", "body", "article#i1", "div#i11", "div#i111", "div#i1111"], + }, + { + desc: "Updating an ID to an displayed element should refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "#i1"); + await node.modifyAttributes([ + { + attributeName: "id", + newValue: "i1-changed", + }, + ]); + }, + shouldRefresh: true, + output: [ + "html", + "body", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Updating an class to a displayed element should refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "body"); + await node.modifyAttributes([ + { + attributeName: "class", + newValue: "test-class", + }, + ]); + }, + shouldRefresh: true, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: + "Updating a non id/class attribute to a displayed element should not " + + "refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "#i11"); + await node.modifyAttributes([ + { + attributeName: "name", + newValue: "value", + }, + ]); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Moving a child in an element that's not displayed should not refresh", + setup() {}, + async run({ walker }) { + // Re-append #i1211 as a last child of #i2. + const parent = await walker.querySelector(walker.rootNode, "#i2"); + const child = await walker.querySelector(walker.rootNode, "#i211"); + await walker.insertBefore(child, parent); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Moving an undisplayed child in a displayed element should not refresh", + setup() {}, + async run({ walker }) { + // Re-append #i2 in body (move it to the end). + const parent = await walker.querySelector(walker.rootNode, "body"); + const child = await walker.querySelector(walker.rootNode, "#i2"); + await walker.insertBefore(child, parent); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: + "Updating attributes on an element that's not displayed should not " + + "refresh", + setup() {}, + async run({ walker }) { + const node = await walker.querySelector(walker.rootNode, "#i2"); + await node.modifyAttributes([ + { + attributeName: "id", + newValue: "i2-changed", + }, + { + attributeName: "class", + newValue: "test-class", + }, + ]); + }, + shouldRefresh: false, + output: [ + "html", + "body.test-class", + "article#i1-changed", + "div#i11", + "div#i111", + "div#i1111", + ], + }, + { + desc: "Removing the currently selected node should refresh", + async setup(inspector) { + await selectNode("#i2-changed", inspector); + }, + async run({ walker, selection }) { + await walker.removeNode(selection.nodeFront); + }, + shouldRefresh: true, + output: ["html", "body.test-class"], + }, + { + desc: "Changing the class of the currently selected node should refresh", + setup() {}, + async run({ selection }) { + await selection.nodeFront.modifyAttributes([ + { + attributeName: "class", + newValue: "test-class-changed", + }, + ]); + }, + shouldRefresh: true, + output: ["html", "body.test-class-changed"], + }, + { + desc: "Changing the id of the currently selected node should refresh", + setup() {}, + async run({ selection }) { + await selection.nodeFront.modifyAttributes([ + { + attributeName: "id", + newValue: "new-id", + }, + ]); + }, + shouldRefresh: true, + output: ["html", "body#new-id.test-class-changed"], + }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + const breadcrumbs = inspector.panelDoc.getElementById( + "inspector-breadcrumbs" + ); + const container = breadcrumbs.querySelector(".html-arrowscrollbox-inner"); + const win = container.ownerDocument.defaultView; + + for (const { desc, setup, run, shouldRefresh, output } of TEST_DATA) { + info("Running test case: " + desc); + + info( + "Listen to markupmutation events from the inspector to know when a " + + "test case has completed" + ); + const onContentMutation = inspector.once("markupmutation"); + + info("Running setup"); + await setup(inspector); + + info("Listen to mutations on the breadcrumbs container"); + let hasBreadcrumbsMutated = false; + const observer = new win.MutationObserver(mutations => { + // Only consider childList changes or tooltiptext/checked attributes + // changes. The rest may be mutations caused by the overflowing arrowbox. + for (const { type, attributeName } of mutations) { + const isChildList = type === "childList"; + const isAttributes = + type === "attributes" && + (attributeName === "checked" || attributeName === "tooltiptext"); + if (isChildList || isAttributes) { + hasBreadcrumbsMutated = true; + break; + } + } + }); + observer.observe(container, { + attributes: true, + childList: true, + subtree: true, + }); + + info("Running the test case"); + await run(inspector); + + info("Wait until the page has mutated"); + await onContentMutation; + + if (shouldRefresh) { + info("The breadcrumbs is expected to refresh, so wait for it"); + await inspector.once("inspector-updated"); + } else { + ok( + !inspector._updateProgress, + "The breadcrumbs widget is not currently updating" + ); + } + + is(shouldRefresh, hasBreadcrumbsMutated, "Has the breadcrumbs refreshed?"); + observer.disconnect(); + + info("Check the output of the breadcrumbs widget"); + is(container.childNodes.length, output.length, "Correct number of buttons"); + for (let i = 0; i < container.childNodes.length; i++) { + is( + output[i], + container.childNodes[i].textContent, + "Text content for button " + i + " is correct" + ); + } + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_namespaced.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_namespaced.js new file mode 100644 index 0000000000..77b25a0ffa --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_namespaced.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the breadcrumbs widget content for namespaced elements is correct. + +const XHTML = ` + + + + + + + + + + + +`; + +const TEST_URI = "data:application/xhtml+xml;charset=utf-8," + encodeURI(XHTML); + +const NODES = [ + { + selector: "clipPath", + nodes: ["svg:svg", "svg:clipPath"], + nodeName: "svg:clipPath", + title: "svg:clipPath#clip", + }, + { + selector: "circle", + nodes: ["svg:svg", "svg:circle"], + nodeName: "svg:circle", + title: "svg:circle", + }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + const container = inspector.panelDoc.getElementById("inspector-breadcrumbs"); + + for (const node of NODES) { + info("Testing node " + node.selector); + + info("Selecting node and waiting for breadcrumbs to update"); + const breadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + await selectNode(node.selector, inspector); + await breadcrumbsUpdated; + + info("Performing checks for node " + node.selector); + + const checkedButton = container.querySelector("button[checked]"); + + const labelTag = checkedButton.querySelector( + ".breadcrumbs-widget-item-tag" + ); + is( + labelTag.textContent, + node.nodeName, + "Node " + node.selector + " has the expected tag name" + ); + + is( + checkedButton.getAttribute("title"), + node.title, + "Node " + node.selector + " has the expected tooltip" + ); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js new file mode 100644 index 0000000000..80540a528d --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_shadowdom.js @@ -0,0 +1,107 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the breadcrumbs widget refreshes correctly when there are markup +// mutations, even if the currently selected node is a slotted node in the shadow DOM. + +const TEST_URL = `data:text/html;charset=utf-8, + +
content
+
+ + `; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + const { markup } = inspector; + const breadcrumbs = inspector.panelDoc.getElementById( + "inspector-breadcrumbs" + ); + + info("Find and expand the test-component shadow DOM host."); + const hostFront = await getNodeFront("test-component", inspector); + const hostContainer = markup.getContainer(hostFront); + await expandContainer(inspector, hostContainer); + + info("Expand the shadow root"); + const shadowRootContainer = hostContainer.getChildContainers()[0]; + await expandContainer(inspector, shadowRootContainer); + + const slotContainer = shadowRootContainer.getChildContainers()[0]; + + info("Select the slot node and wait for the breadcrumbs update"); + const slotNodeFront = slotContainer.node; + let onBreadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + inspector.selection.setNodeFront(slotNodeFront); + await onBreadcrumbsUpdated; + + checkBreadcrumbsContent(breadcrumbs, [ + "html", + "body", + "test-component", + "#shadow-root", + "slot.slot-class", + ]); + + info("Expand the slot"); + await expandContainer(inspector, slotContainer); + + const slotChildContainers = slotContainer.getChildContainers(); + is(slotChildContainers.length, 1, "Expecting 1 slotted child"); + + info("Select the slotted node and wait for the breadcrumbs update"); + const slottedNodeFront = slotChildContainers[0].node; + onBreadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + inspector.selection.setNodeFront(slottedNodeFront); + await onBreadcrumbsUpdated; + + checkBreadcrumbsContent(breadcrumbs, [ + "html", + "body", + "test-component", + "div#el1", + ]); + + info( + "Update the classname of the real element and wait for the breadcrumbs update" + ); + onBreadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function () { + content.document.getElementById("el1").setAttribute("class", "test"); + }); + await onBreadcrumbsUpdated; + + checkBreadcrumbsContent(breadcrumbs, [ + "html", + "body", + "test-component", + "div#el1.test", + ]); +}); + +function checkBreadcrumbsContent(breadcrumbs, selectors) { + info("Check the output of the breadcrumbs widget"); + const container = breadcrumbs.querySelector(".html-arrowscrollbox-inner"); + is( + container.childNodes.length, + selectors.length, + "Correct number of buttons" + ); + for (let i = 0; i < container.childNodes.length; i++) { + is( + container.childNodes[i].textContent, + selectors[i], + "Text content for button " + i + " is correct" + ); + } +} diff --git a/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js b/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js new file mode 100644 index 0000000000..1267d23646 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_breadcrumbs_visibility.js @@ -0,0 +1,114 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that the start and end buttons on the breadcrumb trail bring the right +// crumbs into the visible area, for both LTR and RTL + +const { Toolbox } = require("resource://devtools/client/framework/toolbox.js"); + +const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs_visibility.html"; +const NODE_ONE = "div#aVeryLongIdToExceedTheBreadcrumbTruncationLimit"; +const NODE_TWO = "div#anotherVeryLongIdToExceedTheBreadcrumbTruncationLimit"; +const NODE_THREE = "div#aThirdVeryLongIdToExceedTheTruncationLimit"; +const NODE_FOUR = "div#aFourthOneToExceedTheTruncationLimit"; +const NODE_FIVE = "div#aFifthOneToExceedTheTruncationLimit"; +const NODE_SIX = "div#aSixthOneToExceedTheTruncationLimit"; +const NODE_SEVEN = "div#aSeventhOneToExceedTheTruncationLimit"; + +const NODES = [ + { action: "start", title: NODE_SIX }, + { action: "start", title: NODE_FIVE }, + { action: "start", title: NODE_FOUR }, + { action: "start", title: NODE_THREE }, + { action: "start", title: NODE_TWO }, + { action: "start", title: NODE_ONE }, + { action: "end", title: NODE_TWO }, + { action: "end", title: NODE_THREE }, + { action: "end", title: NODE_FOUR }, + { action: "end", title: NODE_FIVE }, + { action: "end", title: NODE_SIX }, +]; + +add_task(async function () { + const { inspector, toolbox } = await openInspectorForURL(TEST_URI); + + // No way to wait for scrolling to end (Bug 1172171) + // Rather than wait a max time; limit test to instant scroll behavior + inspector.breadcrumbs.arrowScrollBox.scrollBehavior = "instant"; + + await toolbox.switchHost(Toolbox.HostType.WINDOW); + const hostWindow = toolbox.win.parent; + const originalWidth = hostWindow.outerWidth; + const originalHeight = hostWindow.outerHeight; + const inspectorResized = inspector.once("inspector-resize"); + hostWindow.resizeTo(640, 300); + await inspectorResized; + + info("Testing transitions ltr"); + await pushPref("intl.l10n.pseudo", ""); + await testBreadcrumbTransitions(hostWindow, inspector); + + info("Testing transitions rtl"); + await pushPref("intl.l10n.pseudo", "bidi"); + await testBreadcrumbTransitions(hostWindow, inspector); + + hostWindow.resizeTo(originalWidth, originalHeight); +}); + +async function testBreadcrumbTransitions(hostWindow, inspector) { + const breadcrumbs = inspector.panelDoc.getElementById( + "inspector-breadcrumbs" + ); + const startBtn = breadcrumbs.querySelector(".scrollbutton-up"); + const endBtn = breadcrumbs.querySelector(".scrollbutton-down"); + const container = breadcrumbs.querySelector(".html-arrowscrollbox-inner"); + const breadcrumbsUpdated = inspector.once("breadcrumbs-updated"); + + info("Selecting initial node"); + await selectNode(NODE_SEVEN, inspector); + + // So just need to wait for a duration + await breadcrumbsUpdated; + const initialCrumb = container.querySelector("button[checked]"); + is( + isElementInViewport(hostWindow, initialCrumb), + true, + "initial element was visible" + ); + + for (const node of NODES) { + info("Checking for visibility of crumb " + node.title); + if (node.action === "end") { + info("Simulating click of end button"); + EventUtils.synthesizeMouseAtCenter(endBtn, {}, inspector.panelWin); + } else if (node.action === "start") { + info("Simulating click of start button"); + EventUtils.synthesizeMouseAtCenter(startBtn, {}, inspector.panelWin); + } + await breadcrumbsUpdated; + const selector = 'button[title="' + node.title + '"]'; + const relevantCrumb = container.querySelector(selector); + is( + isElementInViewport(hostWindow, relevantCrumb), + true, + node.title + " crumb is visible" + ); + } +} + +function isElementInViewport(window, el) { + const rect = el.getBoundingClientRect(); + + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= window.innerHeight && + rect.right <= window.innerWidth + ); +} + +registerCleanupFunction(function () { + // Restore the host type for other tests. + Services.prefs.clearUserPref("devtools.toolbox.host"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_delete-selected-node-01.js b/devtools/client/inspector/test/browser_inspector_delete-selected-node-01.js new file mode 100644 index 0000000000..8d7b3a1cfc --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_delete-selected-node-01.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test to ensure inspector handles deletion of selected node correctly. + +const TEST_URL = URL_ROOT + "doc_inspector_delete-selected-node-01.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + + const spanNodeFront = await getNodeFrontInFrames( + ["iframe", "span"], + inspector + ); + await selectNode(spanNodeFront, inspector); + + info("Removing selected element."); + const parentNode = spanNodeFront.parentNode(); + await spanNodeFront.inspectorFront.walker.removeNode(spanNodeFront); + + // Wait for the inspector to process the mutation + await inspector.once("inspector-updated"); + is( + inspector.selection.nodeFront, + parentNode, + "Parent node of selected got selected." + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js b/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js new file mode 100644 index 0000000000..6fc8c1031f --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_delete-selected-node-02.js @@ -0,0 +1,145 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that when nodes are being deleted in the page, the current selection +// and therefore the markup view, css rule view, computed view, font view, +// box model view, and breadcrumbs, reset accordingly to show the right node + +const TEST_PAGE = URL_ROOT + "doc_inspector_delete-selected-node-02.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_PAGE); + + await testManuallyDeleteSelectedNode(); + await testAutomaticallyDeleteSelectedNode(); + await testDeleteSelectedNodeContainerFrame(); + await testDeleteWithNonElementNode(); + + async function testManuallyDeleteSelectedNode() { + info( + "Selecting a node, deleting it via context menu and checking that " + + "its parent node is selected and breadcrumbs are updated." + ); + + await selectNode("#deleteManually", inspector); + const nodeToBeDeleted = inspector.selection.nodeFront; + await deleteNodeWithContextMenu(nodeToBeDeleted, inspector); + + info("Performing checks."); + await assertNodeSelectedAndPanelsUpdated( + "#selectedAfterDelete", + "li#selectedAfterDelete" + ); + } + + async function testAutomaticallyDeleteSelectedNode() { + info( + "Selecting a node, deleting it via javascript and checking that " + + "its parent node is selected and breadcrumbs are updated." + ); + + const div = await getNodeFront("#deleteAutomatically", inspector); + await selectNode(div, inspector); + + info("Deleting selected node via javascript."); + await inspector.walker.removeNode(div); + + info("Waiting for inspector to update."); + await inspector.once("inspector-updated"); + + info("Inspector updated, performing checks."); + await assertNodeSelectedAndPanelsUpdated( + "#deleteChildren", + "ul#deleteChildren" + ); + } + + async function testDeleteSelectedNodeContainerFrame() { + info( + "Selecting a node inside iframe, deleting the iframe via javascript " + + "and checking the parent node of the iframe is selected and " + + "breadcrumbs are updated." + ); + + info("Selecting an element inside iframe."); + await selectNodeInFrames(["#deleteIframe", "#deleteInIframe"], inspector); + + info("Deleting parent iframe node via javascript."); + const onInspectorUpdated = inspector.once("inspector-updated"); + + SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.document.querySelector("iframe#deleteIframe").remove(); + }); + + info("Waiting for inspector to update."); + await onInspectorUpdated; + + info("Inspector updated, performing checks."); + await assertNodeSelectedAndPanelsUpdated("body", "body"); + } + + async function testDeleteWithNonElementNode() { + info( + "Selecting a node, deleting it via context menu and checking that " + + "its parent node is selected and breadcrumbs are updated " + + "when the node is followed by a non-element node" + ); + + await selectNode("#deleteWithNonElement", inspector); + const nodeToBeDeleted = inspector.selection.nodeFront; + await deleteNodeWithContextMenu(nodeToBeDeleted, inspector); + + let expectedCrumbs = ["html", "body", "div#deleteToMakeSingleTextNode"]; + await assertNodeSelectedAndCrumbsUpdated(expectedCrumbs, Node.TEXT_NODE); + + // Delete node with key, as cannot delete text node with + // context menu at this time. + inspector.markup._frame.focus(); + EventUtils.synthesizeKey("KEY_Delete"); + await inspector.once("inspector-updated"); + + expectedCrumbs = ["html", "body", "div#deleteToMakeSingleTextNode"]; + await assertNodeSelectedAndCrumbsUpdated(expectedCrumbs, Node.ELEMENT_NODE); + } + + function assertNodeSelectedAndCrumbsUpdated( + expectedCrumbs, + expectedNodeType + ) { + info("Performing checks"); + const actualNodeType = inspector.selection.nodeFront.nodeType; + is(actualNodeType, expectedNodeType, "The node has the right type"); + + const breadcrumbs = inspector.panelDoc.querySelectorAll( + "#inspector-breadcrumbs .html-arrowscrollbox-inner > *" + ); + is( + breadcrumbs.length, + expectedCrumbs.length, + "Have the correct number of breadcrumbs" + ); + for (let i = 0; i < breadcrumbs.length; i++) { + is( + breadcrumbs[i].textContent, + expectedCrumbs[i], + "Text content for button " + i + " is correct" + ); + } + } + + async function assertNodeSelectedAndPanelsUpdated(selector, crumbLabel) { + const nodeFront = await getNodeFront(selector, inspector); + is(inspector.selection.nodeFront, nodeFront, "The right node is selected"); + + const breadcrumbs = inspector.panelDoc.querySelector( + "#inspector-breadcrumbs .html-arrowscrollbox-inner" + ); + is( + breadcrumbs.querySelector("button[checked=true]").textContent, + crumbLabel, + "The right breadcrumb is selected" + ); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_delete-selected-node-03.js b/devtools/client/inspector/test/browser_inspector_delete-selected-node-03.js new file mode 100644 index 0000000000..e8d1c2bb9e --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_delete-selected-node-03.js @@ -0,0 +1,24 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test to ensure inspector can handle destruction of selected node inside an iframe. + +const TEST_URL = URL_ROOT + "doc_inspector_delete-selected-node-01.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + + info("Select a node inside the iframe"); + await selectNodeInFrames(["iframe", "span"], inspector); + + info("Removing iframe."); + const onInspectorUpdated = inspector.once("inspector-updated"); + SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + content.document.querySelector("iframe").remove(); + }); + await onInspectorUpdated; + + const body = await getNodeFront("body", inspector); + is(inspector.selection.nodeFront, body, "Selection is now the body node"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_delete_node_in_frame.js b/devtools/client/inspector/test/browser_inspector_delete_node_in_frame.js new file mode 100644 index 0000000000..53b4b11277 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_delete_node_in_frame.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const TEST_URL_1 = `https://example.com/document-builder.sjs?html= +`; +const TEST_URL_2 = `https://example.com/document-builder.sjs?html=
`; + +// Test that deleting a node in a same-process iframe and doing a navigation +// does not freeze the browser or break the toolbox. +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL_1); + + info("Select a node in a same-process iframe"); + const node = await selectNodeInFrames(["iframe", "div"], inspector); + const parentNode = node.parentNode(); + + info("Removing selected element with the context menu."); + await deleteNodeWithContextMenu(node, inspector); + + is( + inspector.selection.nodeFront, + parentNode, + "The parent node of the deleted node was selected." + ); + + const onInspectorReloaded = inspector.once("reloaded"); + await navigateTo(TEST_URL_2); + await onInspectorReloaded; + + const url2NodeFront = await getNodeFront("#url2", inspector); + ok(url2NodeFront, "Can retrieve a node front after the navigation"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_destroy-after-navigation.js b/devtools/client/inspector/test/browser_inspector_destroy-after-navigation.js new file mode 100644 index 0000000000..2040bccae4 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_destroy-after-navigation.js @@ -0,0 +1,23 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Testing that closing the inspector after navigating to a page doesn't fail. + +const URL_1 = "data:text/plain;charset=UTF-8,abcde"; +const URL_2 = "data:text/plain;charset=UTF-8,12345"; + +add_task(async function () { + const { toolbox } = await openInspectorForURL(URL_1); + + await navigateTo(URL_2); + + info("Destroying toolbox"); + try { + await toolbox.destroy(); + ok(true, "Toolbox destroyed"); + } catch (e) { + ok(false, "An exception occured while destroying toolbox"); + console.error(e); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_destroy-before-ready.js b/devtools/client/inspector/test/browser_inspector_destroy-before-ready.js new file mode 100644 index 0000000000..4df83e3a46 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_destroy-before-ready.js @@ -0,0 +1,26 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that switching to the inspector panel and not waiting for it to be fully +// loaded doesn't fail the test with unhandled rejected promises. + +add_task(async function () { + // At least one assertion is needed to avoid failing the test, but really, + // what we're interested in is just having the test pass when switching to the + // inspector. + ok(true); + + await addTab("data:text/html;charset=utf-8,test inspector destroy"); + + info("Open the toolbox on the debugger panel"); + const toolbox = await gDevTools.showToolboxForTab(gBrowser.selectedTab, { + toolId: "jsdebugger", + }); + + info("Switch to the inspector panel and immediately end the test"); + const onInspectorSelected = toolbox.once("inspector-selected"); + toolbox.selectTool("inspector"); + await onInspectorSelected; +}); diff --git a/devtools/client/inspector/test/browser_inspector_expand-collapse.js b/devtools/client/inspector/test/browser_inspector_expand-collapse.js new file mode 100644 index 0000000000..dd39d5898f --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_expand-collapse.js @@ -0,0 +1,68 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that context menu items exapnd all and collapse are shown properly. + +const TEST_URL = + "data:text/html;charset=utf-8," + + "
"; + +add_task(async function () { + // Test is often exceeding time-out threshold, similar to Bug 1137765 + requestLongerTimeout(2); + + const { inspector } = await openInspectorForURL(TEST_URL); + + info("Selecting the parent node"); + + const front = await getNodeFrontForSelector("#parent-node", inspector); + + await selectNode(front, inspector); + + info("Simulating context menu click on the selected node container."); + let allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: getContainerForNodeFront(front, inspector).tagLine, + }); + let nodeMenuCollapseElement = allMenuItems.find( + item => item.id === "node-menu-collapse" + ); + let nodeMenuExpandElement = allMenuItems.find( + item => item.id === "node-menu-expand" + ); + + ok(nodeMenuCollapseElement.disabled, "Collapse option is disabled"); + ok(!nodeMenuExpandElement.disabled, "ExpandAll option is enabled"); + + info("Testing whether expansion works properly"); + nodeMenuExpandElement.click(); + + info("Waiting for expansion to occur"); + await waitForMultipleChildrenUpdates(inspector); + const markUpContainer = getContainerForNodeFront(front, inspector); + ok(markUpContainer.expanded, "node has been successfully expanded"); + + // reselecting node after expansion + await selectNode(front, inspector); + + info("Testing whether collapse works properly"); + info("Simulating context menu click on the selected node container."); + allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: getContainerForNodeFront(front, inspector).tagLine, + }); + nodeMenuCollapseElement = allMenuItems.find( + item => item.id === "node-menu-collapse" + ); + nodeMenuExpandElement = allMenuItems.find( + item => item.id === "node-menu-expand" + ); + + ok(!nodeMenuCollapseElement.disabled, "Collapse option is enabled"); + ok(!nodeMenuExpandElement.disabled, "ExpandAll option is enabled"); + nodeMenuCollapseElement.click(); + + info("Waiting for collapse to occur"); + await waitForMultipleChildrenUpdates(inspector); + ok(!markUpContainer.expanded, "node has been successfully collapsed"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_eyedropper_ruleview.js b/devtools/client/inspector/test/browser_inspector_eyedropper_ruleview.js new file mode 100644 index 0000000000..ba749fbf10 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_eyedropper_ruleview.js @@ -0,0 +1,50 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const CSS_URI = URL_ROOT + "style_inspector_eyedropper_ruleview.css"; + +const TEST_URI = ` + +`; + +// Test that opening the eyedropper before opening devtools doesn't break links +// in the ruleview. +add_task(async function () { + await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI)); + + const onPickerCommandHandled = new Promise(r => { + const listener = subject => { + Services.obs.removeObserver(listener, "color-picker-command-handled"); + r(subject.wrappedJSObject); + }; + Services.obs.addObserver(listener, "color-picker-command-handled"); + }); + + info("Trigger the eyedropper command"); + const menu = document.getElementById("menu_eyedropper"); + menu.doCommand(); + + info("Wait for the color-picker-command-handled observable"); + const targetFront = await onPickerCommandHandled; + + info("Wait for the eye dropper to be visible"); + const highlighterTestFront = await targetFront.getFront("highlighterTest"); + await asyncWaitUntil(() => highlighterTestFront.isEyeDropperVisible()); + + info("Cancel the eye dropper and wait for the target to be destroyed"); + EventUtils.synthesizeKey("KEY_Escape"); + await waitFor(() => targetFront.isDestroyed()); + + const { inspector, view } = await openRuleView(); + + await selectNode("body", inspector); + + const linkText = getRuleViewLinkTextByIndex(view, 1); + is( + linkText, + "style_inspector_eyedropper_ruleview.css:1", + "link text at index 1 has the correct link." + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_fission_frame.js b/devtools/client/inspector/test/browser_inspector_fission_frame.js new file mode 100644 index 0000000000..fda1471248 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_fission_frame.js @@ -0,0 +1,38 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +/** + * bug 1673627 - Test that remote iframes with short inline content, + * get their inner remote document displayed instead of the inlineTextChild content. + */ +const TEST_URI = `data:text/html,
`; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + const tree = ` + id="root" + iframe + #document + html + head + body + id="com"`; + await assertMarkupViewAsTree(tree, "#root", inspector); +}); + +// Test regular remote frames +const FRAME_URL = `https://example.com/document-builder.sjs?html=
com`; +const TEST_REMOTE_FRAME = `https://example.org/document-builder.sjs?html=
`; +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_REMOTE_FRAME); + const tree = ` + id="org-root" + iframe + #document + html + head + body + com`; + await assertMarkupViewAsTree(tree, "#org-root", inspector); +}); diff --git a/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js b/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js new file mode 100644 index 0000000000..7ac3b51586 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js @@ -0,0 +1,163 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +const EXAMPLE_COM_URI = + "https://example.com/document-builder.sjs?html=
com"; +const EXAMPLE_NET_URI = + "https://example.net/document-builder.sjs?html=
net"; + +const ORG_URL_ROOT = URL_ROOT.replace("example.com", "example.org"); +const TEST_ORG_URI = + ORG_URL_ROOT + "doc_inspector_fission_frame_navigation.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_ORG_URI); + const tree = ` + id="root" + iframe + #document + html + head + body + id="org"`; + // Note: the assertMarkupViewAsTree uses very high level APIs and is similar + // to what should happen when a user interacts with the markup view. + // It is important to avoid explicitly fetching walkers or node fronts during + // the test, as it might cause reparenting of remote frames and make the test + // succeed without actually testing that the feature works correctly. + await assertMarkupViewAsTree(tree, "#root", inspector); + + await navigateIframeTo(inspector, EXAMPLE_COM_URI); + const treeAfterLoadingCom = ` + id="root" + iframe + #document + html + head + body + id="com"`; + await assertMarkupViewAsTree(treeAfterLoadingCom, "#root", inspector); + + await navigateIframeTo(inspector, EXAMPLE_NET_URI); + const treeAfterLoadingNet = ` + id="root" + iframe + #document + html + head + body + id="net"`; + await assertMarkupViewAsTree(treeAfterLoadingNet, "#root", inspector); +}); + +/** + * This test will check the behavior when navigating a frame which is not + * visible in the markup view, because its parentNode has not been expanded yet. + * + * We expect a root-node resource to be emitted, but ideally we should not + * initialize the walker and inspector actors for the target of this root-node + * resource. + */ +add_task(async function navigateFrameNotExpandedInMarkupView() { + if (!isFissionEnabled()) { + // This test only makes sense with Fission and remote frames, otherwise the + // root-node resource will not be emitted for a frame navigation. + return; + } + + const { inspector } = await openInspectorForURL(TEST_ORG_URI); + const resourceCommand = inspector.toolbox.resourceCommand; + + // At this stage the expected layout of the markup view is + // v html (expanded) + // v body (expanded) + // > p (collapsed) + // > div (collapsed) + // + // The iframe we are about to navigate is therefore hidden and we are not + // watching it - ie, it is not in the list of known NodeFronts/Actors. + const resource = await navigateIframeTo(inspector, EXAMPLE_COM_URI); + + is( + resource.resourceType, + resourceCommand.TYPES.ROOT_NODE, + "A resource with resourceType ROOT_NODE was received when navigating" + ); + + // This highlights what doesn't work with the current approach. + // Since the root-node resource is a NodeFront watched via the WalkerFront, + // watching it for a new remote frame target implies initializing the + // inspector & walker fronts. + // + // If the resource was not a front (eg ContentDOMreference?) and was emitted + // by the target actor instead of the walker actor, the client can decide if + // the inspector and walker fronts should be initialized or not. + // + // In this test scenario, the children of the iframe were not known by the + // inspector before this navigation. Per the explanation above, the new target + // should not have an already instantiated inspector front. + // + // This should be fixed when implementing the RootNode resource on the server + // in https://bugzilla.mozilla.org/show_bug.cgi?id=1644190 + todo( + !resource.targetFront.getCachedFront("inspector"), + "The inspector front for the new target should not be initialized" + ); +}); + +async function navigateIframeTo(inspector, url) { + info("Navigate the test iframe to " + url); + + const { commands } = inspector; + const { resourceCommand } = inspector.toolbox; + const onTargetProcessed = waitForTargetProcessed(commands, url); + + const { onResource: onNewRoot } = await resourceCommand.waitForNextResource( + resourceCommand.TYPES.ROOT_NODE, + { + ignoreExistingResources: true, + } + ); + + info("Update the src attribute of the iframe tag"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [url], function (_url) { + content.document.querySelector("iframe").setAttribute("src", _url); + }); + + info("Wait for frameLoad/newRoot to resolve"); + const newRootResult = await onNewRoot; + + info("Wait for pending children updates"); + await inspector.markup._waitForChildren(); + + if (isFissionEnabled()) { + info("Wait until the new target has been processed by TargetCommand"); + await onTargetProcessed; + } + + // Note: the newRootResult changes when the test runs with or without fission. + return newRootResult; +} + +/** + * Returns a promise that waits until the provided commands's TargetCommand has fully + * processed a target with the provided URL. + * This will avoid navigating again before the new resource command have fully + * attached to the new target. + */ +function waitForTargetProcessed(commands, url) { + return new Promise(resolve => { + const onTargetProcessed = targetFront => { + if (targetFront.url !== encodeURI(url)) { + return; + } + commands.targetCommand.off( + "processed-available-target", + onTargetProcessed + ); + resolve(); + }; + commands.targetCommand.on("processed-available-target", onTargetProcessed); + }); +} diff --git a/devtools/client/inspector/test/browser_inspector_fission_switch_target.js b/devtools/client/inspector/test/browser_inspector_fission_switch_target.js new file mode 100644 index 0000000000..f8c7edc5dd --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_fission_switch_target.js @@ -0,0 +1,31 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test target-switching with the inspector. +// This test should check both fission and non-fission target switching. + +const PARENT_PROCESS_URI = "about:robots"; +const EXAMPLE_COM_URI = + "https://example.com/document-builder.sjs?html=
com"; +const EXAMPLE_ORG_URI = + "https://example.org/document-builder.sjs?html=
org"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(PARENT_PROCESS_URI); + const aboutRobotsNodeFront = await getNodeFront(".title-text", inspector); + ok(!!aboutRobotsNodeFront, "Can retrieve a node front from about:robots"); + + info("Navigate to " + EXAMPLE_COM_URI); + await navigateTo(EXAMPLE_COM_URI); + const comNodeFront = await getNodeFront("#com", inspector); + ok(!!comNodeFront, "Can retrieve a node front from example.com"); + + await navigateTo(EXAMPLE_ORG_URI); + const orgNodeFront = await getNodeFront("#org", inspector); + ok(!!orgNodeFront, "Can retrieve a node front from example.org"); + + await navigateTo(PARENT_PROCESS_URI); + const aboutRobotsNodeFront2 = await getNodeFront(".title-text", inspector); + ok(!!aboutRobotsNodeFront2, "Can retrieve a node front from about:robots"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-01.js b/devtools/client/inspector/test/browser_inspector_highlighter-01.js new file mode 100644 index 0000000000..2cdf530073 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-01.js @@ -0,0 +1,43 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that hovering over nodes in the markup-view shows the highlighter over +// those nodes +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector, highlighterTestFront } = await openInspectorForURL( + "data:text/html;charset=utf-8,

foo

bar" + ); + const { waitForHighlighterTypeShown } = getHighlighterTestHelpers(inspector); + + let isVisible = !!inspector.highlighters.getActiveHighlighter( + inspector.highlighters.TYPES.BOXMODEL + ); + ok(!isVisible, "The highlighter is hidden by default"); + + info("Selecting the test node"); + await selectNode("span", inspector); + const container = await getContainerForSelector("h1", inspector); + + const onHighlight = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + + EventUtils.synthesizeMouseAtCenter( + container.tagLine, + { type: "mousemove" }, + inspector.markup.doc.defaultView + ); + await onHighlight; + + isVisible = await highlighterTestFront.isHighlighting(); + ok(isVisible, "The highlighter is shown on a markup container hover"); + + ok( + await highlighterTestFront.assertHighlightedNode("h1"), + "The highlighter highlights the right node" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-02.js b/devtools/client/inspector/test/browser_inspector_highlighter-02.js new file mode 100644 index 0000000000..d112dcb290 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-02.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that the highlighter is correctly displayed over a variety of elements + +const TEST_URI = URL_ROOT + "doc_inspector_highlighter.html"; + +add_task(async function () { + const { inspector, highlighterTestFront } = await openInspectorForURL( + TEST_URI + ); + + info("Selecting the simple, non-transformed DIV"); + await selectAndHighlightNode("#simple-div", inspector); + + let isVisible = await highlighterTestFront.isHighlighting(); + ok(isVisible, "The highlighter is shown"); + ok( + await highlighterTestFront.assertHighlightedNode("#simple-div"), + "The highlighter's outline corresponds to the simple div" + ); + await isNodeCorrectlyHighlighted(highlighterTestFront, "#simple-div"); + + info("Selecting the rotated DIV"); + await selectAndHighlightNode("#rotated-div", inspector); + + isVisible = await highlighterTestFront.isHighlighting(); + ok(isVisible, "The highlighter is shown"); + info( + "Check that the highlighter is displayed at the expected position for rotated div" + ); + await isNodeCorrectlyHighlighted(highlighterTestFront, "#rotated-div"); + + info("Selecting the zero width height DIV"); + await selectAndHighlightNode("#widthHeightZero-div", inspector); + + isVisible = await highlighterTestFront.isHighlighting(); + ok(isVisible, "The highlighter is shown"); + info( + "Check that the highlighter is displayed at the expected position for a zero width height div" + ); + await isNodeCorrectlyHighlighted( + highlighterTestFront, + "#widthHeightZero-div" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-03.js b/devtools/client/inspector/test/browser_inspector_highlighter-03.js new file mode 100644 index 0000000000..8be820248b --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-03.js @@ -0,0 +1,125 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that iframes are correctly highlighted. + +const IFRAME_SRC = ` + hello from iframe`; + +const DOCUMENT_SRC = ` + + + `; + +const TEST_URI = "data:text/html;charset=utf-8," + DOCUMENT_SRC; + +add_task(async function () { + const { inspector, toolbox, highlighterTestFront } = + await openInspectorForURL(TEST_URI); + + info("Waiting for box mode to show."); + const topLevelBodyNodeFront = await getNodeFront("body", inspector); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + topLevelBodyNodeFront + ); + + info("Waiting for element picker to become active."); + await startPicker(toolbox); + + info("Check that hovering iframe padding does highlight the iframe element"); + // the iframe has 13px of padding, so hovering at [1,1] should be enough. + await hoverElement(inspector, "iframe", 1, 1); + + await isNodeCorrectlyHighlighted(highlighterTestFront, "iframe"); + + info("Scrolling the document"); + await setContentPageElementProperty( + "iframe", + "style", + "margin-bottom: 2000px" + ); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => + content.scrollBy(0, 40) + ); + + // target the body within the iframe + const iframeBodySelector = ["iframe", "body"]; + let iframeHighlighterTestFront = highlighterTestFront; + let bodySelectorWithinHighlighterEnv = iframeBodySelector; + + if (isFissionEnabled() || isEveryFrameTargetEnabled()) { + const target = toolbox.commands.targetCommand + .getAllTargets([toolbox.commands.targetCommand.TYPES.FRAME]) + .find(t => t.url.startsWith("https://example.com")); + + // We need to retrieve the highlighterTestFront for the frame target. + iframeHighlighterTestFront = await getHighlighterTestFront(toolbox, { + target, + }); + + bodySelectorWithinHighlighterEnv = ["body"]; + } + + info("Check that hovering the iframe highlights the expected element"); + await hoverElement(inspector, iframeBodySelector, 40, 40); + + ok( + await iframeHighlighterTestFront.assertHighlightedNode( + bodySelectorWithinHighlighterEnv + ), + "highlighter is shown on the iframe body" + ); + await isNodeCorrectlyHighlighted( + iframeHighlighterTestFront, + iframeBodySelector + ); + + info("Scrolling the document up"); + // scroll up so we can inspect the top level document again + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => + content.scrollTo(0, 0) + ); + + info("Check that hovering iframe padding again does work"); + // the iframe has 13px of padding, so hovering at [1,1] should be enough. + await hoverElement(inspector, "iframe", 1, 1); + await isNodeCorrectlyHighlighted(highlighterTestFront, "iframe"); + + info("And finally check that hovering the iframe again does work"); + info("Check that hovering the iframe highlights the expected element"); + await hoverElement(inspector, iframeBodySelector, 40, 40); + + ok( + await iframeHighlighterTestFront.assertHighlightedNode( + bodySelectorWithinHighlighterEnv + ), + "highlighter is shown on the iframe body" + ); + await isNodeCorrectlyHighlighted( + iframeHighlighterTestFront, + iframeBodySelector + ); + + info("Stop the element picker."); + await toolbox.nodePicker.stop({ canceled: true }); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-04.js b/devtools/client/inspector/test/browser_inspector_highlighter-04.js new file mode 100644 index 0000000000..eb8e2b3deb --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-04.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Check that various highlighter elements exist. + +const TEST_URL = "data:text/html;charset=utf-8,
test
"; + +// IDs of all highlighter elements that we expect to find in the canvasFrame. +const ELEMENTS = [ + "box-model-root", + "box-model-elements", + "box-model-margin", + "box-model-border", + "box-model-padding", + "box-model-content", + "box-model-guide-top", + "box-model-guide-right", + "box-model-guide-bottom", + "box-model-guide-left", + "box-model-infobar-container", + "box-model-infobar-tagname", + "box-model-infobar-id", + "box-model-infobar-classes", + "box-model-infobar-pseudo-classes", + "box-model-infobar-dimensions", +]; + +add_task(async function () { + const { inspector, highlighterTestFront } = await openInspectorForURL( + TEST_URL + ); + + info("Show the box-model highlighter"); + const divFront = await getNodeFront("div", inspector); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + divFront + ); + + for (const id of ELEMENTS) { + const foundId = await highlighterTestFront.getHighlighterNodeAttribute( + id, + "id" + ); + is(foundId, id, "Element " + id + " found"); + } + + info("Hide the box-model highlighter"); + await inspector.highlighters.hideHighlighterType( + inspector.highlighters.TYPES.BOXMODEL + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-05.js b/devtools/client/inspector/test/browser_inspector_highlighter-05.js new file mode 100644 index 0000000000..4c4fd4781c --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-05.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 http://mozilla.org/MPL/2.0/. */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +"use strict"; + +// This is testing that the Anonymous Content is properly inserted into the document. +// Usually that is happening during the "interactive" state of the document, to have them +// ready as soon as possible. +// However, in some conditions, that's not possible since we don't have access yet to +// the `CustomContentContainer`, that is used to add the Anonymous Content. +// That can happen if the page has some external resource, as , that takes time +// to load and / or returns the wrong content. This is not happening, for instance, with +// images. +// +// In those case, we want to be sure that if we're not able to insert the Anonymous +// Content at the "interactive" state, we're doing so when the document is loaded. +// +// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1365075 + +const server = createTestHTTPServer(); +const filepath = "/slow.css"; +const cssuri = `http://localhost:${server.identity.primaryPort}${filepath}`; + +// Register a slow css file handler so we can simulate a long loading time. +server.registerContentType("css", "text/css"); +server.registerPathHandler(filepath, (metadata, response) => { + info("CSS has been requested"); + response.processAsync(); + setTimeout(() => { + info("CSS is responding"); + response.finish(); + }, 2000); +}); + +const TEST_URL = + "data:text/html," + + encodeURIComponent(` + + + + + + +

Slow page

+ + +`); + +add_task(async function () { + info("Open the inspector to a blank page."); + const { inspector } = await openInspectorForURL("about:blank"); + + info("Navigate to the test url and waiting for the page to be loaded."); + await navigateTo(TEST_URL); + + info("Shows the box model highligher for the

node."); + const nodeFront = await getNodeFront("p", inspector); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + nodeFront + ); + + info("Check the node is highlighted."); + const highlighterTestFront = await getHighlighterTestFront(inspector.toolbox); + is( + await highlighterTestFront.isHighlighting(), + true, + "Box Model highlighter is working as expected." + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-06.js b/devtools/client/inspector/test/browser_inspector_highlighter-06.js new file mode 100644 index 0000000000..11f1cee1a1 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-06.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that the browser remembers the previous scroll position after reload, even with +// Inspector opened - Bug 1382341. +// NOTE: Using a real file instead data: URL since the bug won't happen on data: URL +const TEST_URI = URL_ROOT + "doc_inspector_highlighter_scroll.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + + await scrollContentPageNodeIntoView(gBrowser.selectedBrowser, "a"); + await selectAndHighlightNode("a", inspector); + + const markupLoaded = inspector.once("markuploaded"); + + const y = await getPageYOffset(); + isnot(y, 0, "window scrolled vertically."); + + info("Reloading page."); + await reloadBrowser(); + + info("Waiting for markupview to load after reload."); + await markupLoaded; + + const newY = await getPageYOffset(); + is(y, newY, "window remember the previous scroll position."); +}); + +async function getPageYOffset() { + return SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => content.pageYOffset + ); +} diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-07.js b/devtools/client/inspector/test/browser_inspector_highlighter-07.js new file mode 100644 index 0000000000..cb8c11cdd0 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-07.js @@ -0,0 +1,90 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/shared-head.js", + this +); + +// Test that the highlighter works when the debugger is paused. + +function debuggerIsPaused(dbg) { + return !!dbg.selectors.getIsPaused(dbg.selectors.getCurrentThread()); +} + +function waitForPaused(dbg) { + return new Promise(resolve => { + if (debuggerIsPaused(dbg)) { + resolve(); + return; + } + + const unsubscribe = dbg.store.subscribe(() => { + if (debuggerIsPaused(dbg)) { + unsubscribe(); + resolve(); + } + }); + }); +} + +const IFRAME_SRC = + "hello from iframe"; + +const DOCUMENT_SRC = + "" + + "" + + "" + + "" + + ""; + +const TEST_URI = "data:text/html;charset=utf-8," + DOCUMENT_SRC; + +add_task(async function () { + const { inspector, toolbox, highlighterTestFront, tab } = + await openInspectorForURL(TEST_URI); + + await gDevTools.showToolboxForTab(tab, { toolId: "jsdebugger" }); + const dbg = await createDebuggerContext(toolbox); + + await waitForPaused(dbg); + + await gDevTools.showToolboxForTab(tab, { toolId: "inspector" }); + + // Needed to get this test to pass consistently :( + await waitForTime(1000); + + info("Waiting for box mode to show."); + const body = await getNodeFront("body", inspector); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + body + ); + + info("Waiting for element picker to become active."); + await startPicker(toolbox); + + info("Moving mouse over iframe padding."); + await hoverElement(inspector, "iframe", 1, 1); + + info("Performing checks"); + await isNodeCorrectlyHighlighted(highlighterTestFront, "iframe"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-08.js b/devtools/client/inspector/test/browser_inspector_highlighter-08.js new file mode 100644 index 0000000000..49b544a325 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-08.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that performing multiple requests to highlight nodes or to hide the highlighter, +// without waiting for the former ones to complete, still works well. +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector, highlighterTestFront } = await openInspectorForURL( + "data:text/html," + ); + const html = await getNodeFront("html", inspector); + const body = await getNodeFront("body", inspector); + const type = inspector.highlighters.TYPES.BOXMODEL; + const getActiveHighlighter = () => { + return inspector.highlighters.getActiveHighlighter(type); + }; + is(getActiveHighlighter(), null, "The highlighter is hidden by default"); + + info("Highlight , and hide the highlighter immediately after"); + await Promise.all([ + inspector.highlighters.showHighlighterTypeForNode(type, body), + inspector.highlighters.hideHighlighterType(type), + ]); + is(getActiveHighlighter(), null, "The highlighter is hidden"); + + info("Highlight , then , then again, synchronously"); + await Promise.all([ + inspector.highlighters.showHighlighterTypeForNode(type, body), + inspector.highlighters.showHighlighterTypeForNode(type, html), + inspector.highlighters.showHighlighterTypeForNode(type, body), + ]); + ok( + await highlighterTestFront.assertHighlightedNode("body"), + "The highlighter highlights " + ); + + info("Highlight , then , then again, synchronously"); + await Promise.all([ + inspector.highlighters.showHighlighterTypeForNode(type, html), + inspector.highlighters.showHighlighterTypeForNode(type, body), + inspector.highlighters.showHighlighterTypeForNode(type, html), + ]); + ok( + await highlighterTestFront.assertHighlightedNode("html"), + "The highlighter highlights " + ); + + info("Hide the highlighter, and highlight immediately after"); + await Promise.all([ + inspector.highlighters.hideHighlighterType(type), + inspector.highlighters.showHighlighterTypeForNode(type, body), + ]); + ok( + await highlighterTestFront.assertHighlightedNode("body"), + "The highlighter highlights " + ); + + info("Highlight , and hide the highlighter immediately after"); + await Promise.all([ + inspector.highlighters.showHighlighterTypeForNode(type, html), + inspector.highlighters.hideHighlighterType(type), + ]); + is(getActiveHighlighter(), null, "The highlighter is hidden"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_01.js b/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_01.js new file mode 100644 index 0000000000..f0daf4bbe8 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_01.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that highlighters can be configured to automatically hide after a delay. +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector } = await openInspectorForURL( + "data:text/html;charset=utf-8,

TEST 1

" + ); + const { waitForHighlighterTypeShown, waitForHighlighterTypeHidden } = + getHighlighterTestHelpers(inspector); + + const HALF_SECOND = 500; + const nodeFront = await getNodeFront("#one", inspector); + const onHighlighterShown = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + const onHighlighterHidden = waitForHighlighterTypeHidden( + inspector.highlighters.TYPES.BOXMODEL + ); + + info("Show Box Model Highlighter, then hide after half a second"); + inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + nodeFront, + { duration: HALF_SECOND } + ); + + info("Wait for Box Model Highlighter shown"); + await onHighlighterShown; + info("Wait for Box Model Highlighter hidden"); + await onHighlighterHidden; +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_02.js b/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_02.js new file mode 100644 index 0000000000..bebc87f593 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_02.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that configuring two different highlighters to autohide +// will not overwrite each other's timers. +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector } = await openInspectorForURL( + "data:text/html;charset=utf-8,

TEST 1

" + ); + + const HALF_SECOND = 500; + const nodeFront = await getNodeFront("#one", inspector); + + const waitForShowEvents = waitForNEvents( + inspector.highlighters, + "highlighter-shown", + 2 + ); + const waitForHideEvents = waitForNEvents( + inspector.highlighters, + "highlighter-hidden", + 2 + ); + + info("Show Box Model Highlighter, then hide after half a second"); + inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + nodeFront, + { duration: HALF_SECOND } + ); + + info("Show Selector Highlighter, then hide after half a second"); + inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.SELECTOR, + nodeFront, + { selector: "#one", duration: HALF_SECOND } + ); + + info("Waiting for 2 highlighter-shown and 2 highlighter-hidden events"); + await Promise.all([waitForShowEvents, waitForHideEvents]); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_03.js b/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_03.js new file mode 100644 index 0000000000..6c13314d2b --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-autohide-config_03.js @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that configuring a highlighter to autohide twice +// will replace the first timer and hide just once. +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector } = await openInspectorForURL( + "data:text/html;charset=utf-8,

TEST 1

" + ); + + const ONE_SECOND = 1000; + const HALF_SECOND = 500; + const nodeFront = await getNodeFront("#one", inspector); + + const waitForShowEvents = waitForNEvents( + inspector.highlighters, + "highlighter-shown", + 2 + ); + const waitForHideEvents = waitForNEvents( + inspector.highlighters, + "highlighter-hidden", + 1 + ); + + info("Show Box Model Highlighter, then hide after half a second"); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + nodeFront, + { duration: HALF_SECOND } + ); + + info("Show Box Model Highlighter again, then hide after one second"); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + nodeFront, + { duration: ONE_SECOND } + ); + + info("Waiting for 2 highlighter-shown and 1 highlighter-hidden event"); + await Promise.all([waitForShowEvents, waitForHideEvents]); + + /* + Since the second duration passed is longer than the first and is supposed to overwrite + the first, it is reasonable to expect that the "highlighter-hidden" event was emitted + after the second (longer) duration expired. As an added check, we naively wait for an + additional time amounting to the sum of both durations to check if the first timer was + somehow not overwritten and fires another "highlighter-hidden" event. + */ + let wasEmitted = false; + const waitForExtraEvent = new Promise((resolve, reject) => { + const _handler = () => { + wasEmitted = true; + resolve(); + }; + + inspector.highlighters.on("highlighter-hidden", _handler, { once: true }); + }); + + info("Wait to see if another highlighter-hidden event is emitted"); + await Promise.race([waitForExtraEvent, wait(HALF_SECOND + ONE_SECOND)]); + + is(wasEmitted, false, "An extra highlighter-hidden event was not emitted"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-autohide.js b/devtools/client/inspector/test/browser_inspector_highlighter-autohide.js new file mode 100644 index 0000000000..d525c81cf0 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-autohide.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that clicking on a node in the markup view or picking a node with the node picker +// shows a highlighter which is automatically hidden after a delay. +add_task(async function () { + info("Loading the test document and opening the inspector"); + const { inspector, toolbox, highlighterTestFront } = + await openInspectorForURL( + "data:text/html;charset=utf-8,

TEST 1

TEST 2

" + ); + const { waitForHighlighterTypeShown, waitForHighlighterTypeHidden } = + getHighlighterTestHelpers(inspector); + + // While in test mode, the configuration to automatically hide Box Model Highlighters + // after a delay is ignored to prevent intermittent test failures from race conditions. + // Restore this behavior just for this test because it is explicitly checked. + const HIGHLIGHTER_AUTOHIDE_TIMER = inspector.HIGHLIGHTER_AUTOHIDE_TIMER; + inspector.HIGHLIGHTER_AUTOHIDE_TIMER = 1000; + + registerCleanupFunction(() => { + // Restore the value to avoid impacting other tests. + inspector.HIGHLIGHTER_AUTOHIDE_TIMER = HIGHLIGHTER_AUTOHIDE_TIMER; + }); + + info( + "Check that selecting an element in the markup-view shows the highlighter and auto-hides it" + ); + let onHighlighterShown = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + const onHighlighterHidden = waitForHighlighterTypeHidden( + inspector.highlighters.TYPES.BOXMODEL + ); + + let delay; + const start = Date.now(); + onHighlighterHidden.then(() => { + delay = Date.now() - start; + }); + + await clickContainer("#one", inspector); + + info("Wait for Box Model Highlighter shown"); + await onHighlighterShown; + info("Wait for Box Model Highlighter hidden"); + await onHighlighterHidden; + + ok(true, "Highlighter was shown and hidden"); + ok( + delay >= inspector.HIGHLIGHTER_AUTOHIDE_TIMER, + `Highlighter was hidden after expected delay (${delay}ms)` + ); + + info("Check that picking a node hides the highlighter right away"); + onHighlighterShown = waitForHighlighterTypeShown( + inspector.highlighters.TYPES.BOXMODEL + ); + await startPicker(toolbox); + await hoverElement(inspector, "#two", 0, 0); + await onHighlighterShown; + ok( + await highlighterTestFront.isHighlighting(), + "Highlighter was shown when hovering the node" + ); + + await pickElement(inspector, "#two", 0, 0); + is( + await highlighterTestFront.isHighlighting(), + false, + "Highlighter gets hidden without delay after picking a node" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-by-type.js b/devtools/client/inspector/test/browser_inspector_highlighter-by-type.js new file mode 100644 index 0000000000..c9e67c8eca --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-by-type.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Check that custom highlighters can be retrieved by type and that they expose +// the expected API. + +const TEST_URL = "data:text/html;charset=utf-8,custom highlighters"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + + await manyInstancesOfCustomHighlighters(inspector); + await showHideMethodsAreAvailable(inspector); + await unknownHighlighterTypeShouldntBeAccepted(inspector); +}); + +async function manyInstancesOfCustomHighlighters({ inspectorFront }) { + const h1 = await inspectorFront.getHighlighterByType("BoxModelHighlighter"); + const h2 = await inspectorFront.getHighlighterByType("BoxModelHighlighter"); + ok(h1 !== h2, "getHighlighterByType returns new instances every time (1)"); + + const h3 = await inspectorFront.getHighlighterByType( + "CssTransformHighlighter" + ); + const h4 = await inspectorFront.getHighlighterByType( + "CssTransformHighlighter" + ); + ok(h3 !== h4, "getHighlighterByType returns new instances every time (2)"); + ok( + h3 !== h1 && h3 !== h2, + "getHighlighterByType returns new instances every time (3)" + ); + ok( + h4 !== h1 && h4 !== h2, + "getHighlighterByType returns new instances every time (4)" + ); + + await h1.finalize(); + await h2.finalize(); + await h3.finalize(); + await h4.finalize(); +} + +async function showHideMethodsAreAvailable({ inspectorFront }) { + const h1 = await inspectorFront.getHighlighterByType("BoxModelHighlighter"); + const h2 = await inspectorFront.getHighlighterByType( + "CssTransformHighlighter" + ); + + ok("show" in h1, "Show method is present on the front API"); + ok("show" in h2, "Show method is present on the front API"); + ok("hide" in h1, "Hide method is present on the front API"); + ok("hide" in h2, "Hide method is present on the front API"); + + await h1.finalize(); + await h2.finalize(); +} + +async function unknownHighlighterTypeShouldntBeAccepted({ inspectorFront }) { + const h = await inspectorFront.getHighlighterByType("whatever"); + ok(!h, "No highlighter was returned for the invalid type"); +} diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-cancel.js b/devtools/client/inspector/test/browser_inspector_highlighter-cancel.js new file mode 100644 index 0000000000..4502870863 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-cancel.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that canceling the element picker zooms back on the focused element. Bug 1224304. + +const TEST_URL = URL_ROOT + "doc_inspector_long-divs.html"; + +const isMac = Services.appinfo.OS === "Darwin"; +const TESTS = [ + { + key: "VK_ESCAPE", + options: {}, + // Escape would open the split console if the toolbox was focused + focusToolbox: false, + }, + { + key: "C", + options: { + metaKey: isMac, + ctrlKey: !isMac, + shiftKey: true, + }, + // Test with the toolbox focused to check the client side event listener. + focusToolbox: true, + }, + { + key: "C", + options: { + metaKey: isMac, + ctrlKey: !isMac, + shiftKey: true, + }, + // Test with the content page focused to check the actor's event listener. + focusToolbox: false, + }, +]; + +for (const { key, options, focusToolbox } of TESTS) { + add_task(async function () { + info(`Testing cancel shortcut: ${key} with toolbox focus: ${focusToolbox}`); + + const { inspector, toolbox, highlighterTestFront } = + await openInspectorForURL(TEST_URL); + await selectAndHighlightNode("#focus-here", inspector); + ok( + await highlighterTestFront.assertHighlightedNode("#focus-here"), + "The highlighter focuses on div#focus-here" + ); + ok( + isSelectedMarkupNodeInView(inspector), + "The currently selected node is on the screen." + ); + + await startPicker(toolbox); + + await hoverElement(inspector, "#zoom-here"); + ok( + !isSelectedMarkupNodeInView(inspector), + "The currently selected node is off the screen." + ); + + if (focusToolbox) { + toolbox.win.focus(); + } + + await cancelPickerByShortcut(toolbox, key, options); + ok( + isSelectedMarkupNodeInView(inspector), + "The currently selected node is focused back on the screen." + ); + + is( + await highlighterTestFront.isHighlighting(), + false, + "The highlighter was hidden" + ); + }); +} + +async function cancelPickerByShortcut(toolbox, key, options) { + info("Key pressed. Waiting for picker to be canceled."); + const onStopped = toolbox.nodePicker.once("picker-node-canceled"); + EventUtils.synthesizeKey(key, options, toolbox.win); + return onStopped; +} + +function isSelectedMarkupNodeInView(inspector) { + const selectedNodeContainer = inspector.markup._selectedContainer.elt; + const bounds = selectedNodeContainer.getBoundingClientRect(); + return bounds.top > 0 && bounds.bottom > 0; +} diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-comments.js b/devtools/client/inspector/test/browser_inspector_highlighter-comments.js new file mode 100644 index 0000000000..615e110d89 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_highlighter-comments.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 http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that hovering over the markup-view's containers doesn't always show the +// highlighter, depending on the type of node hovered over. + +const TEST_PAGE = URL_ROOT + "doc_inspector_highlighter-comments.html"; + +add_task(async function () { + const { inspector, highlighterTestFront } = await openInspectorForURL( + TEST_PAGE + ); + const { waitForHighlighterTypeShown } = getHighlighterTestHelpers(inspector); + const markupView = inspector.markup; + await selectNode("p", inspector); + + info("Hovering over #id1 and waiting for highlighter to appear."); + await hoverElement("#id1"); + await assertHighlighterShownOn("#id1"); + + info("Hovering over comment node and ensuring highlighter doesn't appear."); + await hoverComment(); + await assertHighlighterHidden(); + + info("Hovering over #id1 again and waiting for highlighter to appear."); + await hoverElement("#id1"); + await assertHighlighterShownOn("#id1"); + + info("Hovering over #id2 and waiting for highlighter to appear."); + await hoverElement("#id2"); + await assertHighlighterShownOn("#id2"); + + info("Hovering over + + +`); + +add_task(async function testSlowLoadingFrame() { + const loadingTab = BrowserTestUtils.addTab(gBrowser, TEST_URL); + gBrowser.selectedTab = loadingTab; + + // Note: we cannot use `await addTab` here because the iframe never finishes + // loading. But we still want to wait for the frame to reach the loading state + // and to display a test element so that we can reproduce the initial issue + // fixed by Bug 1765760. + info("Wait for the loading iframe to be ready to test"); + await TestUtils.waitForCondition(async () => { + try { + return await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => { + const iframe = content.document.querySelector("iframe"); + return ( + iframe?.contentDocument.readyState === "loading" && + iframe.contentDocument.getElementById("inframe") + ); + }); + } catch (e) { + return false; + } + }); + + const { inspector } = await openInspector(); + + info("Check the markup view displays the loading iframe successfully"); + await assertMarkupViewAsTree( + ` + body + div id="outsideframe" + script!ignore-children + iframe + #document + html + head + body + div id="inframe"`, + "body", + inspector + ); +}); + +add_task(async function testSlowLoadingDocument() { + info("Create a test server serving a slow document"); + const httpServer = createTestHTTPServer(); + httpServer.registerContentType("html", "text/html"); + + // This promise allows to block serving the complete document from the test. + let unblockRequest; + const onRequestUnblocked = new Promise(r => (unblockRequest = r)); + + httpServer.registerPathHandler(`/`, async function (request, response) { + response.processAsync(); + response.setStatusLine(request.httpVersion, 200, "OK"); + + // Split the page content in 2 parts: + // - opening body tag and the "#start" div will be returned immediately + // - "#end" div and closing body tag are blocked on a promise. + const page_start = "
start
"; + const page_end = "
end
"; + const page = page_start + page_end; + + response.setHeader("Content-Type", "text/html; charset=utf-8", false); + response.setHeader("Content-Length", page.length + "", false); + response.write(page_start); + + await onRequestUnblocked; + + response.write(page_end); + response.finish(); + }); + + const port = httpServer.identity.primaryPort; + const TEST_URL_2 = `http://localhost:${port}/`; + + // Same as in the other task, we cannot wait for the full load. + info("Open a new tab on TEST_URL_2 and select it"); + const loadingTab = BrowserTestUtils.addTab(gBrowser, TEST_URL_2); + gBrowser.selectedTab = loadingTab; + + info("Wait for the #start div to be available in the document"); + await TestUtils.waitForCondition(async () => { + try { + return await ContentTask.spawn(gBrowser.selectedBrowser, {}, () => + content.document.getElementById("start") + ); + } catch (e) { + return false; + } + }); + + const { inspector } = await openInspector(); + + info("Check that the inspector is not blank and only shows the #start div"); + await assertMarkupViewAsTree( + ` + body + div id="start"`, + "body", + inspector + ); + + // Navigate to about:blank to clean the state. + await navigateTo("about:blank"); + + await navigateTo(TEST_URL_2, { waitForLoad: false }); + info("Wait for the #start div to be available as a markupview container"); + await TestUtils.waitForCondition(async () => { + const nodeFront = await getNodeFront("#start", inspector); + return nodeFront && getContainerForNodeFront(nodeFront, inspector); + }); + + info("Check that the inspector is not blank and only shows the #start div"); + await assertMarkupViewAsTree( + ` + body + div id="start"`, + "body", + inspector + ); + + info("Unblock the document request"); + unblockRequest(); + + info("Wait for the #end div to be available as a markupview container"); + await TestUtils.waitForCondition(async () => { + const nodeFront = await getNodeFront("#end", inspector); + return nodeFront && getContainerForNodeFront(nodeFront, inspector); + }); + + info("Check that the inspector will ultimately show the #end div"); + await assertMarkupViewAsTree( + ` + body + div id="start" + div id="end"`, + "body", + inspector + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_inspect_mutated_node.js b/devtools/client/inspector/test/browser_inspector_inspect_mutated_node.js new file mode 100644 index 0000000000..f43fc0bf6f --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_inspect_mutated_node.js @@ -0,0 +1,72 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Check that Inspect Element works even if the selector of the inspected +// element changes after opening the context menu. +// For this test, we explicitly move the element when opening the context menu. + +const TEST_URL = + `data:text/html;charset=utf-8,` + + encodeURIComponent(` + +
+ Inspect me +
+
+
My ID will change
+ + +`); + +add_task(async function () { + await addTab(TEST_URL); + await testNodeWithChangingPath(); + await testNodeWithChangingId(); +}); + +async function testNodeWithChangingPath() { + info("Test that inspect element works if the CSS path changes"); + const inspector = await clickOnInspectMenuItem("span"); + + const selectedNode = inspector.selection.nodeFront; + ok(selectedNode, "A node is selected in the inspector"); + is( + selectedNode.tagName.toLowerCase(), + "span", + "The selected node is correct" + ); + const parentNode = await selectedNode.parentNode(); + is( + parentNode.id, + "parent-2", + "The selected node is under the expected parent node" + ); +} + +async function testNodeWithChangingId() { + info("Test that inspect element works if the id changes"); + const inspector = await clickOnInspectMenuItem("#changing-id"); + + const selectedNode = inspector.selection.nodeFront; + ok(selectedNode, "A node is selected in the inspector"); + is(selectedNode.id.toLowerCase(), "new-id", "The selected node is correct"); +} diff --git a/devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu.js b/devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu.js new file mode 100644 index 0000000000..b531730ed6 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_inspect_node_contextmenu.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Tests for inspecting iframes and frames in browser context menu +const IFRAME_URI = `data:text/html;charset=utf-8,${encodeURI( + `
div in the iframe
` +)}`; +const TEST_IFRAME_DOC_URI = `data:text/html;charset=utf-8,${encodeURI(` +
Salution in top document
+ `)}`; + +// acts as the body element, so we can't use them in a document with other elements +// and have to set a dedicated document so we can test them. +const SAME_ORIGIN_FRAME_URI = `https://example.com/document-builder.sjs?html=

h2 in the same origin frame

`; +const REMOTE_ORIGIN_FRAME_URI = `https://example.org/document-builder.sjs?html=

h3 in the remote frame

`; +const TEST_FRAME_DOC_URI = `https://example.com/document-builder.sjs?html=${encodeURI(` + + + + `)}`; + +add_task(async function () { + await pushPref("devtools.command-button-frames.enabled", true); + await addTab(TEST_IFRAME_DOC_URI); + info( + "Test inspecting element in `; +const REMOTE_FRAME1_URI = + "https://example.com/document-builder.sjs?html=" + + encodeURI(REMOTE_FRAME1_HTML); + +// Build the same_process_frame hierarchy +const SAME_PROCESS_FRAME_URI = + "https://example.net/document-builder.sjs?html=" + + encodeURI(`
in-same_process_frame`); + +// Build the remote_frame2 hierarchy +const NESTED_REMOTE_FRAME_URI = + "https://example.com/document-builder.sjs?html=" + + encodeURI(`
in-nested_remote_frame`); +const REMOTE_FRAME2_HTML = ``; +const REMOTE_FRAME2_URI = + "https://example.org/document-builder.sjs?html=" + + encodeURI(REMOTE_FRAME2_HTML); + +// Assemble all frames in a single test page. +const HTML = ` + + + +`; +const TEST_URI = + "https://example.net/document-builder.sjs?html=" + encodeURI(HTML); + +add_task(async function () { + const tab = await addTab(TEST_URI); + + info("Retrieve the browsing context for nested_same_process_frame"); + const nestedSameProcessFrameBC = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + function () { + const remote_frame1 = content.document.getElementById("remote_frame1"); + return SpecialPowers.spawn(remote_frame1, [], function () { + return content.document.getElementById( + "nested_same_process_frame" + ).browsingContext; + }); + } + ); + await inspectElementInBrowsingContext( + nestedSameProcessFrameBC, + "#in-nested_same_process_frame" + ); + await checkSelectedNode("in-nested_same_process_frame"); + + info("Retrieve the browsing context for same_process_frame"); + const sameProcessFrameBC = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + function () { + return content.document.getElementById("same_process_frame") + .browsingContext; + } + ); + await inspectElementInBrowsingContext( + sameProcessFrameBC, + "#in-same_process_frame" + ); + await checkSelectedNode("in-same_process_frame"); + + info("Retrieve the browsing context for nested_remote_frame"); + const nestedRemoteFrameBC = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + function () { + const remote_frame2 = content.document.getElementById("remote_frame2"); + return SpecialPowers.spawn(remote_frame2, [], function () { + return content.document.getElementById( + "nested_remote_frame" + ).browsingContext; + }); + } + ); + await inspectElementInBrowsingContext( + nestedRemoteFrameBC, + "#in-nested_remote_frame" + ); + await checkSelectedNode("in-nested_remote_frame"); +}); + +/** + * Check the id of currently selected node front in the inspector. + */ +async function checkSelectedNode(id) { + const inspector = await getActiveInspector(); + is( + inspector.selection.nodeFront.id, + id, + "The correct node is selected in the markup view" + ); +} + +/** + * Use inspect element on the element matching the provided selector in a given + * browsing context. + * + * Note: adapted from head.js `clickOnInspectMenuItem` in order to work with a + * browsingContext instead of a test actor. + */ +async function inspectElementInBrowsingContext(browsingContext, selector) { + info( + `Show the context menu for selector "${selector}" in browsing context ${browsingContext.id}` + ); + const contentAreaContextMenu = document.querySelector( + "#contentAreaContextMenu" + ); + const contextOpened = once(contentAreaContextMenu, "popupshown"); + + const options = { type: "contextmenu", button: 2 }; + await BrowserTestUtils.synthesizeMouse( + selector, + 0, + 0, + options, + browsingContext + ); + + await contextOpened; + + info("Triggering the inspect action"); + await gContextMenu.inspectNode(); + + info("Hiding the menu"); + const contextClosed = once(contentAreaContextMenu, "popuphidden"); + contentAreaContextMenu.hidePopup(); + await contextClosed; +} diff --git a/devtools/client/inspector/test/browser_inspector_inspect_parent_process_page.js b/devtools/client/inspector/test/browser_inspector_inspect_parent_process_page.js new file mode 100644 index 0000000000..79e1907152 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_inspect_parent_process_page.js @@ -0,0 +1,29 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Check that the "inspect element" context menu item works for parent process +// chrome pages. +add_task(async function () { + const tab = await addTab("about:debugging"); + + const browser = tab.linkedBrowser; + const document = browser.contentDocument; + + info("Wait until Connect page is displayed"); + await waitUntil(() => document.querySelector(".qa-connect-page")); + const inspector = await clickOnInspectMenuItem(".qa-network-form-input"); + + const selectedNode = inspector.selection.nodeFront; + ok(selectedNode, "A node is selected in the inspector"); + is( + selectedNode.tagName.toLowerCase(), + "input", + "The selected node has the correct tagName" + ); + ok( + selectedNode.className.includes("qa-network-form-input"), + "The selected node has the expected className" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_invalidate.js b/devtools/client/inspector/test/browser_inspector_invalidate.js new file mode 100644 index 0000000000..8d48d2bfc1 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_invalidate.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that highlighter handles geometry changes correctly. + +const TEST_URI = + "data:text/html;charset=utf-8," + + "browser_inspector_invalidate.js\n" + + '
'; + +add_task(async function () { + const { inspector, highlighterTestFront } = await openInspectorForURL( + TEST_URI + ); + const divFront = await getNodeFront("div", inspector); + + info("Waiting for highlighter to activate"); + await inspector.highlighters.showHighlighterTypeForNode( + inspector.highlighters.TYPES.BOXMODEL, + divFront + ); + + let rect = await highlighterTestFront.getSimpleBorderRect(); + is(rect.width, 100, "The highlighter has the right width."); + + info( + "Changing the test element's size and waiting for the highlighter " + + "to update" + ); + await highlighterTestFront.changeHighlightedNodeWaitForUpdate( + "style", + "width: 200px; height: 100px; background:yellow;" + ); + + rect = await highlighterTestFront.getSimpleBorderRect(); + is(rect.width, 200, "The highlighter has the right width after update"); + + info("Waiting for highlighter to hide"); + await inspector.highlighters.hideHighlighterType( + inspector.highlighters.TYPES.BOXMODEL + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_keyboard-shortcuts-copy-outerhtml.js b/devtools/client/inspector/test/browser_inspector_keyboard-shortcuts-copy-outerhtml.js new file mode 100644 index 0000000000..6e64579269 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_keyboard-shortcuts-copy-outerhtml.js @@ -0,0 +1,53 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test copy outer HTML from the keyboard/copy event + +const TEST_URL = URL_ROOT + "doc_inspector_outerhtml.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + const root = inspector.markup._elt; + + info("Test copy outerHTML for COMMENT node"); + const comment = getElementByType(inspector, Node.COMMENT_NODE); + await setSelectionNodeFront(comment, inspector); + await checkClipboard("", root); + + info("Test copy outerHTML for DOCTYPE node"); + const doctype = getElementByType(inspector, Node.DOCUMENT_TYPE_NODE); + await setSelectionNodeFront(doctype, inspector); + await checkClipboard("", root); + + info("Test copy outerHTML for ELEMENT node"); + await selectAndHighlightNode("div", inspector); + await checkClipboard("

Test copy OuterHTML

", root); +}); + +async function setSelectionNodeFront(node, inspector) { + const updated = inspector.once("inspector-updated"); + inspector.selection.setNodeFront(node); + await updated; +} + +async function checkClipboard(expectedText, node) { + try { + await waitForClipboardPromise(() => fireCopyEvent(node), expectedText); + ok(true, "Clipboard successfully filled with : " + expectedText); + } catch (e) { + ok( + false, + "Clipboard could not be filled with the expected text : " + expectedText + ); + } +} + +function getElementByType(inspector, type) { + for (const [node] of inspector.markup._containers) { + if (node.nodeType === type) { + return node; + } + } + return null; +} diff --git a/devtools/client/inspector/test/browser_inspector_keyboard-shortcuts.js b/devtools/client/inspector/test/browser_inspector_keyboard-shortcuts.js new file mode 100644 index 0000000000..d4e1ee5631 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_keyboard-shortcuts.js @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Tests that the keybindings for highlighting different elements work as +// intended. + +const TEST_URI = + "data:text/html;charset=utf-8," + + "Test for the highlighter keybindings" + + "

Greetings, earthlings!" + + " I come in peace.

"; + +const TEST_DATA = [ + { key: "KEY_ArrowLeft", selectedNode: "p" }, + { key: "KEY_ArrowLeft", selectedNode: "body" }, + { key: "KEY_ArrowLeft", selectedNode: "html" }, + { key: "KEY_ArrowRight", selectedNode: "body" }, + { key: "KEY_ArrowRight", selectedNode: "p" }, + { key: "KEY_ArrowRight", selectedNode: "strong" }, +]; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URI); + + info("Selecting the deepest element to start with"); + await selectNode("strong", inspector); + + const nodeFront = await getNodeFront("strong", inspector); + is( + inspector.selection.nodeFront, + nodeFront, + " should be selected initially" + ); + + info("Focusing the currently active breadcrumb button"); + const bc = inspector.breadcrumbs; + bc.nodeHierarchy[bc.currentIndex].button.focus(); + + for (const { key, selectedNode } of TEST_DATA) { + info("Pressing " + key + " to select " + selectedNode); + + const updated = inspector.once("inspector-updated"); + EventUtils.synthesizeKey(key); + await updated; + + const selectedNodeFront = await getNodeFront(selectedNode, inspector); + is( + inspector.selection.nodeFront, + selectedNodeFront, + selectedNode + " is selected." + ); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js new file mode 100644 index 0000000000..9ac925db6e --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js @@ -0,0 +1,388 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that context menu items are enabled / disabled correctly. + +const TEST_URL = URL_ROOT + "doc_inspector_menu.html"; + +const PASTE_MENU_ITEMS = [ + "node-menu-pasteinnerhtml", + "node-menu-pasteouterhtml", + "node-menu-pastebefore", + "node-menu-pasteafter", + "node-menu-pastefirstchild", + "node-menu-pastelastchild", +]; + +const ACTIVE_ON_DOCTYPE_ITEMS = [ + "node-menu-showdomproperties", + "node-menu-useinconsole", +]; + +const ACTIVE_ON_SHADOW_ROOT_ITEMS = [ + "node-menu-pasteinnerhtml", + "node-menu-copyinner", + "node-menu-edithtml", +].concat(ACTIVE_ON_DOCTYPE_ITEMS); + +const ALL_MENU_ITEMS = [ + "node-menu-edithtml", + "node-menu-copyinner", + "node-menu-copyouter", + "node-menu-copyuniqueselector", + "node-menu-copycsspath", + "node-menu-copyxpath", + "node-menu-copyimagedatauri", + "node-menu-delete", + "node-menu-pseudo-hover", + "node-menu-pseudo-active", + "node-menu-pseudo-focus", + "node-menu-pseudo-focus-within", + "node-menu-scrollnodeintoview", + "node-menu-screenshotnode", + "node-menu-add-attribute", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", +].concat(PASTE_MENU_ITEMS, ACTIVE_ON_DOCTYPE_ITEMS); + +const INACTIVE_ON_DOCTYPE_ITEMS = ALL_MENU_ITEMS.filter( + item => !ACTIVE_ON_DOCTYPE_ITEMS.includes(item) +); + +const INACTIVE_ON_DOCUMENT_ITEMS = INACTIVE_ON_DOCTYPE_ITEMS; + +const INACTIVE_ON_SHADOW_ROOT_ITEMS = ALL_MENU_ITEMS.filter( + item => !ACTIVE_ON_SHADOW_ROOT_ITEMS.includes(item) +); + +/** + * Test cases, each item of this array may define the following properties: + * desc: string that will be logged + * selector: selector of the node to be selected + * disabled: items that should have disabled state + * clipboardData: clipboard content + * clipboardDataType: clipboard content type + * attributeTrigger: attribute that will be used as context menu trigger + * shadowRoot: if true, selects the shadow root from the node, rather than + * the node itself. + */ +const TEST_CASES = [ + { + desc: "doctype node with empty clipboard", + selector: null, + disabled: INACTIVE_ON_DOCTYPE_ITEMS, + }, + { + desc: "doctype node with html on clipboard", + clipboardData: "

some text

", + clipboardDataType: "text", + selector: null, + disabled: INACTIVE_ON_DOCTYPE_ITEMS, + }, + { + desc: "element node HTML on the clipboard", + clipboardData: "

some text

", + clipboardDataType: "text", + disabled: [ + "node-menu-copyimagedatauri", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ], + selector: "#sensitivity", + }, + { + desc: " element", + clipboardData: "

some text

", + clipboardDataType: "text", + selector: "html", + disabled: [ + "node-menu-copyimagedatauri", + "node-menu-pastebefore", + "node-menu-pasteafter", + "node-menu-pastefirstchild", + "node-menu-pastelastchild", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + "node-menu-delete", + ], + }, + { + desc: " with HTML on clipboard", + clipboardData: "

some text

", + clipboardDataType: "text", + selector: "body", + disabled: [ + "node-menu-copyimagedatauri", + "node-menu-pastebefore", + "node-menu-pasteafter", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ], + }, + { + desc: " with HTML on clipboard", + clipboardData: "

some text

", + clipboardDataType: "text", + selector: "img", + disabled: [ + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ], + }, + { + desc: " with HTML on clipboard", + clipboardData: "

some text

", + clipboardDataType: "text", + selector: "head", + disabled: [ + "node-menu-copyimagedatauri", + "node-menu-pastebefore", + "node-menu-pasteafter", + "node-menu-screenshotnode", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ], + }, + { + desc: " with no html on clipboard", + selector: "head", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-screenshotnode", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ]), + }, + { + desc: " with text on clipboard", + clipboardData: "some text", + clipboardDataType: "text", + selector: "#paste-area", + disabled: [ + "node-menu-copyimagedatauri", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ], + }, + { + desc: " with base64 encoded image data uri on clipboard", + clipboardData: + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABC" + + "AAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==", + clipboardDataType: "image", + selector: "#paste-area", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ]), + }, + { + desc: " with empty string on clipboard", + clipboardData: "", + clipboardDataType: "text", + selector: "#paste-area", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ]), + }, + { + desc: " with whitespace only on clipboard", + clipboardData: " \n\n\t\n\n \n", + clipboardDataType: "text", + selector: "#paste-area", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ]), + }, + { + desc: " that isn't visible on the page, empty clipboard", + selector: "#hiddenElement", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-screenshotnode", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ]), + }, + { + desc: " nested in another hidden element, empty clipboard", + selector: "#nestedHiddenElement", + disabled: PASTE_MENU_ITEMS.concat([ + "node-menu-copyimagedatauri", + "node-menu-screenshotnode", + "node-menu-copy-attribute", + "node-menu-edit-attribute", + "node-menu-remove-attribute", + ]), + }, + { + desc: " with context menu triggered on attribute, empty clipboard", + selector: "#attributes", + disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]), + attributeTrigger: "data-edit", + }, + { + desc: "Shadow Root", + clipboardData: "

some text

", + clipboardDataType: "text", + disabled: INACTIVE_ON_SHADOW_ROOT_ITEMS, + selector: "#host", + shadowRoot: true, + }, + { + desc: "Document node in iFrame", + disabled: INACTIVE_ON_DOCUMENT_ITEMS, + selector: "iframe", + documentNode: true, + }, +]; + +var clipboard = require("resource://devtools/shared/platform/clipboard.js"); +registerCleanupFunction(() => { + clipboard.copyString(""); + clipboard = null; +}); + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + for (const test of TEST_CASES) { + const { + desc, + disabled, + selector, + attributeTrigger, + documentNode = false, + shadowRoot = false, + } = test; + + info(`Test ${desc}`); + setupClipboard(test.clipboardData, test.clipboardDataType); + + const front = await getNodeFrontForSelector( + selector, + inspector, + documentNode, + shadowRoot + ); + + info("Selecting the specified node."); + await selectNode(front, inspector); + + info("Simulating context menu click on the selected node container."); + const nodeFrontContainer = getContainerForNodeFront(front, inspector); + const contextMenuTrigger = attributeTrigger + ? nodeFrontContainer.tagLine.querySelector( + `[data-attr="${attributeTrigger}"]` + ) + : nodeFrontContainer.tagLine; + + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: contextMenuTrigger, + }); + + for (const id of ALL_MENU_ITEMS) { + const menuItem = allMenuItems.find(item => item.id === id); + const shouldBeDisabled = disabled.includes(id); + const shouldBeDisabledText = shouldBeDisabled ? "disabled" : "enabled"; + is( + menuItem.disabled, + shouldBeDisabled, + `#${id} should be ${shouldBeDisabledText} for test case ${desc}` + ); + } + } +}); + +/** + * A helper that fetches a front for a node that matches the given selector or + * doctype node if the selector is falsy. + */ +async function getNodeFrontForSelector( + selector, + inspector, + documentNode, + shadowRoot +) { + if (selector) { + info("Retrieving front for selector " + selector); + const node = await getNodeFront(selector, inspector); + if (shadowRoot) { + return getShadowRoot(node, inspector); + } + if (documentNode) { + return getFrameDocument(node, inspector); + } + return node; + } + + info("Retrieving front for doctype node"); + const { nodes } = await inspector.walker.children(inspector.walker.rootNode); + return nodes[0]; +} + +/** + * A helper that populates the clipboard with data of given type. Clears the + * clipboard if data is falsy. + */ +function setupClipboard(data, type) { + if (!data) { + info("Clearing the clipboard."); + clipboard.copyString(""); + } else if (type === "text") { + info("Populating clipboard with text."); + clipboard.copyString(data); + } else if (type === "image") { + info("Populating clipboard with image content"); + copyImageToClipboard(data); + } +} + +/** + * The code below is a simplified version of the sdk/clipboard helper set() method. + */ +function copyImageToClipboard(data) { + const imageTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools); + + // Image data is stored as base64 in the test. + const image = atob(data); + + const imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"].createInstance( + Ci.nsISupportsInterfacePointer + ); + imgPtr.data = imageTools.decodeImageFromBuffer( + image, + image.length, + "image/png" + ); + + const xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + xferable.init(null); + xferable.addDataFlavor("image/png"); + xferable.setTransferData("image/png", imgPtr); + + Services.clipboard.setData( + xferable, + null, + Services.clipboard.kGlobalClipboard + ); +} diff --git a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js new file mode 100644 index 0000000000..7a69230aef --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js @@ -0,0 +1,46 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that HTML can be pasted in SVG elements. + +const TEST_URL = URL_ROOT + "doc_inspector_svg.svg"; +const PASTE_AS_FIRST_CHILD = + ''; +const PASTE_AS_LAST_CHILD = + ''; + +add_task(async function () { + const clipboard = require("resource://devtools/shared/platform/clipboard.js"); + + const { inspector } = await openInspectorForURL(TEST_URL); + + const refSelector = "svg"; + const oldHTML = await getContentPageElementProperty(refSelector, "innerHTML"); + await selectNode(refSelector, inspector); + const markupTagLine = getContainerForSelector(refSelector, inspector).tagLine; + + await pasteContent("node-menu-pastefirstchild", PASTE_AS_FIRST_CHILD); + await pasteContent("node-menu-pastelastchild", PASTE_AS_LAST_CHILD); + + const html = await getContentPageElementProperty(refSelector, "innerHTML"); + const expectedHtml = PASTE_AS_FIRST_CHILD + oldHTML + PASTE_AS_LAST_CHILD; + is(html, expectedHtml, "The innerHTML of the SVG node is correct"); + + // Helpers + async function pasteContent(menuId, clipboardData) { + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: markupTagLine, + }); + info(`Testing ${menuId} for ${clipboardData}`); + + await SimpleTest.promiseClipboardChange(clipboardData, () => { + clipboard.copyString(clipboardData); + }); + + const onMutation = inspector.once("markupmutation"); + allMenuItems.find(item => item.id === menuId).click(); + info("Waiting for mutation to occur"); + await onMutation; + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js new file mode 100644 index 0000000000..7fe1b60286 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js @@ -0,0 +1,166 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that different paste items work in the context menu + +const TEST_URL = URL_ROOT + "doc_inspector_menu.html"; +const PASTE_ADJACENT_HTML_DATA = [ + { + desc: "As First Child", + clipboardData: "2", + menuId: "node-menu-pastefirstchild", + }, + { + desc: "As Last Child", + clipboardData: "4", + menuId: "node-menu-pastelastchild", + }, + { + desc: "Before", + clipboardData: "1", + menuId: "node-menu-pastebefore", + }, + { + desc: "After", + clipboardData: "5", + menuId: "node-menu-pasteafter", + }, +]; + +var clipboard = require("resource://devtools/shared/platform/clipboard.js"); +registerCleanupFunction(() => { + clipboard = null; +}); + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + + await testPasteOuterHTMLMenu(); + await testPasteInnerHTMLMenu(); + await testPasteAdjacentHTMLMenu(); + + async function testPasteOuterHTMLMenu() { + info("Testing that 'Paste Outer HTML' menu item works."); + + await SimpleTest.promiseClipboardChange( + "this was pasted (outerHTML)", + () => { + clipboard.copyString("this was pasted (outerHTML)"); + } + ); + + const outerHTMLSelector = "#paste-area h1"; + + const nodeFront = await getNodeFront(outerHTMLSelector, inspector); + await selectNode(nodeFront, inspector); + + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: getContainerForNodeFront(nodeFront, inspector).tagLine, + }); + + const onNodeReselected = inspector.markup.once("reselectedonremoved"); + allMenuItems.find(item => item.id === "node-menu-pasteouterhtml").click(); + + info("Waiting for inspector selection to update"); + await onNodeReselected; + + const outerHTML = await getContentPageElementProperty("body", "outerHTML"); + ok( + outerHTML.includes(clipboard.getText()), + "Clipboard content was pasted into the node's outer HTML." + ); + ok( + !(await hasMatchingElementInContentPage(outerHTMLSelector)), + "The original node was removed." + ); + } + + async function testPasteInnerHTMLMenu() { + info("Testing that 'Paste Inner HTML' menu item works."); + + await SimpleTest.promiseClipboardChange( + "this was pasted (innerHTML)", + () => { + clipboard.copyString("this was pasted (innerHTML)"); + } + ); + const innerHTMLSelector = "#paste-area .inner"; + const getInnerHTML = () => + getContentPageElementProperty(innerHTMLSelector, "innerHTML"); + const origInnerHTML = await getInnerHTML(); + + const nodeFront = await getNodeFront(innerHTMLSelector, inspector); + await selectNode(nodeFront, inspector); + + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: getContainerForNodeFront(nodeFront, inspector).tagLine, + }); + + const onMutation = inspector.once("markupmutation"); + allMenuItems.find(item => item.id === "node-menu-pasteinnerhtml").click(); + info("Waiting for mutation to occur"); + await onMutation; + + ok( + (await getInnerHTML()) === clipboard.getText(), + "Clipboard content was pasted into the node's inner HTML." + ); + ok( + await hasMatchingElementInContentPage(innerHTMLSelector), + "The original node has been preserved." + ); + await undoChange(inspector); + ok( + (await getInnerHTML()) === origInnerHTML, + "Previous innerHTML has been restored after undo" + ); + } + + async function testPasteAdjacentHTMLMenu() { + const refSelector = "#paste-area .adjacent .ref"; + const adjacentNodeSelector = "#paste-area .adjacent"; + const nodeFront = await getNodeFront(refSelector, inspector); + await selectNode(nodeFront, inspector); + const markupTagLine = getContainerForNodeFront( + nodeFront, + inspector + ).tagLine; + + for (const { clipboardData, menuId } of PASTE_ADJACENT_HTML_DATA) { + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: markupTagLine, + }); + info(`Testing ${menuId} for ${clipboardData}`); + + await SimpleTest.promiseClipboardChange(clipboardData, () => { + clipboard.copyString(clipboardData); + }); + + const onMutation = inspector.once("markupmutation"); + allMenuItems.find(item => item.id === menuId).click(); + info("Waiting for mutation to occur"); + await onMutation; + } + + let html = await getContentPageElementProperty( + adjacentNodeSelector, + "innerHTML" + ); + ok( + html.trim() === '12345', + "The Paste as Last Child / as First Child / Before / After worked as " + + "expected" + ); + await undoChange(inspector); + + html = await getContentPageElementProperty( + adjacentNodeSelector, + "innerHTML" + ); + ok( + html.trim() === '1234', + "Undo works for paste adjacent HTML" + ); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js b/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js new file mode 100644 index 0000000000..ac6ae35ad1 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_menu-04-use-in-console.js @@ -0,0 +1,57 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Tests "Use in Console" menu item + +const TEST_URL = URL_ROOT + "doc_inspector_menu.html"; + +add_task(async function () { + // Disable eager evaluation to avoid intermittent failures due to pending + // requests to evaluateJSAsync. + await pushPref("devtools.webconsole.input.eagerEvaluation", false); + + const { inspector, toolbox } = await openInspectorForURL(TEST_URL); + + info("Testing 'Use in Console' menu item."); + + await selectNode("#console-var", inspector); + const container = await getContainerForSelector("#console-var", inspector); + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: container.tagLine, + }); + const menuItem = allMenuItems.find(i => i.id === "node-menu-useinconsole"); + menuItem.click(); + + await inspector.once("console-var-ready"); + + const hud = toolbox.getPanel("webconsole").hud; + + const getConsoleResults = () => hud.ui.outputNode.querySelectorAll(".result"); + + is(hud.getInputValue(), "temp0", "first console variable is named temp0"); + hud.ui.wrapper.dispatchEvaluateExpression(); + + await waitUntil(() => getConsoleResults().length === 1); + let result = getConsoleResults()[0]; + ok( + result.textContent.includes('

'), + "variable temp0 references correct node" + ); + + await selectNode("#console-var-multi", inspector); + menuItem.click(); + await inspector.once("console-var-ready"); + + is(hud.getInputValue(), "temp1", "second console variable is named temp1"); + hud.ui.wrapper.dispatchEvaluateExpression(); + + await waitUntil(() => getConsoleResults().length === 2); + result = getConsoleResults()[1]; + ok( + result.textContent.includes('

'), + "variable temp1 references correct node" + ); + + hud.ui.wrapper.dispatchClearHistory(); +}); diff --git a/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js b/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js new file mode 100644 index 0000000000..4e2122a24a --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_menu-05-attribute-items.js @@ -0,0 +1,124 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Test that attribute items work in the context menu + +const TEST_URL = URL_ROOT + "doc_inspector_menu.html"; + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL); + await selectNode("#attributes", inspector); + + await testAddAttribute(); + await testCopyAttributeValue(); + await testCopyLongAttributeValue(); + await testEditAttribute(); + await testRemoveAttribute(); + + async function testAddAttribute() { + info("Triggering 'Add Attribute' and waiting for mutation to occur"); + const addAttribute = getMenuItem("node-menu-add-attribute"); + addAttribute.click(); + + EventUtils.sendString('class="u-hidden"'); + const onMutation = inspector.once("markupmutation"); + EventUtils.synthesizeKey("KEY_Enter"); + await onMutation; + + const hasAttribute = await hasMatchingElementInContentPage( + "#attributes.u-hidden" + ); + ok(hasAttribute, "attribute was successfully added"); + } + + async function testCopyAttributeValue() { + info( + "Testing 'Copy Attribute Value' and waiting for clipboard promise to resolve" + ); + const copyAttributeValue = getMenuItem("node-menu-copy-attribute"); + + info( + "Triggering 'Copy Attribute Value' and waiting for clipboard to copy the value" + ); + inspector.markup.contextMenu.nodeMenuTriggerInfo = { + type: "attribute", + name: "data-edit", + value: "the", + }; + + await waitForClipboardPromise(() => copyAttributeValue.click(), "the"); + } + + async function testCopyLongAttributeValue() { + info("Testing 'Copy Attribute Value' copies very long attribute values"); + const copyAttributeValue = getMenuItem("node-menu-copy-attribute"); + const longAttribute = + "#01234567890123456789012345678901234567890123456789" + + "12345678901234567890123456789012345678901234567890123456789012345678901" + + "23456789012345678901234567890123456789012345678901234567890123456789012" + + "34567890123456789012345678901234567890123456789012345678901234567890123"; + + inspector.markup.contextMenu.nodeMenuTriggerInfo = { + type: "attribute", + name: "data-edit", + value: longAttribute, + }; + + await waitForClipboardPromise( + () => copyAttributeValue.click(), + longAttribute + ); + } + + async function testEditAttribute() { + info("Testing 'Edit Attribute' menu item"); + const editAttribute = getMenuItem("node-menu-edit-attribute"); + + info("Triggering 'Edit Attribute' and waiting for mutation to occur"); + inspector.markup.contextMenu.nodeMenuTriggerInfo = { + type: "attribute", + name: "data-edit", + }; + editAttribute.click(); + EventUtils.sendString("data-edit='edited'"); + const onMutation = inspector.once("markupmutation"); + EventUtils.synthesizeKey("KEY_Enter"); + await onMutation; + + const isAttributeChanged = await hasMatchingElementInContentPage( + "#attributes[data-edit='edited']" + ); + ok(isAttributeChanged, "attribute was successfully edited"); + } + + async function testRemoveAttribute() { + info("Testing 'Remove Attribute' menu item"); + const removeAttribute = getMenuItem("node-menu-remove-attribute"); + + info("Triggering 'Remove Attribute' and waiting for mutation to occur"); + inspector.markup.contextMenu.nodeMenuTriggerInfo = { + type: "attribute", + name: "data-remove", + }; + const onMutation = inspector.once("markupmutation"); + removeAttribute.click(); + await onMutation; + + const hasAttribute = await hasMatchingElementInContentPage( + "#attributes[data-remove]" + ); + ok(!hasAttribute, "attribute was successfully removed"); + } + + function getMenuItem(id) { + const allMenuItems = openContextMenuAndGetAllItems(inspector, { + target: getContainerForSelector("#attributes", inspector).tagLine, + }); + const menuItem = allMenuItems.find(i => i.id === id); + ok(menuItem, "Menu item '" + id + "' found"); + // Close the menu so synthesizing future keys won't select menu items. + EventUtils.synthesizeKey("KEY_Escape"); + return menuItem; + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_menu-06-other.js b/devtools/client/inspector/test/browser_inspector_menu-06-other.js new file mode 100644 index 0000000000..34572cb934 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_menu-06-other.js @@ -0,0 +1,160 @@ +/* Any copyright is dedicated to the Public Domain. +http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + +// Tests for menuitem functionality that doesn't fit into any specific category +const TEST_URL = URL_ROOT + "doc_inspector_menu.html"; +add_task(async function () { + const { inspector, toolbox } = await openInspectorForURL(TEST_URL); + await testShowDOMProperties(); + await testDuplicateNode(); + await testDeleteNode(); + await testDeleteTextNode(); + await testDeleteRootNode(); + await testScrollIntoView(); + + async function testDuplicateNode() { + info("Testing 'Duplicate Node' menu item for normal elements."); + + await selectNode(".duplicate", inspector); + is( + await getNumberOfMatchingElementsInContentPage(".duplicate"), + 1, + "There should initially be 1 .duplicate node" + ); + + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const menuItem = allMenuItems.find( + item => item.id === "node-menu-duplicatenode" + ); + ok(menuItem, "'Duplicate node' menu item should exist"); + + info("Triggering 'Duplicate Node' and waiting for inspector to update"); + const updated = inspector.once("markupmutation"); + menuItem.click(); + await updated; + + is( + await getNumberOfMatchingElementsInContentPage(".duplicate"), + 2, + "The duplicated node should be in the markup." + ); + + const container = await getContainerForSelector( + ".duplicate + .duplicate", + inspector + ); + ok(container, "A MarkupContainer should be created for the new node"); + } + + async function testDeleteNode() { + info("Testing 'Delete Node' menu item for normal elements."); + await selectNode("#delete", inspector); + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const deleteNode = allMenuItems.find( + item => item.id === "node-menu-delete" + ); + ok(deleteNode, "the popup menu has a delete menu item"); + const updated = inspector.once("inspector-updated"); + + info("Triggering 'Delete Node' and waiting for inspector to update"); + deleteNode.click(); + await updated; + + ok(!(await hasMatchingElementInContentPage("#delete")), "Node deleted"); + } + + async function testDeleteTextNode() { + info("Testing 'Delete Node' menu item for text elements."); + const { walker } = inspector; + const divBefore = await walker.querySelector( + walker.rootNode, + "#nestedHiddenElement" + ); + const { nodes } = await walker.children(divBefore); + await selectNode(nodes[0], inspector, "test-highlight"); + + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const deleteNode = allMenuItems.find( + item => item.id === "node-menu-delete" + ); + ok(deleteNode, "the popup menu has a delete menu item"); + ok(!deleteNode.disabled, "the delete menu item is not disabled"); + const updated = inspector.once("inspector-updated"); + + info("Triggering 'Delete Node' and waiting for inspector to update"); + deleteNode.click(); + await updated; + + const divAfter = await walker.querySelector( + walker.rootNode, + "#nestedHiddenElement" + ); + const nodesAfter = (await walker.children(divAfter)).nodes; + ok(!nodesAfter.length, "the node still had children"); + } + + async function testDeleteRootNode() { + info("Testing 'Delete Node' menu item does not delete root node."); + await selectNode("html", inspector); + + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const deleteNode = allMenuItems.find( + item => item.id === "node-menu-delete" + ); + deleteNode.click(); + + await new Promise(resolve => { + executeSoon(resolve); + }); + + const hasDocumentElement = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => !!content.document.documentElement + ); + ok(hasDocumentElement, "Document element still alive."); + } + + async function testShowDOMProperties() { + info("Testing 'Show DOM Properties' menu item."); + const allMenuItems = openContextMenuAndGetAllItems(inspector); + const showDOMPropertiesNode = allMenuItems.find( + item => item.id === "node-menu-showdomproperties" + ); + ok(showDOMPropertiesNode, "the popup menu has a show dom properties item"); + + const consoleOpened = toolbox.once("webconsole-ready"); + + info("Triggering 'Show DOM Properties' and waiting for inspector open"); + showDOMPropertiesNode.click(); + await consoleOpened; + + const webconsoleUI = toolbox.getPanel("webconsole").hud.ui; + + await poll( + () => { + const messages = [ + ...webconsoleUI.outputNode.querySelectorAll(".message"), + ]; + const nodeMessage = messages.find(m => m.textContent.includes("body")); + // wait for the object to be expanded + return ( + nodeMessage && + nodeMessage.querySelectorAll(".object-inspector .node").length > 10 + ); + }, + "Waiting for the element node to be expanded", + 10, + 1000 + ); + + info("Close split console"); + await toolbox.toggleSplitConsole(); + } + + function testScrollIntoView() { + // Follow up bug to add this test - https://bugzilla.mozilla.org/show_bug.cgi?id=1154107 + todo(false, "Verify that node is scrolled into the viewport."); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_navigate_to_errors.js b/devtools/client/inspector/test/browser_inspector_navigate_to_errors.js new file mode 100644 index 0000000000..7e43c68e17 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_navigate_to_errors.js @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that inspector works when navigating to error pages. + +const TEST_URL_1 = + 'data:text/html,page'; +const TEST_URL_2 = "http://127.0.0.1:36325/"; +const TEST_URL_3 = "https://www.wronguri.wronguri/"; +const TEST_URL_4 = "data:text/html,test-doc-4"; + +add_task(async function () { + // Open the inspector on a valid URL + const { inspector } = await openInspectorForURL(TEST_URL_1); + + info("Navigate to closed port"); + await navigateTo(TEST_URL_2, { isErrorPage: true }); + + const documentURI = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => { + return content.document.documentURI; + } + ); + ok(documentURI.startsWith("about:neterror"), "content is correct."); + + const hasPage = await getNodeFront("#test-doc-1", inspector); + ok( + !hasPage, + "Inspector actor is no longer able to reach previous page DOM node" + ); + + const hasNetErrorNode = await getNodeFront("#errorShortDesc", inspector); + ok(hasNetErrorNode, "Inspector actor is able to reach error page DOM node"); + + const bundle = Services.strings.createBundle( + "chrome://global/locale/appstrings.properties" + ); + let domain = TEST_URL_2.match(/^http:\/\/(.*)\/$/)[1]; + let errorMsg = bundle.formatStringFromName("connectionFailure", [domain]); + is( + await getDisplayedNodeTextContent("#errorShortDesc", inspector), + errorMsg, + "Inpector really inspects the error page" + ); + + info("Navigate to unknown domain"); + await navigateTo(TEST_URL_3, { isErrorPage: true }); + + domain = TEST_URL_3.match(/^https:\/\/(.*)\/$/)[1]; + errorMsg = bundle.formatStringFromName("dnsNotFound2", [domain]); + is( + await getDisplayedNodeTextContent("#errorShortDesc", inspector), + errorMsg, + "Inspector really inspects the new error page" + ); + + info("Navigate to a valid url"); + await navigateTo(TEST_URL_4); + + is( + await getDisplayedNodeTextContent("body", inspector), + "test-doc-4", + "Inspector really inspects the valid url" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_navigation.js b/devtools/client/inspector/test/browser_inspector_navigation.js new file mode 100644 index 0000000000..68f1edc4ca --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_navigation.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 http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that inspector updates when page is navigated. + +const TEST_URL_FILE = + "browser/devtools/client/inspector/test/doc_inspector_breadcrumbs.html"; + +const TEST_URL_1 = "https://test1.example.org/" + TEST_URL_FILE; +const TEST_URL_2 = "https://test2.example.org/" + TEST_URL_FILE; + +// Bug 1340592: "srcset" attribute causes bfcache events (pageshow/pagehide) +// with buggy "persisted" values. +const TEST_URL_3 = + "data:text/html;charset=utf-8," + + encodeURIComponent(''); +const TEST_URL_4 = + "data:text/html;charset=utf-8," + encodeURIComponent("

bar

"); + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL_1); + + await selectNode("#i1", inspector); + + info("Navigating to a different page."); + await navigateTo(TEST_URL_2); + + ok(true, "New page loaded"); + await selectNode("#i1", inspector); + + const markuploaded = inspector.once("markuploaded"); + const onUpdated = inspector.once("inspector-updated"); + + info("Going back in history"); + gBrowser.goBack(); + + info("Waiting for markup view to load after going back in history."); + await markuploaded; + + info("Check that the inspector updates"); + await onUpdated; + + ok(true, "Old page loaded"); + const url = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => content.location.href + ); + is(url, TEST_URL_1, "URL is correct."); + + await selectNode("#i1", inspector); +}); + +add_task(async function () { + const { inspector } = await openInspectorForURL(TEST_URL_3); + + await selectNode("img", inspector); + + info("Navigating to a different page."); + await navigateTo(TEST_URL_4); + + ok(true, "New page loaded"); + await selectNode("#h1", inspector); + + const markuploaded = inspector.once("markuploaded"); + const onUpdated = inspector.once("inspector-updated"); + + info("Going back in history"); + gBrowser.goBack(); + + info("Waiting for markup view to load after going back in history."); + await markuploaded; + + info("Check that the inspector updates"); + await onUpdated; + + ok(true, "Old page loaded"); + const url = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => content.location.href + ); + is(url, TEST_URL_3, "URL is correct."); + + await selectNode("img", inspector); +}); diff --git a/devtools/client/inspector/test/browser_inspector_open_on_neterror.js b/devtools/client/inspector/test/browser_inspector_open_on_neterror.js new file mode 100644 index 0000000000..96354d3fab --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_open_on_neterror.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 http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that inspector works correctly when opened against a net error page + +const TEST_URL_1 = "http://127.0.0.1:36325/"; +const TEST_URL_2 = "data:text/html,test-doc-2"; + +add_task(async function () { + // We cannot directly use addTab here as waiting for error pages requires + // a specific code path. + gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TEST_URL_1); + await BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser); + + const { inspector } = await openInspector(); + ok(true, "Inspector loaded on the already opened net error"); + + const documentURI = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + () => content.document.documentURI + ); + ok( + documentURI.startsWith("about:neterror"), + "content is really a net error page." + ); + + const netErrorNode = await getNodeFront(".container", inspector); + ok(netErrorNode, "The inspector can get a node front from the neterror page"); + + info("Navigate to a valid url"); + await navigateTo(TEST_URL_2); + + is( + await getDisplayedNodeTextContent("body", inspector), + "test-doc-2", + "Inspector really inspects the valid url" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_pane-toggle-01.js b/devtools/client/inspector/test/browser_inspector_pane-toggle-01.js new file mode 100644 index 0000000000..e5335c3e93 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-01.js @@ -0,0 +1,36 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the inspector panel has a 3 pane toggle button, and that +// this button is visible both in BOTTOM and SIDE hosts. + +add_task(async function () { + info("Switch to 2 pane inspector to test the 3 pane toggle button behavior"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + info("Open the inspector in a bottom toolbox host"); + const { inspector, toolbox } = await openInspectorForURL( + "about:blank", + "bottom" + ); + + const button = inspector.panelDoc.querySelector(".sidebar-toggle"); + ok(button, "The toggle button exists in the DOM"); + ok(button.getAttribute("title"), "The title tooltip has initial state"); + ok( + button.classList.contains("pane-collapsed"), + "The button is in collapsed state" + ); + ok(!!button.getClientRects().length, "The button is visible"); + + info("Switch the host to the right"); + await toolbox.switchHost("right"); + + ok(!!button.getClientRects().length, "The button is still visible"); + ok( + button.classList.contains("pane-collapsed"), + "The button is still in collapsed state" + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_pane-toggle-02.js b/devtools/client/inspector/test/browser_inspector_pane-toggle-02.js new file mode 100644 index 0000000000..132f0feb7f --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-02.js @@ -0,0 +1,85 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the 3 pane toggle button can toggle on and off the inspector's 3 pane mode, +// and the 3 panes rendered are all of equal widths in the BOTTOM host. + +add_task(async function () { + info("Switch to 2 pane inspector to test the 3 pane toggle button behavior"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + const { inspector } = await openInspectorForURL("about:blank"); + const { panelDoc: doc } = inspector; + const button = doc.querySelector(".sidebar-toggle"); + const ruleViewSidebar = + inspector.sidebarSplitBoxRef.current.startPanelContainer; + const toolboxWidth = doc.getElementById("inspector-splitter-box").clientWidth; + + ok( + button.classList.contains("pane-collapsed"), + "The button is in collapsed state" + ); + + info("Click on the toggle button to toggle ON 3 pane inspector"); + let onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; + + info("Checking the state of the 3 pane inspector"); + let sidebarWidth = inspector.splitBox.state.width; + const sidebarSplitBoxWidth = inspector.sidebarSplitBoxRef.current.state.width; + ok( + !button.classList.contains("pane-collapsed"), + "The button is in expanded state" + ); + ok(doc.getElementById("ruleview-panel"), "The rule view panel exist"); + is( + inspector.sidebar.getCurrentTabID(), + "layoutview", + "Layout view is shown in the sidebar" + ); + is( + ruleViewSidebar.style.display, + "block", + "The split rule view sidebar is displayed" + ); + is(sidebarWidth, (toolboxWidth * 2) / 3, "Got correct main split box width"); + is( + sidebarSplitBoxWidth, + toolboxWidth / 3, + "Got correct sidebar split box width" + ); + + info("Click on the toggle button to toggle OFF the 3 pane inspector"); + onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; + + info("Checking the state of the 2 pane inspector"); + sidebarWidth = inspector.splitBox.state.width; + ok( + button.classList.contains("pane-collapsed"), + "The button is in collapsed state" + ); + is( + inspector.sidebar.getCurrentTabID(), + "ruleview", + "Rule view is shown in the sidebar" + ); + is( + ruleViewSidebar.style.display, + "none", + "The split rule view sidebar is hidden" + ); + is(sidebarWidth, sidebarSplitBoxWidth, "Got correct sidebar width"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_pane-toggle-03.js b/devtools/client/inspector/test/browser_inspector_pane-toggle-03.js new file mode 100644 index 0000000000..1522781832 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-03.js @@ -0,0 +1,60 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the 3 pane inspector toggle can render the middle and right panels of equal +// sizes when the original sidebar can be doubled in width and be smaller than half the +// toolbox's width in the BOTTOM host. + +const SIDEBAR_WIDTH = 200; + +add_task(async function () { + info("Switch to 2 pane inspector to test the 3 pane toggle button behavior"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + const { inspector } = await openInspectorForURL("about:blank"); + const { panelDoc: doc } = inspector; + const button = doc.querySelector(".sidebar-toggle"); + const toolboxWidth = doc.getElementById("inspector-splitter-box").clientWidth; + + if (toolboxWidth < 600) { + ok(true, "Can't run the full test because the toolbox width is too small."); + } else { + info("Set the sidebar width to 200px"); + inspector.splitBox.setState({ width: SIDEBAR_WIDTH }); + + info("Click on the toggle button to toggle ON 3 pane inspector"); + let onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; + + info("Checking the sizes of the 3 pane inspector"); + let sidebarWidth = inspector.splitBox.state.width; + const sidebarSplitBoxWidth = + inspector.sidebarSplitBoxRef.current.state.width; + is(sidebarWidth, SIDEBAR_WIDTH * 2, "Got correct main split box width"); + is( + sidebarSplitBoxWidth, + SIDEBAR_WIDTH, + "Got correct sidebar split box width" + ); + + info("Click on the toggle button to toggle OFF the 3 pane inspector"); + onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; + + info("Checking the sidebar size of the 2 pane inspector"); + sidebarWidth = inspector.splitBox.state.width; + is(sidebarWidth, SIDEBAR_WIDTH, "Got correct sidebar width"); + } +}); diff --git a/devtools/client/inspector/test/browser_inspector_pane-toggle-04.js b/devtools/client/inspector/test/browser_inspector_pane-toggle-04.js new file mode 100644 index 0000000000..024fca816b --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-04.js @@ -0,0 +1,55 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the 3 pane inspector toggle button can render the bottom-left and +// bottom-right panels of equal sizes in the SIDE host. + +add_task(async function () { + info("Switch to 2 pane inspector to test the 3 pane toggle button behavior"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + const { inspector, toolbox } = await openInspectorForURL("about:blank"); + const { panelDoc: doc } = inspector; + + info("Switch the host to the right"); + await toolbox.switchHost("right"); + + // Switching hosts is not correctly waiting when DevTools run in content frame + // See Bug 1571421. + await wait(1000); + + const button = doc.querySelector(".sidebar-toggle"); + const toolboxWidth = doc.getElementById("inspector-splitter-box").clientWidth; + + info("Click on the toggle button to toggle ON 3 pane inspector"); + let onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; + + info("Checking the sizes of the 3 pane inspector"); + const sidebarSplitBoxWidth = inspector.sidebarSplitBoxRef.current.state.width; + is( + sidebarSplitBoxWidth, + toolboxWidth / 2, + "Got correct sidebar split box width" + ); + + info("Click on the toggle button to toggle OFF the 3 pane inspector"); + onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; + + info("Checking the sidebar size of the 2 pane inspector"); + const sidebarWidth = inspector.splitBox.state.width; + is(sidebarWidth, toolboxWidth, "Got correct sidebar width"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_pane-toggle-05.js b/devtools/client/inspector/test/browser_inspector_pane-toggle-05.js new file mode 100644 index 0000000000..d2653639a0 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-05.js @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Tests whether the initial selected tab is displayed when switched from 3 pane to 2 pane +// and back to 3 pane when no other tab is selected explicitly. Rule view is displayed +// immediately on toggling to 2 pane. +add_task(async function () { + info( + "Switch to 2 pane inspector and back to 3 pane to test whether the selected tab is used" + ); + await pushPref("devtools.inspector.three-pane-enabled", true); + + const { inspector } = await openInspectorForURL("about:blank"); + + inspector.sidebar.select("changesview"); + + is( + inspector.sidebar.getCurrentTabID(), + "changesview", + "Changes view should be the active sidebar" + ); + + info("Click on the toggle button to toggle OFF 3 pane inspector"); + await toggleSidebar(inspector); + + is( + inspector.sidebar.getCurrentTabID(), + "ruleview", + "Rules view should be the active sidebar on toggle to 2 pane" + ); + + info("Click on the toggle button to toggle ON 3 pane inspector"); + await toggleSidebar(inspector); + + is( + inspector.sidebar.getCurrentTabID(), + "changesview", + "Changes view should be the active sidebar again" + ); +}); + +// Tests whether the selected pane in 2 pane view is also used after toggling to 3 pane view. +add_task(async function () { + info("Switch to 3 pane to test whether the selected pane is preserved"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + const { inspector } = await openInspectorForURL("about:blank"); + + inspector.sidebar.select("changesview"); + + is( + inspector.sidebar.getCurrentTabID(), + "changesview", + "Changes view should be the active sidebar" + ); + + info("Click on the toggle button to toggle ON 3 pane inspector"); + await toggleSidebar(inspector); + + is( + inspector.sidebar.getCurrentTabID(), + "changesview", + "Changes view should still be the active sidebar" + ); +}); + +// Tests whether the selected pane is layout view, if rule view is selected before toggling to 3 pane. +add_task(async function () { + info("Switch to 3 pane to test whether the selected pane is layout view"); + await pushPref("devtools.inspector.three-pane-enabled", false); + + const { inspector } = await openInspectorForURL("about:blank"); + + inspector.sidebar.select("ruleview"); + + is( + inspector.sidebar.getCurrentTabID(), + "ruleview", + "Rules view should be the active sidebar in 2 pane" + ); + + info("Click on the toggle button to toggle ON 3 pane inspector"); + await toggleSidebar(inspector); + + is( + inspector.sidebar.getCurrentTabID(), + "layoutview", + "Layout view should be the active sidebar in 3 pane" + ); +}); + +const toggleSidebar = async inspector => { + const { panelDoc: doc } = inspector; + const button = doc.querySelector(".sidebar-toggle"); + + const onRuleViewAdded = inspector.once("ruleview-added"); + EventUtils.synthesizeMouseAtCenter( + button, + {}, + inspector.panelDoc.defaultView + ); + await onRuleViewAdded; +}; diff --git a/devtools/client/inspector/test/browser_inspector_pane-toggle-layout-invariant.js b/devtools/client/inspector/test/browser_inspector_pane-toggle-layout-invariant.js new file mode 100644 index 0000000000..1abc7db975 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane-toggle-layout-invariant.js @@ -0,0 +1,32 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that documents with suppressed whitespace nodes are unaffected by the +// activation of the inspector panel. + +const TEST_URL = URL_ROOT + "doc_inspector_pane-toggle-layout-invariant.html"; + +async function getInvariantRect() { + return SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + const invariant = content.document.getElementById("invariant"); + return invariant.getBoundingClientRect(); + }); +} + +add_task(async function () { + await addTab(TEST_URL); + + // Get the initial position of the "invariant" element. We'll later check it + // again and we'll expect to get the same value. + const beforeRect = await getInvariantRect(); + + // Open the inspector. + await openInspector(); + + const afterRect = await getInvariantRect(); + + is(afterRect.x, beforeRect.x, "invariant x should be same as initial value."); + is(afterRect.y, beforeRect.y, "invariant y should be same as initial value."); +}); diff --git a/devtools/client/inspector/test/browser_inspector_pane_state_restore.js b/devtools/client/inspector/test/browser_inspector_pane_state_restore.js new file mode 100644 index 0000000000..8eb345e3de --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_pane_state_restore.js @@ -0,0 +1,75 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests that the previous inspector split box sizes are restored when reopening the +// inspector. + +const EXPECTED_INITIAL_WIDTH = 101; +const EXPECTED_INITIAL_HEIGHT = 102; +const EXPECTED_INITIAL_SIDEBAR_WIDTH = 103; + +const EXPECTED_NEW_WIDTH = 150; +const EXPECTED_NEW_HEIGHT = 100; +const EXPECTED_NEW_SIDEBAR_WIDTH = 250; + +add_task(async function () { + // Simulate that the user has already stored their preferred split boxes widths. + await pushPref( + "devtools.toolsidebar-width.inspector", + EXPECTED_INITIAL_WIDTH + ); + await pushPref( + "devtools.toolsidebar-height.inspector", + EXPECTED_INITIAL_HEIGHT + ); + await pushPref( + "devtools.toolsidebar-width.inspector.splitsidebar", + EXPECTED_INITIAL_SIDEBAR_WIDTH + ); + + const { inspector } = await openInspectorForURL("about:blank"); + + info("Check the initial size of the inspector."); + const { width, height, splitSidebarWidth } = inspector.getSidebarSize(); + is(width, EXPECTED_INITIAL_WIDTH, "Got correct initial width."); + is(height, EXPECTED_INITIAL_HEIGHT, "Got correct initial height."); + is( + splitSidebarWidth, + EXPECTED_INITIAL_SIDEBAR_WIDTH, + "Got correct initial split sidebar width." + ); + + info("Simulate updates to the dimensions of the various splitboxes."); + inspector.splitBox.setState({ + width: EXPECTED_NEW_WIDTH, + height: EXPECTED_NEW_HEIGHT, + }); + inspector.sidebarSplitBoxRef.current.setState({ + width: EXPECTED_NEW_SIDEBAR_WIDTH, + }); + + await closeToolbox(); + + info( + "Check the stored sizes of the inspector in the preferences when the inspector " + + "is closed" + ); + const storedWidth = Services.prefs.getIntPref( + "devtools.toolsidebar-width.inspector" + ); + const storedHeight = Services.prefs.getIntPref( + "devtools.toolsidebar-height.inspector" + ); + const storedSplitSidebarWidth = Services.prefs.getIntPref( + "devtools.toolsidebar-width.inspector.splitsidebar" + ); + is(storedWidth, EXPECTED_NEW_WIDTH, "Got correct stored width."); + is(storedHeight, EXPECTED_NEW_HEIGHT, "Got correct stored height"); + is( + storedSplitSidebarWidth, + EXPECTED_NEW_SIDEBAR_WIDTH, + "Got correct stored split sidebar width." + ); +}); diff --git a/devtools/client/inspector/test/browser_inspector_picker-reset-reference.js b/devtools/client/inspector/test/browser_inspector_picker-reset-reference.js new file mode 100644 index 0000000000..2a9bc39ca6 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_picker-reset-reference.js @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that the node picker reset its reference for the last hovered node when the user +// stops picking (See Bug 1736183). + +const TEST_URL = + "data:text/html;charset=utf8,

Pick target

ignore me

"; + +add_task(async () => { + const { inspector, toolbox, highlighterTestFront } = + await openInspectorForURL(TEST_URL); + + const { waitForHighlighterTypeHidden } = getHighlighterTestHelpers(inspector); + + info( + "Start the picker and hover an element to populate the picker hovered node reference" + ); + await startPicker(toolbox); + await hoverElement(inspector, "#target"); + ok( + await highlighterTestFront.assertHighlightedNode("#target"), + "The highlighter is shown on the expected node" + ); + + info("Hit Escape to cancel picking"); + let onHighlighterHidden = waitForHighlighterTypeHidden( + inspector.highlighters.TYPES.BOXMODEL + ); + await stopPickerWithEscapeKey(toolbox); + await onHighlighterHidden; + + info("And start it again, and hover the same node again"); + await startPicker(toolbox); + await hoverElement(inspector, "#target"); + ok( + await highlighterTestFront.assertHighlightedNode("#target"), + "The highlighter is shown on the expected node again" + ); + + info("Pick the element to stop the picker"); + onHighlighterHidden = waitForHighlighterTypeHidden( + inspector.highlighters.TYPES.BOXMODEL + ); + // nodePicker isPicking property is set to false _after_ picker-node-picked event, so + // we need to wait for picker-stopped here. + const onPickerStopped = toolbox.nodePicker.once("picker-stopped"); + await pickElement(inspector, "#target", 5, 5); + await onHighlighterHidden; + await onPickerStopped; + + info("And start it and hover the same node, again"); + await startPicker(toolbox); + await hoverElement(inspector, "#target"); + ok( + await highlighterTestFront.assertHighlightedNode("#target"), + "The highlighter is shown on the expected node again" + ); + + info("Stop the picker to avoid pending Promise"); + onHighlighterHidden = waitForHighlighterTypeHidden( + inspector.highlighters.TYPES.BOXMODEL + ); + await stopPickerWithEscapeKey(toolbox); + await onHighlighterHidden; +}); diff --git a/devtools/client/inspector/test/browser_inspector_picker-shift-key.js b/devtools/client/inspector/test/browser_inspector_picker-shift-key.js new file mode 100644 index 0000000000..2eb1c04709 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_picker-shift-key.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// Test that the node picker can pick elements with pointer-events: none when holding Shift. + +const TEST_URI = `data:text/html;charset=utf-8, + +
+
Regular element
+
Element with pointer-events: none
+
Element with opacity of 0.1
+
Element with opacity of 0
+
`; +const IS_OSX = Services.appinfo.OS === "Darwin"; + +add_task(async function () { + const { inspector, toolbox } = await openInspectorForURL(TEST_URI); + + const body = await getNodeFront("body", inspector); + is( + inspector.selection.nodeFront, + body, + "By default the body node is selected" + ); + + info("Start the element picker"); + await startPicker(toolbox); + + info( + "Shift-clicking element with pointer-events: none does select the element" + ); + await clickElement({ + inspector, + selector: "#nopointer", + shiftKey: true, + }); + await checkElementSelected("#nopointer", inspector); + + info("Shift-clicking element with default pointer-events value also works"); + await clickElement({ + inspector, + selector: "#pointer", + shiftKey: true, + }); + await checkElementSelected("#pointer", inspector); + + info( + "Clicking element with pointer-events: none without holding Shift won't select the element but its parent" + ); + await clickElement({ + inspector, + selector: "#nopointer", + shiftKey: false, + }); + await checkElementSelected("main", inspector); + + info("Shift-clicking transluscent visible element works"); + await clickElement({ + inspector, + selector: "#transluscent", + shiftKey: true, + }); + await checkElementSelected("#transluscent", inspector); + + info("Shift-clicking invisible element select its parent"); + await clickElement({ + inspector, + selector: "#invisible", + shiftKey: true, + }); + await checkElementSelected("main", inspector); +}); + +async function clickElement({ selector, inspector, shiftKey }) { + const onSelectionChanged = inspector.once("inspector-updated"); + await safeSynthesizeMouseEventAtCenterInContentPage(selector, { + shiftKey, + // Hold meta/ctrl so we don't have to start the picker again + [IS_OSX ? "metaKey" : "ctrlKey"]: true, + }); + await onSelectionChanged; +} + +async function checkElementSelected(selector, inspector) { + const el = await getNodeFront(selector, inspector); + is( + inspector.selection.nodeFront, + el, + `The element ${selector} is now selected` + ); +} diff --git a/devtools/client/inspector/test/browser_inspector_picker-stop-on-eyedropper.js b/devtools/client/inspector/test/browser_inspector_picker-stop-on-eyedropper.js new file mode 100644 index 0000000000..bed9e2e82c --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_picker-stop-on-eyedropper.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 http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that the highlighter's picker is stopped when the eyedropper tool is +// selected + +const TEST_URI = + "data:text/html;charset=UTF-8," + + "testing the highlighter goes away on eyedropper selection"; + +add_task(async function () { + const { toolbox } = await openInspectorForURL(TEST_URI); + const pickerStopped = toolbox.nodePicker.once("picker-stopped"); + const eyeDropperButtonClasses = + toolbox.getPanel("inspector").eyeDropperButton.classList; + + const eyeDropperStopped = waitFor( + () => !eyeDropperButtonClasses.contains("checked") + ); + + info("Starting the inspector picker"); + await startPicker(toolbox); + + info("Starting the eyedropper tool"); + await startEyeDropper(toolbox); + + ok(eyeDropperButtonClasses.contains("checked"), "eyedropper is started"); + + info("Waiting for the picker-stopped event to be fired"); + await pickerStopped; + + ok( + true, + "picker-stopped event fired after eyedropper is selected; picker is closed" + ); + + info("Starting the inspector picker again"); + await startPicker(toolbox); + + info("Checking if eyedropper is stopped"); + await eyeDropperStopped; + + ok(true, "eyedropper stopped after node picker; eyedropper is closed"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_picker-stop-on-tool-change.js b/devtools/client/inspector/test/browser_inspector_picker-stop-on-tool-change.js new file mode 100644 index 0000000000..e8a5796f49 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_picker-stop-on-tool-change.js @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// Test that the highlighter's picker is stopped when a different tool is +// selected + +const TEST_URI = + "data:text/html;charset=UTF-8," + + "testing the highlighter goes away on tool selection"; + +add_task(async function () { + const { toolbox } = await openInspectorForURL(TEST_URI); + const pickerStopped = toolbox.nodePicker.once("picker-stopped"); + + info("Starting the inspector picker"); + await startPicker(toolbox); + + info("Selecting another tool than the inspector in the toolbox"); + await toolbox.selectNextTool(); + + info("Waiting for the picker-stopped event to be fired"); + await pickerStopped; + + ok(true, "picker-stopped event fired after switch tools; picker is closed"); +}); diff --git a/devtools/client/inspector/test/browser_inspector_picker-useragent-widget.js b/devtools/client/inspector/test/browser_inspector_picker-useragent-widget.js new file mode 100644 index 0000000000..95c6954f24 --- /dev/null +++ b/devtools/client/inspector/test/browser_inspector_picker-useragent-widget.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 http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const TEST_URI = `data:text/html;charset=utf-8, + + `; + +// Test that using the node picker on user agent widgets only selects shadow dom +// elements if `devtools.inspector.showAllAnonymousContent` is true. +// If not, we should only surface the host, in this case, the