summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/test
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webconsole/test')
-rw-r--r--devtools/client/webconsole/test/.eslintrc.js6
-rw-r--r--devtools/client/webconsole/test/browser.ini396
-rw-r--r--devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js52
-rw-r--r--devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js92
-rw-r--r--devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js107
-rw-r--r--devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js31
-rw-r--r--devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js63
-rw-r--r--devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js75
-rw-r--r--devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js77
-rw-r--r--devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js78
-rw-r--r--devtools/client/webconsole/test/browser_cached_messages.js59
-rw-r--r--devtools/client/webconsole/test/browser_console.js160
-rw-r--r--devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js92
-rw-r--r--devtools/client/webconsole/test/browser_console_clear_method.js41
-rw-r--r--devtools/client/webconsole/test/browser_console_clear_on_reload.js86
-rw-r--r--devtools/client/webconsole/test/browser_console_click_focus.js59
-rw-r--r--devtools/client/webconsole/test/browser_console_consolejsm_output.js285
-rw-r--r--devtools/client/webconsole/test/browser_console_copy_command.js76
-rw-r--r--devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js97
-rw-r--r--devtools/client/webconsole/test/browser_console_dead_objects.js88
-rw-r--r--devtools/client/webconsole/test/browser_console_error_source_click.js79
-rw-r--r--devtools/client/webconsole/test/browser_console_filters.js60
-rw-r--r--devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js114
-rw-r--r--devtools/client/webconsole/test/browser_console_history_persist.js119
-rw-r--r--devtools/client/webconsole/test/browser_console_iframe_messages.js114
-rw-r--r--devtools/client/webconsole/test/browser_console_keyboard_accessibility.js89
-rw-r--r--devtools/client/webconsole/test/browser_console_log_inspectable_object.js52
-rw-r--r--devtools/client/webconsole/test/browser_console_native_getters.js101
-rw-r--r--devtools/client/webconsole/test/browser_console_navigation_marker.js81
-rw-r--r--devtools/client/webconsole/test/browser_console_netlogging.js38
-rw-r--r--devtools/client/webconsole/test/browser_console_nsiconsolemessage.js85
-rw-r--r--devtools/client/webconsole/test/browser_console_open_or_focus.js46
-rw-r--r--devtools/client/webconsole/test/browser_console_optimized_out_vars.js91
-rw-r--r--devtools/client/webconsole/test/browser_console_private_browsing.js192
-rw-r--r--devtools/client/webconsole/test/browser_console_server_logging.js74
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view.js204
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js59
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js135
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_filter.js80
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_highlighter.js97
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_special_names.js38
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js109
-rw-r--r--devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js112
-rw-r--r--devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js157
-rw-r--r--devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js71
-rw-r--r--devtools/client/webconsole/test/browser_jsterm_inspect.js47
-rw-r--r--devtools/client/webconsole/test/browser_longstring_hang.js57
-rw-r--r--devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js74
-rw-r--r--devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js47
-rw-r--r--devtools/client/webconsole/test/browser_output_longstring_expand.js85
-rw-r--r--devtools/client/webconsole/test/browser_repeated_messages_accuracy.js178
-rw-r--r--devtools/client/webconsole/test/browser_result_format_as_string.js40
-rw-r--r--devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js86
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js69
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_assert.js56
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js47
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js60
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js130
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js64
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js245
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js27
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js110
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js45
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js55
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js60
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js40
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js41
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js20
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js47
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js50
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js26
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js49
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js35
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js89
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js70
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js367
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js123
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js84
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js106
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js36
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js53
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js44
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js39
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js29
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js68
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js155
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js64
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js100
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js211
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js97
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js33
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js52
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js80
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js70
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js67
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js59
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js76
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js84
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js267
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js37
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_611795.js67
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js26
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js64
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js119
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js82
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js54
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js36
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js47
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js149
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js32
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js120
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js47
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js84
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_632817.js217
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js81
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js235
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js57
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js102
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js109
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js67
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js36
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js54
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js79
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js75
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_704295.js41
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js35
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js63
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js83
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js32
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js62
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js142
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js88
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js35
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js140
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js227
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js57
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js42
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js48
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js114
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_cd_iframe.js115
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_certificate_messages.js81
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_chrome.js38
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_clear_method.js131
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_clickable_urls.js103
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_closure_inspection.js100
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_column_numbers.js46
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_completion.js106
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js85
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js81
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_extras.js43
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_logging_api.js102
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js39
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_trace_async.js75
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js50
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js51
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js66
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_count.js77
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js56
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js104
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_execution_scope.js37
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js57
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js95
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_for_of.js32
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_history.js62
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js126
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js92
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js34
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js35
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js55
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_jsterm.js195
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js56
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js96
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_log_file_filter.js83
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_message_node_id.js28
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_multiline_input.js70
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_netlogging.js139
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js44
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js30
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js95
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_notifications.js77
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js54
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_01.js122
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_02.js183
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_03.js168
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_04.js129
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_05.js177
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_06.js283
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js72
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js122
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js66
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js70
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js113
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js47
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_events.js54
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_order.js47
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_regexp.js35
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_output_table.js199
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_promise.js35
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_property_provider.js46
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_reflow.js33
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js76
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js67
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js39
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js73
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_split.js268
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_split_escape_key.js158
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_split_focus.js66
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_split_persist.js119
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js38
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js83
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js54
-rw-r--r--devtools/client/webconsole/test/browser_webconsole_view_source.js52
-rw-r--r--devtools/client/webconsole/test/head.js1844
-rw-r--r--devtools/client/webconsole/test/test-autocomplete-in-stackframe.html50
-rw-r--r--devtools/client/webconsole/test/test-bug-585956-console-trace.html27
-rw-r--r--devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-canvas-css.html17
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-canvas-css.js10
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-css-loader.css10
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^1
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-css-loader.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-css-parser.css10
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-css-parser.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html16
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js8
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-html.html16
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-image.html15
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-image.jpgbin0 -> 2532 bytes
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-imagemap.html17
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html19
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml8
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml10
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-svg.xhtml17
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-workers.html18
-rw-r--r--devtools/client/webconsole/test/test-bug-595934-workers.js14
-rw-r--r--devtools/client/webconsole/test/test-bug-597136-external-script-errors.html25
-rw-r--r--devtools/client/webconsole/test/test-bug-597136-external-script-errors.js9
-rw-r--r--devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html18
-rw-r--r--devtools/client/webconsole/test/test-bug-599725-response-headers.sjs25
-rw-r--r--devtools/client/webconsole/test/test-bug-600183-charset.html9
-rw-r--r--devtools/client/webconsole/test/test-bug-600183-charset.html^headers^1
-rw-r--r--devtools/client/webconsole/test/test-bug-601177-log-levels.html20
-rw-r--r--devtools/client/webconsole/test/test-bug-601177-log-levels.js8
-rw-r--r--devtools/client/webconsole/test/test-bug-603750-websocket.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-603750-websocket.js20
-rw-r--r--devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html21
-rw-r--r--devtools/client/webconsole/test/test-bug-618078-network-exceptions.html24
-rw-r--r--devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html23
-rw-r--r--devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs16
-rw-r--r--devtools/client/webconsole/test/test-bug-632275-getters.html20
-rw-r--r--devtools/client/webconsole/test/test-bug-632347-iterators-generators.html56
-rw-r--r--devtools/client/webconsole/test/test-bug-644419-log-limits.html21
-rw-r--r--devtools/client/webconsole/test/test-bug-646025-console-file-location.html12
-rw-r--r--devtools/client/webconsole/test/test-bug-658368-time-methods.html24
-rw-r--r--devtools/client/webconsole/test/test-bug-737873-mixedcontent.html15
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html13
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html28
-rw-r--r--devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html16
-rw-r--r--devtools/client/webconsole/test/test-bug-766001-console-log.js10
-rw-r--r--devtools/client/webconsole/test/test-bug-766001-js-console-links.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-766001-js-errors.js8
-rw-r--r--devtools/client/webconsole/test/test-bug-782653-css-errors-1.css10
-rw-r--r--devtools/client/webconsole/test/test-bug-782653-css-errors-2.css10
-rw-r--r--devtools/client/webconsole/test/test-bug-782653-css-errors.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-837351-security-errors.html15
-rw-r--r--devtools/client/webconsole/test/test-bug-859170-longstring-hang.html23
-rw-r--r--devtools/client/webconsole/test/test-bug-869003-iframe.html20
-rw-r--r--devtools/client/webconsole/test/test-bug-869003-top-window.html14
-rw-r--r--devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html15
-rw-r--r--devtools/client/webconsole/test/test-bug-989025-iframe-parent.html13
-rw-r--r--devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html11
-rw-r--r--devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js10
-rw-r--r--devtools/client/webconsole/test/test-bug_923281_console_log_filter.html12
-rw-r--r--devtools/client/webconsole/test/test-bug_923281_test1.js7
-rw-r--r--devtools/client/webconsole/test/test-bug_923281_test2.js8
-rw-r--r--devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html35
-rw-r--r--devtools/client/webconsole/test/test-certificate-messages.html22
-rw-r--r--devtools/client/webconsole/test/test-closure-optimized-out.html34
-rw-r--r--devtools/client/webconsole/test/test-closures.html26
-rw-r--r--devtools/client/webconsole/test/test-console-api-stackframe.html32
-rw-r--r--devtools/client/webconsole/test/test-console-assert.html23
-rw-r--r--devtools/client/webconsole/test/test-console-clear.html16
-rw-r--r--devtools/client/webconsole/test/test-console-column.html17
-rw-r--r--devtools/client/webconsole/test/test-console-count-external-file.js11
-rw-r--r--devtools/client/webconsole/test/test-console-count.html56
-rw-r--r--devtools/client/webconsole/test/test-console-extras.html18
-rw-r--r--devtools/client/webconsole/test/test-console-output-02.html66
-rw-r--r--devtools/client/webconsole/test/test-console-output-03.html30
-rw-r--r--devtools/client/webconsole/test/test-console-output-04.html77
-rw-r--r--devtools/client/webconsole/test/test-console-output-dom-elements.html91
-rw-r--r--devtools/client/webconsole/test/test-console-output-events.html42
-rw-r--r--devtools/client/webconsole/test/test-console-output-regexp.html23
-rw-r--r--devtools/client/webconsole/test/test-console-replaced-api.html12
-rw-r--r--devtools/client/webconsole/test/test-console-server-logging-array.sjs32
-rw-r--r--devtools/client/webconsole/test/test-console-server-logging.sjs32
-rw-r--r--devtools/client/webconsole/test/test-console-table.html63
-rw-r--r--devtools/client/webconsole/test/test-console-trace-async.html24
-rw-r--r--devtools/client/webconsole/test/test-console-workers.html13
-rw-r--r--devtools/client/webconsole/test/test-console.html34
-rw-r--r--devtools/client/webconsole/test/test-consoleiframes.html13
-rw-r--r--devtools/client/webconsole/test/test-cu-reporterror.js4
-rw-r--r--devtools/client/webconsole/test/test-data.json1
-rw-r--r--devtools/client/webconsole/test/test-data.json^headers^1
-rw-r--r--devtools/client/webconsole/test/test-duplicate-error.html21
-rw-r--r--devtools/client/webconsole/test/test-encoding-ISO-8859-1.html7
-rw-r--r--devtools/client/webconsole/test/test-error.html21
-rw-r--r--devtools/client/webconsole/test/test-eval-in-stackframe.html39
-rw-r--r--devtools/client/webconsole/test/test-exception-stackframe.html43
-rw-r--r--devtools/client/webconsole/test/test-file-location.js12
-rw-r--r--devtools/client/webconsole/test/test-filter.html11
-rw-r--r--devtools/client/webconsole/test/test-for-of.html8
-rw-r--r--devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html15
-rw-r--r--devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html15
-rw-r--r--devtools/client/webconsole/test/test-iframe1.html10
-rw-r--r--devtools/client/webconsole/test/test-iframe2.html11
-rw-r--r--devtools/client/webconsole/test/test-iframe3.html11
-rw-r--r--devtools/client/webconsole/test/test-image.pngbin0 -> 580 bytes
-rw-r--r--devtools/client/webconsole/test/test-mixedcontent-securityerrors.html21
-rw-r--r--devtools/client/webconsole/test/test-mutation.html16
-rw-r--r--devtools/client/webconsole/test/test-network-request.html40
-rw-r--r--devtools/client/webconsole/test/test-network.html11
-rw-r--r--devtools/client/webconsole/test/test-observe-http-ajax.html17
-rw-r--r--devtools/client/webconsole/test/test-own-console.html24
-rw-r--r--devtools/client/webconsole/test/test-property-provider.html14
-rw-r--r--devtools/client/webconsole/test/test-repeated-messages.html53
-rw-r--r--devtools/client/webconsole/test/test-result-format-as-string.html25
-rw-r--r--devtools/client/webconsole/test/test-trackingprotection-securityerrors.html12
-rw-r--r--devtools/client/webconsole/test/test-webconsole-error-observer.html25
-rw-r--r--devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html10
-rw-r--r--devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^1
-rw-r--r--devtools/client/webconsole/test/test_bug1092055_shouldwarn.html15
-rw-r--r--devtools/client/webconsole/test/test_bug1092055_shouldwarn.js2
-rw-r--r--devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^1
-rw-r--r--devtools/client/webconsole/test/test_bug_1010953_cspro.html20
-rw-r--r--devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^2
-rw-r--r--devtools/client/webconsole/test/test_bug_1247459_violation.html15
-rw-r--r--devtools/client/webconsole/test/test_bug_770099_violation.html13
-rw-r--r--devtools/client/webconsole/test/test_bug_770099_violation.html^headers^1
-rw-r--r--devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs53
-rw-r--r--devtools/client/webconsole/test/test_hsts-invalid-headers.sjs39
-rw-r--r--devtools/client/webconsole/test/testscript.js2
352 files changed, 22823 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/.eslintrc.js b/devtools/client/webconsole/test/.eslintrc.js
new file mode 100644
index 000000000..8d15a76d9
--- /dev/null
+++ b/devtools/client/webconsole/test/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the shared list of defined globals for mochitests.
+ "extends": "../../../.eslintrc.mochitests.js"
+};
diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini
new file mode 100644
index 000000000..918411182
--- /dev/null
+++ b/devtools/client/webconsole/test/browser.ini
@@ -0,0 +1,396 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+ head.js
+ test-bug-585956-console-trace.html
+ test-bug-593003-iframe-wrong-hud-iframe.html
+ test-bug-593003-iframe-wrong-hud.html
+ test-bug-595934-canvas-css.html
+ test-bug-595934-canvas-css.js
+ test-bug-595934-css-loader.css
+ test-bug-595934-css-loader.css^headers^
+ test-bug-595934-css-loader.html
+ test-bug-595934-css-parser.css
+ test-bug-595934-css-parser.html
+ test-bug-595934-empty-getelementbyid.html
+ test-bug-595934-empty-getelementbyid.js
+ test-bug-595934-html.html
+ test-bug-595934-image.html
+ test-bug-595934-image.jpg
+ test-bug-595934-imagemap.html
+ test-bug-595934-malformedxml-external.html
+ test-bug-595934-malformedxml-external.xml
+ test-bug-595934-malformedxml.xhtml
+ test-bug-595934-svg.xhtml
+ test-bug-595934-workers.html
+ test-bug-595934-workers.js
+ test-bug-597136-external-script-errors.html
+ test-bug-597136-external-script-errors.js
+ test-bug-597756-reopen-closed-tab.html
+ test-bug-599725-response-headers.sjs
+ test-bug-600183-charset.html
+ test-bug-600183-charset.html^headers^
+ test-bug-601177-log-levels.html
+ test-bug-601177-log-levels.js
+ test-bug-603750-websocket.html
+ test-bug-603750-websocket.js
+ test-bug-613013-console-api-iframe.html
+ test-bug-618078-network-exceptions.html
+ test-bug-621644-jsterm-dollar.html
+ test-bug-630733-response-redirect-headers.sjs
+ test-bug-632275-getters.html
+ test-bug-632347-iterators-generators.html
+ test-bug-644419-log-limits.html
+ test-bug-646025-console-file-location.html
+ test-bug-658368-time-methods.html
+ test-bug-737873-mixedcontent.html
+ test-bug-752559-ineffective-iframe-sandbox-warning0.html
+ test-bug-752559-ineffective-iframe-sandbox-warning1.html
+ test-bug-752559-ineffective-iframe-sandbox-warning2.html
+ test-bug-752559-ineffective-iframe-sandbox-warning3.html
+ test-bug-752559-ineffective-iframe-sandbox-warning4.html
+ test-bug-752559-ineffective-iframe-sandbox-warning5.html
+ test-bug-752559-ineffective-iframe-sandbox-warning-inner.html
+ test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html
+ test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html
+ test-bug-762593-insecure-passwords-about-blank-web-console-warning.html
+ test-bug-762593-insecure-passwords-web-console-warning.html
+ test-bug-766001-console-log.js
+ test-bug-766001-js-console-links.html
+ test-bug-766001-js-errors.js
+ test-bug-782653-css-errors-1.css
+ test-bug-782653-css-errors-2.css
+ test-bug-782653-css-errors.html
+ test-bug-837351-security-errors.html
+ test-bug-859170-longstring-hang.html
+ test-bug-869003-iframe.html
+ test-bug-869003-top-window.html
+ test-closure-optimized-out.html
+ test-closures.html
+ test-console-assert.html
+ test-console-clear.html
+ test-console-count.html
+ test-console-count-external-file.js
+ test-console-extras.html
+ test-console-replaced-api.html
+ test-console-server-logging.sjs
+ test-console-server-logging-array.sjs
+ test-console.html
+ test-console-workers.html
+ test-console-table.html
+ test-console-output-02.html
+ test-console-output-03.html
+ test-console-output-04.html
+ test-console-output-dom-elements.html
+ test-console-output-events.html
+ test-console-output-regexp.html
+ test-console-column.html
+ test-consoleiframes.html
+ test-console-trace-async.html
+ test-certificate-messages.html
+ test-cu-reporterror.js
+ test-data.json
+ test-data.json^headers^
+ test-duplicate-error.html
+ test-encoding-ISO-8859-1.html
+ test-error.html
+ test-eval-in-stackframe.html
+ test-file-location.js
+ test-filter.html
+ test-for-of.html
+ test_hpkp-invalid-headers.sjs
+ test_hsts-invalid-headers.sjs
+ test-iframe-762593-insecure-form-action.html
+ test-iframe-762593-insecure-frame.html
+ test-iframe1.html
+ test-iframe2.html
+ test-iframe3.html
+ test-image.png
+ test-mixedcontent-securityerrors.html
+ test-mutation.html
+ test-network-request.html
+ test-network.html
+ test-observe-http-ajax.html
+ test-own-console.html
+ test-property-provider.html
+ test-repeated-messages.html
+ test-result-format-as-string.html
+ test-trackingprotection-securityerrors.html
+ test-webconsole-error-observer.html
+ test_bug_770099_violation.html
+ test_bug_770099_violation.html^headers^
+ test-autocomplete-in-stackframe.html
+ testscript.js
+ test-bug_923281_console_log_filter.html
+ test-bug_923281_test1.js
+ test-bug_923281_test2.js
+ test-bug_939783_console_trace_duplicates.html
+ test-bug-952277-highlight-nodes-in-vview.html
+ test-bug-609872-cd-iframe-parent.html
+ test-bug-609872-cd-iframe-child.html
+ test-bug-989025-iframe-parent.html
+ test-bug_1050691_click_function_to_source.html
+ test-bug_1050691_click_function_to_source.js
+ test-console-api-stackframe.html
+ test-exception-stackframe.html
+ test_bug_1010953_cspro.html^headers^
+ test_bug_1010953_cspro.html
+ test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
+ test_bug1045902_console_csp_ignore_reflected_xss_message.html
+ test_bug1092055_shouldwarn.js^headers^
+ test_bug1092055_shouldwarn.js
+ test_bug1092055_shouldwarn.html
+ test_bug_1247459_violation.html
+ !/devtools/client/framework/test/shared-head.js
+ !/devtools/client/netmonitor/test/sjs_cors-test-server.sjs
+ !/image/test/mochitest/blue.png
+
+[browser_bug1045902_console_csp_ignore_reflected_xss_message.js]
+skip-if = (e10s && debug) || (e10s && os == 'win') # Bug 1221499 enabled these on windows
+[browser_bug664688_sandbox_update_after_navigation.js]
+[browser_bug_638949_copy_link_location.js]
+subsuite = clipboard
+[browser_bug_862916_console_dir_and_filter_off.js]
+skip-if = (e10s && (os == 'win' || os == 'mac')) # Bug 1243976
+[browser_bug_865288_repeat_different_objects.js]
+[browser_bug_865871_variables_view_close_on_esc_key.js]
+[browser_bug_869003_inspect_cross_domain_object.js]
+[browser_bug_871156_ctrlw_close_tab.js]
+[browser_cached_messages.js]
+[browser_console.js]
+[browser_console_addonsdk_loader_exception.js]
+[browser_console_clear_method.js]
+[browser_console_clear_on_reload.js]
+[browser_console_click_focus.js]
+[browser_console_consolejsm_output.js]
+[browser_console_copy_command.js]
+subsuite = clipboard
+[browser_console_dead_objects.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_console_copy_entire_message_context_menu.js]
+subsuite = clipboard
+[browser_console_error_source_click.js]
+[browser_console_filters.js]
+[browser_console_iframe_messages.js]
+[browser_console_keyboard_accessibility.js]
+[browser_console_log_inspectable_object.js]
+[browser_console_native_getters.js]
+[browser_console_navigation_marker.js]
+[browser_console_netlogging.js]
+[browser_console_nsiconsolemessage.js]
+[browser_console_optimized_out_vars.js]
+[browser_console_private_browsing.js]
+skip-if = e10s # Bug 1042253 - webconsole e10s tests
+[browser_console_server_logging.js]
+[browser_console_variables_view.js]
+[browser_console_variables_view_filter.js]
+[browser_console_variables_view_dom_nodes.js]
+[browser_console_variables_view_dont_sort_non_sortable_classes_properties.js]
+[browser_console_variables_view_special_names.js]
+[browser_console_variables_view_while_debugging.js]
+[browser_console_variables_view_while_debugging_and_inspecting.js]
+[browser_eval_in_debugger_stackframe.js]
+[browser_eval_in_debugger_stackframe2.js]
+[browser_jsterm_inspect.js]
+skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1243966
+[browser_longstring_hang.js]
+[browser_output_breaks_after_console_dir_uninspectable.js]
+[browser_output_longstring_expand.js]
+[browser_repeated_messages_accuracy.js]
+[browser_result_format_as_string.js]
+[browser_warn_user_about_replaced_api.js]
+[browser_webconsole_allow_mixedcontent_securityerrors.js]
+tags = mcb
+[browser_webconsole_script_errordoc_urls.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_assert.js]
+[browser_webconsole_block_mixedcontent_securityerrors.js]
+tags = mcb
+[browser_webconsole_bug_579412_input_focus.js]
+[browser_webconsole_bug_580001_closing_after_completion.js]
+[browser_webconsole_bug_580030_errors_after_page_reload.js]
+[browser_webconsole_bug_580454_timestamp_l10n.js]
+[browser_webconsole_bug_582201_duplicate_errors.js]
+[browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js]
+[browser_webconsole_bug_585237_line_limit.js]
+[browser_webconsole_bug_585956_console_trace.js]
+[browser_webconsole_bug_585991_autocomplete_keys.js]
+[browser_webconsole_bug_585991_autocomplete_popup.js]
+[browser_webconsole_bug_586388_select_all.js]
+[browser_webconsole_bug_587617_output_copy.js]
+subsuite = clipboard
+[browser_webconsole_bug_588342_document_focus.js]
+[browser_webconsole_bug_588730_text_node_insertion.js]
+[browser_webconsole_bug_588967_input_expansion.js]
+[browser_webconsole_bug_589162_css_filter.js]
+[browser_webconsole_bug_592442_closing_brackets.js]
+[browser_webconsole_bug_593003_iframe_wrong_hud.js]
+[browser_webconsole_bug_594497_history_arrow_keys.js]
+[browser_webconsole_bug_595223_file_uri.js]
+[browser_webconsole_bug_595350_multiple_windows_and_tabs.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_bug_595934_message_categories.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js]
+[browser_webconsole_bug_597136_external_script_errors.js]
+[browser_webconsole_bug_597136_network_requests_from_chrome.js]
+[browser_webconsole_bug_597460_filter_scroll.js]
+[browser_webconsole_bug_597756_reopen_closed_tab.js]
+[browser_webconsole_bug_599725_response_headers.js]
+[browser_webconsole_bug_600183_charset.js]
+[browser_webconsole_bug_601177_log_levels.js]
+[browser_webconsole_bug_601352_scroll.js]
+[browser_webconsole_bug_601667_filter_buttons.js]
+[browser_webconsole_bug_603750_websocket.js]
+[browser_webconsole_bug_611795.js]
+[browser_webconsole_bug_613013_console_api_iframe.js]
+[browser_webconsole_bug_613280_jsterm_copy.js]
+subsuite = clipboard
+[browser_webconsole_bug_613642_maintain_scroll.js]
+[browser_webconsole_bug_613642_prune_scroll.js]
+[browser_webconsole_bug_614793_jsterm_scroll.js]
+[browser_webconsole_bug_618078_network_exceptions.js]
+[browser_webconsole_bug_621644_jsterm_dollar.js]
+[browser_webconsole_bug_622303_persistent_filters.js]
+[browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js]
+skip-if = os != "win"
+[browser_webconsole_bug_630733_response_redirect_headers.js]
+[browser_webconsole_bug_632275_getters_document_width.js]
+[browser_webconsole_bug_632347_iterators_generators.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_bug_632817.js]
+skip-if = true # Bug 1244707
+[browser_webconsole_bug_642108_pruneTest.js]
+[browser_webconsole_autocomplete_and_selfxss.js]
+subsuite = clipboard
+[browser_webconsole_bug_644419_log_limits.js]
+[browser_webconsole_bug_646025_console_file_location.js]
+[browser_webconsole_bug_651501_document_body_autocomplete.js]
+[browser_webconsole_bug_653531_highlighter_console_helper.js]
+skip-if = true # Requires direct access to content nodes
+[browser_webconsole_bug_658368_time_methods.js]
+[browser_webconsole_bug_659907_console_dir.js]
+[browser_webconsole_bug_660806_history_nav.js]
+[browser_webconsole_bug_664131_console_group.js]
+[browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js]
+[browser_webconsole_bug_704295.js]
+[browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js]
+[browser_webconsole_bug_737873_mixedcontent.js]
+tags = mcb
+[browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js]
+[browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js]
+[browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js]
+skip-if = true # Bug 1110500 - mouse event failure in test
+[browser_webconsole_bug_764572_output_open_url.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_bug_766001_JS_Console_in_Debugger.js]
+[browser_webconsole_bug_770099_violation.js]
+skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243978
+[browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js]
+[browser_webconsole_bug_804845_ctrl_key_nav.js]
+skip-if = os != "mac"
+[browser_webconsole_bug_817834_add_edited_input_to_history.js]
+[browser_webconsole_bug_837351_securityerrors.js]
+[browser_webconsole_filter_buttons_contextmenu.js]
+[browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
+skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug intermittent)
+[browser_webconsole_bug_1010953_cspro.js]
+skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243967
+[browser_webconsole_bug_1247459_violation.js]
+skip-if = e10s && (os == 'win') # Bug 1264955
+[browser_webconsole_certificate_messages.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_show_subresource_security_errors.js]
+skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243987
+[browser_webconsole_cached_autocomplete.js]
+[browser_webconsole_chrome.js]
+[browser_webconsole_clear_method.js]
+[browser_webconsole_clickable_urls.js]
+[browser_webconsole_closure_inspection.js]
+[browser_webconsole_completion.js]
+[browser_webconsole_console_extras.js]
+[browser_webconsole_console_logging_api.js]
+[browser_webconsole_console_logging_workers_api.js]
+[browser_webconsole_console_trace_async.js]
+[browser_webconsole_count.js]
+[browser_webconsole_dont_navigate_on_doubleclick.js]
+[browser_webconsole_execution_scope.js]
+[browser_webconsole_for_of.js]
+[browser_webconsole_history.js]
+[browser_webconsole_hpkp_invalid-headers.js]
+[browser_webconsole_hsts_invalid-headers.js]
+skip-if = e10s # Bug 1042253 - webconsole e10s tests
+[browser_webconsole_input_field_focus_on_panel_select.js]
+[browser_webconsole_inspect-parsed-documents.js]
+[browser_webconsole_js_input_expansion.js]
+[browser_webconsole_jsterm.js]
+skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
+[browser_webconsole_live_filtering_of_message_types.js]
+[browser_webconsole_live_filtering_on_search_strings.js]
+[browser_webconsole_message_node_id.js]
+[browser_webconsole_multiline_input.js]
+[browser_webconsole_netlogging.js]
+skip-if = true # Bug 1298364
+[browser_webconsole_netlogging_basic.js]
+[browser_webconsole_netlogging_panel.js]
+[browser_webconsole_netlogging_reset_filter.js]
+[browser_webconsole_notifications.js]
+[browser_webconsole_open-links-without-callback.js]
+[browser_webconsole_promise.js]
+[browser_webconsole_output_copy_newlines.js]
+subsuite = clipboard
+[browser_webconsole_output_order.js]
+[browser_webconsole_property_provider.js]
+skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
+[browser_webconsole_scratchpad_panel_link.js]
+[browser_webconsole_split.js]
+[browser_webconsole_split_escape_key.js]
+[browser_webconsole_split_focus.js]
+[browser_webconsole_split_persist.js]
+[browser_webconsole_trackingprotection_errors.js]
+tags = trackingprotection
+[browser_webconsole_view_source.js]
+[browser_webconsole_reflow.js]
+[browser_webconsole_log_file_filter.js]
+[browser_webconsole_expandable_timestamps.js]
+[browser_webconsole_autocomplete_accessibility.js]
+[browser_webconsole_autocomplete_in_debugger_stackframe.js]
+[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
+[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
+[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
+[browser_console_history_persist.js]
+[browser_webconsole_output_01.js]
+[browser_webconsole_output_02.js]
+[browser_webconsole_output_03.js]
+[browser_webconsole_output_04.js]
+[browser_webconsole_output_05.js]
+[browser_webconsole_output_06.js]
+[browser_webconsole_output_dom_elements_01.js]
+[browser_webconsole_output_dom_elements_02.js]
+skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
+[browser_webconsole_output_dom_elements_03.js]
+skip-if = e10s # Bug 1241019
+[browser_webconsole_output_dom_elements_04.js]
+skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
+[browser_webconsole_output_dom_elements_05.js]
+[browser_webconsole_output_events.js]
+[browser_webconsole_output_regexp.js]
+[browser_webconsole_output_table.js]
+[browser_console_variables_view_highlighter.js]
+[browser_webconsole_start_netmon_first.js]
+[browser_webconsole_console_trace_duplicates.js]
+[browser_webconsole_cd_iframe.js]
+[browser_webconsole_autocomplete_crossdomain_iframe.js]
+[browser_webconsole_console_custom_styles.js]
+[browser_webconsole_console_api_stackframe.js]
+[browser_webconsole_exception_stackframe.js]
+[browser_webconsole_column_numbers.js]
+[browser_console_open_or_focus.js]
+[browser_webconsole_bug_922212_console_dirxml.js]
+[browser_webconsole_shows_reqs_in_netmonitor.js]
+[browser_netmonitor_shows_reqs_in_webconsole.js]
+[browser_webconsole_bug_1050691_click_function_to_source.js]
+[browser_webconsole_context_menu_open_in_var_view.js]
+[browser_webconsole_context_menu_store_as_global.js]
+[browser_webconsole_strict_mode_errors.js]
diff --git a/devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js b/devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js
new file mode 100644
index 000000000..cfbf61795
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js
@@ -0,0 +1,52 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that a file with an unsupported CSP directive ('reflected-xss filter')
+// displays the appropriate message to the console.
+
+"use strict";
+
+const EXPECTED_RESULT = "Not supporting directive \u2018reflected-xss\u2019. " +
+ "Directive and values will be ignored.";
+const TEST_FILE = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test_bug1045902_console_csp_ignore_reflected_xss_" +
+ "message.html";
+
+var hud = undefined;
+
+var TEST_URI = "data:text/html;charset=utf8,Web Console CSP ignoring " +
+ "reflected XSS (bug 1045902)";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+
+ yield loadDocument(browser);
+ yield testViolationMessage();
+
+ hud = null;
+});
+
+function loadDocument(browser) {
+ hud.jsterm.clearOutput();
+ browser.loadURI(TEST_FILE);
+ return BrowserTestUtils.browserLoaded(browser);
+}
+
+function testViolationMessage() {
+ let aOutputNode = hud.outputNode;
+
+ return waitForSuccess({
+ name: "Confirming that CSP logs messages to the console when " +
+ "\u2018reflected-xss\u2019 directive is used!",
+ validator: function () {
+ console.log(aOutputNode.textContent);
+ let success = false;
+ success = aOutputNode.textContent.indexOf(EXPECTED_RESULT) > -1;
+ return success;
+ }
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js b/devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js
new file mode 100644
index 000000000..1aacb61c1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug664688_sandbox_update_after_navigation.js
@@ -0,0 +1,92 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests if the JSTerm sandbox is updated when the user navigates from one
+// domain to another, in order to avoid permission denied errors with a sandbox
+// created for a different origin.
+
+"use strict";
+
+add_task(function* () {
+ const TEST_URI1 = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+ const TEST_URI2 = "http://example.org/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+ yield loadTab(TEST_URI1);
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("window.location.href");
+
+ info("wait for window.location.href");
+
+ let msgForLocation1 = {
+ webconsole: hud,
+ messages: [
+ {
+ name: "window.location.href jsterm input",
+ text: "window.location.href",
+ category: CATEGORY_INPUT,
+ },
+ {
+ name: "window.location.href result is displayed",
+ text: TEST_URI1,
+ category: CATEGORY_OUTPUT,
+ },
+ ],
+ };
+
+ yield waitForMessages(msgForLocation1);
+
+ // load second url
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2);
+ yield loadBrowser(gBrowser.selectedBrowser);
+
+ is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
+ "no permission denied errors");
+
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("window.location.href");
+
+ info("wait for window.location.href after page navigation");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "window.location.href jsterm input",
+ text: "window.location.href",
+ category: CATEGORY_INPUT,
+ },
+ {
+ name: "window.location.href result is displayed",
+ text: TEST_URI2,
+ category: CATEGORY_OUTPUT,
+ },
+ ],
+ });
+
+ is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
+ "no permission denied errors");
+
+ // Navigation clears messages. Wait for that clear to happen before
+ // continuing the test or it might destroy messages we wait later on (Bug
+ // 1270234).
+ let cleared = hud.jsterm.once("messages-cleared");
+
+ gBrowser.goBack();
+
+ info("Waiting for messages to be cleared due to navigation");
+ yield cleared;
+
+ info("Messages cleared after navigation; checking location");
+ hud.jsterm.execute("window.location.href");
+
+ info("wait for window.location.href after goBack()");
+ yield waitForMessages(msgForLocation1);
+ is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
+ "no permission denied errors");
+});
diff --git a/devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js b/devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js
new file mode 100644
index 000000000..54bdbe499
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug_638949_copy_link_location.js
@@ -0,0 +1,107 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test for the "Copy link location" context menu item shown when you right
+// click network requests in the output.
+
+"use strict";
+
+add_task(function* () {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html?_date=" + Date.now();
+ const COMMAND_NAME = "consoleCmd_copyURL";
+ const CONTEXT_MENU_ID = "#menu_copyURL";
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.webconsole.filter.networkinfo");
+ });
+
+ Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true);
+
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ let output = hud.outputNode;
+ let menu = hud.iframeWindow.document.getElementById("output-contextmenu");
+
+ hud.jsterm.clearOutput();
+ content.console.log("bug 638949");
+
+ // Test that the "Copy Link Location" command is disabled for non-network
+ // messages.
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug 638949",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ output.focus();
+ let message = [...result.matched][0];
+
+ goUpdateCommand(COMMAND_NAME);
+ ok(!isEnabled(), COMMAND_NAME + " is disabled");
+
+ // Test that the "Copy Link Location" menu item is hidden for non-network
+ // messages.
+ yield waitForContextMenu(menu, message, () => {
+ let isHidden = menu.querySelector(CONTEXT_MENU_ID).hidden;
+ ok(isHidden, CONTEXT_MENU_ID + " is hidden");
+ });
+
+ hud.jsterm.clearOutput();
+ // Reloading will produce network logging
+ content.location.reload();
+
+ // Test that the "Copy Link Location" command is enabled and works
+ // as expected for any network-related message.
+ // This command should copy only the URL.
+ [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test-console.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ output.focus();
+ message = [...result.matched][0];
+ hud.ui.output.selectMessage(message);
+
+ goUpdateCommand(COMMAND_NAME);
+ ok(isEnabled(), COMMAND_NAME + " is enabled");
+
+ info("expected clipboard value: " + message.url);
+
+ let deferred = promise.defer();
+
+ waitForClipboard((aData) => {
+ return aData.trim() == message.url;
+ }, () => {
+ goDoCommand(COMMAND_NAME);
+ }, () => {
+ deferred.resolve(null);
+ }, () => {
+ deferred.reject(null);
+ });
+
+ yield deferred.promise;
+
+ // Test that the "Copy Link Location" menu item is visible for network-related
+ // messages.
+ yield waitForContextMenu(menu, message, () => {
+ let isVisible = !menu.querySelector(CONTEXT_MENU_ID).hidden;
+ ok(isVisible, CONTEXT_MENU_ID + " is visible");
+ });
+
+ // Return whether "Copy Link Location" command is enabled or not.
+ function isEnabled() {
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand(COMMAND_NAME);
+ return controller && controller.isCommandEnabled(COMMAND_NAME);
+ }
+});
diff --git a/devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js b/devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js
new file mode 100644
index 000000000..9d04076ee
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug_862916_console_dir_and_filter_off.js
@@ -0,0 +1,31 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the output for console.dir() works even if Logging filter is off.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test for bug 862916";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ ok(hud, "web console opened");
+
+ hud.setFilterState("log", false);
+ registerCleanupFunction(() => hud.setFilterState("log", true));
+
+ hud.jsterm.execute("window.fooBarz = 'bug862916'; " +
+ "console.dir(window)");
+
+ let varView = yield hud.jsterm.once("variablesview-fetched");
+ ok(varView, "variables view object");
+
+ yield findVariableViewProperties(varView, [
+ { name: "fooBarz", value: "bug862916" },
+ ], { webconsole: hud });
+});
+
diff --git a/devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js b/devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js
new file mode 100644
index 000000000..86ab5bd39
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug_865288_repeat_different_objects.js
@@ -0,0 +1,63 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that makes sure messages are not considered repeated when console.log()
+// is invoked with different objects, see bug 865288.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-repeated-messages.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ info("waiting for 3 console.log objects");
+
+ hud.jsterm.clearOutput(true);
+ hud.jsterm.execute("window.testConsoleObjects()");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "3 console.log messages",
+ text: "abba",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ count: 3,
+ repeats: 1,
+ objects: true,
+ }],
+ });
+
+ let msgs = [...result.matched];
+ is(msgs.length, 3, "3 message elements");
+
+ for (let i = 0; i < msgs.length; i++) {
+ info("test message element #" + i);
+
+ let msg = msgs[i];
+ let clickable = msg.querySelector(".message-body a");
+ ok(clickable, "clickable object #" + i);
+
+ msg.scrollIntoView(false);
+ yield clickObject(clickable, i);
+ }
+
+ function* clickObject(obj, i) {
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(obj, 2, 2, {}, hud.iframeWindow);
+ });
+
+ let varView = yield hud.jsterm.once("variablesview-fetched");
+ ok(varView, "variables view fetched #" + i);
+
+ yield findVariableViewProperties(varView, [
+ { name: "id", value: "abba" + i },
+ ], { webconsole: hud });
+ }
+});
+
diff --git a/devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js b/devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js
new file mode 100644
index 000000000..044525b28
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug_865871_variables_view_close_on_esc_key.js
@@ -0,0 +1,75 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the variables view sidebar can be closed by pressing Escape in the
+// web console.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-eval-in-stackframe.html";
+
+function test() {
+ let hud;
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ let {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+ let jsterm = hud.jsterm;
+ let result;
+ let vview;
+ let msg;
+
+ yield openSidebar("fooObj",
+ 'testProp: "testValue"',
+ { name: "testProp", value: "testValue" });
+
+ let prop = result.matchedProp;
+ ok(prop, "matched the |testProp| property in the variables view");
+
+ vview.window.focus();
+
+ let sidebarClosed = jsterm.once("sidebar-closed");
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield sidebarClosed;
+
+ jsterm.clearOutput();
+
+ yield openSidebar("window.location",
+ "Location \u2192 http://example.com/browser/",
+ { name: "host", value: "example.com" });
+
+ vview.window.focus();
+
+ msg.scrollIntoView();
+ sidebarClosed = jsterm.once("sidebar-closed");
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ yield sidebarClosed;
+
+ function* openSidebar(objName, expectedText, expectedObj) {
+ msg = yield jsterm.execute(objName);
+ ok(msg, "output message found");
+
+ let anchor = msg.querySelector("a");
+ let body = msg.querySelector(".message-body");
+ ok(anchor, "object anchor");
+ ok(body, "message body");
+ ok(body.textContent.includes(expectedText), "message text check");
+
+ msg.scrollIntoView();
+ yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+
+ let vviewVar = yield jsterm.once("variablesview-fetched");
+ vview = vviewVar._variablesView;
+ ok(vview, "variables view object exists");
+
+ [result] = yield findVariableViewProperties(vviewVar, [
+ expectedObj,
+ ], { webconsole: hud });
+ }
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js b/devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js
new file mode 100644
index 000000000..685148fc7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug_869003_inspect_cross_domain_object.js
@@ -0,0 +1,77 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that users can inspect objects logged from cross-domain iframes -
+// bug 869003.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-869003-top-window.html";
+
+add_task(function* () {
+ // This test is slightly more involved: it opens the web console, then the
+ // variables view for a given object, it updates a property in the view and
+ // checks the result. We can get a timeout with debug builds on slower
+ // machines.
+ requestLongerTimeout(2);
+
+ yield loadTab("data:text/html;charset=utf8,<p>hello");
+ let hud = yield openConsole();
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log message",
+ text: "foobar",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ objects: true,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ ok(msg, "message element");
+
+ let body = msg.querySelector(".message-body");
+ ok(body, "message body");
+
+ let clickable = result.clickableElements[0];
+ ok(clickable, "clickable object found");
+ ok(body.textContent.includes('{ hello: "world!",'), "message text check");
+
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+ });
+
+ let aVar = yield hud.jsterm.once("variablesview-fetched");
+ ok(aVar, "variables view fetched");
+ ok(aVar._variablesView, "variables view object");
+
+ [result] = yield findVariableViewProperties(aVar, [
+ { name: "hello", value: "world!" },
+ { name: "bug", value: 869003 },
+ ], { webconsole: hud });
+
+ let prop = result.matchedProp;
+ ok(prop, "matched the |hello| property in the variables view");
+
+ // Check that property value updates work.
+ aVar = yield updateVariablesViewProperty({
+ property: prop,
+ field: "value",
+ string: "'omgtest'",
+ webconsole: hud,
+ });
+
+ info("onFetchAfterUpdate");
+
+ yield findVariableViewProperties(aVar, [
+ { name: "hello", value: "omgtest" },
+ { name: "bug", value: 869003 },
+ ], { webconsole: hud });
+});
diff --git a/devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js b/devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js
new file mode 100644
index 000000000..c1698cf91
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_bug_871156_ctrlw_close_tab.js
@@ -0,0 +1,78 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that Ctrl-W closes the Browser Console and that Ctrl-W closes the
+// current tab when using the Web Console - bug 871156.
+
+"use strict";
+
+add_task(function* () {
+ const TEST_URI = "data:text/html;charset=utf8,<title>bug871156</title>\n" +
+ "<p>hello world";
+ let firstTab = gBrowser.selectedTab;
+
+ Services.prefs.setBoolPref("browser.tabs.animate", false);
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.tabs.animate");
+ });
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ ok(hud, "Web Console opened");
+
+ let tabClosed = promise.defer();
+ let toolboxDestroyed = promise.defer();
+ let tabSelected = promise.defer();
+
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = gDevTools.getToolbox(target);
+
+ gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
+ gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
+ info("tab closed");
+ tabClosed.resolve(null);
+ });
+
+ gBrowser.tabContainer.addEventListener("TabSelect", function onTabSelect() {
+ gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect);
+ if (gBrowser.selectedTab == firstTab) {
+ info("tab selected");
+ tabSelected.resolve(null);
+ }
+ });
+
+ toolbox.once("destroyed", () => {
+ info("toolbox destroyed");
+ toolboxDestroyed.resolve(null);
+ });
+
+ // Get out of the web console initialization.
+ executeSoon(() => {
+ EventUtils.synthesizeKey("w", { accelKey: true });
+ });
+
+ yield promise.all([tabClosed.promise, toolboxDestroyed.promise,
+ tabSelected.promise]);
+ info("promise.all resolved. start testing the Browser Console");
+
+ hud = yield HUDService.toggleBrowserConsole();
+ ok(hud, "Browser Console opened");
+
+ let deferred = promise.defer();
+
+ Services.obs.addObserver(function onDestroy() {
+ Services.obs.removeObserver(onDestroy, "web-console-destroyed");
+ ok(true, "the Browser Console closed");
+
+ deferred.resolve(null);
+ }, "web-console-destroyed", false);
+
+ waitForFocus(() => {
+ EventUtils.synthesizeKey("w", { accelKey: true }, hud.iframeWindow);
+ }, hud.iframeWindow);
+
+ yield deferred.promise;
+});
diff --git a/devtools/client/webconsole/test/browser_cached_messages.js b/devtools/client/webconsole/test/browser_cached_messages.js
new file mode 100644
index 000000000..bf69deee3
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_cached_messages.js
@@ -0,0 +1,59 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test to see if the cached messages are displayed when the console UI is
+// opened.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-webconsole-error-observer.html";
+
+// On e10s, the exception is triggered in child process
+// and is ignored by test harness
+if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ loadTab(TEST_URI).then(testOpenUI);
+}
+
+function testOpenUI(aTestReopen) {
+ openConsole().then((hud) => {
+ waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "log Bazzle",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "error Bazzle",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "bazBug611032",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "cssColorBug611032",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ },
+ ],
+ }).then(() => {
+ closeConsole(gBrowser.selectedTab).then(() => {
+ aTestReopen && info("will reopen the Web Console");
+ executeSoon(aTestReopen ? testOpenUI : finishTest);
+ });
+ });
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_console.js b/devtools/client/webconsole/test/browser_console.js
new file mode 100644
index 000000000..7bd1ffdc2
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console.js
@@ -0,0 +1,160 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the basic features of the Browser Console, bug 587757.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html?" + Date.now();
+const TEST_FILE = "chrome://mochitests/content/browser/devtools/client/" +
+ "webconsole/test/test-cu-reporterror.js";
+
+const TEST_XHR_ERROR_URI = `http://example.com/404.html?${Date.now()}`;
+
+const TEST_IMAGE = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-image.png";
+
+"use strict";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let opened = waitForConsole();
+
+ let hud = HUDService.getBrowserConsole();
+ ok(!hud, "browser console is not open");
+ info("wait for the browser console to open with ctrl-shift-j");
+ EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, window);
+
+ hud = yield opened;
+ ok(hud, "browser console opened");
+
+ yield consoleOpened(hud);
+});
+
+function consoleOpened(hud) {
+ hud.jsterm.clearOutput(true);
+
+ expectUncaughtException();
+ executeSoon(() => {
+ foobarExceptionBug587757();
+ });
+
+ // Add a message from a chrome window.
+ hud.iframeWindow.console.log("bug587757a");
+
+ // Check Cu.reportError stack.
+ // Use another js script to not depend on the test file line numbers.
+ Services.scriptloader.loadSubScript(TEST_FILE, hud.iframeWindow);
+
+ // Add a message from a content window.
+ content.console.log("bug587757b");
+
+ // Test eval.
+ hud.jsterm.execute("document.location.href");
+
+ // Check for network requests.
+ let xhr = new XMLHttpRequest();
+ xhr.onload = () => console.log("xhr loaded, status is: " + xhr.status);
+ xhr.open("get", TEST_URI, true);
+ xhr.send();
+
+ // Check for xhr error.
+ let xhrErr = new XMLHttpRequest();
+ xhrErr.onload = () => {
+ console.log("xhr error loaded, status is: " + xhrErr.status);
+ };
+ xhrErr.open("get", TEST_XHR_ERROR_URI, true);
+ xhrErr.send();
+
+ // Check that Fetch requests are categorized as "XHR".
+ fetch(TEST_IMAGE).then(() => { console.log("fetch loaded"); });
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "chrome window console.log() is displayed",
+ text: "bug587757a",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "Cu.reportError is displayed",
+ text: "bug1141222",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ stacktrace: [{
+ file: TEST_FILE,
+ line: 2,
+ }, {
+ file: TEST_FILE,
+ line: 4,
+ },
+ // Ignore the rest of the stack,
+ // just assert Cu.reportError call site
+ // and consoleOpened call
+ ]
+ },
+ {
+ name: "content window console.log() is displayed",
+ text: "bug587757b",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "jsterm eval result",
+ text: "browser.xul",
+ category: CATEGORY_OUTPUT,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "exception message",
+ text: "foobarExceptionBug587757",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ name: "network message",
+ text: "test-console.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_INFO,
+ isXhr: true,
+ },
+ {
+ name: "xhr error message",
+ text: "404.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_ERROR,
+ isXhr: true,
+ },
+ {
+ name: "network message",
+ text: "test-image.png",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_INFO,
+ isXhr: true,
+ },
+ ],
+ });
+}
+
+function waitForConsole() {
+ let deferred = promise.defer();
+
+ Services.obs.addObserver(function observer(aSubject) {
+ Services.obs.removeObserver(observer, "web-console-created");
+ aSubject.QueryInterface(Ci.nsISupportsString);
+
+ let hud = HUDService.getBrowserConsole();
+ ok(hud, "browser console is open");
+ is(aSubject.data, hud.hudId, "notification hudId is correct");
+
+ executeSoon(() => deferred.resolve(hud));
+ }, "web-console-created", false);
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js b/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js
new file mode 100644
index 000000000..3eec65de3
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js
@@ -0,0 +1,92 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that exceptions from scripts loaded with the addon-sdk loader are
+// opened correctly in View Source from the Browser Console.
+// See bug 866950.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>hello world from bug 866950";
+
+function test() {
+ requestLongerTimeout(2);
+
+ let webconsole, browserconsole;
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ let {tab} = yield loadTab(TEST_URI);
+ webconsole = yield openConsole(tab);
+ ok(webconsole, "web console opened");
+
+ browserconsole = yield HUDService.toggleBrowserConsole();
+ ok(browserconsole, "browser console opened");
+
+ // Cause an exception in a script loaded with the addon-sdk loader.
+ let toolbox = gDevTools.getToolbox(webconsole.target);
+ let oldPanels = toolbox._toolPanels;
+ // non-iterable
+ toolbox._toolPanels = {};
+
+ function fixToolbox() {
+ toolbox._toolPanels = oldPanels;
+ }
+
+ info("generate exception and wait for message");
+
+ executeSoon(() => {
+ executeSoon(fixToolbox);
+ expectUncaughtException();
+ toolbox.getToolPanels();
+ });
+
+ let [result] = yield waitForMessages({
+ webconsole: browserconsole,
+ messages: [{
+ text: "TypeError: this._toolPanels is not iterable",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+
+ fixToolbox();
+
+ let msg = [...result.matched][0];
+ ok(msg, "message element found");
+ let locationNode = msg
+ .querySelector(".message .message-location > .frame-link");
+ ok(locationNode, "message location element found");
+
+ let url = locationNode.getAttribute("data-url");
+ info("location node url: " + url);
+ ok(url.indexOf("resource://") === 0, "error comes from a subscript");
+
+ let viewSource = browserconsole.viewSource;
+ let URL = null;
+ let clickPromise = promise.defer();
+ browserconsole.viewSourceInDebugger = (sourceURL) => {
+ info("browserconsole.viewSourceInDebugger() was invoked: " + sourceURL);
+ URL = sourceURL;
+ clickPromise.resolve(null);
+ };
+
+ msg.scrollIntoView();
+ EventUtils.synthesizeMouse(locationNode, 2, 2, {},
+ browserconsole.iframeWindow);
+
+ info("wait for click on locationNode");
+ yield clickPromise.promise;
+
+ info("view-source url: " + URL);
+ ok(URL, "we have some source URL after the click");
+ isnot(URL.indexOf("toolbox.js"), -1,
+ "we have the expected view source URL");
+ is(URL.indexOf("->"), -1, "no -> in the URL given to view-source");
+
+ browserconsole.viewSourceInDebugger = viewSource;
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_console_clear_method.js b/devtools/client/webconsole/test/browser_console_clear_method.js
new file mode 100644
index 000000000..33b43850e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_clear_method.js
@@ -0,0 +1,41 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that console.clear() does not clear the output of the browser console.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>Bug 1296870";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield HUDService.toggleBrowserConsole();
+
+ info("Log a new message from the content page");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.console.log("msg");
+ });
+ yield waitForMessage("msg", hud);
+
+ info("Send a console.clear() from the content page");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.console.clear();
+ });
+ yield waitForMessage("Console was cleared", hud);
+
+ info("Check that the messages logged after the first clear are still displayed");
+ isnot(hud.outputNode.textContent.indexOf("msg"), -1, "msg is in the output");
+});
+
+function waitForMessage(message, webconsole) {
+ return waitForMessages({
+ webconsole,
+ messages: [{
+ text: message,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_console_clear_on_reload.js b/devtools/client/webconsole/test/browser_console_clear_on_reload.js
new file mode 100644
index 000000000..223eb028d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_clear_on_reload.js
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that clear output on page reload works - bug 705921.
+// Check that clear output and page reload remove the sidebar - bug 971967.
+
+"use strict";
+
+add_task(function* () {
+ const PREF = "devtools.webconsole.persistlog";
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+ Services.prefs.setBoolPref(PREF, false);
+ registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ ok(hud, "Web Console opened");
+
+ yield openSidebar("fooObj", { name: "testProp", value: "testValue" });
+
+ let sidebarClosed = hud.jsterm.once("sidebar-closed");
+ hud.jsterm.clearOutput();
+ yield sidebarClosed;
+
+ hud.jsterm.execute("console.log('foobarz1')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarz1",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ yield openSidebar("fooObj", { name: "testProp", value: "testValue" });
+
+ BrowserReload();
+
+ sidebarClosed = hud.jsterm.once("sidebar-closed");
+ loadBrowser(gBrowser.selectedBrowser);
+ yield sidebarClosed;
+
+ hud.jsterm.execute("console.log('foobarz2')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test-console.html",
+ category: CATEGORY_NETWORK,
+ },
+ {
+ text: "foobarz2",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ is(hud.outputNode.textContent.indexOf("foobarz1"), -1,
+ "foobarz1 has been removed from output");
+
+ function* openSidebar(objName, expectedObj) {
+ let msg = yield hud.jsterm.execute(objName);
+ ok(msg, "output message found");
+
+ let anchor = msg.querySelector("a");
+ let body = msg.querySelector(".message-body");
+ ok(anchor, "object anchor");
+ ok(body, "message body");
+
+ yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+
+ let vviewVar = yield hud.jsterm.once("variablesview-fetched");
+ let vview = vviewVar._variablesView;
+ ok(vview, "variables view object exists");
+
+ yield findVariableViewProperties(vviewVar, [
+ expectedObj,
+ ], { webconsole: hud });
+ }
+});
diff --git a/devtools/client/webconsole/test/browser_console_click_focus.js b/devtools/client/webconsole/test/browser_console_click_focus.js
new file mode 100644
index 000000000..f405f0bbf
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_click_focus.js
@@ -0,0 +1,59 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the input field is focused when the console is opened.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Dolske Digs Bacon",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ let outputItem = msg.querySelector(".message-body");
+ ok(outputItem, "found a logged message");
+
+ let inputNode = hud.jsterm.inputNode;
+ ok(inputNode.getAttribute("focused"), "input node is focused, first");
+
+ yield waitForBlurredInput(inputNode);
+
+ EventUtils.sendMouseEvent({type: "click"}, hud.outputNode);
+ ok(inputNode.getAttribute("focused"), "input node is focused, second time");
+
+ yield waitForBlurredInput(inputNode);
+
+ info("Setting a text selection and making sure a click does not re-focus");
+ let selection = hud.iframeWindow.getSelection();
+ selection.selectAllChildren(outputItem);
+
+ EventUtils.sendMouseEvent({type: "click"}, hud.outputNode);
+ ok(!inputNode.getAttribute("focused"),
+ "input node is not focused after drag");
+});
+
+function waitForBlurredInput(inputNode) {
+ return new Promise(resolve => {
+ let lostFocus = () => {
+ inputNode.removeEventListener("blur", lostFocus);
+ ok(!inputNode.getAttribute("focused"), "input node is not focused");
+ resolve();
+ };
+ inputNode.addEventListener("blur", lostFocus);
+ document.getElementById("urlbar").click();
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_console_consolejsm_output.js b/devtools/client/webconsole/test/browser_console_consolejsm_output.js
new file mode 100644
index 000000000..e5b37843e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_consolejsm_output.js
@@ -0,0 +1,285 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that Console.jsm outputs messages to the Browser Console, bug 851231.
+
+"use strict";
+
+function onNewMessage(aEvent, aNewMessages) {
+ for (let msg of aNewMessages) {
+ // Messages that shouldn't be output contain the substring FAIL_TEST
+ if (msg.node.textContent.includes("FAIL_TEST")) {
+ ok(false, "Message shouldn't have been output: " + msg.node.textContent);
+ }
+ }
+}
+
+add_task(function* () {
+ let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
+ let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
+ storage.clearEvents();
+
+ let {console} = Cu.import("resource://gre/modules/Console.jsm", {});
+ console.log("bug861338-log-cached");
+
+ let hud = yield HUDService.toggleBrowserConsole();
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "cached console.log message",
+ text: "bug861338-log-cached",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ hud.jsterm.clearOutput(true);
+
+ function testTrace() {
+ console.trace();
+ }
+
+ console.time("foobarTimer");
+ let foobar = { bug851231prop: "bug851231value" };
+
+ console.log("bug851231-log");
+ console.info("bug851231-info");
+ console.warn("bug851231-warn");
+ console.error("bug851231-error", foobar);
+ console.debug("bug851231-debug");
+ console.dir(document);
+ testTrace();
+ console.timeEnd("foobarTimer");
+
+ info("wait for the Console.jsm messages");
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "console.log output",
+ text: "bug851231-log",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "console.info output",
+ text: "bug851231-info",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_INFO,
+ },
+ {
+ name: "console.warn output",
+ text: "bug851231-warn",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_WARNING,
+ },
+ {
+ name: "console.error output",
+ text: /\bbug851231-error\b.+\{\s*bug851231prop:\s"bug851231value"\s*\}/,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ objects: true,
+ },
+ {
+ name: "console.debug output",
+ text: "bug851231-debug",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "console.trace output",
+ consoleTrace: {
+ file: "browser_console_consolejsm_output.js",
+ fn: "testTrace",
+ },
+ },
+ {
+ name: "console.dir output",
+ consoleDir: /XULDocument\s+.+\s+chrome:\/\/.+\/browser\.xul/,
+ },
+ {
+ name: "console.time output",
+ consoleTime: "foobarTimer",
+ },
+ {
+ name: "console.timeEnd output",
+ consoleTimeEnd: "foobarTimer",
+ },
+ ],
+ });
+
+ let consoleErrorMsg = results[3];
+ ok(consoleErrorMsg, "console.error message element found");
+ let clickable = consoleErrorMsg.clickableElements[0];
+ ok(clickable, "clickable object found for console.error");
+
+ let deferred = promise.defer();
+
+ let onFetch = (aEvent, aVar) => {
+ // Skip the notification from console.dir variablesview-fetched.
+ if (aVar._variablesView != hud.jsterm._variablesView) {
+ return;
+ }
+ hud.jsterm.off("variablesview-fetched", onFetch);
+
+ deferred.resolve(aVar);
+ };
+
+ hud.jsterm.on("variablesview-fetched", onFetch);
+
+ clickable.scrollIntoView(false);
+
+ info("wait for variablesview-fetched");
+ executeSoon(() =>
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow));
+
+ let varView = yield deferred.promise;
+ ok(varView, "object inspector opened on click");
+
+ yield findVariableViewProperties(varView, [{
+ name: "bug851231prop",
+ value: "bug851231value",
+ }], { webconsole: hud });
+
+ yield HUDService.toggleBrowserConsole();
+});
+
+add_task(function* testPrefix() {
+ let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
+ let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
+ storage.clearEvents();
+
+ let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
+ let consoleOptions = {
+ maxLogLevel: "error",
+ prefix: "Log Prefix",
+ };
+ let console2 = new ConsoleAPI(consoleOptions);
+ console2.error("Testing a prefix");
+ console2.log("FAIL_TEST: Below the maxLogLevel");
+
+ let hud = yield HUDService.toggleBrowserConsole();
+ hud.ui.on("new-messages", onNewMessage);
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "cached console.error message",
+ prefix: "Log Prefix:",
+ severity: SEVERITY_ERROR,
+ text: "Testing a prefix",
+ }],
+ });
+
+ hud.jsterm.clearOutput(true);
+ hud.ui.off("new-messages", onNewMessage);
+ yield HUDService.toggleBrowserConsole();
+});
+
+add_task(function* testMaxLogLevelPrefMissing() {
+ let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
+ let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
+ storage.clearEvents();
+
+ let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
+ let consoleOptions = {
+ maxLogLevel: "error",
+ maxLogLevelPref: "testing.maxLogLevel",
+ };
+ let console = new ConsoleAPI(consoleOptions);
+
+ is(Services.prefs.getPrefType(consoleOptions.maxLogLevelPref),
+ Services.prefs.PREF_INVALID,
+ "Check log level pref is missing");
+
+ // Since the maxLogLevelPref doesn't exist, we should fallback to the passed
+ // maxLogLevel of "error".
+ console.warn("FAIL_TEST: Below the maxLogLevel");
+ console.error("Error should be shown");
+
+ let hud = yield HUDService.toggleBrowserConsole();
+
+ hud.ui.on("new-messages", onNewMessage);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "defaulting to error level",
+ severity: SEVERITY_ERROR,
+ text: "Error should be shown",
+ }],
+ });
+
+ hud.jsterm.clearOutput(true);
+ hud.ui.off("new-messages", onNewMessage);
+ yield HUDService.toggleBrowserConsole();
+});
+
+add_task(function* testMaxLogLevelPref() {
+ let consoleStorage = Cc["@mozilla.org/consoleAPI-storage;1"];
+ let storage = consoleStorage.getService(Ci.nsIConsoleAPIStorage);
+ storage.clearEvents();
+
+ let {ConsoleAPI} = Cu.import("resource://gre/modules/Console.jsm", {});
+ let consoleOptions = {
+ maxLogLevel: "error",
+ maxLogLevelPref: "testing.maxLogLevel",
+ };
+
+ info("Setting the pref to warn");
+ Services.prefs.setCharPref(consoleOptions.maxLogLevelPref, "Warn");
+
+ let console = new ConsoleAPI(consoleOptions);
+
+ is(console.maxLogLevel, "warn", "Check pref was read at initialization");
+
+ console.info("FAIL_TEST: info is below the maxLogLevel");
+ console.error("Error should be shown");
+ console.warn("Warn should be shown due to the initial pref value");
+
+ info("Setting the pref to info");
+ Services.prefs.setCharPref(consoleOptions.maxLogLevelPref, "INFO");
+ is(console.maxLogLevel, "info", "Check pref was lowercased");
+
+ console.info("info should be shown due to the pref change being observed");
+
+ info("Clearing the pref");
+ Services.prefs.clearUserPref(consoleOptions.maxLogLevelPref);
+
+ console.warn("FAIL_TEST: Shouldn't be shown due to defaulting to error");
+ console.error("Should be shown due to defaulting to error");
+
+ let hud = yield HUDService.toggleBrowserConsole();
+ hud.ui.on("new-messages", onNewMessage);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "error > warn",
+ severity: SEVERITY_ERROR,
+ text: "Error should be shown",
+ },
+ {
+ name: "warn is the inital pref value",
+ severity: SEVERITY_WARNING,
+ text: "Warn should be shown due to the initial pref value",
+ },
+ {
+ name: "pref changed to info",
+ severity: SEVERITY_INFO,
+ text: "info should be shown due to the pref change being observed",
+ },
+ {
+ name: "default to intial maxLogLevel if pref is removed",
+ severity: SEVERITY_ERROR,
+ text: "Should be shown due to defaulting to error",
+ }],
+ });
+
+ hud.jsterm.clearOutput(true);
+ hud.ui.off("new-messages", onNewMessage);
+ yield HUDService.toggleBrowserConsole();
+});
diff --git a/devtools/client/webconsole/test/browser_console_copy_command.js b/devtools/client/webconsole/test/browser_console_copy_command.js
new file mode 100644
index 000000000..c4ed4360f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_copy_command.js
@@ -0,0 +1,76 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the `copy` console helper works as intended.
+
+"use strict";
+
+var gWebConsole, gJSTerm;
+
+var TEXT = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
+ "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
+ "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
+ "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
+ "dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
+ "proident, sunt in culpa qui officia deserunt mollit anim id est laborum." +
+ new Date();
+
+var ID = "select-me";
+
+add_task(function* init() {
+ yield loadTab("data:text/html;charset=utf-8," +
+ "<body>" +
+ " <div>" +
+ " <h1>Testing copy command</h1>" +
+ " <p>This is some example text</p>" +
+ " <p id='select-me'>" + TEXT + "</p>" +
+ " </div>" +
+ " <div><p></p></div>" +
+ "</body>");
+
+ gWebConsole = yield openConsole();
+ gJSTerm = gWebConsole.jsterm;
+});
+
+add_task(function* testCopy() {
+ let RANDOM = Math.random();
+ let string = "Text: " + RANDOM;
+ let obj = {a: 1, b: "foo", c: RANDOM};
+
+ let samples = [
+ [RANDOM, RANDOM],
+ [JSON.stringify(string), string],
+ [obj.toSource(), JSON.stringify(obj, null, " ")],
+ [
+ "$('#" + ID + "')",
+ content.document.getElementById(ID).outerHTML
+ ]
+ ];
+ for (let [source, reference] of samples) {
+ let deferredResult = promise.defer();
+
+ SimpleTest.waitForClipboard(
+ "" + reference,
+ () => {
+ let command = "copy(" + source + ")";
+ info("Attempting to copy: " + source);
+ info("Executing command: " + command);
+ gJSTerm.execute(command, msg => {
+ is(msg, undefined, "Command success: " + command);
+ });
+ },
+ deferredResult.resolve,
+ deferredResult.reject);
+
+ yield deferredResult.promise;
+ }
+});
+
+add_task(function* cleanup() {
+ gWebConsole = gJSTerm = null;
+ gBrowser.removeTab(gBrowser.selectedTab);
+ finishTest();
+});
diff --git a/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js
new file mode 100644
index 000000000..bdd4f7179
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_copy_entire_message_context_menu.js
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* globals goDoCommand */
+
+"use strict";
+
+// Test copying of the entire console message when right-clicked
+// with no other text selected. See Bug 1100562.
+
+add_task(function* () {
+ let hud;
+ let outputNode;
+ let contextMenu;
+
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console.html";
+
+ const { tab, browser } = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+ outputNode = hud.outputNode;
+ contextMenu = hud.iframeWindow.document.getElementById("output-contextmenu");
+
+ registerCleanupFunction(() => {
+ hud = outputNode = contextMenu = null;
+ });
+
+ hud.jsterm.clearOutput();
+
+ yield ContentTask.spawn(browser, {}, function* () {
+ let button = content.document.getElementById("testTrace");
+ button.click();
+ });
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "bug 1100562",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ lines: 1,
+ },
+ {
+ name: "console.trace output",
+ consoleTrace: true,
+ lines: 3,
+ },
+ ]
+ });
+
+ outputNode.focus();
+
+ for (let result of results) {
+ let message = [...result.matched][0];
+
+ yield waitForContextMenu(contextMenu, message, () => {
+ let copyItem = contextMenu.querySelector("#cMenu_copy");
+ copyItem.doCommand();
+
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+ });
+
+ let clipboardText;
+
+ yield waitForClipboardPromise(
+ () => goDoCommand("cmd_copy"),
+ (str) => {
+ clipboardText = str;
+ return message.textContent == clipboardText;
+ }
+ );
+
+ ok(clipboardText, "Clipboard text was found and saved");
+
+ let lines = clipboardText.split("\n");
+ ok(lines.length > 0, "There is at least one newline in the message");
+ is(lines.pop(), "", "There is a newline at the end");
+ is(lines.length, result.lines, `There are ${result.lines} lines in the message`);
+
+ // Test the first line for "timestamp message repeat file:line"
+ let firstLine = lines.shift();
+ ok(/^[\d:.]+ .+ \d+ .+:\d+$/.test(firstLine),
+ "The message's first line has the right format");
+
+ // Test the remaining lines (stack trace) for "TABfunctionName sourceURL:line:col"
+ for (let line of lines) {
+ ok(/^\t.+ .+:\d+:\d+$/.test(line), "The stack trace line has the right format");
+ }
+ }
+
+ yield closeConsole(tab);
+ yield finishTest();
+});
diff --git a/devtools/client/webconsole/test/browser_console_dead_objects.js b/devtools/client/webconsole/test/browser_console_dead_objects.js
new file mode 100644
index 000000000..46b15d59b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_dead_objects.js
@@ -0,0 +1,88 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that Dead Objects do not break the Web/Browser Consoles.
+// See bug 883649.
+// This test does:
+// - opens a new tab,
+// - opens the Browser Console,
+// - stores a reference to the content document of the tab on the chrome
+// window object,
+// - closes the tab,
+// - tries to use the object that was pointing to the now-defunct content
+// document. This is the dead object.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>dead objects!";
+
+function test() {
+ let hud = null;
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.chrome.enabled");
+ });
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ Services.prefs.setBoolPref("devtools.chrome.enabled", true);
+ yield loadTab(TEST_URI);
+
+ info("open the browser console");
+
+ hud = yield HUDService.toggleBrowserConsole();
+ ok(hud, "browser console opened");
+
+ let jsterm = hud.jsterm;
+
+ jsterm.clearOutput();
+
+ // Add the reference to the content document.
+ yield jsterm.execute("Cu = Components.utils;" +
+ "Cu.import('resource://gre/modules/Services.jsm');" +
+ "chromeWindow = Services.wm.getMostRecentWindow('" +
+ "navigator:browser');" +
+ "foobarzTezt = chromeWindow.content.document;" +
+ "delete chromeWindow");
+
+ gBrowser.removeCurrentTab();
+
+ let msg = yield jsterm.execute("foobarzTezt");
+
+ isnot(hud.outputNode.textContent.indexOf("[object DeadObject]"), -1,
+ "dead object found");
+
+ jsterm.setInputValue("foobarzTezt");
+
+ for (let c of ".hello") {
+ EventUtils.synthesizeKey(c, {}, hud.iframeWindow);
+ }
+
+ yield jsterm.execute();
+
+ isnot(hud.outputNode.textContent.indexOf("can't access dead object"), -1,
+ "'cannot access dead object' message found");
+
+ // Click the second execute output.
+ let clickable = msg.querySelector("a");
+ ok(clickable, "clickable object found");
+ isnot(clickable.textContent.indexOf("[object DeadObject]"), -1,
+ "message text check");
+
+ msg.scrollIntoView();
+
+ executeSoon(() => {
+ EventUtils.synthesizeMouseAtCenter(clickable, {}, hud.iframeWindow);
+ });
+
+ yield jsterm.once("variablesview-fetched");
+ ok(true, "variables view fetched");
+
+ msg = yield jsterm.execute("delete window.foobarzTezt; 2013-26");
+
+ isnot(msg.textContent.indexOf("1987"), -1, "result message found");
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_console_error_source_click.js b/devtools/client/webconsole/test/browser_console_error_source_click.js
new file mode 100644
index 000000000..5839f20d5
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_error_source_click.js
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that JS errors and CSS warnings open view source when their source link
+// is clicked in the Browser Console. See bug 877778.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>hello world from bug 877778 " +
+ "<button onclick='foobar.explode()' " +
+ "style='test-color: green-please'>click!</button>";
+
+add_task(function* () {
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["devtools.browserconsole.filter.cssparser", true]
+ ]}, resolve);
+ });
+
+ yield loadTab(TEST_URI);
+ let hud = yield HUDService.toggleBrowserConsole();
+ ok(hud, "browser console opened");
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ info("generate exception and wait for the message");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let button = content.document.querySelector("button");
+ button.click();
+ });
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "ReferenceError: foobar is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "Unknown property \u2018test-color\u2019",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ },
+ ],
+ });
+
+ let viewSourceCalled = false;
+
+ let viewSource = hud.viewSource;
+ hud.viewSource = () => {
+ viewSourceCalled = true;
+ };
+
+ for (let result of results) {
+ viewSourceCalled = false;
+
+ let msg = [...result.matched][0];
+ ok(msg, "message element found for: " + result.text);
+ ok(!msg.classList.contains("filtered-by-type"), "message element is not filtered");
+ let selector = ".message .message-location .frame-link-source";
+ let locationNode = msg.querySelector(selector);
+ ok(locationNode, "message location element found");
+
+ locationNode.click();
+
+ ok(viewSourceCalled, "view source opened");
+ }
+
+ hud.viewSource = viewSource;
+
+ yield finishTest();
+});
diff --git a/devtools/client/webconsole/test/browser_console_filters.js b/devtools/client/webconsole/test/browser_console_filters.js
new file mode 100644
index 000000000..072766fdb
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_filters.js
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the Browser Console does not use the same filter prefs as the Web
+// Console. See bug 878186.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>browser console filters";
+const WEB_CONSOLE_PREFIX = "devtools.webconsole.filter.";
+const BROWSER_CONSOLE_PREFIX = "devtools.browserconsole.filter.";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ info("open the web console");
+ let hud = yield openConsole();
+ ok(hud, "web console opened");
+
+ is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), true,
+ "'exception' filter is enabled (browser console)");
+ is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), true,
+ "'exception' filter is enabled (web console)");
+
+ info("toggle 'exception' filter");
+ hud.setFilterState("exception", false);
+
+ is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), true,
+ "'exception' filter is enabled (browser console)");
+ is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), false,
+ "'exception' filter is disabled (web console)");
+
+ hud.setFilterState("exception", true);
+
+ // We need to let the console opening event loop to finish.
+ let deferred = promise.defer();
+ executeSoon(() => closeConsole().then(() => deferred.resolve(null)));
+ yield deferred.promise;
+
+ info("web console closed");
+ hud = yield HUDService.toggleBrowserConsole();
+ ok(hud, "browser console opened");
+
+ is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), true,
+ "'exception' filter is enabled (browser console)");
+ is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), true,
+ "'exception' filter is enabled (web console)");
+
+ info("toggle 'exception' filter");
+ hud.setFilterState("exception", false);
+
+ is(Services.prefs.getBoolPref(BROWSER_CONSOLE_PREFIX + "exception"), false,
+ "'exception' filter is disabled (browser console)");
+ is(Services.prefs.getBoolPref(WEB_CONSOLE_PREFIX + "exception"), true,
+ "'exception' filter is enabled (web console)");
+
+ hud.setFilterState("exception", true);
+});
diff --git a/devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js b/devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js
new file mode 100644
index 000000000..d3fdb08be
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js
@@ -0,0 +1,114 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Bug 922161 - Hide Browser Console JS input field if devtools.chrome.enabled
+ * is false.
+ * when devtools.chrome.enabled then
+ * -browser console jsterm should be enabled
+ * -browser console object inspector properties should be set.
+ * -webconsole jsterm should be enabled
+ * -webconsole object inspector properties should be set.
+ *
+ * when devtools.chrome.enabled == false then
+ * -browser console jsterm should be disabled
+ * -browser console object inspector properties should not be set.
+ * -webconsole jsterm should be enabled
+ * -webconsole object inspector properties should be set.
+ */
+
+"use strict";
+
+function testObjectInspectorPropertiesAreNotSet(variablesView) {
+ is(variablesView.eval, null, "vview.eval is null");
+ is(variablesView.switch, null, "vview.switch is null");
+ is(variablesView.delete, null, "vview.delete is null");
+}
+
+function* getVariablesView(hud) {
+ function openVariablesView(event, vview) {
+ deferred.resolve(vview._variablesView);
+ }
+
+ let deferred = promise.defer();
+
+ // Filter out other messages to ensure ours stays visible.
+ hud.ui.filterBox.value = "browser_console_hide_jsterm_test";
+
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("new Object({ browser_console_hide_jsterm_test: true })");
+
+ let [message] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Object { browser_console_hide_jsterm_test: true }",
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ hud.jsterm.once("variablesview-fetched", openVariablesView);
+
+ let anchor = [...message.matched][0].querySelector("a");
+
+ executeSoon(() =>
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow)
+ );
+
+ return deferred.promise;
+}
+
+function testJSTermIsVisible(hud) {
+ let inputContainer = hud.ui.window.document
+ .querySelector(".jsterm-input-container");
+ isnot(inputContainer.style.display, "none", "input is visible");
+}
+
+function testObjectInspectorPropertiesAreSet(variablesView) {
+ isnot(variablesView.eval, null, "vview.eval is set");
+ isnot(variablesView.switch, null, "vview.switch is set");
+ isnot(variablesView.delete, null, "vview.delete is set");
+}
+
+function testJSTermIsNotVisible(hud) {
+ let inputContainer = hud.ui.window.document
+ .querySelector(".jsterm-input-container");
+ is(inputContainer.style.display, "none", "input is not visible");
+}
+
+function* testRunner() {
+ let browserConsole, webConsole, variablesView;
+
+ Services.prefs.setBoolPref("devtools.chrome.enabled", true);
+
+ browserConsole = yield HUDService.toggleBrowserConsole();
+ variablesView = yield getVariablesView(browserConsole);
+ testJSTermIsVisible(browserConsole);
+ testObjectInspectorPropertiesAreSet(variablesView);
+
+ let {tab: browserTab} = yield loadTab("data:text/html;charset=utf8,hello world");
+ webConsole = yield openConsole(browserTab);
+ variablesView = yield getVariablesView(webConsole);
+ testJSTermIsVisible(webConsole);
+ testObjectInspectorPropertiesAreSet(variablesView);
+ yield closeConsole(browserTab);
+
+ yield HUDService.toggleBrowserConsole();
+ Services.prefs.setBoolPref("devtools.chrome.enabled", false);
+
+ browserConsole = yield HUDService.toggleBrowserConsole();
+ variablesView = yield getVariablesView(browserConsole);
+ testJSTermIsNotVisible(browserConsole);
+ testObjectInspectorPropertiesAreNotSet(variablesView);
+
+ webConsole = yield openConsole(browserTab);
+ variablesView = yield getVariablesView(webConsole);
+ testJSTermIsVisible(webConsole);
+ testObjectInspectorPropertiesAreSet(variablesView);
+ yield closeConsole(browserTab);
+}
+
+function test() {
+ Task.spawn(testRunner).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_console_history_persist.js b/devtools/client/webconsole/test/browser_console_history_persist.js
new file mode 100644
index 000000000..61c4cbf4d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_history_persist.js
@@ -0,0 +1,119 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that console command input is persisted across toolbox loads.
+// See Bug 943306.
+
+"use strict";
+
+requestLongerTimeout(2);
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "persisting history - bug 943306";
+const INPUT_HISTORY_COUNT = 10;
+
+add_task(function* () {
+ info("Setting custom input history pref to " + INPUT_HISTORY_COUNT);
+ Services.prefs.setIntPref("devtools.webconsole.inputHistoryCount",
+ INPUT_HISTORY_COUNT);
+
+ // First tab: run a bunch of commands and then make sure that you can
+ // navigate through their history.
+ yield loadTab(TEST_URI);
+ let hud1 = yield openConsole();
+ is(JSON.stringify(hud1.jsterm.history), "[]",
+ "No history on first tab initially");
+ yield populateInputHistory(hud1);
+ is(JSON.stringify(hud1.jsterm.history),
+ '["0","1","2","3","4","5","6","7","8","9"]',
+ "First tab has populated history");
+
+ // Second tab: Just make sure that you can navigate through the history
+ // generated by the first tab.
+ yield loadTab(TEST_URI);
+ let hud2 = yield openConsole();
+ is(JSON.stringify(hud2.jsterm.history),
+ '["0","1","2","3","4","5","6","7","8","9"]',
+ "Second tab has populated history");
+ yield testNaviatingHistoryInUI(hud2);
+ is(JSON.stringify(hud2.jsterm.history),
+ '["0","1","2","3","4","5","6","7","8","9",""]',
+ "An empty entry has been added in the second tab due to history perusal");
+
+ // Third tab: Should have the same history as first tab, but if we run a
+ // command, then the history of the first and second shouldn't be affected
+ yield loadTab(TEST_URI);
+ let hud3 = yield openConsole();
+ is(JSON.stringify(hud3.jsterm.history),
+ '["0","1","2","3","4","5","6","7","8","9"]',
+ "Third tab has populated history");
+
+ // Set input value separately from execute so UP arrow accurately navigates
+ // history.
+ hud3.jsterm.setInputValue('"hello from third tab"');
+ hud3.jsterm.execute();
+
+ is(JSON.stringify(hud1.jsterm.history),
+ '["0","1","2","3","4","5","6","7","8","9"]',
+ "First tab history hasn't changed due to command in third tab");
+ is(JSON.stringify(hud2.jsterm.history),
+ '["0","1","2","3","4","5","6","7","8","9",""]',
+ "Second tab history hasn't changed due to command in third tab");
+ is(JSON.stringify(hud3.jsterm.history),
+ '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
+ "Third tab has updated history (and purged the first result) after " +
+ "running a command");
+
+ // Fourth tab: Should have the latest command from the third tab, followed
+ // by the rest of the history from the first tab.
+ yield loadTab(TEST_URI);
+ let hud4 = yield openConsole();
+ is(JSON.stringify(hud4.jsterm.history),
+ '["1","2","3","4","5","6","7","8","9","\\"hello from third tab\\""]',
+ "Fourth tab has most recent history");
+
+ yield hud4.jsterm.clearHistory();
+ is(JSON.stringify(hud4.jsterm.history), "[]",
+ "Clearing history for a tab works");
+
+ yield loadTab(TEST_URI);
+ let hud5 = yield openConsole();
+ is(JSON.stringify(hud5.jsterm.history), "[]",
+ "Clearing history carries over to a new tab");
+
+ info("Clearing custom input history pref");
+ Services.prefs.clearUserPref("devtools.webconsole.inputHistoryCount");
+});
+
+/**
+ * Populate the history by running the following commands:
+ * [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ */
+function* populateInputHistory(hud) {
+ let jsterm = hud.jsterm;
+
+ for (let i = 0; i < INPUT_HISTORY_COUNT; i++) {
+ // Set input value separately from execute so UP arrow accurately navigates
+ // history.
+ jsterm.setInputValue(i);
+ jsterm.execute();
+ }
+}
+
+/**
+ * Check pressing up results in history traversal like:
+ * [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
+ */
+function* testNaviatingHistoryInUI(hud) {
+ let jsterm = hud.jsterm;
+ jsterm.focus();
+
+ // Count backwards from original input and make sure that pressing up
+ // restores this.
+ for (let i = INPUT_HISTORY_COUNT - 1; i >= 0; i--) {
+ EventUtils.synthesizeKey("VK_UP", {});
+ is(jsterm.getInputValue(), i, "Pressing up restores last input");
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_console_iframe_messages.js b/devtools/client/webconsole/test/browser_console_iframe_messages.js
new file mode 100644
index 000000000..9bf3fe2b7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_iframe_messages.js
@@ -0,0 +1,114 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that cached messages from nested iframes are displayed in the
+// Web/Browser Console.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-consoleiframes.html";
+
+const expectedMessages = [
+ {
+ text: "main file",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "blah",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR
+ },
+ {
+ text: "iframe 2",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG
+ },
+ {
+ text: "iframe 3",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG
+ }
+];
+
+// "iframe 1" console messages can be coalesced into one if they follow each
+// other in the sequence of messages (depending on timing). If they do not, then
+// they will be displayed in the console output independently, as separate
+// messages. This is why we need to match any of the following two rules.
+const expectedMessagesAny = [
+ {
+ name: "iframe 1 (count: 2)",
+ text: "iframe 1",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ count: 2
+ },
+ {
+ name: "iframe 1 (repeats: 2)",
+ text: "iframe 1",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 2
+ },
+];
+
+add_task(function* () {
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ ok(hud, "web console opened");
+
+ yield testWebConsole(hud);
+ yield closeConsole();
+ info("web console closed");
+
+ hud = yield HUDService.toggleBrowserConsole();
+ yield testBrowserConsole(hud);
+ yield closeConsole();
+});
+
+function* testWebConsole(hud) {
+ yield waitForMessages({
+ webconsole: hud,
+ messages: expectedMessages,
+ });
+
+ info("first messages matched");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: expectedMessagesAny,
+ matchCondition: "any",
+ });
+}
+
+function* testBrowserConsole(hud) {
+ ok(hud, "browser console opened");
+
+ // TODO: The browser console doesn't show page's console.log statements
+ // in e10s windows. See Bug 1241289.
+ if (Services.appinfo.browserTabsRemoteAutostart) {
+ todo(false, "Bug 1241289");
+ return;
+ }
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: expectedMessages,
+ });
+
+ info("first messages matched");
+ yield waitForMessages({
+ webconsole: hud,
+ messages: expectedMessagesAny,
+ matchCondition: "any",
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js
new file mode 100644
index 000000000..c64e45f5d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js
@@ -0,0 +1,89 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that basic keyboard shortcuts work in the web console.
+
+"use strict";
+
+add_task(function* () {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ ok(hud, "Web Console opened");
+
+ info("dump some spew into the console for scrolling");
+ hud.jsterm.execute("(function() { for (var i = 0; i < 100; i++) { " +
+ "console.log('foobarz' + i);" +
+ "}})();");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarz99",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let currentPosition = hud.ui.outputWrapper.scrollTop;
+ let bottom = currentPosition;
+
+ EventUtils.synthesizeKey("VK_PAGE_UP", {});
+ isnot(hud.ui.outputWrapper.scrollTop, currentPosition,
+ "scroll position changed after page up");
+
+ currentPosition = hud.ui.outputWrapper.scrollTop;
+ EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
+ ok(hud.ui.outputWrapper.scrollTop > currentPosition,
+ "scroll position now at bottom");
+
+ EventUtils.synthesizeKey("VK_HOME", {});
+ is(hud.ui.outputWrapper.scrollTop, 0, "scroll position now at top");
+
+ EventUtils.synthesizeKey("VK_END", {});
+
+ let scrollTop = hud.ui.outputWrapper.scrollTop;
+ ok(scrollTop > 0 && Math.abs(scrollTop - bottom) <= 5,
+ "scroll position now at bottom");
+
+ info("try ctrl-l to clear output");
+ executeSoon(() => {
+ let clearShortcut;
+ if (Services.appinfo.OS === "Darwin") {
+ clearShortcut = WCUL10n.getStr("webconsole.clear.keyOSX");
+ } else {
+ clearShortcut = WCUL10n.getStr("webconsole.clear.key");
+ }
+ synthesizeKeyShortcut(clearShortcut);
+ });
+ yield hud.jsterm.once("messages-cleared");
+
+ is(hud.outputNode.textContent.indexOf("foobarz1"), -1, "output cleared");
+ is(hud.jsterm.inputNode.getAttribute("focused"), "true",
+ "jsterm input is focused");
+
+ info("try ctrl-f to focus filter");
+ synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key"));
+ ok(!hud.jsterm.inputNode.getAttribute("focused"),
+ "jsterm input is not focused");
+ is(hud.ui.filterBox.getAttribute("focused"), "true",
+ "filter input is focused");
+
+ if (Services.appinfo.OS == "Darwin") {
+ ok(hud.ui.getFilterState("network"), "network category is enabled");
+ EventUtils.synthesizeKey("t", { ctrlKey: true });
+ ok(!hud.ui.getFilterState("network"), "accesskey for Network works");
+ EventUtils.synthesizeKey("t", { ctrlKey: true });
+ ok(hud.ui.getFilterState("network"), "accesskey for Network works (again)");
+ } else {
+ EventUtils.synthesizeKey("N", { altKey: true });
+ let net = hud.ui.document.querySelector("toolbarbutton[category=net]");
+ is(hud.ui.document.activeElement, net,
+ "accesskey for Network category focuses the Net button");
+ }
+});
diff --git a/devtools/client/webconsole/test/browser_console_log_inspectable_object.js b/devtools/client/webconsole/test/browser_console_log_inspectable_object.js
new file mode 100644
index 000000000..f9fd85295
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_log_inspectable_object.js
@@ -0,0 +1,52 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that objects given to console.log() are inspectable.
+
+"use strict";
+
+add_task(function* () {
+ yield loadTab("data:text/html;charset=utf8,test for bug 676722 - " +
+ "inspectable objects for window.console");
+
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput(true);
+
+ yield hud.jsterm.execute("myObj = {abba: 'omgBug676722'}");
+ hud.jsterm.execute("console.log('fooBug676722', myObj)");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "fooBug676722",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ objects: true,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ ok(msg, "message element");
+
+ let body = msg.querySelector(".message-body");
+ ok(body, "message body");
+
+ let clickable = result.clickableElements[0];
+ ok(clickable, "the console.log() object anchor was found");
+ ok(body.textContent.includes('{ abba: "omgBug676722" }'),
+ "clickable node content is correct");
+
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+ });
+
+ let varView = yield hud.jsterm.once("variablesview-fetched");
+ ok(varView, "object inspector opened on click");
+
+ yield findVariableViewProperties(varView, [{
+ name: "abba",
+ value: "omgBug676722",
+ }], { webconsole: hud });
+});
diff --git a/devtools/client/webconsole/test/browser_console_native_getters.js b/devtools/client/webconsole/test/browser_console_native_getters.js
new file mode 100644
index 000000000..1afb70796
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_native_getters.js
@@ -0,0 +1,101 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that native getters and setters for DOM elements work as expected in
+// variables view - bug 870220.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<title>bug870220</title>\n" +
+ "<p>hello world\n<p>native getters!";
+
+requestLongerTimeout(2);
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ let jsterm = hud.jsterm;
+
+ jsterm.execute("document");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "HTMLDocument \u2192 data:text/html;charset=utf8",
+ category: CATEGORY_OUTPUT,
+ objects: true,
+ }],
+ });
+
+ let clickable = result.clickableElements[0];
+ ok(clickable, "clickable object found");
+
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+ });
+
+ let fetchedVar = yield jsterm.once("variablesview-fetched");
+
+ let variablesView = fetchedVar._variablesView;
+ ok(variablesView, "variables view object");
+
+ let results = yield findVariableViewProperties(fetchedVar, [
+ { name: "title", value: "bug870220" },
+ { name: "bgColor" },
+ ], { webconsole: hud });
+
+ let prop = results[1].matchedProp;
+ ok(prop, "matched the |bgColor| property in the variables view");
+
+ // Check that property value updates work.
+ let updatedVar = yield updateVariablesViewProperty({
+ property: prop,
+ field: "value",
+ string: "'red'",
+ webconsole: hud,
+ });
+
+ info("on fetch after background update");
+
+ jsterm.clearOutput(true);
+ jsterm.execute("document.bgColor");
+
+ [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "red",
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ yield findVariableViewProperties(updatedVar, [
+ { name: "bgColor", value: "red" },
+ ], { webconsole: hud });
+
+ jsterm.execute("$$('p')");
+
+ [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Array [",
+ category: CATEGORY_OUTPUT,
+ objects: true,
+ }],
+ });
+
+ clickable = result.clickableElements[0];
+ ok(clickable, "clickable object found");
+
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+ });
+
+ fetchedVar = yield jsterm.once("variablesview-fetched");
+
+ yield findVariableViewProperties(fetchedVar, [
+ { name: "0.textContent", value: /hello world/ },
+ { name: "1.textContent", value: /native getters/ },
+ ], { webconsole: hud });
+});
diff --git a/devtools/client/webconsole/test/browser_console_navigation_marker.js b/devtools/client/webconsole/test/browser_console_navigation_marker.js
new file mode 100644
index 000000000..e8ec84caf
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_navigation_marker.js
@@ -0,0 +1,81 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the navigation marker shows on page reload - bug 793996.
+
+"use strict";
+
+const PREF = "devtools.webconsole.persistlog";
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+var hud;
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ let { browser } = yield loadTab(TEST_URI);
+ hud = yield openConsole();
+
+ yield consoleOpened();
+
+ let loaded = loadBrowser(browser);
+ BrowserReload();
+ yield loaded;
+
+ yield onReload();
+
+ isnot(hud.outputNode.textContent.indexOf("foobarz1"), -1,
+ "foobarz1 is still in the output");
+
+ Services.prefs.clearUserPref(PREF);
+
+ hud = null;
+});
+
+function consoleOpened() {
+ ok(hud, "Web Console opened");
+
+ hud.jsterm.clearOutput();
+
+ ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+ content.console.log("foobarz1");
+ });
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarz1",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+function onReload() {
+ ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+ content.console.log("foobarz2");
+ });
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "page reload",
+ text: "test-console.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "foobarz2",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "navigation marker",
+ text: "test-console.html",
+ type: Messages.NavigationMarker,
+ }],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_console_netlogging.js b/devtools/client/webconsole/test/browser_console_netlogging.js
new file mode 100644
index 000000000..a6f7bec48
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_netlogging.js
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that network log messages bring up the network panel.
+
+"use strict";
+
+const TEST_NETWORK_REQUEST_URI =
+ "http://example.com/browser/devtools/client/webconsole/test/" +
+ "test-network-request.html";
+
+add_task(function* () {
+ let finishedRequest = waitForFinishedRequest(({ request }) => {
+ return request.url === TEST_NETWORK_REQUEST_URI;
+ });
+
+ const hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI,
+ "browserConsole");
+ let request = yield finishedRequest;
+
+ ok(request, "Page load was logged");
+
+ let client = hud.ui.webConsoleClient;
+ let args = [request.actor];
+ const postData = yield getPacket(client, "getRequestPostData", args);
+ const responseContent = yield getPacket(client, "getResponseContent", args);
+
+ is(request.request.url, TEST_NETWORK_REQUEST_URI,
+ "Logged network entry is page load");
+ is(request.request.method, "GET", "Method is correct");
+ ok(!postData.postData.text, "No request body was stored");
+ ok(postData.postDataDiscarded, "Request body was discarded");
+ ok(!responseContent.content.text, "No response body was stored");
+ ok(responseContent.contentDiscarded || request.fromCache,
+ "Response body was discarded or response came from the cache");
+});
diff --git a/devtools/client/webconsole/test/browser_console_nsiconsolemessage.js b/devtools/client/webconsole/test/browser_console_nsiconsolemessage.js
new file mode 100644
index 000000000..fedd0c71c
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_nsiconsolemessage.js
@@ -0,0 +1,85 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that nsIConsoleMessages are displayed in the Browser Console.
+// See bug 859756.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<title>bug859756</title>\n" +
+ "<p>hello world\n<p>nsIConsoleMessages ftw!";
+
+function test() {
+ const FILTER_PREF = "devtools.browserconsole.filter.jslog";
+ Services.prefs.setBoolPref(FILTER_PREF, true);
+
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(FILTER_PREF);
+ });
+
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+
+ // Test for cached nsIConsoleMessages.
+ Services.console.logStringMessage("test1 for bug859756");
+
+ info("open web console");
+ let hud = yield openConsole(tab);
+
+ ok(hud, "web console opened");
+ Services.console.logStringMessage("do-not-show-me");
+
+ ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+ content.console.log("foobarz");
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarz",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let text = hud.outputNode.textContent;
+ is(text.indexOf("do-not-show-me"), -1,
+ "nsIConsoleMessages are not displayed");
+ is(text.indexOf("test1 for bug859756"), -1,
+ "nsIConsoleMessages are not displayed (confirmed)");
+
+ yield closeConsole(tab);
+
+ info("web console closed");
+ hud = yield HUDService.toggleBrowserConsole();
+ ok(hud, "browser console opened");
+
+ Services.console.logStringMessage("test2 for bug859756");
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test1 for bug859756",
+ category: CATEGORY_JS,
+ }, {
+ text: "test2 for bug859756",
+ category: CATEGORY_JS,
+ }, {
+ text: "do-not-show-me",
+ category: CATEGORY_JS,
+ }],
+ });
+
+ let msg = [...results[2].matched][0];
+ ok(msg, "message element for do-not-show-me (nsIConsoleMessage)");
+ isnot(msg.textContent.indexOf("do-not-show"), -1,
+ "element content is correct");
+ ok(!msg.classList.contains("filtered-by-type"), "element is not filtered");
+
+ hud.setFilterState("jslog", false);
+
+ ok(msg.classList.contains("filtered-by-type"), "element is filtered");
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_console_open_or_focus.js b/devtools/client/webconsole/test/browser_console_open_or_focus.js
new file mode 100644
index 000000000..d537c9aad
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_open_or_focus.js
@@ -0,0 +1,46 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the "browser console" menu item opens or focuses (if already open)
+// the console window instead of toggling it open/close.
+
+"use strict";
+
+var {Tools} = require("devtools/client/definitions");
+
+add_task(function* () {
+ let currWindow, hud, mainWindow;
+
+ mainWindow = Services.wm.getMostRecentWindow(null);
+
+ yield HUDService.openBrowserConsoleOrFocus();
+
+ hud = HUDService.getBrowserConsole();
+
+ console.log("testmessage");
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "testmessage"
+ }],
+ });
+
+ currWindow = Services.wm.getMostRecentWindow(null);
+ is(currWindow.document.documentURI, Tools.webConsole.url,
+ "The Browser Console is open and has focus");
+
+ mainWindow.focus();
+
+ yield HUDService.openBrowserConsoleOrFocus();
+
+ currWindow = Services.wm.getMostRecentWindow(null);
+ is(currWindow.document.documentURI, Tools.webConsole.url,
+ "The Browser Console is open and has focus");
+
+ yield HUDService.toggleBrowserConsole();
+
+ hud = HUDService.getBrowserConsole();
+ ok(!hud, "Browser Console has been closed");
+});
diff --git a/devtools/client/webconsole/test/browser_console_optimized_out_vars.js b/devtools/client/webconsole/test/browser_console_optimized_out_vars.js
new file mode 100644
index 000000000..dc898eb2b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_optimized_out_vars.js
@@ -0,0 +1,91 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that inspecting an optimized out variable works when execution is
+// paused.
+
+"use strict";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+function test() {
+ Task.spawn(function* () {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-closure-optimized-out.html";
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ let { toolbox, panel, panelWin } = yield openDebugger();
+
+ let sources = panelWin.DebuggerView.Sources;
+ yield panel.addBreakpoint({ actor: sources.values[0], line: 18 });
+ yield ensureThreadClientState(panel, "resumed");
+
+ let fetchedScopes = panelWin.once(panelWin.EVENTS.FETCHED_SCOPES);
+
+ // Cause the debuggee to pause
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let button = content.document.querySelector("button");
+ button.click();
+ });
+
+ yield fetchedScopes;
+ ok(true, "Scopes were fetched");
+
+ yield toolbox.selectTool("webconsole");
+
+ // This is the meat of the test: evaluate the optimized out variable.
+ hud.jsterm.execute("upvar");
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "optimized out",
+ category: CATEGORY_OUTPUT,
+ }]
+ });
+
+ finishTest();
+ }).then(null, aError => {
+ ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+ });
+}
+
+// Debugger helper functions stolen from devtools/client/debugger/test/head.js.
+
+function ensureThreadClientState(aPanel, aState) {
+ let thread = aPanel.panelWin.gThreadClient;
+ let state = thread.state;
+
+ info("Thread is: '" + state + "'.");
+
+ if (state == aState) {
+ return promise.resolve(null);
+ }
+ return waitForThreadEvents(aPanel, aState);
+}
+
+function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) {
+ info("Waiting for thread event: '" + aEventName + "' to fire: " +
+ aEventRepeat + " time(s).");
+
+ let deferred = promise.defer();
+ let thread = aPanel.panelWin.gThreadClient;
+ let count = 0;
+
+ thread.addListener(aEventName, function onEvent(eventName, ...args) {
+ info("Thread event '" + eventName + "' fired: " + (++count) + " time(s).");
+
+ if (count == aEventRepeat) {
+ ok(true, "Enough '" + eventName + "' thread events have been fired.");
+ thread.removeListener(eventName, onEvent);
+ deferred.resolve.apply(deferred, args);
+ }
+ });
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_console_private_browsing.js b/devtools/client/webconsole/test/browser_console_private_browsing.js
new file mode 100644
index 000000000..4b3a79329
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_private_browsing.js
@@ -0,0 +1,192 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 874061: test for how the browser and web consoles display messages coming
+// from private windows. See bug for description of expected behavior.
+
+"use strict";
+
+function test() {
+ const TEST_URI = "data:text/html;charset=utf8,<p>hello world! bug 874061" +
+ "<button onclick='console.log(\"foobar bug 874061\");" +
+ "fooBazBaz.yummy()'>click</button>";
+ let ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"]
+ .getService(Ci.nsIConsoleAPIStorage);
+ let privateWindow, privateBrowser, privateTab, privateContent;
+ let hud, expectedMessages, nonPrivateMessage;
+
+ // This test is slightly more involved: it opens the web console twice,
+ // a new private window once, and the browser console twice. We can get
+ // a timeout with debug builds on slower machines.
+ requestLongerTimeout(2);
+ start();
+
+ function start() {
+ gBrowser.selectedTab = gBrowser.addTab("data:text/html;charset=utf8," +
+ "<p>hello world! I am not private!");
+ gBrowser.selectedBrowser.addEventListener("load", onLoadTab, true);
+ }
+
+ function onLoadTab() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoadTab, true);
+ info("onLoadTab()");
+
+ // Make sure we have a clean state to start with.
+ Services.console.reset();
+ ConsoleAPIStorage.clearEvents();
+
+ // Add a non-private message to the browser console.
+ ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+ content.console.log("bug874061-not-private");
+ });
+
+ nonPrivateMessage = {
+ name: "console message from a non-private window",
+ text: "bug874061-not-private",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ };
+
+ privateWindow = OpenBrowserWindow({ private: true });
+ ok(privateWindow, "new private window");
+ ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window's private");
+
+ whenDelayedStartupFinished(privateWindow, onPrivateWindowReady);
+ }
+
+ function onPrivateWindowReady() {
+ info("private browser window opened");
+ privateBrowser = privateWindow.gBrowser;
+
+ privateTab = privateBrowser.selectedTab = privateBrowser.addTab(TEST_URI);
+ privateBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ info("private tab opened");
+ privateBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ privateContent = privateBrowser.selectedBrowser.contentWindow;
+ ok(PrivateBrowsingUtils.isBrowserPrivate(privateBrowser.selectedBrowser),
+ "tab window is private");
+ openConsole(privateTab).then(consoleOpened);
+ }, true);
+ }
+
+ function addMessages() {
+ let button = privateContent.document.querySelector("button");
+ ok(button, "button in page");
+ EventUtils.synthesizeMouse(button, 2, 2, {}, privateContent);
+ }
+
+ function consoleOpened(injectedHud) {
+ hud = injectedHud;
+ ok(hud, "web console opened");
+
+ addMessages();
+ expectedMessages = [
+ {
+ name: "script error",
+ text: "fooBazBaz is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ name: "console message",
+ text: "foobar bug 874061",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ ];
+
+ // Make sure messages are displayed in the web console as they happen, even
+ // if this is a private tab.
+ waitForMessages({
+ webconsole: hud,
+ messages: expectedMessages,
+ }).then(testCachedMessages);
+ }
+
+ function testCachedMessages() {
+ info("testCachedMessages()");
+ closeConsole(privateTab).then(() => {
+ info("web console closed");
+ openConsole(privateTab).then(consoleReopened);
+ });
+ }
+
+ function consoleReopened(injectedHud) {
+ hud = injectedHud;
+ ok(hud, "web console reopened");
+
+ // Make sure that cached messages are displayed in the web console, even
+ // if this is a private tab.
+ waitForMessages({
+ webconsole: hud,
+ messages: expectedMessages,
+ }).then(testBrowserConsole);
+ }
+
+ function testBrowserConsole() {
+ info("testBrowserConsole()");
+ closeConsole(privateTab).then(() => {
+ info("web console closed");
+ HUDService.toggleBrowserConsole().then(onBrowserConsoleOpen);
+ });
+ }
+
+ // Make sure that the cached messages from private tabs are not displayed in
+ // the browser console.
+ function checkNoPrivateMessages() {
+ let text = hud.outputNode.textContent;
+ is(text.indexOf("fooBazBaz"), -1, "no exception displayed");
+ is(text.indexOf("bug 874061"), -1, "no console message displayed");
+ }
+
+ function onBrowserConsoleOpen(injectedHud) {
+ hud = injectedHud;
+ ok(hud, "browser console opened");
+
+ checkNoPrivateMessages();
+ addMessages();
+ expectedMessages.push(nonPrivateMessage);
+
+ // Make sure that live messages are displayed in the browser console, even
+ // from private tabs.
+ waitForMessages({
+ webconsole: hud,
+ messages: expectedMessages,
+ }).then(testPrivateWindowClose);
+ }
+
+ function testPrivateWindowClose() {
+ info("close the private window and check if private messages are removed");
+ hud.jsterm.once("private-messages-cleared", () => {
+ isnot(hud.outputNode.textContent.indexOf("bug874061-not-private"), -1,
+ "non-private messages are still shown after private window closed");
+ checkNoPrivateMessages();
+
+ info("close the browser console");
+ HUDService.toggleBrowserConsole().then(() => {
+ info("reopen the browser console");
+ executeSoon(() =>
+ HUDService.toggleBrowserConsole().then(onBrowserConsoleReopen));
+ });
+ });
+ privateWindow.BrowserTryToCloseWindow();
+ }
+
+ function onBrowserConsoleReopen(injectedHud) {
+ hud = injectedHud;
+ ok(hud, "browser console reopened");
+
+ // Make sure that the non-private message is still shown after reopen.
+ waitForMessages({
+ webconsole: hud,
+ messages: [nonPrivateMessage],
+ }).then(() => {
+ // Make sure that no private message is displayed after closing the
+ // private window and reopening the Browser Console.
+ checkNoPrivateMessages();
+ executeSoon(finishTest);
+ });
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_console_server_logging.js b/devtools/client/webconsole/test/browser_console_server_logging.js
new file mode 100644
index 000000000..eaef12330
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_server_logging.js
@@ -0,0 +1,74 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that server log appears in the console panel - bug 1168872
+add_task(function* () {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console-server-logging.sjs";
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ // Set logging filter and wait till it's set on the backend
+ hud.setFilterState("serverlog", true);
+ yield updateServerLoggingListener(hud);
+
+ BrowserReloadSkipCache();
+
+ // Note that the test is also checking out the (printf like)
+ // formatters and encoding of UTF8 characters (see the one at the end).
+ let text = "values: string Object { a: 10 } 123 1.12 \u2713";
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: text,
+ category: CATEGORY_SERVER,
+ severity: SEVERITY_LOG,
+ }],
+ });
+ // Clean up filter
+ hud.setFilterState("serverlog", false);
+ yield updateServerLoggingListener(hud);
+});
+
+add_task(function* () {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test/test-console-server-logging-array.sjs";
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ // Set logging filter and wait till it's set on the backend
+ hud.setFilterState("serverlog", true);
+ yield updateServerLoggingListener(hud);
+
+ BrowserReloadSkipCache();
+ // Note that the test is also checking out the (printf like)
+ // formatters and encoding of UTF8 characters (see the one at the end).
+ let text = "Object { best: \"Firefox\", reckless: \"Chrome\", " +
+ "new_ie: \"Safari\", new_new_ie: \"Edge\" }";
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: text,
+ category: CATEGORY_SERVER,
+ severity: SEVERITY_LOG,
+ }],
+ });
+ // Clean up filter
+ hud.setFilterState("serverlog", false);
+ yield updateServerLoggingListener(hud);
+});
+
+function updateServerLoggingListener(hud) {
+ let deferred = promise.defer();
+ hud.ui._updateServerLoggingListener(response => {
+ deferred.resolve(response);
+ });
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_console_variables_view.js b/devtools/client/webconsole/test/browser_console_variables_view.js
new file mode 100644
index 000000000..ecd8071ce
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view.js
@@ -0,0 +1,204 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that variables view works as expected in the web console.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-eval-in-stackframe.html";
+
+var hud, gVariablesView;
+
+registerCleanupFunction(function () {
+ hud = gVariablesView = null;
+});
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+
+ let msg = yield hud.jsterm.execute("(function foo(){})");
+
+ ok(msg, "output message found");
+ ok(msg.textContent.includes("function foo()"),
+ "message text check");
+
+ executeSoon(() => {
+ EventUtils.synthesizeMouse(msg.querySelector("a"), 2, 2, {}, hud.iframeWindow);
+ });
+
+ let varView = yield hud.jsterm.once("variablesview-fetched");
+ ok(varView, "object inspector opened on click");
+
+ yield findVariableViewProperties(varView, [{
+ name: "name",
+ value: "foo",
+ }], { webconsole: hud });
+});
+
+add_task(function* () {
+ let msg = yield hud.jsterm.execute("fooObj");
+
+ ok(msg, "output message found");
+ ok(msg.textContent.includes('{ testProp: "testValue" }'),
+ "message text check");
+
+ let anchor = msg.querySelector("a");
+ ok(anchor, "object link found");
+
+ let fetched = hud.jsterm.once("variablesview-fetched");
+
+ // executeSoon
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+
+ let view = yield fetched;
+
+ let results = yield onFooObjFetch(view);
+
+ let vView = yield onTestPropFound(results);
+ let results2 = yield onFooObjFetchAfterUpdate(vView);
+
+ let vView2 = yield onUpdatedTestPropFound(results2);
+ let results3 = yield onFooObjFetchAfterPropRename(vView2);
+
+ let vView3 = yield onRenamedTestPropFound(results3);
+ let results4 = yield onPropUpdateError(vView3);
+
+ yield onRenamedTestPropFoundAgain(results4);
+
+ let prop = results4[0].matchedProp;
+ yield testPropDelete(prop);
+});
+
+function onFooObjFetch(aVar) {
+ gVariablesView = aVar._variablesView;
+ ok(gVariablesView, "variables view object");
+
+ return findVariableViewProperties(aVar, [
+ { name: "testProp", value: "testValue" },
+ ], { webconsole: hud });
+}
+
+function onTestPropFound(aResults) {
+ let prop = aResults[0].matchedProp;
+ ok(prop, "matched the |testProp| property in the variables view");
+
+ is("testValue", aResults[0].value,
+ "|fooObj.testProp| value is correct");
+
+ // Check that property value updates work and that jsterm functions can be
+ // used.
+ return updateVariablesViewProperty({
+ property: prop,
+ field: "value",
+ string: "document.title + window.location + $('p')",
+ webconsole: hud
+ });
+}
+
+function onFooObjFetchAfterUpdate(aVar) {
+ info("onFooObjFetchAfterUpdate");
+ let expectedValue = content.document.title + content.location +
+ "[object HTMLParagraphElement]";
+
+ return findVariableViewProperties(aVar, [
+ { name: "testProp", value: expectedValue },
+ ], { webconsole: hud });
+}
+
+function onUpdatedTestPropFound(aResults) {
+ let prop = aResults[0].matchedProp;
+ ok(prop, "matched the updated |testProp| property value");
+
+ is(content.wrappedJSObject.fooObj.testProp, aResults[0].value,
+ "|fooObj.testProp| value has been updated");
+
+ // Check that property name updates work.
+ return updateVariablesViewProperty({
+ property: prop,
+ field: "name",
+ string: "testUpdatedProp",
+ webconsole: hud
+ });
+}
+
+function* onFooObjFetchAfterPropRename(aVar) {
+ info("onFooObjFetchAfterPropRename");
+
+ let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let para = content.wrappedJSObject.document.querySelector("p");
+ return content.document.title + content.location + para;
+ });
+
+ // Check that the new value is in the variables view.
+ return findVariableViewProperties(aVar, [
+ { name: "testUpdatedProp", value: expectedValue },
+ ], { webconsole: hud });
+}
+
+function onRenamedTestPropFound(aResults) {
+ let prop = aResults[0].matchedProp;
+ ok(prop, "matched the renamed |testProp| property");
+
+ ok(!content.wrappedJSObject.fooObj.testProp,
+ "|fooObj.testProp| has been deleted");
+ is(content.wrappedJSObject.fooObj.testUpdatedProp, aResults[0].value,
+ "|fooObj.testUpdatedProp| is correct");
+
+ // Check that property value updates that cause exceptions are reported in
+ // the web console output.
+ return updateVariablesViewProperty({
+ property: prop,
+ field: "value",
+ string: "foobarzFailure()",
+ webconsole: hud
+ });
+}
+
+function* onPropUpdateError(aVar) {
+ info("onPropUpdateError");
+
+ let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let para = content.wrappedJSObject.document.querySelector("p");
+ return content.document.title + content.location + para;
+ });
+
+ // Make sure the property did not change.
+ return findVariableViewProperties(aVar, [
+ { name: "testUpdatedProp", value: expectedValue },
+ ], { webconsole: hud });
+}
+
+function onRenamedTestPropFoundAgain(aResults) {
+ let prop = aResults[0].matchedProp;
+ ok(prop, "matched the renamed |testProp| property again");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "exception in property update reported in the web console output",
+ text: "foobarzFailure",
+ category: CATEGORY_OUTPUT,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+}
+
+function testPropDelete(aProp) {
+ gVariablesView.window.focus();
+ aProp.focus();
+
+ executeSoon(() => {
+ EventUtils.synthesizeKey("VK_DELETE", {}, gVariablesView.window);
+ });
+
+ return waitForSuccess({
+ name: "property deleted",
+ timeout: 60000,
+ validator: () => !("testUpdatedProp" in content.wrappedJSObject.fooObj)
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js b/devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js
new file mode 100644
index 000000000..522fe4754
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_dom_nodes.js
@@ -0,0 +1,59 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that ensures DOM nodes are rendered correctly in VariablesView.
+
+"use strict";
+
+function test() {
+ const TEST_URI = `
+ data:text/html;charset=utf-8,
+ <html>
+ <head>
+ <title>Test for DOM nodes in variables view</title>
+ </head>
+ <body>
+ <div></div>
+ <div id="testID"></div>
+ <div class="single-class"></div>
+ <div class="multiple-classes another-class"></div>
+ <div class="class-and-id" id="class-and-id"></div>
+ <div class="multiple-classes-and-id another-class"
+ id="multiple-classes-and-id"></div>
+ <div class=" whitespace-start"></div>
+ <div class="whitespace-end "></div>
+ <div class="multiple spaces"></div>
+ </body>
+ </html>
+`;
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+ const jsterm = hud.jsterm;
+
+ let deferred = promise.defer();
+ jsterm.once("variablesview-fetched", (_, val) => deferred.resolve(val));
+ jsterm.execute("inspect(document.querySelectorAll('div'))");
+
+ let variableScope = yield deferred.promise;
+ ok(variableScope, "Variables view opened");
+
+ yield findVariableViewProperties(variableScope, [
+ { name: "0", value: "<div>"},
+ { name: "1", value: "<div#testID>"},
+ { name: "2", value: "<div.single-class>"},
+ { name: "3", value: "<div.multiple-classes.another-class>"},
+ { name: "4", value: "<div#class-and-id.class-and-id>"},
+ { name: "5", value: "<div#multiple-classes-and-id." +
+ "multiple-classes-and-id.another-class>"},
+ { name: "6", value: "<div.whitespace-start>"},
+ { name: "7", value: "<div.whitespace-end>"},
+ { name: "8", value: "<div.multiple.spaces>"},
+ ], { webconsole: hud});
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js b/devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js
new file mode 100644
index 000000000..ec9ffa7b7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js
@@ -0,0 +1,135 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* Test case that ensures Array and other list types are not sorted in variables
+ * view.
+ *
+ * The tested types are:
+ * - Array
+ * - Int8Array
+ * - Int16Array
+ * - Int32Array
+ * - Uint8Array
+ * - Uint16Array
+ * - Uint32Array
+ * - Uint8ClampedArray
+ * - Float32Array
+ * - Float64Array
+ * - NodeList
+ */
+
+function test() {
+ const TEST_URI = "data:text/html;charset=utf-8, \
+ <html> \
+ <head> \
+ <title>Test document for bug 977500</title> \
+ </head> \
+ <body> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ <div></div> \
+ </body> \
+ </html>";
+
+ let jsterm;
+
+ function* runner() {
+ const typedArrayTypes = ["Int8Array", "Int16Array", "Int32Array",
+ "Uint8Array", "Uint16Array", "Uint32Array",
+ "Uint8ClampedArray", "Float32Array",
+ "Float64Array"];
+
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+ jsterm = hud.jsterm;
+
+ // Create an ArrayBuffer of 80 bytes to test TypedArrays. 80 bytes is
+ // enough to get 10 items in all different TypedArrays.
+ yield jsterm.execute("let buf = new ArrayBuffer(80);");
+
+ // Array
+ yield testNotSorted("Array(0,1,2,3,4,5,6,7,8,9,10)");
+ // NodeList
+ yield testNotSorted("document.querySelectorAll('div')");
+ // Object
+ yield testSorted("Object({'hello':1,1:5,10:2,4:2,'abc':1})");
+
+ // Typed arrays.
+ for (let type of typedArrayTypes) {
+ yield testNotSorted("new " + type + "(buf)");
+ }
+ }
+
+ /**
+ * A helper that ensures the properties are not sorted when an object
+ * specified by aObject is inspected.
+ *
+ * @param string aObject
+ * A string that, once executed, creates and returns the object to
+ * inspect.
+ */
+ function* testNotSorted(aObject) {
+ info("Testing " + aObject);
+ let deferred = promise.defer();
+ jsterm.once("variablesview-fetched", (_, aVar) => deferred.resolve(aVar));
+ jsterm.execute("inspect(" + aObject + ")");
+
+ let variableScope = yield deferred.promise;
+ ok(variableScope, "Variables view opened");
+
+ // If the properties are sorted: keys = ["0", "1", "10",...] <- incorrect
+ // If the properties are not sorted: keys = ["0", "1", "2",...] <- correct
+ let keyIterator = variableScope._store.keys();
+ is(keyIterator.next().value, "0", "First key is 0");
+ is(keyIterator.next().value, "1", "Second key is 1");
+
+ // If the properties are sorted, the next one will be 10.
+ is(keyIterator.next().value, "2", "Third key is 2, not 10");
+ }
+ /**
+ * A helper that ensures the properties are sorted when an object
+ * specified by aObject is inspected.
+ *
+ * @param string aObject
+ * A string that, once executed, creates and returns the object to
+ * inspect.
+ */
+ function* testSorted(aObject) {
+ info("Testing " + aObject);
+ let deferred = promise.defer();
+ jsterm.once("variablesview-fetched", (_, aVar) => deferred.resolve(aVar));
+ jsterm.execute("inspect(" + aObject + ")");
+
+ let variableScope = yield deferred.promise;
+ ok(variableScope, "Variables view opened");
+
+ // If the properties are sorted:
+ // keys = ["1", "4", "10",..., "abc", "hello"] <- correct
+ // If the properties are not sorted:
+ // keys = ["1", "10", "4",...] <- incorrect
+ let keyIterator = variableScope._store.keys();
+ is(keyIterator.next().value, "1", "First key should be 1");
+ is(keyIterator.next().value, "4", "Second key should be 4");
+
+ // If the properties are sorted, the next one will be 10.
+ is(keyIterator.next().value, "10", "Third key is 10");
+ // If sorted next properties should be "abc" then "hello"
+ is(keyIterator.next().value, "abc", "Fourth key is abc");
+ is(keyIterator.next().value, "hello", "Fifth key is hello");
+ }
+
+ Task.spawn(runner).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_filter.js b/devtools/client/webconsole/test/browser_console_variables_view_filter.js
new file mode 100644
index 000000000..142410839
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_filter.js
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that variables view filter feature works fine in the console.
+
+function props(view, prefix = "") {
+ // First match only the visible one, not hidden by a search
+ let visible = [...view].filter(([id, prop]) => prop._isMatch);
+ // Then flatten the list into a list of strings
+ // being the jsonpath of each attribute being visible in the view
+ return visible.reduce((list, [id, prop]) => {
+ list.push(prefix + id);
+ return list.concat(props(prop, prefix + id + "."));
+ }, []);
+}
+
+function assertAttrs(view, expected, message) {
+ is(props(view).join(","), expected, message);
+}
+
+add_task(function* () {
+ yield loadTab("data:text/html;charset=utf-8,webconsole-filter");
+
+ let hud = yield openConsole();
+
+ let jsterm = hud.jsterm;
+
+ let fetched = jsterm.once("variablesview-fetched");
+
+ yield jsterm.execute("inspect({ foo: { bar : \"baz\" } })");
+
+ let view = yield fetched;
+ let variablesView = view._variablesView;
+ let searchbox = variablesView._searchboxNode;
+
+ assertAttrs(view, "foo,__proto__",
+ "To start with, we just see the top level foo attr");
+
+ fetched = jsterm.once("variablesview-fetched");
+ searchbox.value = "bar";
+ searchbox.doCommand();
+ view = yield fetched;
+
+ assertAttrs(view, "",
+ "If we don't manually expand nested attr, we don't see them");
+
+ fetched = jsterm.once("variablesview-fetched");
+ searchbox.value = "";
+ searchbox.doCommand();
+ view = yield fetched;
+
+ assertAttrs(view, "foo",
+ "If we reset the search, we get back to original state");
+
+ yield [...view][0][1].expand();
+
+ fetched = jsterm.once("variablesview-fetched");
+ searchbox.value = "bar";
+ searchbox.doCommand();
+ view = yield fetched;
+
+ assertAttrs(view, "foo,foo.bar", "Now if we expand, we see the nested attr");
+
+ fetched = jsterm.once("variablesview-fetched");
+ searchbox.value = "baz";
+ searchbox.doCommand();
+ view = yield fetched;
+
+ assertAttrs(view, "foo,foo.bar", "We can also search for attr values");
+
+ fetched = jsterm.once("variablesview-fetched");
+ searchbox.value = "";
+ searchbox.doCommand();
+ view = yield fetched;
+
+ assertAttrs(view, "foo",
+ "If we reset again, we get back to original state again");
+});
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_highlighter.js b/devtools/client/webconsole/test/browser_console_variables_view_highlighter.js
new file mode 100644
index 000000000..c1b2194de
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_highlighter.js
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that variables view is linked to the inspector for highlighting and
+// selecting DOM nodes
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-952277-highlight-nodes-in-vview.html";
+
+var gWebConsole, gJSTerm, gVariablesView, gToolbox;
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(hud => {
+ consoleOpened(hud);
+ });
+ });
+}
+
+function consoleOpened(hud) {
+ gWebConsole = hud;
+ gJSTerm = hud.jsterm;
+ gToolbox = gDevTools.getToolbox(hud.target);
+ gJSTerm.execute("document.querySelectorAll('p')").then(onQSAexecuted);
+}
+
+function onQSAexecuted(msg) {
+ ok(msg, "output message found");
+ let anchor = msg.querySelector("a");
+ ok(anchor, "object link found");
+
+ gJSTerm.once("variablesview-fetched", onNodeListViewFetched);
+
+ executeSoon(() =>
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, gWebConsole.iframeWindow)
+ );
+}
+
+function onNodeListViewFetched(event, variable) {
+ gVariablesView = variable._variablesView;
+ ok(gVariablesView, "variables view object");
+
+ // Transform the vview into an array we can filter properties from
+ let props = [...variable].map(([id, prop]) => [id, prop]);
+
+ // These properties are the DOM nodes ones
+ props = props.filter(v => v[0].match(/[0-9]+/));
+
+ function hoverOverDomNodeVariableAndAssertHighlighter(index) {
+ if (props[index]) {
+ let prop = props[index][1];
+
+ gToolbox.once("node-highlight", () => {
+ ok(true, "The highlighter was shown on hover of the DOMNode");
+ gToolbox.highlighterUtils.unhighlight().then(() => {
+ clickOnDomNodeVariableAndAssertInspectorSelected(index);
+ });
+ });
+
+ // Rather than trying to emulate a mouseenter event, let's call the
+ // variable's highlightDomNode and see if it has the desired effect
+ prop.highlightDomNode();
+ } else {
+ finishUp();
+ }
+ }
+
+ function clickOnDomNodeVariableAndAssertInspectorSelected(index) {
+ let prop = props[index][1];
+
+ // Make sure the inspector is initialized so we can listen to its events
+ gToolbox.initInspector().then(() => {
+ // Rather than trying to click on the value here, let's just call the
+ // variable's openNodeInInspector function and see if it has the
+ // desired effect
+ prop.openNodeInInspector().then(() => {
+ is(gToolbox.currentToolId, "inspector",
+ "The toolbox switched over the inspector on DOMNode click");
+ gToolbox.selectTool("webconsole").then(() => {
+ hoverOverDomNodeVariableAndAssertHighlighter(index + 1);
+ });
+ });
+ });
+ }
+
+ hoverOverDomNodeVariableAndAssertHighlighter(0);
+}
+
+function finishUp() {
+ gWebConsole = gJSTerm = gVariablesView = gToolbox = null;
+
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_special_names.js b/devtools/client/webconsole/test/browser_console_variables_view_special_names.js
new file mode 100644
index 000000000..fa0dd4b92
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_special_names.js
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that variables view handles special names like "<return>"
+// properly for ordinary displays.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test for bug 1084430";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ ok(hud, "web console opened");
+
+ hud.setFilterState("log", false);
+ registerCleanupFunction(() => hud.setFilterState("log", true));
+
+ hud.jsterm.execute("inspect({ '<return>': 47, '<exception>': 91 })");
+
+ let varView = yield hud.jsterm.once("variablesview-fetched");
+ ok(varView, "variables view object");
+
+ let props = yield findVariableViewProperties(varView, [
+ { name: "<return>", value: 47 },
+ { name: "<exception>", value: 91 },
+ ], { webconsole: hud });
+
+ for (let prop of props) {
+ ok(!prop.matchedProp._internalItem, prop.name + " is not marked internal");
+ let target = prop.matchedProp._target;
+ ok(!target.hasAttribute("pseudo-item"),
+ prop.name + " is not a pseudo-item");
+ }
+});
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js
new file mode 100644
index 000000000..e83a8d626
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging.js
@@ -0,0 +1,109 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that makes sure web console eval happens in the user-selected stackframe
+// from the js debugger, when changing the value of a property in the variables
+// view.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-eval-in-stackframe.html";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let dbgPanel = yield openDebugger();
+ yield waitForFrameAdded();
+ yield openConsole();
+ yield testVariablesView(hud);
+});
+
+function* waitForFrameAdded() {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = gDevTools.getToolbox(target);
+ let thread = toolbox.threadClient;
+
+ info("Waiting for framesadded");
+ yield new Promise(resolve => {
+ thread.addOneTimeListener("framesadded", resolve);
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.firstCall();
+ });
+ });
+}
+
+function* testVariablesView(hud) {
+ let jsterm = hud.jsterm;
+ let msg = yield jsterm.execute("fooObj");
+ ok(msg, "output message found");
+ ok(msg.textContent.includes('{ testProp2: "testValue2" }'),
+ "message text check");
+
+ let anchor = msg.querySelector("a");
+ ok(anchor, "object link found");
+
+ info("Waiting for variable view to appear");
+ let variable = yield new Promise(resolve => {
+ jsterm.once("variablesview-fetched", (e, variable) => {
+ resolve(variable);
+ });
+ executeSoon(() => EventUtils.synthesizeMouse(anchor, 2, 2, {},
+ hud.iframeWindow));
+ });
+
+ info("Waiting for findVariableViewProperties");
+ let results = yield findVariableViewProperties(variable, [
+ { name: "testProp2", value: "testValue2" },
+ { name: "testProp", value: "testValue", dontMatch: true },
+ ], { webconsole: hud });
+
+ let prop = results[0].matchedProp;
+ ok(prop, "matched the |testProp2| property in the variables view");
+
+ // Check that property value updates work and that jsterm functions can be
+ // used.
+ variable = yield updateVariablesViewProperty({
+ property: prop,
+ field: "value",
+ string: "document.title + foo2 + $('p')",
+ webconsole: hud
+ });
+
+ info("onFooObjFetchAfterUpdate");
+ let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let para = content.wrappedJSObject.document.querySelector("p");
+ return content.document.title + "foo2SecondCall" + para;
+ });
+
+ results = yield findVariableViewProperties(variable, [
+ { name: "testProp2", value: expectedValue },
+ ], { webconsole: hud });
+
+ prop = results[0].matchedProp;
+ ok(prop, "matched the updated |testProp2| property value");
+
+ // Check that testProp2 was updated.
+ yield new Promise(resolve => {
+ executeSoon(() => {
+ jsterm.execute("fooObj.testProp2").then(resolve);
+ });
+ });
+
+ expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let para = content.wrappedJSObject.document.querySelector("p");
+ return content.document.title + "foo2SecondCall" + para;
+ });
+
+ isnot(hud.outputNode.textContent.indexOf(expectedValue), -1,
+ "fooObj.testProp2 is correct");
+}
diff --git a/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js
new file mode 100644
index 000000000..556e7275d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_variables_view_while_debugging_and_inspecting.js
@@ -0,0 +1,112 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that makes sure web console eval works while the js debugger paused the
+// page, and while the inspector is active. See bug 886137.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-eval-in-stackframe.html";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let dbgPanel = yield openDebugger();
+ yield openInspector();
+ yield waitForFrameAdded();
+
+ yield openConsole();
+ yield testVariablesView(hud);
+});
+
+function* waitForFrameAdded() {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = gDevTools.getToolbox(target);
+ let thread = toolbox.threadClient;
+
+ info("Waiting for framesadded");
+ yield new Promise(resolve => {
+ thread.addOneTimeListener("framesadded", resolve);
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.firstCall();
+ });
+ });
+}
+
+function* testVariablesView(hud) {
+ info("testVariablesView");
+ let jsterm = hud.jsterm;
+
+ let msg = yield jsterm.execute("fooObj");
+ ok(msg, "output message found");
+ ok(msg.textContent.includes('{ testProp2: "testValue2" }'),
+ "message text check");
+
+ let anchor = msg.querySelector("a");
+ ok(anchor, "object link found");
+
+ info("Waiting for variable view to appear");
+ let variable = yield new Promise(resolve => {
+ jsterm.once("variablesview-fetched", (e, variable) => {
+ resolve(variable);
+ });
+ executeSoon(() => EventUtils.synthesizeMouse(anchor, 2, 2, {},
+ hud.iframeWindow));
+ });
+
+ info("Waiting for findVariableViewProperties");
+ let results = yield findVariableViewProperties(variable, [
+ { name: "testProp2", value: "testValue2" },
+ { name: "testProp", value: "testValue", dontMatch: true },
+ ], { webconsole: hud });
+
+ let prop = results[0].matchedProp;
+ ok(prop, "matched the |testProp2| property in the variables view");
+
+ // Check that property value updates work and that jsterm functions can be
+ // used.
+ variable = yield updateVariablesViewProperty({
+ property: prop,
+ field: "value",
+ string: "document.title + foo2 + $('p')",
+ webconsole: hud
+ });
+
+ info("onFooObjFetchAfterUpdate");
+ let expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let para = content.wrappedJSObject.document.querySelector("p");
+ return content.document.title + "foo2SecondCall" + para;
+ });
+
+ results = yield findVariableViewProperties(variable, [
+ { name: "testProp2", value: expectedValue },
+ ], { webconsole: hud });
+
+ prop = results[0].matchedProp;
+ ok(prop, "matched the updated |testProp2| property value");
+
+ // Check that testProp2 was updated.
+ yield new Promise(resolve => {
+ executeSoon(() => {
+ jsterm.execute("fooObj.testProp2").then(resolve);
+ });
+ });
+
+ expectedValue = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let para = content.wrappedJSObject.document.querySelector("p");
+ return content.document.title + "foo2SecondCall" + para;
+ });
+
+ isnot(hud.outputNode.textContent.indexOf(expectedValue), -1,
+ "fooObj.testProp2 is correct");
+}
diff --git a/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js
new file mode 100644
index 000000000..bc923ff44
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe.js
@@ -0,0 +1,157 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that makes sure web console eval happens in the user-selected stackframe
+// from the js debugger.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-eval-in-stackframe.html";
+
+var gWebConsole, gJSTerm, gDebuggerWin, gThread, gDebuggerController;
+var gStackframes;
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(consoleOpened);
+ });
+}
+
+function consoleOpened(hud) {
+ gWebConsole = hud;
+ gJSTerm = hud.jsterm;
+ gJSTerm.execute("foo").then(onExecuteFoo);
+}
+
+function onExecuteFoo() {
+ isnot(gWebConsole.outputNode.textContent.indexOf("globalFooBug783499"), -1,
+ "|foo| value is correct");
+
+ gJSTerm.clearOutput();
+
+ // Test for Bug 690529 - Web Console and Scratchpad should evaluate
+ // expressions in the scope of the content window, not in a sandbox.
+ executeSoon(() => {
+ gJSTerm.execute("foo2 = 'newFoo'; window.foo2").then(onNewFoo2);
+ });
+}
+
+function onNewFoo2(msg) {
+ is(gWebConsole.outputNode.textContent.indexOf("undefined"), -1,
+ "|undefined| is not displayed after adding |foo2|");
+
+ ok(msg, "output result found");
+
+ isnot(msg.textContent.indexOf("newFoo"), -1,
+ "'newFoo' is displayed after adding |foo2|");
+
+ gJSTerm.clearOutput();
+
+ info("openDebugger");
+ executeSoon(() => openDebugger().then(debuggerOpened));
+}
+
+function debuggerOpened(aResult) {
+ gDebuggerWin = aResult.panelWin;
+ gDebuggerController = gDebuggerWin.DebuggerController;
+ gThread = gDebuggerController.activeThread;
+ gStackframes = gDebuggerController.StackFrames;
+
+ info("openConsole");
+ executeSoon(() =>
+ openConsole().then(() =>
+ gJSTerm.execute("foo + foo2").then(onExecuteFooAndFoo2)
+ )
+ );
+}
+
+function onExecuteFooAndFoo2() {
+ let expected = "globalFooBug783499newFoo";
+ isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
+ "|foo + foo2| is displayed after starting the debugger");
+
+ executeSoon(() => {
+ gJSTerm.clearOutput();
+
+ info("openDebugger");
+ openDebugger().then(() => {
+ gThread.addOneTimeListener("framesadded", onFramesAdded);
+
+ info("firstCall()");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.firstCall();
+ });
+ });
+ });
+}
+
+function onFramesAdded() {
+ info("onFramesAdded, openConsole() now");
+ executeSoon(() =>
+ openConsole().then(() =>
+ gJSTerm.execute("foo + foo2").then(onExecuteFooAndFoo2InSecondCall)
+ )
+ );
+}
+
+function onExecuteFooAndFoo2InSecondCall() {
+ let expected = "globalFooBug783499foo2SecondCall";
+ isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
+ "|foo + foo2| from |secondCall()|");
+
+ function runOpenConsole() {
+ openConsole().then(() => {
+ gJSTerm.execute("foo + foo2 + foo3").then(onExecuteFoo23InFirstCall);
+ });
+ }
+
+ executeSoon(() => {
+ gJSTerm.clearOutput();
+
+ info("openDebugger and selectFrame(1)");
+
+ openDebugger().then(() => {
+ gStackframes.selectFrame(1);
+
+ info("openConsole");
+ executeSoon(() => runOpenConsole());
+ });
+ });
+}
+
+function onExecuteFoo23InFirstCall() {
+ let expected = "fooFirstCallnewFoofoo3FirstCall";
+ isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
+ "|foo + foo2 + foo3| from |firstCall()|");
+
+ executeSoon(() =>
+ gJSTerm.execute("foo = 'abba'; foo3 = 'bug783499'; foo + foo3").then(
+ onExecuteFooAndFoo3ChangesInFirstCall));
+}
+
+var onExecuteFooAndFoo3ChangesInFirstCall = Task.async(function*() {
+ let expected = "abbabug783499";
+ isnot(gWebConsole.outputNode.textContent.indexOf(expected), -1,
+ "|foo + foo3| updated in |firstCall()|");
+
+ yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
+ is(content.wrappedJSObject.foo, "globalFooBug783499",
+ "|foo| in content window");
+ is(content.wrappedJSObject.foo2, "newFoo", "|foo2| in content window");
+ ok(!content.wrappedJSObject.foo3,
+ "|foo3| was not added to the content window");
+ });
+
+ gWebConsole = gJSTerm = gDebuggerWin = gThread = gDebuggerController =
+ gStackframes = null;
+ executeSoon(finishTest);
+});
diff --git a/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js
new file mode 100644
index 000000000..bc116d443
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_eval_in_debugger_stackframe2.js
@@ -0,0 +1,71 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test to make sure that web console commands can fire while paused at a
+// breakpoint that was triggered from a JS call. Relies on asynchronous js
+// evaluation over the protocol - see Bug 1088861.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-eval-in-stackframe.html";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ info("open the web console");
+ let hud = yield openConsole();
+ let {jsterm} = hud;
+
+ info("open the debugger");
+ let {panelWin} = yield openDebugger();
+ let {DebuggerController} = panelWin;
+ let {activeThread} = DebuggerController;
+
+ let firstCall = promise.defer();
+ let frameAdded = promise.defer();
+ executeSoon(() => {
+ info("Executing firstCall");
+ activeThread.addOneTimeListener("framesadded", () => {
+ executeSoon(frameAdded.resolve);
+ });
+ jsterm.execute("firstCall()").then(firstCall.resolve);
+ });
+
+ info("Waiting for a frame to be added");
+ yield frameAdded.promise;
+
+ info("Executing basic command while paused");
+ yield executeAndConfirm(jsterm, "1 + 2", "3");
+
+ info("Executing command using scoped variables while paused");
+ yield executeAndConfirm(jsterm, "foo + foo2",
+ '"globalFooBug783499foo2SecondCall"');
+
+ info("Resuming the thread");
+ activeThread.resume();
+
+ info("Checking the first command, which is the last to resolve since it " +
+ "paused");
+ let node = yield firstCall.promise;
+ is(node.querySelector(".message-body").textContent,
+ "undefined",
+ "firstCall() returned correct value");
+});
+
+function* executeAndConfirm(jsterm, input, output) {
+ info("Executing command `" + input + "`");
+
+ let node = yield jsterm.execute(input);
+
+ is(node.querySelector(".message-body").textContent, output,
+ "Expected result from call to " + input);
+}
diff --git a/devtools/client/webconsole/test/browser_jsterm_inspect.js b/devtools/client/webconsole/test/browser_jsterm_inspect.js
new file mode 100644
index 000000000..aa18cbff6
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_jsterm_inspect.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the inspect() jsterm helper function works.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>hello bug 869981";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ let jsterm = hud.jsterm;
+
+ /* Check that the window object is inspected */
+ jsterm.execute("testProp = 'testValue'");
+
+ let updated = jsterm.once("variablesview-updated");
+ jsterm.execute("inspect(window)");
+ let view = yield updated;
+ ok(view, "variables view object");
+
+ // The single variable view contains a scope with the variable name
+ // and unnamed subitem that contains the properties
+ let variable = view.getScopeAtIndex(0).get(undefined);
+ ok(variable, "variable object");
+
+ yield findVariableViewProperties(variable, [
+ { name: "testProp", value: "testValue" },
+ { name: "document", value: /HTMLDocument \u2192 data:/ },
+ ], { webconsole: hud });
+
+ /* Check that a primitive value can be inspected, too */
+ let updated2 = jsterm.once("variablesview-updated");
+ jsterm.execute("inspect(1)");
+ let view2 = yield updated2;
+ ok(view2, "variables view object");
+
+ // Check the label of the scope - it should contain the value
+ let scope = view.getScopeAtIndex(0);
+ ok(scope, "variable object");
+
+ is(scope.name, "1", "The value of the primitive var is correct");
+});
diff --git a/devtools/client/webconsole/test/browser_longstring_hang.js b/devtools/client/webconsole/test/browser_longstring_hang.js
new file mode 100644
index 000000000..036ad6e88
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_longstring_hang.js
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that very long strings do not hang the browser.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-859170-longstring-hang.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ info("wait for the initial long string");
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "find 'foobar', no 'foobaz', in long string output",
+ text: "foobar",
+ noText: "foobaz",
+ category: CATEGORY_WEBDEV,
+ longString: true,
+ },
+ ],
+ });
+
+ let clickable = results[0].longStrings[0];
+ ok(clickable, "long string ellipsis is shown");
+ clickable.scrollIntoView(false);
+
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+
+ info("wait for long string expansion");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "find 'foobaz' after expand, but no 'boom!' at the end",
+ text: "foobaz",
+ noText: "boom!",
+ category: CATEGORY_WEBDEV,
+ longString: false,
+ },
+ {
+ text: "too long to be displayed",
+ longString: false,
+ },
+ ],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js b/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js
new file mode 100644
index 000000000..f6f057549
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_netmonitor_shows_reqs_in_webconsole.js
@@ -0,0 +1,74 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Test that the netmonitor " +
+ "displays requests that have been recorded in the " +
+ "web console, even if the netmonitor hadn't opened yet.";
+
+const TEST_FILE = "test-network-request.html";
+const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/" + TEST_FILE;
+
+const NET_PREF = "devtools.webconsole.filter.networkinfo";
+Services.prefs.setBoolPref(NET_PREF, true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(NET_PREF);
+});
+
+add_task(function* () {
+ let { tab, browser } = yield loadTab(TEST_URI);
+
+ // Test that the request appears in the console.
+ let hud = yield openConsole();
+ info("Web console is open");
+
+ yield loadDocument(browser);
+ info("Document loaded.");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "network message",
+ text: TEST_FILE,
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG
+ }
+ ]
+ });
+
+ // Test that the request appears in the network panel.
+ let target = TargetFactory.forTab(tab);
+ let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
+ info("Network panel is open.");
+
+ testNetmonitor(toolbox);
+});
+
+function loadDocument(browser) {
+ let deferred = promise.defer();
+
+ browser.addEventListener("load", function onLoad() {
+ browser.removeEventListener("load", onLoad, true);
+ deferred.resolve();
+ }, true);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PATH);
+
+ return deferred.promise;
+}
+
+function testNetmonitor(toolbox) {
+ let monitor = toolbox.getCurrentPanel();
+ let { RequestsMenu } = monitor.panelWin.NetMonitorView;
+ RequestsMenu.lazyUpdate = false;
+
+ is(RequestsMenu.itemCount, 1, "Network request appears in the network panel");
+
+ let item = RequestsMenu.getItemAtIndex(0);
+ is(item.attachment.method, "GET", "The attached method is correct.");
+ is(item.attachment.url, TEST_PATH, "The attached url is correct.");
+}
diff --git a/devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js b/devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js
new file mode 100644
index 000000000..38a5b5419
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_output_breaks_after_console_dir_uninspectable.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Make sure that the Web Console output does not break after we try to call
+// console.dir() for objects that are not inspectable.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,test for bug 773466";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput(true);
+
+ hud.jsterm.execute("console.log('fooBug773466a')");
+ hud.jsterm.execute("myObj = Object.create(null)");
+ hud.jsterm.execute("console.dir(myObj)");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "fooBug773466a",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ name: "console.dir output",
+ consoleDir: "[object Object]",
+ }],
+ });
+
+ content.console.log("fooBug773466b");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "fooBug773466b",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_output_longstring_expand.js b/devtools/client/webconsole/test/browser_output_longstring_expand.js
new file mode 100644
index 000000000..bae8ca128
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_output_longstring_expand.js
@@ -0,0 +1,85 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that long strings can be expanded in the console output.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,test for bug 787981 - check " +
+ "that long strings can be expanded in the output.";
+
+add_task(function* () {
+ let { DebuggerServer } = require("devtools/server/main");
+
+ let longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4))
+ .join("a") + "foobar";
+ let initialString =
+ longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput(true);
+ hud.jsterm.execute("console.log('bazbaz', '" + longString + "', 'boom')");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log output",
+ text: ["bazbaz", "boom", initialString],
+ noText: "foobar",
+ longString: true,
+ }],
+ });
+
+ let clickable = result.longStrings[0];
+ ok(clickable, "long string ellipsis is shown");
+
+ clickable.scrollIntoView(false);
+
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "full string",
+ text: ["bazbaz", "boom", longString],
+ category: CATEGORY_WEBDEV,
+ longString: false,
+ }],
+ });
+
+ hud.jsterm.clearOutput(true);
+ let msg = yield execute(hud, "'" + longString + "'");
+
+ isnot(msg.textContent.indexOf(initialString), -1,
+ "initial string is shown");
+ is(msg.textContent.indexOf(longString), -1,
+ "full string is not shown");
+
+ clickable = msg.querySelector(".longStringEllipsis");
+ ok(clickable, "long string ellipsis is shown");
+
+ clickable.scrollIntoView(false);
+
+ EventUtils.synthesizeMouse(clickable, 3, 4, {}, hud.iframeWindow);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "full string",
+ text: longString,
+ category: CATEGORY_OUTPUT,
+ longString: false,
+ }],
+ });
+});
+
+function execute(hud, str) {
+ let deferred = promise.defer();
+ hud.jsterm.execute(str, deferred.resolve);
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_repeated_messages_accuracy.js b/devtools/client/webconsole/test/browser_repeated_messages_accuracy.js
new file mode 100644
index 000000000..36b13ce02
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_repeated_messages_accuracy.js
@@ -0,0 +1,178 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that makes sure messages are not considered repeated when coming from
+// different lines of code, or from different severities, etc.
+// See bugs 720180 and 800510.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-repeated-messages.html";
+const PREF = "devtools.webconsole.persistlog";
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield consoleOpened(hud);
+
+ let loaded = loadBrowser(browser);
+ BrowserReload();
+ yield loaded;
+
+ yield testCSSRepeats(hud);
+ yield testCSSRepeatsAfterReload(hud);
+ yield testConsoleRepeats(hud);
+ yield testConsoleFalsyValues(hud);
+
+ Services.prefs.clearUserPref(PREF);
+});
+
+function consoleOpened(hud) {
+ // Check that css warnings are not coalesced if they come from different
+ // lines.
+ info("waiting for 2 css warnings");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "two css warnings",
+ category: CATEGORY_CSS,
+ count: 2,
+ repeats: 1,
+ }],
+ });
+}
+
+function testCSSRepeats(hud) {
+ info("wait for repeats after page reload");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "two css warnings, repeated twice",
+ category: CATEGORY_CSS,
+ repeats: 2,
+ count: 2,
+ }],
+ });
+}
+
+function testCSSRepeatsAfterReload(hud) {
+ hud.jsterm.clearOutput(true);
+ hud.jsterm.execute("testConsole()");
+
+ info("wait for repeats with the console API");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "console.log 'foo repeat' repeated twice",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 2,
+ },
+ {
+ name: "console.log 'foo repeat' repeated once",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 1,
+ },
+ {
+ name: "console.error 'foo repeat' repeated once",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ repeats: 1,
+ },
+ ],
+ });
+}
+
+function testConsoleRepeats(hud) {
+ hud.jsterm.clearOutput(true);
+ hud.jsterm.execute("undefined");
+
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.console.log("undefined");
+ });
+
+ info("make sure console API messages are not coalesced with jsterm output");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "'undefined' jsterm input message",
+ text: "undefined",
+ category: CATEGORY_INPUT,
+ },
+ {
+ name: "'undefined' jsterm output message",
+ text: "undefined",
+ category: CATEGORY_OUTPUT,
+ },
+ {
+ name: "'undefined' console.log message",
+ text: "undefined",
+ category: CATEGORY_WEBDEV,
+ repeats: 1,
+ },
+ ],
+ });
+}
+
+function testConsoleFalsyValues(hud) {
+ hud.jsterm.clearOutput(true);
+ hud.jsterm.execute("testConsoleFalsyValues()");
+
+ info("wait for repeats of falsy values with the console API");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "console.log 'NaN' repeated once",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 1,
+ },
+ {
+ name: "console.log 'undefined' repeated once",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 1,
+ },
+ {
+ name: "console.log 'null' repeated once",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 1,
+ },
+ {
+ name: "console.log 'NaN' repeated twice",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 2,
+ },
+ {
+ name: "console.log 'undefined' repeated twice",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 2,
+ },
+ {
+ name: "console.log 'null' repeated twice",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 2,
+ },
+ ],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_result_format_as_string.js b/devtools/client/webconsole/test/browser_result_format_as_string.js
new file mode 100644
index 000000000..0352d0afa
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_result_format_as_string.js
@@ -0,0 +1,40 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Make sure that JS eval result are properly formatted as strings.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-result-format-as-string.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput(true);
+
+ let msg = yield execute(hud, "document.querySelector('p')");
+
+ is(hud.outputNode.textContent.indexOf("bug772506_content"), -1,
+ "no content element found");
+ ok(!hud.outputNode.querySelector("#foobar"), "no #foobar element found");
+
+ ok(msg, "eval output node found");
+ is(msg.textContent.indexOf("<div>"), -1,
+ "<div> string is not displayed");
+ isnot(msg.textContent.indexOf("<p>"), -1,
+ "<p> string is displayed");
+
+ EventUtils.synthesizeMouseAtCenter(msg, {type: "mousemove"});
+ ok(!gBrowser._bug772506, "no content variable");
+});
+
+function execute(hud, str) {
+ let deferred = promise.defer();
+ hud.jsterm.execute(str, deferred.resolve);
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js b/devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js
new file mode 100644
index 000000000..0eeb6eaa3
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_warn_user_about_replaced_api.js
@@ -0,0 +1,86 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_REPLACED_API_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-console-replaced-api.html";
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/testscript.js";
+const PREF = "devtools.webconsole.persistlog";
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ let { browser } = yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ yield testWarningNotPresent(hud);
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(browser, TEST_REPLACED_API_URI);
+ yield loaded;
+
+ let hud2 = yield openConsole();
+
+ yield testWarningPresent(hud2);
+
+ Services.prefs.clearUserPref(PREF);
+});
+
+function testWarningNotPresent(hud) {
+ let deferred = promise.defer();
+
+ is(hud.outputNode.textContent.indexOf("logging API"), -1,
+ "no warning displayed");
+
+ // Bug 862024: make sure the warning doesn't show after page reload.
+ info("reload " + TEST_URI);
+ executeSoon(function () {
+ let browser = gBrowser.selectedBrowser;
+ ContentTask.spawn(browser, null, "() => content.location.reload()");
+ });
+
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "testscript.js",
+ category: CATEGORY_NETWORK,
+ }],
+ }).then(() => executeSoon(() => {
+ is(hud.outputNode.textContent.indexOf("logging API"), -1,
+ "no warning displayed");
+ closeConsole().then(deferred.resolve);
+ }));
+
+ return deferred.promise;
+}
+
+function testWarningPresent(hud) {
+ info("wait for the warning to show");
+ let deferred = promise.defer();
+
+ let warning = {
+ webconsole: hud,
+ messages: [{
+ text: /logging API .+ disabled by a script/,
+ category: CATEGORY_JS,
+ severity: SEVERITY_WARNING,
+ }],
+ };
+
+ waitForMessages(warning).then(() => {
+ hud.jsterm.clearOutput();
+
+ executeSoon(() => {
+ info("reload the test page and wait for the warning to show");
+ waitForMessages(warning).then(deferred.resolve);
+ let browser = gBrowser.selectedBrowser;
+ ContentTask.spawn(browser, null, "() => content.location.reload()");
+ });
+ });
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js b/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
new file mode 100644
index 000000000..07f6372d0
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_allow_mixedcontent_securityerrors.js
@@ -0,0 +1,69 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The test loads a web page with mixed active and display content
+// on it while the "block mixed content" settings are _off_.
+// It then checks that the loading mixed content warning messages
+// are logged to the console and have the correct "Learn More"
+// url appended to them.
+// Bug 875456 - Log mixed content messages from the Mixed Content
+// Blocker to the Security Pane in the Web Console
+
+"use strict";
+
+const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test-mixedcontent-securityerrors.html";
+const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
+ "Mixed_content" + DOCS_GA_PARAMS;
+
+add_task(function* () {
+ yield pushPrefEnv();
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Logged mixed active content",
+ text: "Loading mixed (insecure) active content " +
+ "\u201chttp://example.com/\u201d on a secure page",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ {
+ name: "Logged mixed passive content - image",
+ text: "Loading mixed (insecure) display content " +
+ "\u201chttp://example.com/tests/image/test/mochitest/blue.png\u201d " +
+ "on a secure page",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ ],
+ });
+
+ yield testClickOpenNewTab(hud, results);
+});
+
+function pushPrefEnv() {
+ let deferred = promise.defer();
+ let options = {"set":
+ [["security.mixed_content.block_active_content", false],
+ ["security.mixed_content.block_display_content", false]
+ ]};
+ SpecialPowers.pushPrefEnv(options, deferred.resolve);
+ return deferred.promise;
+}
+
+function testClickOpenNewTab(hud, results) {
+ let warningNode = results[0].clickableElements[0];
+ ok(warningNode, "link element");
+ ok(warningNode.classList.contains("learn-more-link"), "link class name");
+ return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_assert.js b/devtools/client/webconsole/test/browser_webconsole_assert.js
new file mode 100644
index 000000000..7fc9693f1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_assert.js
@@ -0,0 +1,56 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that console.assert() works as expected (i.e. outputs only on falsy
+// asserts). See bug 760193.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-assert.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ yield consoleOpened(hud);
+});
+
+function consoleOpened(hud) {
+ hud.jsterm.execute("test()");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "undefined",
+ category: CATEGORY_OUTPUT,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "start",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "false assert",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "falsy assert",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "end",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(() => {
+ let nodes = hud.outputNode.querySelectorAll(".message");
+ is(nodes.length, 6,
+ "only six messages are displayed, no output from the true assert");
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
new file mode 100644
index 000000000..0ba168078
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that properties starting with underscores or dollars can be
+// autocompleted (bug 967468).
+
+add_task(function* () {
+ const TEST_URI = "data:text/html;charset=utf8,test autocompletion with " +
+ "$ or _";
+ yield loadTab(TEST_URI);
+
+ function* autocomplete(term) {
+ let deferred = promise.defer();
+
+ jsterm.setInputValue(term);
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, deferred.resolve);
+
+ yield deferred.promise;
+
+ ok(popup.itemCount > 0,
+ "There's " + popup.itemCount + " suggestions for '" + term + "'");
+ }
+
+ let { jsterm } = yield openConsole();
+ let popup = jsterm.autocompletePopup;
+
+ yield jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
+
+ // Should work with bug 967468.
+ yield autocomplete("Object.__d");
+ yield autocomplete("testObject.$$a");
+
+ // Here's when things go wrong in bug 967468.
+ yield autocomplete("Object.__de");
+ yield autocomplete("testObject.$$aa");
+
+ // Should work with bug 1207868.
+ yield jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};");
+ yield autocomplete("foobar");
+ yield autocomplete("blargh");
+ yield autocomplete("foobar.a");
+ yield autocomplete("blargh.a");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js
new file mode 100644
index 000000000..bcd2e22d0
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_accessibility.js
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the autocomplete input is being blurred and focused when selecting a value.
+// This will help screen-readers notify users of the value that was set in the input.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test code completion";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+
+ info("Type 'd' to open the autocomplete popup");
+ yield autocomplete(jsterm, "d");
+
+ // Add listeners for focus and blur events.
+ let wasBlurred = false;
+ input.addEventListener("blur", () => {
+ wasBlurred = true;
+ }, {
+ once: true
+ });
+
+ let wasFocused = false;
+ input.addEventListener("blur", () => {
+ ok(wasBlurred, "jsterm input received a blur event before received back the focus");
+ wasFocused = true;
+ }, {
+ once: true
+ });
+
+ info("Close the autocomplete popup by simulating a TAB key event");
+ let onPopupClosed = jsterm.autocompletePopup.once("popup-closed");
+ EventUtils.synthesizeKey("VK_TAB", {});
+
+ info("Wait for the autocomplete popup to be closed");
+ yield onPopupClosed;
+
+ ok(wasFocused, "jsterm input received a focus event");
+});
+
+function* autocomplete(jsterm, value) {
+ let popup = jsterm.autocompletePopup;
+
+ yield new Promise(resolve => {
+ jsterm.setInputValue(value);
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ ok(popup.isOpen && popup.itemCount > 0,
+ "Autocomplete popup is open and contains suggestions");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js
new file mode 100644
index 000000000..d0c6eb673
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_and_selfxss.js
@@ -0,0 +1,130 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642615";
+
+XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
+ "@mozilla.org/widget/clipboardhelper;1",
+ "nsIClipboardHelper");
+var WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield consoleOpened(hud);
+});
+
+function consoleOpened(HUD) {
+ let deferred = promise.defer();
+
+ let jsterm = HUD.jsterm;
+ let stringToCopy = "foobazbarBug642615";
+
+ jsterm.clearOutput();
+
+ ok(!jsterm.completeNode.value, "no completeNode.value");
+
+ jsterm.setInputValue("doc");
+
+ let completionValue;
+
+ // wait for key "u"
+ function onCompletionValue() {
+ completionValue = jsterm.completeNode.value;
+
+ // Arguments: expected, setup, success, failure.
+ waitForClipboard(
+ stringToCopy,
+ function () {
+ clipboardHelper.copyString(stringToCopy);
+ },
+ onClipboardCopy,
+ finishTest);
+ }
+
+ function onClipboardCopy() {
+ testSelfXss();
+
+ jsterm.setInputValue("docu");
+ info("wait for completion update after clipboard paste");
+ updateEditUIVisibility();
+ jsterm.once("autocomplete-updated", onClipboardPaste);
+ goDoCommand("cmd_paste");
+ }
+
+ // Self xss prevention tests (bug 994134)
+ function testSelfXss() {
+ info("Self-xss paste tests");
+ WebConsoleUtils.usageCount = 0;
+ is(WebConsoleUtils.usageCount, 0, "Test for usage count getter");
+ // Input some commands to check if usage counting is working
+ for (let i = 0; i <= 3; i++) {
+ jsterm.setInputValue(i);
+ jsterm.execute();
+ }
+ is(WebConsoleUtils.usageCount, 4, "Usage count incremented");
+ WebConsoleUtils.usageCount = 0;
+ updateEditUIVisibility();
+
+ let oldVal = jsterm.getInputValue();
+ goDoCommand("cmd_paste");
+ let notificationbox = jsterm.hud.document.getElementById("webconsole-notificationbox");
+ let notification = notificationbox.getNotificationWithValue("selfxss-notification");
+ ok(notification, "Self-xss notification shown");
+ is(oldVal, jsterm.getInputValue(), "Paste blocked by self-xss prevention");
+
+ // Allow pasting
+ jsterm.setInputValue("allow pasting");
+ let evt = document.createEvent("KeyboardEvent");
+ evt.initKeyEvent("keyup", true, true, window,
+ 0, 0, 0, 0,
+ 0, " ".charCodeAt(0));
+ jsterm.inputNode.dispatchEvent(evt);
+ jsterm.setInputValue("");
+ goDoCommand("cmd_paste");
+ isnot("", jsterm.getInputValue(), "Paste works");
+ }
+ function onClipboardPaste() {
+ ok(!jsterm.completeNode.value, "no completion value after paste");
+
+ info("wait for completion update after undo");
+ jsterm.once("autocomplete-updated", onCompletionValueAfterUndo);
+
+ // Get out of the webconsole event loop.
+ executeSoon(() => {
+ goDoCommand("cmd_undo");
+ });
+ }
+
+ function onCompletionValueAfterUndo() {
+ is(jsterm.completeNode.value, completionValue,
+ "same completeNode.value after undo");
+
+ info("wait for completion update after clipboard paste (ctrl-v)");
+ jsterm.once("autocomplete-updated", () => {
+ ok(!jsterm.completeNode.value,
+ "no completion value after paste (ctrl-v)");
+
+ // using executeSoon() to get out of the webconsole event loop.
+ executeSoon(deferred.resolve);
+ });
+
+ // Get out of the webconsole event loop.
+ executeSoon(() => {
+ EventUtils.synthesizeKey("v", {accelKey: true});
+ });
+ }
+
+ info("wait for completion value after typing 'docu'");
+ jsterm.once("autocomplete-updated", onCompletionValue);
+
+ EventUtils.synthesizeKey("u", {});
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js
new file mode 100644
index 000000000..b4471bd6b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_crossdomain_iframe.js
@@ -0,0 +1,64 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that autocomplete doesn't break when trying to reach into objects from
+// a different domain, bug 989025.
+
+"use strict";
+
+function test() {
+ let hud;
+
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-989025-iframe-parent.html";
+
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+
+ hud.jsterm.execute("document.title");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "989025 - iframe parent",
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ let autocompleteUpdated = hud.jsterm.once("autocomplete-updated");
+
+ hud.jsterm.setInputValue("window[0].document");
+ executeSoon(() => {
+ EventUtils.synthesizeKey(".", {});
+ });
+
+ yield autocompleteUpdated;
+
+ hud.jsterm.setInputValue("window[0].document.title");
+ EventUtils.synthesizeKey("VK_RETURN", {});
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Permission denied",
+ category: CATEGORY_OUTPUT,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+
+ hud.jsterm.execute("window.location");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test-bug-989025-iframe-parent.html",
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ yield closeConsole(tab);
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js
new file mode 100644
index 000000000..60ba5ff0e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_in_debugger_stackframe.js
@@ -0,0 +1,245 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that makes sure web console autocomplete happens in the user-selected
+// stackframe from the js debugger.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-autocomplete-in-stackframe.html";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+var gStackframes;
+registerCleanupFunction(function () {
+ gStackframes = null;
+});
+
+requestLongerTimeout(2);
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ yield testCompletion(hud);
+});
+
+function* testCompletion(hud) {
+ let jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+ let popup = jsterm.autocompletePopup;
+
+ // Test that document.title gives string methods. Native getters must execute.
+ input.value = "document.title.";
+ input.setSelectionRange(input.value.length, input.value.length);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ let newItems = popup.getItems();
+ ok(newItems.length > 0, "'document.title.' gave a list of suggestions");
+ ok(newItems.some(function (item) {
+ return item.label == "substr";
+ }), "autocomplete results do contain substr");
+ ok(newItems.some(function (item) {
+ return item.label == "toLowerCase";
+ }), "autocomplete results do contain toLowerCase");
+ ok(newItems.some(function (item) {
+ return item.label == "strike";
+ }), "autocomplete results do contain strike");
+
+ // Test if 'f' gives 'foo1' but not 'foo2' or 'foo3'
+ input.value = "f";
+ input.setSelectionRange(1, 1);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(newItems.length > 0, "'f' gave a list of suggestions");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo1";
+ }), "autocomplete results do contain foo1");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo1Obj";
+ }), "autocomplete results do contain foo1Obj");
+ ok(newItems.every(function (item) {
+ return item.label != "foo2";
+ }), "autocomplete results do not contain foo2");
+ ok(newItems.every(function (item) {
+ return item.label != "foo2Obj";
+ }), "autocomplete results do not contain foo2Obj");
+ ok(newItems.every(function (item) {
+ return item.label != "foo3";
+ }), "autocomplete results do not contain foo3");
+ ok(newItems.every(function (item) {
+ return item.label != "foo3Obj";
+ }), "autocomplete results do not contain foo3Obj");
+
+ // Test if 'foo1Obj.' gives 'prop1' and 'prop2'
+ input.value = "foo1Obj.";
+ input.setSelectionRange(8, 8);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(!newItems.every(function (item) {
+ return item.label != "prop1";
+ }), "autocomplete results do contain prop1");
+ ok(!newItems.every(function (item) {
+ return item.label != "prop2";
+ }), "autocomplete results do contain prop2");
+
+ // Test if 'foo1Obj.prop2.' gives 'prop21'
+ input.value = "foo1Obj.prop2.";
+ input.setSelectionRange(14, 14);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(!newItems.every(function (item) {
+ return item.label != "prop21";
+ }), "autocomplete results do contain prop21");
+
+ info("Opening Debugger");
+ let dbg = yield openDebugger();
+
+ info("Waiting for pause");
+ yield pauseDebugger(dbg);
+
+ info("Opening Console again");
+ yield openConsole();
+
+ // From this point on the
+ // Test if 'f' gives 'foo3' and 'foo1' but not 'foo2'
+ input.value = "f";
+ input.setSelectionRange(1, 1);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(newItems.length > 0, "'f' gave a list of suggestions");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo3";
+ }), "autocomplete results do contain foo3");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo3Obj";
+ }), "autocomplete results do contain foo3Obj");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo1";
+ }), "autocomplete results do contain foo1");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo1Obj";
+ }), "autocomplete results do contain foo1Obj");
+ ok(newItems.every(function (item) {
+ return item.label != "foo2";
+ }), "autocomplete results do not contain foo2");
+ ok(newItems.every(function (item) {
+ return item.label != "foo2Obj";
+ }), "autocomplete results do not contain foo2Obj");
+
+ yield openDebugger();
+
+ gStackframes.selectFrame(1);
+
+ info("openConsole");
+ yield openConsole();
+
+ // Test if 'f' gives 'foo2' and 'foo1' but not 'foo3'
+ input.value = "f";
+ input.setSelectionRange(1, 1);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(newItems.length > 0, "'f' gave a list of suggestions");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo2";
+ }), "autocomplete results do contain foo2");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo2Obj";
+ }), "autocomplete results do contain foo2Obj");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo1";
+ }), "autocomplete results do contain foo1");
+ ok(!newItems.every(function (item) {
+ return item.label != "foo1Obj";
+ }), "autocomplete results do contain foo1Obj");
+ ok(newItems.every(function (item) {
+ return item.label != "foo3";
+ }), "autocomplete results do not contain foo3");
+ ok(newItems.every(function (item) {
+ return item.label != "foo3Obj";
+ }), "autocomplete results do not contain foo3Obj");
+
+ // Test if 'foo2Obj.' gives 'prop1'
+ input.value = "foo2Obj.";
+ input.setSelectionRange(8, 8);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(!newItems.every(function (item) {
+ return item.label != "prop1";
+ }), "autocomplete results do contain prop1");
+
+ // Test if 'foo2Obj.prop1.' gives 'prop11'
+ input.value = "foo2Obj.prop1.";
+ input.setSelectionRange(14, 14);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(!newItems.every(function (item) {
+ return item.label != "prop11";
+ }), "autocomplete results do contain prop11");
+
+ // Test if 'foo2Obj.prop1.prop11.' gives suggestions for a string
+ // i.e. 'length'
+ input.value = "foo2Obj.prop1.prop11.";
+ input.setSelectionRange(21, 21);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ ok(!newItems.every(function (item) {
+ return item.label != "length";
+ }), "autocomplete results do contain length");
+
+ // Test if 'foo1Obj[0].' throws no errors.
+ input.value = "foo2Obj[0].";
+ input.setSelectionRange(11, 11);
+ yield new Promise(resolve => {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve);
+ });
+
+ newItems = popup.getItems();
+ is(newItems.length, 0, "no items for foo2Obj[0]");
+}
+
+function pauseDebugger(aResult) {
+ let debuggerWin = aResult.panelWin;
+ let debuggerController = debuggerWin.DebuggerController;
+ let thread = debuggerController.activeThread;
+ gStackframes = debuggerController.StackFrames;
+ return new Promise(resolve => {
+ thread.addOneTimeListener("framesadded", resolve);
+
+ info("firstCall()");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.firstCall();
+ });
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js b/devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js
new file mode 100644
index 000000000..afa3dd55d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js
@@ -0,0 +1,27 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the autocomplete popup closes on switching tabs. See bug 900448.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>bug 900448 - autocomplete " +
+ "popup closes on tab switch";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ let popup = hud.jsterm.autocompletePopup;
+ let popupShown = once(popup, "popup-opened");
+
+ hud.jsterm.setInputValue("sc");
+ EventUtils.synthesizeKey("r", {});
+
+ yield popupShown;
+
+ yield loadTab("data:text/html;charset=utf-8,<p>testing autocomplete closes");
+
+ ok(!popup.isOpen, "Popup closes on tab switch");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
new file mode 100644
index 000000000..ff4157a3b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -0,0 +1,110 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// The test loads a web page with mixed active and display content
+// on it while the "block mixed content" settings are _on_.
+// It then checks that the blocked mixed content warning messages
+// are logged to the console and have the correct "Learn More"
+// url appended to them. After the first test finishes, it invokes
+// a second test that overrides the mixed content blocker settings
+// by clicking on the doorhanger shield and validates that the
+// appropriate messages are logged to console.
+// Bug 875456 - Log mixed content messages from the Mixed Content
+// Blocker to the Security Pane in the Web Console
+
+"use strict";
+
+const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test-mixedcontent-securityerrors.html";
+const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
+ "Mixed_content" + DOCS_GA_PARAMS;
+
+add_task(function* () {
+ yield pushPrefEnv();
+
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Logged blocking mixed active content",
+ text: "Blocked loading mixed active content \u201chttp://example.com/\u201d",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_ERROR,
+ objects: true,
+ },
+ {
+ name: "Logged blocking mixed passive content - image",
+ text: "Blocked loading mixed active content \u201chttp://example.com/\u201d",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_ERROR,
+ objects: true,
+ },
+ ],
+ });
+
+ yield testClickOpenNewTab(hud, results[0]);
+
+ let results2 = yield mixedContentOverrideTest2(hud, browser);
+
+ yield testClickOpenNewTab(hud, results2[0]);
+});
+
+function pushPrefEnv() {
+ let deferred = promise.defer();
+ let options = {
+ "set": [
+ ["security.mixed_content.block_active_content", true],
+ ["security.mixed_content.block_display_content", true],
+ ["security.mixed_content.use_hsts", false],
+ ["security.mixed_content.send_hsts_priming", false],
+ ]
+ };
+ SpecialPowers.pushPrefEnv(options, deferred.resolve);
+ return deferred.promise;
+}
+
+function mixedContentOverrideTest2(hud, browser) {
+ let deferred = promise.defer();
+ let {gIdentityHandler} = browser.ownerGlobal;
+ ok(gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+ "Mixed Active Content state appeared on identity box");
+ gIdentityHandler.disableMixedContentProtection();
+
+ waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Logged blocking mixed active content",
+ text: "Loading mixed (insecure) active content " +
+ "\u201chttp://example.com/\u201d on a secure page",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ {
+ name: "Logged blocking mixed passive content - image",
+ text: "Loading mixed (insecure) display content" +
+ " \u201chttp://example.com/tests/image/test/mochitest/blue.png\u201d" +
+ " on a secure page",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ ],
+ }).then(msgs => deferred.resolve(msgs), e => console.error(e));
+
+ return deferred.promise;
+}
+
+function testClickOpenNewTab(hud, match) {
+ let warningNode = match.clickableElements[0];
+ ok(warningNode, "link element");
+ ok(warningNode.classList.contains("learn-more-link"), "link class name");
+ return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js b/devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js
new file mode 100644
index 000000000..ee141a72f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab("data:text/html;charset=utf8,<title>Test for " +
+ "Bug 1006027");
+
+ const hud = yield openConsole(tab);
+
+ hud.jsterm.execute("console.log('bug1006027')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log",
+ text: "bug1006027",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ info("hud.outputNode.textContent:\n" + hud.outputNode.textContent);
+ let timestampNodes = hud.outputNode.querySelectorAll("span.timestamp");
+ let aTimestampMilliseconds = Array.prototype.map.call(timestampNodes,
+ function (value) {
+ // We are parsing timestamps as local time, relative to the begin of
+ // the epoch.
+ // This is not the correct value of the timestamp, but good enough for
+ // comparison.
+ return Date.parse("T" + String.trim(value.textContent));
+ });
+
+ let minTimestamp = Math.min.apply(null, aTimestampMilliseconds);
+ let maxTimestamp = Math.max.apply(null, aTimestampMilliseconds);
+ ok(Math.abs(maxTimestamp - minTimestamp) < 2000,
+ "console.log message timestamp spread < 2000ms confirmed");
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js b/devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js
new file mode 100644
index 000000000..ace13f8d1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_1010953_cspro.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* We are loading:
+a script that is allowed by the CSP header but not by the CSPRO header
+an image which is allowed by the CSPRO header but not by the CSP header.
+
+So we expect a warning (image has been blocked) and a report
+ (script should not load and was reported)
+
+The expected console messages in the constants CSP_VIOLATION_MSG and
+CSP_REPORT_MSG are confirmed to be found in the console messages.
+*/
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Web Console CSP report only " +
+ "test (bug 1010953)";
+const TEST_VIOLATION = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test_bug_1010953_cspro.html";
+const CSP_VIOLATION_MSG = "Content Security Policy: The page\u2019s settings " +
+ "blocked the loading of a resource at " +
+ "http://some.example.com/test.png " +
+ "(\u201cimg-src http://example.com\u201d).";
+const CSP_REPORT_MSG = "Content Security Policy: The page\u2019s settings " +
+ "observed the loading of a resource at " +
+ "http://some.example.com/test_bug_1010953_cspro.js " +
+ "(\u201cscript-src http://example.com\u201d). A CSP report is " +
+ "being sent.";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(browser, TEST_VIOLATION);
+ yield loaded;
+
+ yield waitForSuccess({
+ name: "Confirmed that CSP and CSP-Report-Only log different messages to " +
+ "the console.",
+ validator: function () {
+ console.log(hud.outputNode.textContent);
+ let success = false;
+ success = hud.outputNode.textContent.indexOf(CSP_VIOLATION_MSG) > -1 &&
+ hud.outputNode.textContent.indexOf(CSP_REPORT_MSG) > -1;
+ return success;
+ }
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js b/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js
new file mode 100644
index 000000000..9b220b4a2
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js
@@ -0,0 +1,60 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that clicking on a function displays its source in the debugger.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug_1050691_click_function_to_source.html";
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ // Open the Debugger panel.
+ let debuggerPanel = yield openDebugger();
+ // And right after come back to the Console panel.
+ yield openConsole();
+ yield testWithDebuggerOpen(hud, debuggerPanel);
+});
+
+function* testWithDebuggerOpen(hud, debuggerPanel) {
+ let clickable = yield printFunction(hud);
+ let panelWin = debuggerPanel.panelWin;
+ let onEditorLocationSet = panelWin.once(panelWin.EVENTS.EDITOR_LOCATION_SET);
+ synthesizeClick(clickable, hud);
+ yield onEditorLocationSet;
+ ok(isDebuggerCaretPos(debuggerPanel, 7),
+ "Clicking on a function should go to its source in the debugger view");
+}
+
+function synthesizeClick(clickable, hud) {
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+}
+
+var printFunction = Task.async(function* (hud) {
+ hud.jsterm.clearOutput();
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.foo();
+ });
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+ let msg = [...result.matched][0];
+ let clickable = msg.querySelector("a");
+ ok(clickable, "clickable item for object should exist");
+ return clickable;
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js b/devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js
new file mode 100644
index 000000000..26bac7f57
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_1247459_violation.js
@@ -0,0 +1,40 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console CSP messages for two META policies
+// are correctly displayed.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Web Console CSP violation test";
+const TEST_VIOLATION = "https://example.com/browser/devtools/client/" +
+ "webconsole/test/test_bug_1247459_violation.html";
+const CSP_VIOLATION_MSG = "Content Security Policy: The page\u2019s settings " +
+ "blocked the loading of a resource at " +
+ "http://some.example.com/test.png (\u201cimg-src " +
+ "https://example.com\u201d).";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(browser, TEST_VIOLATION);
+ yield loaded;
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "CSP policy URI warning displayed successfully",
+ text: CSP_VIOLATION_MSG,
+ repeats: 2
+ }
+ ]
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js b/devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js
new file mode 100644
index 000000000..fb0182d8b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_578437_page_reload.js
@@ -0,0 +1,41 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the console object still exists after a page reload.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+var browser;
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then((tab) => {
+ browser = tab.browser;
+
+ browser.addEventListener("DOMContentLoaded", testPageReload, false);
+ content.location.reload();
+ });
+ });
+ browser.addEventListener("DOMContentLoaded", onLoad, false);
+}
+
+function testPageReload() {
+ browser.removeEventListener("DOMContentLoaded", testPageReload, false);
+
+ let console = browser.contentWindow.wrappedJSObject.console;
+
+ is(typeof console, "object", "window.console is an object, after page reload");
+ is(typeof console.log, "function", "console.log is a function");
+ is(typeof console.info, "function", "console.info is a function");
+ is(typeof console.warn, "function", "console.warn is a function");
+ is(typeof console.error, "function", "console.error is a function");
+ is(typeof console.exception, "function", "console.exception is a function");
+
+ browser = null;
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js b/devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js
new file mode 100644
index 000000000..551dbd361
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_579412_input_focus.js
@@ -0,0 +1,20 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the input field is focused when the console is opened.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ let inputNode = hud.jsterm.inputNode;
+ ok(inputNode.getAttribute("focused"), "input node is focused");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js b/devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js
new file mode 100644
index 000000000..4c5fbf9c8
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_580001_closing_after_completion.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests to ensure that errors don't appear when the console is closed while a
+// completion is being performed.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ yield testClosingAfterCompletion(hud, browser);
+});
+
+function testClosingAfterCompletion(hud, browser) {
+ let deferred = promise.defer();
+
+ let errorWhileClosing = false;
+ function errorListener() {
+ errorWhileClosing = true;
+ }
+
+ browser.addEventListener("error", errorListener, false);
+
+ // Focus the jsterm and perform the keycombo to close the WebConsole.
+ hud.jsterm.focus();
+
+ gDevTools.once("toolbox-destroyed", function () {
+ browser.removeEventListener("error", errorListener, false);
+ is(errorWhileClosing, false, "no error while closing the WebConsole");
+ deferred.resolve();
+ });
+
+ if (Services.appinfo.OS == "Darwin") {
+ EventUtils.synthesizeKey("i", { accelKey: true, altKey: true });
+ } else {
+ EventUtils.synthesizeKey("i", { accelKey: true, shiftKey: true });
+ }
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js b/devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js
new file mode 100644
index 000000000..af00bf913
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_580030_errors_after_page_reload.js
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that errors still show up in the Web Console after a page reload.
+// See bug 580030: the error handler fails silently after page reload.
+// https://bugzilla.mozilla.org/show_bug.cgi?id=580030
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-error.html";
+
+function test() {
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+ info("console opened");
+
+ executeSoon(() => {
+ hud.jsterm.clearOutput();
+ info("wait for reload");
+ content.location.reload();
+ });
+
+ yield hud.target.once("navigate");
+ info("target navigated");
+
+ let button = content.document.querySelector("button");
+ ok(button, "button found");
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ EventUtils.sendMouseEvent({type: "click"}, button, content);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "fooBazBaz is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js b/devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js
new file mode 100644
index 000000000..6cd03164b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_580454_timestamp_l10n.js
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that appropriately-localized timestamps are printed.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ const TEST_TIMESTAMP = 12345678;
+ let date = new Date(TEST_TIMESTAMP);
+ let localizedString = WCUL10n.timestampString(TEST_TIMESTAMP);
+ isnot(localizedString.indexOf(date.getHours()), -1, "the localized " +
+ "timestamp contains the hours");
+ isnot(localizedString.indexOf(date.getMinutes()), -1, "the localized " +
+ "timestamp contains the minutes");
+ isnot(localizedString.indexOf(date.getSeconds()), -1, "the localized " +
+ "timestamp contains the seconds");
+ isnot(localizedString.indexOf(date.getMilliseconds()), -1, "the localized " +
+ "timestamp contains the milliseconds");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js b/devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js
new file mode 100644
index 000000000..5e7b141eb
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_582201_duplicate_errors.js
@@ -0,0 +1,49 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that exceptions thrown by content don't show up twice in the Web
+// Console.
+
+"use strict";
+
+const INIT_URI = "data:text/html;charset=utf8,hello world";
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-duplicate-error.html";
+
+add_task(function* () {
+ yield loadTab(INIT_URI);
+
+ let hud = yield openConsole();
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "fooDuplicateError1",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "test-duplicate-error.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let text = hud.outputNode.textContent;
+ let error1pos = text.indexOf("fooDuplicateError1");
+ ok(error1pos > -1, "found fooDuplicateError1");
+ if (error1pos > -1) {
+ ok(text.indexOf("fooDuplicateError1", error1pos + 1) == -1,
+ "no duplicate for fooDuplicateError1");
+ }
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js b/devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js
new file mode 100644
index 000000000..7dd271388
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/browser/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ testCompletion(hud);
+});
+
+function testCompletion(hud) {
+ let jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+
+ jsterm.setInputValue("");
+ EventUtils.synthesizeKey("VK_TAB", {});
+ is(jsterm.completeNode.value, "<- no result", "<- no result - matched");
+ is(input.value, "", "inputnode is empty - matched");
+ is(input.getAttribute("focused"), "true", "input is still focused");
+
+ // Any thing which is not in property autocompleter
+ jsterm.setInputValue("window.Bug583816");
+ EventUtils.synthesizeKey("VK_TAB", {});
+ is(jsterm.completeNode.value, " <- no result",
+ "completenode content - matched");
+ is(input.value, "window.Bug583816", "inputnode content - matched");
+ is(input.getAttribute("focused"), "true", "input is still focused");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js b/devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js
new file mode 100644
index 000000000..974557ec0
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_585237_line_limit.js
@@ -0,0 +1,89 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console limits the number of lines displayed according to
+// the user's preferences.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,test for bug 585237";
+
+var outputNode;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ outputNode = hud.outputNode;
+
+ hud.jsterm.clearOutput();
+
+ let prefBranch = Services.prefs.getBranch("devtools.hud.loglimit.");
+ prefBranch.setIntPref("console", 20);
+
+ for (let i = 0; i < 30; i++) {
+ yield ContentTask.spawn(gBrowser.selectedBrowser, i, function (i) {
+ // must change message to prevent repeats
+ content.console.log("foo #" + i);
+ });
+ }
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foo #29",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ is(countMessageNodes(), 20, "there are 20 message nodes in the output " +
+ "when the log limit is set to 20");
+
+ yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
+ content.console.log("bar bug585237");
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bar bug585237",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ is(countMessageNodes(), 20, "there are still 20 message nodes in the " +
+ "output when adding one more");
+
+ prefBranch.setIntPref("console", 30);
+ for (let i = 0; i < 20; i++) {
+ yield ContentTask.spawn(gBrowser.selectedBrowser, i, function (i) {
+ // must change message to prevent repeats
+ content.console.log("boo #" + i);
+ });
+ }
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "boo #19",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ is(countMessageNodes(), 30, "there are 30 message nodes in the output " +
+ "when the log limit is set to 30");
+
+ prefBranch.clearUserPref("console");
+
+ outputNode = null;
+});
+
+function countMessageNodes() {
+ return outputNode.querySelectorAll(".message").length;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js b/devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js
new file mode 100644
index 000000000..c38fd52c1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_585956_console_trace.js
@@ -0,0 +1,70 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-bug-585956-console-trace.html";
+
+add_task(function* () {
+ let {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello");
+ let hud = yield openConsole(tab);
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.trace output",
+ consoleTrace: {
+ file: "test-bug-585956-console-trace.html",
+ fn: "window.foobar585956c",
+ },
+ }],
+ });
+
+ let node = [...result.matched][0];
+ ok(node, "found trace log node");
+
+ let obj = node._messageObject;
+ ok(obj, "console.trace message object");
+
+ // The expected stack trace object.
+ let stacktrace = [
+ {
+ columnNumber: 3,
+ filename: TEST_URI,
+ functionName: "window.foobar585956c",
+ language: 2,
+ lineNumber: 9
+ },
+ {
+ columnNumber: 10,
+ filename: TEST_URI,
+ functionName: "foobar585956b",
+ language: 2,
+ lineNumber: 14
+ },
+ {
+ columnNumber: 10,
+ filename: TEST_URI,
+ functionName: "foobar585956a",
+ language: 2,
+ lineNumber: 18
+ },
+ {
+ columnNumber: 1,
+ filename: TEST_URI,
+ functionName: "",
+ language: 2,
+ lineNumber: 21
+ }
+ ];
+
+ ok(obj._stacktrace, "found stacktrace object");
+ is(obj._stacktrace.toSource(), stacktrace.toSource(),
+ "stacktrace is correct");
+ isnot(node.textContent.indexOf("bug-585956"), -1, "found file name");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
new file mode 100644
index 000000000..0021a8cc1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js
@@ -0,0 +1,367 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete " +
+ "popup keyboard usage test";
+
+// We should turn off auto-multiline editing during these tests
+const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
+var HUD, popup, jsterm, inputNode, completeNode;
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, false);
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ yield consoleOpened(hud);
+ yield popupHideAfterTab();
+ yield testReturnKey();
+ yield dontShowArrayNumbers();
+ yield testReturnWithNoSelection();
+ yield popupHideAfterReturnWithNoSelection();
+ yield testCompletionInText();
+ yield popupHideAfterCompletionInText();
+
+ HUD = popup = jsterm = inputNode = completeNode = null;
+ Services.prefs.setBoolPref(PREF_AUTO_MULTILINE, true);
+});
+
+var consoleOpened = Task.async(function* (hud) {
+ let deferred = promise.defer();
+ HUD = hud;
+ info("web console opened");
+
+ jsterm = HUD.jsterm;
+
+ yield jsterm.execute("window.foobarBug585991={" +
+ "'item0': 'value0'," +
+ "'item1': 'value1'," +
+ "'item2': 'value2'," +
+ "'item3': 'value3'" +
+ "}");
+ yield jsterm.execute("window.testBug873250a = 'hello world';"
+ + "window.testBug873250b = 'hello world 2';");
+ popup = jsterm.autocompletePopup;
+ completeNode = jsterm.completeNode;
+ inputNode = jsterm.inputNode;
+
+ ok(!popup.isOpen, "popup is not open");
+
+ popup.once("popup-opened", () => {
+ ok(popup.isOpen, "popup is open");
+
+ // 4 values, and the following properties:
+ // __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__
+ // __proto__ hasOwnProperty isPrototypeOf propertyIsEnumerable
+ // toLocaleString toString toSource unwatch valueOf watch constructor.
+ is(popup.itemCount, 19, "popup.itemCount is correct");
+
+ let sameItems = popup.getItems().reverse().map(function (e) {
+ return e.label;
+ });
+
+ ok(sameItems.every(function (prop, index) {
+ return [
+ "__defineGetter__",
+ "__defineSetter__",
+ "__lookupGetter__",
+ "__lookupSetter__",
+ "__proto__",
+ "constructor",
+ "hasOwnProperty",
+ "isPrototypeOf",
+ "item0",
+ "item1",
+ "item2",
+ "item3",
+ "propertyIsEnumerable",
+ "toLocaleString",
+ "toSource",
+ "toString",
+ "unwatch",
+ "valueOf",
+ "watch",
+ ][index] === prop;
+ }), "getItems returns the items we expect");
+
+ is(popup.selectedIndex, 18,
+ "Index of the first item from bottom is selected.");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
+
+ is(popup.selectedIndex, 0, "index 0 is selected");
+ is(popup.selectedItem.label, "watch", "watch is selected");
+ is(completeNode.value, prefix + "watch",
+ "completeNode.value holds watch");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(popup.selectedIndex, 1, "index 1 is selected");
+ is(popup.selectedItem.label, "valueOf", "valueOf is selected");
+ is(completeNode.value, prefix + "valueOf",
+ "completeNode.value holds valueOf");
+
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(popup.selectedIndex, 0, "index 0 is selected");
+ is(popup.selectedItem.label, "watch", "watch is selected");
+ is(completeNode.value, prefix + "watch",
+ "completeNode.value holds watch");
+
+ let currentSelectionIndex = popup.selectedIndex;
+
+ EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
+
+ ok(popup.selectedIndex > currentSelectionIndex,
+ "Index is greater after PGDN");
+
+ currentSelectionIndex = popup.selectedIndex;
+ EventUtils.synthesizeKey("VK_PAGE_UP", {});
+
+ ok(popup.selectedIndex < currentSelectionIndex,
+ "Index is less after Page UP");
+
+ EventUtils.synthesizeKey("VK_END", {});
+ is(popup.selectedIndex, 18, "index is last after End");
+
+ EventUtils.synthesizeKey("VK_HOME", {});
+ is(popup.selectedIndex, 0, "index is first after Home");
+
+ info("press Tab and wait for popup to hide");
+ popup.once("popup-closed", () => {
+ deferred.resolve();
+ });
+ EventUtils.synthesizeKey("VK_TAB", {});
+ });
+
+ jsterm.setInputValue("window.foobarBug585991");
+ EventUtils.synthesizeKey(".", {});
+
+ return deferred.promise;
+});
+
+function popupHideAfterTab() {
+ let deferred = promise.defer();
+
+ // At this point the completion suggestion should be accepted.
+ ok(!popup.isOpen, "popup is not open");
+
+ is(jsterm.getInputValue(), "window.foobarBug585991.watch",
+ "completion was successful after VK_TAB");
+
+ ok(!completeNode.value, "completeNode is empty");
+
+ popup.once("popup-opened", function onShown() {
+ ok(popup.isOpen, "popup is open");
+
+ is(popup.itemCount, 19, "popup.itemCount is correct");
+
+ is(popup.selectedIndex, 18, "First index from bottom is selected");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
+
+ is(popup.selectedIndex, 0, "index 0 is selected");
+ is(popup.selectedItem.label, "watch", "watch is selected");
+ is(completeNode.value, prefix + "watch",
+ "completeNode.value holds watch");
+
+ popup.once("popup-closed", function onHidden() {
+ ok(!popup.isOpen, "popup is not open after VK_ESCAPE");
+
+ is(jsterm.getInputValue(), "window.foobarBug585991.",
+ "completion was cancelled");
+
+ ok(!completeNode.value, "completeNode is empty");
+
+ deferred.resolve();
+ }, false);
+
+ info("press Escape to close the popup");
+ executeSoon(function () {
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ });
+ }, false);
+
+ info("wait for completion: window.foobarBug585991.");
+ executeSoon(function () {
+ jsterm.setInputValue("window.foobarBug585991");
+ EventUtils.synthesizeKey(".", {});
+ });
+
+ return deferred.promise;
+}
+
+function testReturnKey() {
+ let deferred = promise.defer();
+
+ popup.once("popup-opened", function onShown() {
+ ok(popup.isOpen, "popup is open");
+
+ is(popup.itemCount, 19, "popup.itemCount is correct");
+
+ is(popup.selectedIndex, 18, "First index from bottom is selected");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ let prefix = jsterm.getInputValue().replace(/[\S]/g, " ");
+
+ is(popup.selectedIndex, 0, "index 0 is selected");
+ is(popup.selectedItem.label, "watch", "watch is selected");
+ is(completeNode.value, prefix + "watch",
+ "completeNode.value holds watch");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(popup.selectedIndex, 1, "index 1 is selected");
+ is(popup.selectedItem.label, "valueOf", "valueOf is selected");
+ is(completeNode.value, prefix + "valueOf",
+ "completeNode.value holds valueOf");
+
+ popup.once("popup-closed", function onHidden() {
+ ok(!popup.isOpen, "popup is not open after VK_RETURN");
+
+ is(jsterm.getInputValue(), "window.foobarBug585991.valueOf",
+ "completion was successful after VK_RETURN");
+
+ ok(!completeNode.value, "completeNode is empty");
+
+ deferred.resolve();
+ }, false);
+
+ info("press Return to accept suggestion. wait for popup to hide");
+
+ executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
+ }, false);
+
+ info("wait for completion suggestions: window.foobarBug585991.");
+
+ executeSoon(function () {
+ jsterm.setInputValue("window.foobarBug58599");
+ EventUtils.synthesizeKey("1", {});
+ EventUtils.synthesizeKey(".", {});
+ });
+
+ return deferred.promise;
+}
+
+function* dontShowArrayNumbers() {
+ let deferred = promise.defer();
+
+ info("dontShowArrayNumbers");
+ yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.foobarBug585991 = ["Sherlock Holmes"];
+ });
+
+ jsterm = HUD.jsterm;
+ popup = jsterm.autocompletePopup;
+
+ popup.once("popup-opened", function onShown() {
+ let sameItems = popup.getItems().map(function (e) {
+ return e.label;
+ });
+ ok(!sameItems.some(function (prop) {
+ prop === "0";
+ }), "Completing on an array doesn't show numbers.");
+
+ popup.once("popup-closed", function popupHidden() {
+ deferred.resolve();
+ }, false);
+
+ info("wait for popup to hide");
+ executeSoon(() => EventUtils.synthesizeKey("VK_ESCAPE", {}));
+ }, false);
+
+ info("wait for popup to show");
+ executeSoon(() => {
+ jsterm.setInputValue("window.foobarBug585991");
+ EventUtils.synthesizeKey(".", {});
+ });
+
+ return deferred.promise;
+}
+
+function testReturnWithNoSelection() {
+ let deferred = promise.defer();
+
+ info("test pressing return with open popup, but no selection, see bug 873250");
+
+ popup.once("popup-opened", function onShown() {
+ ok(popup.isOpen, "popup is open");
+ is(popup.itemCount, 2, "popup.itemCount is correct");
+ isnot(popup.selectedIndex, -1, "popup.selectedIndex is correct");
+
+ info("press Return and wait for popup to hide");
+ popup.once("popup-closed", function popupHidden() {
+ deferred.resolve();
+ });
+ executeSoon(() => EventUtils.synthesizeKey("VK_RETURN", {}));
+ });
+
+ executeSoon(() => {
+ info("wait for popup to show");
+ jsterm.setInputValue("window.testBu");
+ EventUtils.synthesizeKey("g", {});
+ });
+
+ return deferred.promise;
+}
+
+function popupHideAfterReturnWithNoSelection() {
+ ok(!popup.isOpen, "popup is not open after VK_RETURN");
+
+ is(jsterm.getInputValue(), "", "inputNode is empty after VK_RETURN");
+ is(completeNode.value, "", "completeNode is empty");
+ is(jsterm.history[jsterm.history.length - 1], "window.testBug",
+ "jsterm history is correct");
+
+ return promise.resolve();
+}
+
+function testCompletionInText() {
+ info("test that completion works inside text, see bug 812618");
+
+ let deferred = promise.defer();
+
+ popup.once("popup-opened", function onShown() {
+ ok(popup.isOpen, "popup is open");
+ is(popup.itemCount, 2, "popup.itemCount is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(popup.selectedIndex, 0, "popup.selectedIndex is correct");
+ ok(!completeNode.value, "completeNode.value is empty");
+
+ let items = popup.getItems().reverse().map(e => e.label);
+ let sameItems = items.every((prop, index) =>
+ ["testBug873250a", "testBug873250b"][index] === prop);
+ ok(sameItems, "getItems returns the items we expect");
+
+ info("press Tab and wait for popup to hide");
+ popup.once("popup-closed", function popupHidden() {
+ deferred.resolve();
+ });
+ EventUtils.synthesizeKey("VK_TAB", {});
+ });
+
+ jsterm.setInputValue("dump(window.testBu)");
+ inputNode.selectionStart = inputNode.selectionEnd = 18;
+ EventUtils.synthesizeKey("g", {});
+ return deferred.promise;
+}
+
+function popupHideAfterCompletionInText() {
+ // At this point the completion suggestion should be accepted.
+ ok(!popup.isOpen, "popup is not open");
+ is(jsterm.getInputValue(), "dump(window.testBug873250b)",
+ "completion was successful after VK_TAB");
+ is(inputNode.selectionStart, 26, "cursor location is correct");
+ is(inputNode.selectionStart, inputNode.selectionEnd,
+ "cursor location (confirmed)");
+ ok(!completeNode.value, "completeNode is empty");
+
+ return promise.resolve();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js
new file mode 100644
index 000000000..df1a42edf
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_585991_autocomplete_popup.js
@@ -0,0 +1,123 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>bug 585991 - autocomplete " +
+ "popup test";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ yield consoleOpened(hud);
+});
+
+function consoleOpened(HUD) {
+ let deferred = promise.defer();
+
+ let items = [
+ {label: "item0", value: "value0"},
+ {label: "item1", value: "value1"},
+ {label: "item2", value: "value2"},
+ ];
+
+ let popup = HUD.jsterm.autocompletePopup;
+ let input = HUD.jsterm.inputNode;
+
+ ok(!popup.isOpen, "popup is not open");
+ ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
+
+ popup.once("popup-opened", () => {
+ ok(popup.isOpen, "popup is open");
+
+ is(popup.itemCount, 0, "no items");
+ ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
+
+ popup.setItems(items);
+
+ is(popup.itemCount, items.length, "items added");
+
+ let sameItems = popup.getItems();
+ is(sameItems.every(function (item, index) {
+ return item === items[index];
+ }), true, "getItems returns back the same items");
+
+ is(popup.selectedIndex, 2, "Index of the first item from bottom is selected.");
+ is(popup.selectedItem, items[2], "First item from bottom is selected");
+ checkActiveDescendant(popup, input);
+
+ popup.selectedIndex = 1;
+
+ is(popup.selectedIndex, 1, "index 1 is selected");
+ is(popup.selectedItem, items[1], "item1 is selected");
+ checkActiveDescendant(popup, input);
+
+ popup.selectedItem = items[2];
+
+ is(popup.selectedIndex, 2, "index 2 is selected");
+ is(popup.selectedItem, items[2], "item2 is selected");
+ checkActiveDescendant(popup, input);
+
+ is(popup.selectPreviousItem(), items[1], "selectPreviousItem() works");
+
+ is(popup.selectedIndex, 1, "index 1 is selected");
+ is(popup.selectedItem, items[1], "item1 is selected");
+ checkActiveDescendant(popup, input);
+
+ is(popup.selectNextItem(), items[2], "selectNextItem() works");
+
+ is(popup.selectedIndex, 2, "index 2 is selected");
+ is(popup.selectedItem, items[2], "item2 is selected");
+ checkActiveDescendant(popup, input);
+
+ ok(popup.selectNextItem(), "selectNextItem() works");
+
+ is(popup.selectedIndex, 0, "index 0 is selected");
+ is(popup.selectedItem, items[0], "item0 is selected");
+ checkActiveDescendant(popup, input);
+
+ items.push({label: "label3", value: "value3"});
+ popup.appendItem(items[3]);
+
+ is(popup.itemCount, items.length, "item3 appended");
+
+ popup.selectedIndex = 3;
+ is(popup.selectedItem, items[3], "item3 is selected");
+ checkActiveDescendant(popup, input);
+
+ popup.removeItem(items[2]);
+
+ is(popup.selectedIndex, 2, "index2 is selected");
+ is(popup.selectedItem, items[3], "item3 is still selected");
+ checkActiveDescendant(popup, input);
+ is(popup.itemCount, items.length - 1, "item2 removed");
+
+ popup.clearItems();
+ is(popup.itemCount, 0, "items cleared");
+ ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
+
+ popup.once("popup-closed", () => {
+ deferred.resolve();
+ });
+ popup.hidePopup();
+ });
+
+ popup.openPopup(input);
+
+ return deferred.promise;
+}
+
+function checkActiveDescendant(popup, input) {
+ let activeElement = input.ownerDocument.activeElement;
+ let descendantId = activeElement.getAttribute("aria-activedescendant");
+ let popupItem = popup._tooltip.panel.querySelector("#" + descendantId);
+ let cloneItem = input.ownerDocument.querySelector("#" + descendantId);
+
+ ok(popupItem, "Active descendant is found in the popup list");
+ ok(cloneItem, "Active descendant is found in the list clone");
+ is(popupItem.innerHTML, cloneItem.innerHTML,
+ "Cloned item has the same HTML as the original element");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js b/devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js
new file mode 100644
index 000000000..cda31191e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_586388_select_all.js
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ yield testSelectionWhenMovingBetweenBoxes(hud);
+ performTestsAfterOutput(hud);
+});
+
+var testSelectionWhenMovingBetweenBoxes = Task.async(function* (hud) {
+ let jsterm = hud.jsterm;
+
+ // Fill the console with some output.
+ jsterm.clearOutput();
+ yield jsterm.execute("1 + 2");
+ yield jsterm.execute("3 + 4");
+ yield jsterm.execute("5 + 6");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "3",
+ category: CATEGORY_OUTPUT,
+ },
+ {
+ text: "7",
+ category: CATEGORY_OUTPUT,
+ },
+ {
+ text: "11",
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+});
+
+function performTestsAfterOutput(hud) {
+ let outputNode = hud.outputNode;
+
+ ok(outputNode.childNodes.length >= 3, "the output node has children after " +
+ "executing some JavaScript");
+
+ // Test that the global Firefox "Select All" functionality (e.g. Edit >
+ // Select All) works properly in the Web Console.
+ let commandController = hud.ui._commandController;
+ ok(commandController != null, "the window has a command controller object");
+
+ commandController.selectAll();
+
+ let selectedCount = hud.ui.output.getSelectedMessages().length;
+ is(selectedCount, outputNode.childNodes.length,
+ "all console messages are selected after performing a regular browser " +
+ "select-all operation");
+
+ hud.iframeWindow.getSelection().removeAllRanges();
+
+ // Test the context menu "Select All" (which has a different code path) works
+ // properly as well.
+ let contextMenuId = hud.ui.outputWrapper.getAttribute("context");
+ let contextMenu = hud.ui.document.getElementById(contextMenuId);
+ ok(contextMenu != null, "the output node has a context menu");
+
+ let selectAllItem = contextMenu.querySelector("*[command='cmd_selectAll']");
+ ok(selectAllItem != null,
+ "the context menu on the output node has a \"Select All\" item");
+
+ outputNode.focus();
+
+ selectAllItem.doCommand();
+
+ selectedCount = hud.ui.output.getSelectedMessages().length;
+ is(selectedCount, outputNode.childNodes.length,
+ "all console messages are selected after performing a select-all " +
+ "operation from the context menu");
+
+ hud.iframeWindow.getSelection().removeAllRanges();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js b/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js
new file mode 100644
index 000000000..208baf3d6
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js
@@ -0,0 +1,106 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* globals goUpdateCommand goDoCommand */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+var HUD, outputNode;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ yield consoleOpened(hud);
+ yield testContextMenuCopy();
+
+ HUD = outputNode = null;
+});
+
+function consoleOpened(hud) {
+ HUD = hud;
+
+ let deferred = promise.defer();
+
+ // See bugs 574036, 586386 and 587617.
+ outputNode = HUD.outputNode;
+
+ HUD.jsterm.clearOutput();
+
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled");
+
+ ContentTask.spawn(gBrowser.selectedBrowser, null,
+ "() => content.console.log('Hello world! bug587617')");
+
+ waitForMessages({
+ webconsole: HUD,
+ messages: [{
+ text: "bug587617",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(([result]) => {
+ let msg = [...result.matched][0];
+ HUD.ui.output.selectMessage(msg);
+
+ outputNode.focus();
+
+ goUpdateCommand("cmd_copy");
+ controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+
+ // Remove new lines and whitespace since getSelection() includes
+ // a new line between message and line number, but the clipboard doesn't
+ // @see bug 1119503
+ let selection = (HUD.iframeWindow.getSelection() + "")
+ .replace(/\r?\n|\r| /g, "");
+ isnot(selection.indexOf("bug587617"), -1,
+ "selection text includes 'bug587617'");
+
+ waitForClipboard((str) => {
+ // Strip out spaces for comparison ease
+ return selection.trim() == str.trim().replace(/ /g, "");
+ }, () => {
+ goDoCommand("cmd_copy");
+ }, deferred.resolve, deferred.resolve);
+ });
+ return deferred.promise;
+}
+
+// Test that the context menu "Copy" (which has a different code path) works
+// properly as well.
+function testContextMenuCopy() {
+ let deferred = promise.defer();
+
+ let contextMenuId = HUD.ui.outputWrapper.getAttribute("context");
+ let contextMenu = HUD.ui.document.getElementById(contextMenuId);
+ ok(contextMenu, "the output node has a context menu");
+
+ let copyItem = contextMenu.querySelector("*[command='cmd_copy']");
+ ok(copyItem, "the context menu on the output node has a \"Copy\" item");
+
+ // Remove new lines and whitespace since getSelection() includes
+ // a new line between message and line number, but the clipboard doesn't
+ // @see bug 1119503
+ let selection = (HUD.iframeWindow.getSelection() + "")
+ .replace(/\r?\n|\r| /g, "");
+
+ copyItem.doCommand();
+
+ waitForClipboard((str) => {
+ // Strip out spaces for comparison ease
+ return selection.trim() == str.trim().replace(/ /g, "");
+ }, () => {
+ goDoCommand("cmd_copy");
+ }, deferred.resolve, deferred.resolve);
+ HUD = outputNode = null;
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js b/devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js
new file mode 100644
index 000000000..ff926fc13
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_588342_document_focus.js
@@ -0,0 +1,36 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 588342";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ yield checkConsoleFocus(hud);
+
+ let isFocused = yield ContentTask.spawn(browser, { }, function* () {
+ var fm = Components.classes["@mozilla.org/focus-manager;1"].
+ getService(Components.interfaces.nsIFocusManager);
+ return fm.focusedWindow == content;
+ });
+
+ ok(isFocused, "content document has focus");
+});
+
+function* checkConsoleFocus(hud) {
+ let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+
+ yield new Promise(resolve => {
+ waitForFocus(resolve);
+ });
+
+ is(hud.jsterm.inputNode.getAttribute("focused"), "true",
+ "jsterm input is focused on web console open");
+ is(fm.focusedWindow, hud.iframeWindow, "hud window is focused");
+ yield closeConsole(null);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js b/devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js
new file mode 100644
index 000000000..94a0ad77e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_588730_text_node_insertion.js
@@ -0,0 +1,53 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that adding text to one of the output labels doesn't cause errors.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield testTextNodeInsertion(hud);
+});
+
+// Test for bug 588730: Adding a text node to an existing label element causes
+// warnings
+function testTextNodeInsertion(hud) {
+ let deferred = promise.defer();
+ let outputNode = hud.outputNode;
+
+ let label = document.createElementNS(
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "label");
+ outputNode.appendChild(label);
+
+ let error = false;
+ let listener = {
+ observe: function (aMessage) {
+ let messageText = aMessage.message;
+ if (messageText.indexOf("JavaScript Warning") !== -1) {
+ error = true;
+ }
+ }
+ };
+
+ Services.console.registerListener(listener);
+
+ // This shouldn't fail.
+ label.appendChild(document.createTextNode("foo"));
+
+ executeSoon(function () {
+ Services.console.unregisterListener(listener);
+ ok(!error, "no error when adding text nodes as children of labels");
+
+ return deferred.resolve();
+ });
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js b/devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js
new file mode 100644
index 000000000..c590495c4
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_588967_input_expansion.js
@@ -0,0 +1,44 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ testInputExpansion(hud);
+});
+
+function testInputExpansion(hud) {
+ let input = hud.jsterm.inputNode;
+
+ input.focus();
+
+ is(input.getAttribute("multiline"), "true", "multiline is enabled");
+
+ let ordinaryHeight = input.clientHeight;
+
+ // Tests if the inputNode expands.
+ input.value = "hello\nworld\n";
+ let length = input.value.length;
+ input.selectionEnd = length;
+ input.selectionStart = length;
+ // Performs an "d". This will trigger/test for the input event that should
+ // change the height of the inputNode.
+ EventUtils.synthesizeKey("d", {});
+ ok(input.clientHeight > ordinaryHeight, "the input expanded");
+
+ // Test if the inputNode shrinks again.
+ input.value = "";
+ EventUtils.synthesizeKey("d", {});
+ is(input.clientHeight, ordinaryHeight, "the input's height is normal again");
+
+ input = length = null;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js b/devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js
new file mode 100644
index 000000000..509c875f8
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_589162_css_filter.js
@@ -0,0 +1,39 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<div style='font-size:3em;" +
+ "foobarCssParser:baz'>test CSS parser filter</div>";
+
+/**
+ * Unit test for bug 589162:
+ * CSS filtering on the console does not work
+ */
+add_task(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+
+ // CSS warnings are disabled by default.
+ hud.setFilterState("cssparser", true);
+ hud.jsterm.clearOutput();
+
+ BrowserReload();
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarCssParser",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ }],
+ });
+
+ hud.setFilterState("cssparser", false);
+
+ let msg = "the unknown CSS property warning is not displayed, " +
+ "after filtering";
+ testLogEntry(hud.outputNode, "foobarCssParser", msg, true, true);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js b/devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js
new file mode 100644
index 000000000..adbf13086
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_592442_closing_brackets.js
@@ -0,0 +1,29 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that, when the user types an extraneous closing bracket, no error
+// appears.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,test for bug 592442";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+ let jsterm = hud.jsterm;
+
+ jsterm.setInputValue("document.getElementById)");
+
+ let error = false;
+ try {
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY);
+ } catch (ex) {
+ error = true;
+ }
+
+ ok(!error, "no error was thrown when an extraneous bracket was inserted");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js b/devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js
new file mode 100644
index 000000000..9f429a3d1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_593003_iframe_wrong_hud.js
@@ -0,0 +1,68 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-593003-iframe-wrong-hud.html";
+
+const TEST_IFRAME_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-bug-593003-iframe-wrong-" +
+ "hud-iframe.html";
+
+const TEST_DUMMY_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-console.html";
+
+add_task(function* () {
+
+ let tab1 = (yield loadTab(TEST_URI)).tab;
+ yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.console.log("FOO");
+ });
+ yield openConsole();
+
+ let tab2 = (yield loadTab(TEST_DUMMY_URI)).tab;
+ yield openConsole(gBrowser.selectedTab);
+
+ info("Reloading tab 1");
+ yield reloadTab(tab1);
+
+ info("Checking for messages");
+ yield checkMessages(tab1, tab2);
+
+ info("Cleaning up");
+ yield closeConsole(tab1);
+ yield closeConsole(tab2);
+});
+
+function* reloadTab(tab) {
+ let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ tab.linkedBrowser.reload();
+ yield loaded;
+}
+
+function* checkMessages(tab1, tab2) {
+ let hud1 = yield openConsole(tab1);
+ let outputNode1 = hud1.outputNode;
+
+ info("Waiting for messages");
+ yield waitForMessages({
+ webconsole: hud1,
+ messages: [{
+ text: TEST_IFRAME_URI,
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }]
+ });
+
+ let hud2 = yield openConsole(tab2);
+ let outputNode2 = hud2.outputNode;
+
+ isnot(outputNode1, outputNode2,
+ "the two HUD outputNodes must be different");
+
+ let msg = "Didn't find the iframe network request in tab2";
+ testLogEntry(outputNode2, TEST_IFRAME_URI, msg, true, true);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js b/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js
new file mode 100644
index 000000000..514f875c0
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_594497_history_arrow_keys.js
@@ -0,0 +1,155 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var jsterm, inputNode, values;
+
+var TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 594497 and bug 619598";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ setup(hud);
+ performTests();
+
+ jsterm = inputNode = values = null;
+});
+
+function setup(HUD) {
+ jsterm = HUD.jsterm;
+ inputNode = jsterm.inputNode;
+
+ jsterm.focus();
+
+ ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
+
+ values = ["document", "window", "document.body"];
+ values.push(values.join(";\n"), "document.location");
+
+ // Execute each of the values;
+ for (let i = 0; i < values.length; i++) {
+ jsterm.setInputValue(values[i]);
+ jsterm.execute();
+ }
+}
+
+function performTests() {
+ EventUtils.synthesizeKey("VK_UP", {});
+
+
+ is(jsterm.getInputValue(), values[4],
+ "VK_UP: jsterm.getInputValue() #4 is correct");
+
+ ok(inputNode.selectionStart == values[4].length &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.getInputValue(), values[3],
+ "VK_UP: jsterm.getInputValue() #3 is correct");
+
+ ok(inputNode.selectionStart == values[3].length &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ inputNode.setSelectionRange(values[3].length - 2, values[3].length - 2);
+
+ EventUtils.synthesizeKey("VK_UP", {});
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.getInputValue(), values[3],
+ "VK_UP two times: jsterm.getInputValue() #3 is correct");
+
+ ok(inputNode.selectionStart == jsterm.getInputValue().indexOf("\n") &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.getInputValue(), values[3],
+ "VK_UP again: jsterm.getInputValue() #3 is correct");
+
+ ok(inputNode.selectionStart == 0 &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.getInputValue(), values[2],
+ "VK_UP: jsterm.getInputValue() #2 is correct");
+
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.getInputValue(), values[1],
+ "VK_UP: jsterm.getInputValue() #1 is correct");
+
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.getInputValue(), values[0],
+ "VK_UP: jsterm.getInputValue() #0 is correct");
+
+ ok(inputNode.selectionStart == values[0].length &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(jsterm.getInputValue(), values[1],
+ "VK_DOWN: jsterm.getInputValue() #1 is correct");
+
+ ok(inputNode.selectionStart == values[1].length &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(jsterm.getInputValue(), values[2],
+ "VK_DOWN: jsterm.getInputValue() #2 is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(jsterm.getInputValue(), values[3],
+ "VK_DOWN: jsterm.getInputValue() #3 is correct");
+
+ ok(inputNode.selectionStart == values[3].length &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ inputNode.setSelectionRange(2, 2);
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(jsterm.getInputValue(), values[3],
+ "VK_DOWN two times: jsterm.getInputValue() #3 is correct");
+
+ ok(inputNode.selectionStart > jsterm.getInputValue().lastIndexOf("\n") &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(jsterm.getInputValue(), values[3],
+ "VK_DOWN again: jsterm.getInputValue() #3 is correct");
+
+ ok(inputNode.selectionStart == values[3].length &&
+ inputNode.selectionStart == inputNode.selectionEnd,
+ "caret location is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ is(jsterm.getInputValue(), values[4],
+ "VK_DOWN: jsterm.getInputValue() #4 is correct");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+
+ ok(!jsterm.getInputValue(),
+ "VK_DOWN: jsterm.getInputValue() is empty");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js b/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
new file mode 100644
index 000000000..d57d724ca
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_595223_file_uri.js
@@ -0,0 +1,64 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PREF = "devtools.webconsole.persistlog";
+const TEST_FILE = "test-network.html";
+const TEST_URI = "data:text/html;charset=utf8,<p>test file URI";
+
+var hud;
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ let jar = getJar(getRootDirectory(gTestPath));
+ let dir = jar ?
+ extractJarToTmp(jar) :
+ getChromeDir(getResolvedURI(gTestPath));
+
+ dir.append(TEST_FILE);
+ let uri = Services.io.newFileURI(dir);
+
+ let { browser } = yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri.spec);
+ yield loaded;
+
+ yield testMessages();
+
+ Services.prefs.clearUserPref(PREF);
+ hud = null;
+});
+
+function testMessages() {
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "running network console logging tests",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-network.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-image.png",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "testscript.js",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js b/devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js
new file mode 100644
index 000000000..1951cb366
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js
@@ -0,0 +1,100 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console doesn't leak when multiple tabs and windows are
+// opened and then closed.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 595350";
+
+var win1 = window, win2;
+var openTabs = [];
+var loadedTabCount = 0;
+
+function test() {
+ requestLongerTimeout(3);
+
+ // Add two tabs in the main window.
+ addTabs(win1);
+
+ // Open a new window.
+ win2 = OpenBrowserWindow();
+ win2.addEventListener("load", onWindowLoad, true);
+}
+
+function onWindowLoad(aEvent) {
+ win2.removeEventListener(aEvent.type, onWindowLoad, true);
+
+ // Add two tabs in the new window.
+ addTabs(win2);
+}
+
+function addTabs(aWindow) {
+ for (let i = 0; i < 2; i++) {
+ let tab = aWindow.gBrowser.addTab(TEST_URI);
+ openTabs.push(tab);
+
+ tab.linkedBrowser.addEventListener("load", function onLoad(aEvent) {
+ tab.linkedBrowser.removeEventListener(aEvent.type, onLoad, true);
+
+ loadedTabCount++;
+ info("tabs loaded: " + loadedTabCount);
+ if (loadedTabCount >= 4) {
+ executeSoon(openConsoles);
+ }
+ }, true);
+ }
+}
+
+function openConsoles() {
+ function open(i) {
+ let tab = openTabs[i];
+ openConsole(tab).then(function (hud) {
+ ok(hud, "HUD is open for tab " + i);
+ let window = hud.target.tab.linkedBrowser.contentWindow;
+ window.console.log("message for tab " + i);
+
+ if (i >= openTabs.length - 1) {
+ // Use executeSoon() to allow the promise to resolve.
+ executeSoon(closeConsoles);
+ }
+ else {
+ executeSoon(() => open(i + 1));
+ }
+ });
+ }
+
+ // open the Web Console for each of the four tabs and log a message.
+ open(0);
+}
+
+function closeConsoles() {
+ let consolesClosed = 0;
+
+ function onWebConsoleClose(aSubject, aTopic) {
+ if (aTopic == "web-console-destroyed") {
+ consolesClosed++;
+ info("consoles destroyed: " + consolesClosed);
+ if (consolesClosed == 4) {
+ // Use executeSoon() to allow all the observers to execute.
+ executeSoon(finishTest);
+ }
+ }
+ }
+
+ Services.obs.addObserver(onWebConsoleClose, "web-console-destroyed", false);
+
+ registerCleanupFunction(() => {
+ Services.obs.removeObserver(onWebConsoleClose, "web-console-destroyed");
+ });
+
+ win2.close();
+
+ win1.gBrowser.removeTab(openTabs[0]);
+ win1.gBrowser.removeTab(openTabs[1]);
+
+ openTabs = win1 = win2 = null;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js b/devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js
new file mode 100644
index 000000000..855cfbb88
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_595934_message_categories.js
@@ -0,0 +1,211 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 595934 - message categories coverage.";
+const TESTS_PATH = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/";
+const TESTS = [
+ {
+ // #0
+ file: "test-bug-595934-css-loader.html",
+ category: "CSS Loader",
+ matchString: "text/css",
+ },
+ {
+ // #1
+ file: "test-bug-595934-imagemap.html",
+ category: "Layout: ImageMap",
+ matchString: "shape=\"rect\"",
+ },
+ {
+ // #2
+ file: "test-bug-595934-html.html",
+ category: "HTML",
+ matchString: "multipart/form-data",
+ onload: function () {
+ let form = content.document.querySelector("form");
+ form.submit();
+ },
+ },
+ {
+ // #3
+ file: "test-bug-595934-workers.html",
+ category: "Web Worker",
+ matchString: "fooBarWorker",
+ },
+ {
+ // #4
+ file: "test-bug-595934-malformedxml.xhtml",
+ category: "malformed-xml",
+ matchString: "no root element found",
+ },
+ {
+ // #5
+ file: "test-bug-595934-svg.xhtml",
+ category: "SVG",
+ matchString: "fooBarSVG",
+ },
+ {
+ // #6
+ file: "test-bug-595934-css-parser.html",
+ category: "CSS Parser",
+ matchString: "foobarCssParser",
+ },
+ {
+ // #7
+ file: "test-bug-595934-malformedxml-external.html",
+ category: "malformed-xml",
+ matchString: "</html>",
+ },
+ {
+ // #8
+ file: "test-bug-595934-empty-getelementbyid.html",
+ category: "DOM",
+ matchString: "getElementById",
+ },
+ {
+ // #9
+ file: "test-bug-595934-canvas-css.html",
+ category: "CSS Parser",
+ matchString: "foobarCanvasCssParser",
+ },
+ {
+ // #10
+ file: "test-bug-595934-image.html",
+ category: "Image",
+ matchString: "corrupt",
+ },
+];
+
+var pos = -1;
+
+var foundCategory = false;
+var foundText = false;
+var pageLoaded = false;
+var pageError = false;
+var output = null;
+var jsterm = null;
+var hud = null;
+var testEnded = false;
+
+var TestObserver = {
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+ observe: function testObserve(subject) {
+ if (testEnded || !(subject instanceof Ci.nsIScriptError)) {
+ return;
+ }
+
+ let expectedCategory = TESTS[pos].category;
+
+ info("test #" + pos + " console observer got " + subject.category +
+ ", is expecting " + expectedCategory);
+
+ if (subject.category == expectedCategory) {
+ foundCategory = true;
+ startNextTest();
+ } else {
+ info("unexpected message was: " + subject.sourceName + ":" +
+ subject.lineNumber + "; " + subject.errorMessage);
+ }
+ }
+};
+
+function consoleOpened(hudConsole) {
+ hud = hudConsole;
+ output = hud.outputNode;
+ jsterm = hud.jsterm;
+
+ Services.console.registerListener(TestObserver);
+
+ registerCleanupFunction(testEnd);
+
+ testNext();
+}
+
+function testNext() {
+ jsterm.clearOutput();
+ foundCategory = false;
+ foundText = false;
+ pageLoaded = false;
+ pageError = false;
+
+ pos++;
+ info("testNext: #" + pos);
+ if (pos < TESTS.length) {
+ test = TESTS[pos];
+
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "message for test #" + pos + ": '" + test.matchString + "'",
+ text: test.matchString,
+ }],
+ }).then(() => {
+ foundText = true;
+ startNextTest();
+ });
+
+ let testLocation = TESTS_PATH + test.file;
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad(evt) {
+ if (content.location.href != testLocation) {
+ return;
+ }
+ gBrowser.selectedBrowser.removeEventListener(evt.type, onLoad, true);
+
+ pageLoaded = true;
+ test.onload && test.onload(evt);
+
+ if (test.expectError) {
+ content.addEventListener("error", function _onError() {
+ content.removeEventListener("error", _onError);
+ pageError = true;
+ startNextTest();
+ });
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ } else {
+ pageError = true;
+ }
+
+ startNextTest();
+ }, true);
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, testLocation);
+ } else {
+ testEnded = true;
+ finishTest();
+ }
+}
+
+function testEnd() {
+ if (!testEnded) {
+ info("foundCategory " + foundCategory + " foundText " + foundText +
+ " pageLoaded " + pageLoaded + " pageError " + pageError);
+ }
+
+ Services.console.unregisterListener(TestObserver);
+ hud = TestObserver = output = jsterm = null;
+}
+
+function startNextTest() {
+ if (!testEnded && foundCategory && foundText && pageLoaded && pageError) {
+ testNext();
+ }
+}
+
+function test() {
+ requestLongerTimeout(2);
+
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(consoleOpened);
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js b/devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js
new file mode 100644
index 000000000..e14c3a069
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js
@@ -0,0 +1,97 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+var tab1, tab2, win1, win2;
+var noErrors = true;
+
+function tab1Loaded() {
+ win2 = OpenBrowserWindow();
+ whenDelayedStartupFinished(win2, win2Loaded);
+}
+
+function win2Loaded() {
+ tab2 = win2.gBrowser.addTab(TEST_URI);
+ win2.gBrowser.selectedTab = tab2;
+ tab2.linkedBrowser.addEventListener("load", tab2Loaded, true);
+}
+
+function tab2Loaded(aEvent) {
+ tab2.linkedBrowser.removeEventListener(aEvent.type, tab2Loaded, true);
+
+ let consolesOpened = 0;
+ function onWebConsoleOpen() {
+ consolesOpened++;
+ if (consolesOpened == 2) {
+ executeSoon(closeConsoles);
+ }
+ }
+
+ function openConsoles() {
+ try {
+ let target1 = TargetFactory.forTab(tab1);
+ gDevTools.showToolbox(target1, "webconsole").then(onWebConsoleOpen);
+ } catch (ex) {
+ ok(false, "gDevTools.showToolbox(target1) exception: " + ex);
+ noErrors = false;
+ }
+
+ try {
+ let target2 = TargetFactory.forTab(tab2);
+ gDevTools.showToolbox(target2, "webconsole").then(onWebConsoleOpen);
+ } catch (ex) {
+ ok(false, "gDevTools.showToolbox(target2) exception: " + ex);
+ noErrors = false;
+ }
+ }
+
+ function closeConsoles() {
+ try {
+ let target1 = TargetFactory.forTab(tab1);
+ gDevTools.closeToolbox(target1).then(function () {
+ try {
+ let target2 = TargetFactory.forTab(tab2);
+ gDevTools.closeToolbox(target2).then(testEnd);
+ } catch (ex) {
+ ok(false, "gDevTools.closeToolbox(target2) exception: " + ex);
+ noErrors = false;
+ }
+ });
+ } catch (ex) {
+ ok(false, "gDevTools.closeToolbox(target1) exception: " + ex);
+ noErrors = false;
+ }
+ }
+
+ function testEnd() {
+ ok(noErrors, "there were no errors");
+
+ win1.gBrowser.removeTab(tab1);
+
+ Array.forEach(win2.gBrowser.tabs, function (aTab) {
+ win2.gBrowser.removeTab(aTab);
+ });
+
+ executeSoon(function () {
+ win2.close();
+ tab1 = tab2 = win1 = win2 = null;
+ finishTest();
+ });
+ }
+
+ openConsoles();
+}
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ tab1 = gBrowser.selectedTab;
+ win1 = window;
+ tab1Loaded();
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js b/devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js
new file mode 100644
index 000000000..336700ada
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_597136_external_script_errors.js
@@ -0,0 +1,33 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-bug-597136-external-script-" +
+ "errors.html";
+
+function test() {
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ BrowserTestUtils.synthesizeMouseAtCenter("button", {}, gBrowser.selectedBrowser);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bogus is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js b/devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js
new file mode 100644
index 000000000..473f02ccc
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_597136_network_requests_from_chrome.js
@@ -0,0 +1,52 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that network requests from chrome don't cause the Web Console to
+// throw exceptions.
+
+"use strict";
+
+const TEST_URI = "http://example.com/";
+
+var good = true;
+var listener = {
+ QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver ]),
+ observe: function (subject) {
+ if (subject instanceof Ci.nsIScriptError &&
+ subject.category === "XPConnect JavaScript" &&
+ subject.sourceName.includes("webconsole")) {
+ good = false;
+ }
+ }
+};
+
+var xhr;
+
+function test() {
+ Services.console.registerListener(listener);
+
+ // trigger a lazy-load of the HUD Service
+ HUDService;
+
+ xhr = new XMLHttpRequest();
+ xhr.addEventListener("load", xhrComplete, false);
+ xhr.open("GET", TEST_URI, true);
+ xhr.send(null);
+}
+
+function xhrComplete() {
+ xhr.removeEventListener("load", xhrComplete, false);
+ window.setTimeout(checkForException, 0);
+}
+
+function checkForException() {
+ ok(good, "no exception was thrown when sending a network request from a " +
+ "chrome window");
+
+ Services.console.unregisterListener(listener);
+ listener = xhr = null;
+
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js
new file mode 100644
index 000000000..2de4c9f21
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_597460_filter_scroll.js
@@ -0,0 +1,80 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-network.html";
+const PREF = "devtools.webconsole.persistlog";
+
+add_task(function* () {
+ Services.prefs.setBoolPref(PREF, true);
+
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let results = yield consoleOpened(hud);
+
+ testScroll(results, hud);
+
+ Services.prefs.clearUserPref(PREF);
+});
+
+function consoleOpened(hud) {
+ let deferred = promise.defer();
+
+ for (let i = 0; i < 200; i++) {
+ content.console.log("test message " + i);
+ }
+
+ hud.setFilterState("network", false);
+ hud.setFilterState("networkinfo", false);
+
+ hud.ui.filterBox.value = "test message";
+ hud.ui.adjustVisibilityOnSearchStringChange();
+
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console messages displayed",
+ text: "test message 199",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(() => {
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test-network.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(deferred.resolve);
+
+ content.location.reload();
+ });
+
+ return deferred.promise;
+}
+
+function testScroll([result], hud) {
+ let scrollNode = hud.ui.outputWrapper;
+ let msgNode = [...result.matched][0];
+ ok(msgNode.classList.contains("filtered-by-type"),
+ "network message is filtered by type");
+ ok(msgNode.classList.contains("filtered-by-string"),
+ "network message is filtered by string");
+
+ ok(scrollNode.scrollTop > 0, "scroll location is not at the top");
+
+ // Make sure the Web Console output is scrolled as near as possible to the
+ // bottom.
+ let nodeHeight = msgNode.clientHeight;
+ ok(scrollNode.scrollTop >= scrollNode.scrollHeight - scrollNode.clientHeight -
+ nodeHeight * 2, "scroll location is correct");
+
+ hud.setFilterState("network", true);
+ hud.setFilterState("networkinfo", true);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js b/devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js
new file mode 100644
index 000000000..5a8280eed
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_597756_reopen_closed_tab.js
@@ -0,0 +1,70 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-597756-reopen-closed-tab.html";
+
+var HUD;
+
+add_task(function* () {
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ let { browser } = yield loadTab(TEST_URI);
+ HUD = yield openConsole();
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ yield reload(browser);
+
+ yield testMessages();
+
+ yield closeConsole();
+
+ // Close and reopen
+ gBrowser.removeCurrentTab();
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ let tab = yield loadTab(TEST_URI);
+ HUD = yield openConsole();
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ yield reload(tab.browser);
+
+ yield testMessages();
+
+ HUD = null;
+});
+
+function reload(browser) {
+ let loaded = loadBrowser(browser);
+ browser.reload();
+ return loaded;
+}
+
+function testMessages() {
+ return waitForMessages({
+ webconsole: HUD,
+ messages: [{
+ name: "error message displayed",
+ text: "fooBug597756_error",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js b/devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js
new file mode 100644
index 000000000..4849793cb
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_599725_response_headers.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const INIT_URI = "data:text/plain;charset=utf8,hello world";
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-599725-response-headers.sjs";
+
+function performTest(request, hud) {
+ let deferred = promise.defer();
+
+ let headers = null;
+
+ function readHeader(name) {
+ for (let header of headers) {
+ if (header.name == name) {
+ return header.value;
+ }
+ }
+ return null;
+ }
+
+ hud.ui.proxy.webConsoleClient.getResponseHeaders(request.actor,
+ function (response) {
+ headers = response.headers;
+ ok(headers, "we have the response headers for reload");
+
+ let contentType = readHeader("Content-Type");
+ let contentLength = readHeader("Content-Length");
+
+ ok(!contentType, "we do not have the Content-Type header");
+ isnot(contentLength, 60, "Content-Length != 60");
+
+ executeSoon(deferred.resolve);
+ });
+
+ return deferred.promise;
+}
+
+let waitForRequest = Task.async(function*(hud) {
+ let request = yield waitForFinishedRequest(req=> {
+ return req.response.status === "304";
+ });
+
+ yield performTest(request, hud);
+});
+
+add_task(function* () {
+ let { browser } = yield loadTab(INIT_URI);
+
+ let hud = yield openConsole();
+
+ let gotLastRequest = waitForRequest(hud);
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(browser, TEST_URI);
+ yield loaded;
+
+ let reloaded = loadBrowser(browser);
+ ContentTask.spawn(browser, null, "() => content.location.reload()");
+ yield reloaded;
+
+ yield gotLastRequest;
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js b/devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js
new file mode 100644
index 000000000..153863824
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_600183_charset.js
@@ -0,0 +1,59 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const INIT_URI = "data:text/html;charset=utf-8,Web Console - bug 600183 test";
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-600183-charset.html";
+
+function performTest(lastFinishedRequest, console) {
+ let deferred = promise.defer();
+
+ ok(lastFinishedRequest, "charset test page was loaded and logged");
+ HUDService.lastFinishedRequest.callback = null;
+
+ executeSoon(() => {
+ console.webConsoleClient.getResponseContent(lastFinishedRequest.actor,
+ (response) => {
+ ok(!response.contentDiscarded, "response body was not discarded");
+
+ let body = response.content.text;
+ ok(body, "we have the response body");
+
+ // 的问候!
+ let chars = "\u7684\u95ee\u5019!";
+ isnot(body.indexOf("<p>" + chars + "</p>"), -1,
+ "found the chinese simplified string");
+
+ HUDService.lastFinishedRequest.callback = null;
+ executeSoon(deferred.resolve);
+ });
+ });
+
+ return deferred.promise;
+}
+
+function waitForRequest() {
+ let deferred = promise.defer();
+ HUDService.lastFinishedRequest.callback = (req, console) => {
+ performTest(req, console).then(deferred.resolve);
+ };
+ return deferred.promise;
+}
+
+add_task(function* () {
+ let { browser } = yield loadTab(INIT_URI);
+
+ yield openConsole();
+
+ let gotLastRequest = waitForRequest();
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(browser, TEST_URI);
+ yield loaded;
+
+ yield gotLastRequest;
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js b/devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js
new file mode 100644
index 000000000..9dd81c9fd
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_601177_log_levels.js
@@ -0,0 +1,76 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 601177: log levels";
+const TEST_URI2 = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-601177-log-levels.html";
+
+add_task(function* () {
+ Services.prefs.setBoolPref("javascript.options.strict", true);
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ yield testLogLevels(hud);
+
+ Services.prefs.clearUserPref("javascript.options.strict");
+});
+
+function testLogLevels(hud) {
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2);
+
+ info("waiting for messages");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "test-bug-601177-log-levels.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-bug-601177-log-levels.js",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-image.png",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "foobar-known-to-fail.png",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "foobarBug601177exception",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "undefinedPropertyBug601177",
+ category: CATEGORY_JS,
+ severity: SEVERITY_WARNING,
+ },
+ {
+ text: "foobarBug601177strictError",
+ category: CATEGORY_JS,
+ severity: SEVERITY_WARNING,
+ },
+ ],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js
new file mode 100644
index 000000000..89bd83a7a
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_601352_scroll.js
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the console output scrolls to JS eval results when there are many
+// messages displayed. See bug 601352.
+
+"use strict";
+
+add_task(function* () {
+ let {tab} = yield loadTab("data:text/html;charset=utf-8,Web Console test " +
+ "for bug 601352");
+ let hud = yield openConsole(tab);
+ hud.jsterm.clearOutput();
+
+ yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let longMessage = "";
+ for (let i = 0; i < 50; i++) {
+ longMessage += "LongNonwrappingMessage";
+ }
+
+ for (let i = 0; i < 50; i++) {
+ content.console.log("test1 message " + i);
+ }
+
+ content.console.log(longMessage);
+
+ for (let i = 0; i < 50; i++) {
+ content.console.log("test2 message " + i);
+ }
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test1 message 0",
+ }, {
+ text: "test1 message 49",
+ }, {
+ text: "LongNonwrappingMessage",
+ }, {
+ text: "test2 message 0",
+ }, {
+ text: "test2 message 49",
+ }],
+ });
+
+ let node = yield hud.jsterm.execute("1+1");
+
+ let scrollNode = hud.ui.outputWrapper;
+ let rectNode = node.getBoundingClientRect();
+ let rectOutput = scrollNode.getBoundingClientRect();
+
+ yield ContentTask.spawn(gBrowser.selectedBrowser, {
+ rectNode,
+ rectOutput,
+ scrollHeight: scrollNode.scrollHeight,
+ scrollTop: scrollNode.scrollTop,
+ clientHeight: scrollNode.clientHeight,
+ }, function* (args) {
+ console.debug("rectNode", args.rectNode, "rectOutput", args.rectOutput);
+ console.log("scrollNode scrollHeight", args.scrollHeight,
+ "scrollTop", args.scrollTop, "clientHeight",
+ args.clientHeight);
+ });
+
+ isnot(scrollNode.scrollTop, 0, "scroll location is not at the top");
+
+ // The bounding client rect .top/left coordinates are relative to the
+ // console iframe.
+
+ // Visible scroll viewport.
+ let height = rectOutput.height;
+
+ // Top and bottom coordinates of the last message node, relative to the
+ // outputNode.
+ let top = rectNode.top - rectOutput.top;
+ let bottom = top + rectNode.height;
+ info("node top " + top + " node bottom " + bottom + " node clientHeight " +
+ node.clientHeight);
+
+ ok(top >= 0 && bottom <= height, "last message is visible");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js b/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
new file mode 100644
index 000000000..6dae0a7b7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_601667_filter_buttons.js
@@ -0,0 +1,267 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the filter button UI logic works correctly.
+
+"use strict";
+
+const TEST_URI = "http://example.com/";
+const FILTER_PREF_DOMAIN = "devtools.webconsole.filter.";
+
+var hud, hudId, hudBox;
+var prefs = {};
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+ hudId = hud.hudId;
+ hudBox = hud.ui.rootElement;
+
+ savePrefs();
+
+ testFilterButtons();
+
+ restorePrefs();
+
+ hud = hudId = hudBox = null;
+});
+
+function savePrefs() {
+ let branch = Services.prefs.getBranch(FILTER_PREF_DOMAIN);
+ let children = branch.getChildList("");
+ for (let child of children) {
+ prefs[child] = branch.getBoolPref(child);
+ }
+}
+
+function restorePrefs() {
+ let branch = Services.prefs.getBranch(FILTER_PREF_DOMAIN);
+ for (let p in prefs) {
+ branch.setBoolPref(p, prefs[p]);
+ }
+}
+
+function testFilterButtons() {
+ testMenuFilterButton("net");
+ testMenuFilterButton("css");
+ testMenuFilterButton("js");
+ testMenuFilterButton("logging");
+ testMenuFilterButton("security");
+ testMenuFilterButton("server");
+
+ testIsolateFilterButton("net");
+ testIsolateFilterButton("css");
+ testIsolateFilterButton("js");
+ testIsolateFilterButton("logging");
+ testIsolateFilterButton("security");
+ testIsolateFilterButton("server");
+}
+
+function testMenuFilterButton(category) {
+ let selector = ".webconsole-filter-button[category=\"" + category + "\"]";
+ let button = hudBox.querySelector(selector);
+ ok(button, "we have the \"" + category + "\" button");
+
+ let firstMenuItem = button.querySelector("menuitem");
+ ok(firstMenuItem, "we have the first menu item for the \"" + category +
+ "\" button");
+
+ // Turn all the filters off, if they were on.
+ let menuItem = firstMenuItem;
+ while (menuItem != null) {
+ if (menuItem.hasAttribute("prefKey") && isChecked(menuItem)) {
+ chooseMenuItem(menuItem);
+ }
+ menuItem = menuItem.nextSibling;
+ }
+
+ // Turn all the filters on; make sure the button gets checked.
+ menuItem = firstMenuItem;
+ let prefKey;
+ while (menuItem) {
+ if (menuItem.hasAttribute("prefKey")) {
+ prefKey = menuItem.getAttribute("prefKey");
+ chooseMenuItem(menuItem);
+ ok(isChecked(menuItem), "menu item " + prefKey + " for category " +
+ category + " is checked after clicking it");
+ ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "on after clicking the appropriate menu item");
+ }
+ menuItem = menuItem.nextSibling;
+ }
+ ok(isChecked(button), "the button for category " + category + " is " +
+ "checked after turning on all its menu items");
+
+ // Turn one filter off; make sure the button is still checked.
+ prefKey = firstMenuItem.getAttribute("prefKey");
+ chooseMenuItem(firstMenuItem);
+ ok(!isChecked(firstMenuItem), "the first menu item for category " +
+ category + " is no longer checked after clicking it");
+ ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "turned off after clicking the appropriate menu item");
+ ok(isChecked(button), "the button for category " + category + " is still " +
+ "checked after turning off its first menu item");
+
+ // Turn all the filters off by clicking the main part of the button.
+ let subbutton = getMainButton(button);
+ ok(subbutton, "we have the subbutton for category " + category);
+
+ clickButton(subbutton);
+ ok(!isChecked(button), "the button for category " + category + " is " +
+ "no longer checked after clicking its main part");
+
+ menuItem = firstMenuItem;
+ while (menuItem) {
+ prefKey = menuItem.getAttribute("prefKey");
+ if (prefKey) {
+ ok(!isChecked(menuItem), "menu item " + prefKey + " for category " +
+ category + " is no longer checked after clicking the button");
+ ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "off after clicking the button");
+ }
+ menuItem = menuItem.nextSibling;
+ }
+
+ // Turn all the filters on by clicking the main part of the button.
+ clickButton(subbutton);
+
+ ok(isChecked(button), "the button for category " + category + " is " +
+ "checked after clicking its main part");
+
+ menuItem = firstMenuItem;
+ while (menuItem) {
+ if (menuItem.hasAttribute("prefKey")) {
+ prefKey = menuItem.getAttribute("prefKey");
+ // The CSS/Log menu item should not be checked. See bug 971798.
+ if (category == "css" && prefKey == "csslog") {
+ ok(!isChecked(menuItem), "menu item " + prefKey + " for category " +
+ category + " should not be checked after clicking the button");
+ ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "off after clicking the button");
+ } else {
+ ok(isChecked(menuItem), "menu item " + prefKey + " for category " +
+ category + " is checked after clicking the button");
+ ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "on after clicking the button");
+ }
+ }
+ menuItem = menuItem.nextSibling;
+ }
+
+ // Uncheck the main button by unchecking all the filters
+ menuItem = firstMenuItem;
+ while (menuItem) {
+ // The csslog menu item is already unchecked at this point.
+ // Make sure it is not selected. See bug 971798.
+ prefKey = menuItem.getAttribute("prefKey");
+ if (prefKey && prefKey != "csslog") {
+ chooseMenuItem(menuItem);
+ }
+ menuItem = menuItem.nextSibling;
+ }
+
+ ok(!isChecked(button), "the button for category " + category + " is " +
+ "unchecked after unchecking all its filters");
+
+ // Turn all the filters on again by clicking the button.
+ clickButton(subbutton);
+}
+
+function testIsolateFilterButton(category) {
+ let selector = ".webconsole-filter-button[category=\"" + category + "\"]";
+ let targetButton = hudBox.querySelector(selector);
+ ok(targetButton, "we have the \"" + category + "\" button");
+
+ // Get the main part of the filter button.
+ let subbutton = getMainButton(targetButton);
+ ok(subbutton, "we have the subbutton for category " + category);
+
+ // Turn on all the filters by alt clicking the main part of the button.
+ altClickButton(subbutton);
+ ok(isChecked(targetButton), "the button for category " + category +
+ " is checked after isolating for filter");
+
+ // Check if all the filters for the target button are on.
+ let menuItems = targetButton.querySelectorAll("menuitem");
+ Array.forEach(menuItems, (item) => {
+ let prefKey = item.getAttribute("prefKey");
+ // The CSS/Log filter should not be checked. See bug 971798.
+ if (category == "css" && prefKey == "csslog") {
+ ok(!isChecked(item), "menu item " + prefKey + " for category " +
+ category + " should not be checked after isolating for " + category);
+ ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages should be " +
+ "turned off after isolating for " + category);
+ } else if (prefKey) {
+ ok(isChecked(item), "menu item " + prefKey + " for category " +
+ category + " is checked after isolating for " + category);
+ ok(hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "turned on after isolating for " + category);
+ }
+ });
+
+ // Ensure all other filter buttons are toggled off and their
+ // associated filters are turned off
+ let buttons = hudBox.querySelectorAll(".webconsole-filter-button[category]");
+ Array.forEach(buttons, (filterButton) => {
+ if (filterButton !== targetButton) {
+ let categoryBtn = filterButton.getAttribute("category");
+ ok(!isChecked(filterButton), "the button for category " +
+ categoryBtn + " is unchecked after isolating for " + category);
+
+ menuItems = filterButton.querySelectorAll("menuitem");
+ Array.forEach(menuItems, (item) => {
+ let prefKey = item.getAttribute("prefKey");
+ if (prefKey) {
+ ok(!isChecked(item), "menu item " + prefKey + " for category " +
+ category + " is unchecked after isolating for " + category);
+ ok(!hud.ui.filterPrefs[prefKey], prefKey + " messages are " +
+ "turned off after isolating for " + category);
+ }
+ });
+
+ // Turn all the filters on again by clicking the button.
+ let mainButton = getMainButton(filterButton);
+ clickButton(mainButton);
+ }
+ });
+}
+
+/**
+ * Return the main part of the target filter button.
+ */
+function getMainButton(targetButton) {
+ let anonymousNodes = hud.ui.document.getAnonymousNodes(targetButton);
+ let subbutton;
+
+ for (let i = 0; i < anonymousNodes.length; i++) {
+ let node = anonymousNodes[i];
+ if (node.classList.contains("toolbarbutton-menubutton-button")) {
+ subbutton = node;
+ break;
+ }
+ }
+
+ return subbutton;
+}
+
+function clickButton(node) {
+ EventUtils.sendMouseEvent({ type: "click" }, node);
+}
+
+function altClickButton(node) {
+ EventUtils.sendMouseEvent({ type: "click", altKey: true }, node);
+}
+
+function chooseMenuItem(node) {
+ let event = document.createEvent("XULCommandEvent");
+ event.initCommandEvent("command", true, true, window, 0, false, false, false,
+ false, null);
+ node.dispatchEvent(event);
+}
+
+function isChecked(node) {
+ return node.getAttribute("checked") === "true";
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js b/devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js
new file mode 100644
index 000000000..f14530d06
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_603750_websocket.js
@@ -0,0 +1,37 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-603750-websocket.html";
+const TEST_URI2 = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 603750: Web Socket errors";
+
+add_task(function* () {
+ yield loadTab(TEST_URI2);
+
+ let hud = yield openConsole();
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "ws://0.0.0.0:81",
+ source: { url: "test-bug-603750-websocket.js" },
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "ws://0.0.0.0:82",
+ source: { url: "test-bug-603750-websocket.js" },
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ ]
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_611795.js b/devtools/client/webconsole/test/browser_webconsole_bug_611795.js
new file mode 100644
index 000000000..1fa4d717e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_611795.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = 'data:text/html;charset=utf-8,<div style="-moz-opacity:0;">' +
+ 'test repeated css warnings</div><p style="-moz-opacity:0">' +
+ "hi</p>";
+var hud;
+
+/**
+ * Unit test for bug 611795:
+ * Repeated CSS messages get collapsed into one.
+ */
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+ hud.jsterm.clearOutput(true);
+
+ BrowserReload();
+ yield loadBrowser(gBrowser.selectedBrowser);
+
+ yield onContentLoaded();
+ yield testConsoleLogRepeats();
+
+ hud = null;
+});
+
+function onContentLoaded() {
+ let cssWarning = "Unknown property \u2018-moz-opacity\u2019. Declaration dropped.";
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: cssWarning,
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ repeats: 2,
+ }],
+ });
+}
+
+function testConsoleLogRepeats() {
+ let jsterm = hud.jsterm;
+
+ jsterm.clearOutput();
+
+ jsterm.setInputValue("for (let i = 0; i < 10; ++i) console.log('this is a " +
+ "line of reasonably long text that I will use to " +
+ "verify that the repeated text node is of an " +
+ "appropriate size.');");
+ jsterm.execute();
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "this is a line of reasonably long text",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ repeats: 10,
+ }],
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js b/devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js
new file mode 100644
index 000000000..5d0067958
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_613013_console_api_iframe.js
@@ -0,0 +1,26 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-613013-console-api-iframe.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ BrowserReload();
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarBug613013",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js b/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
new file mode 100644
index 000000000..95752021d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js
@@ -0,0 +1,64 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 613280";
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then((HUD) => {
+ ContentTask.spawn(gBrowser.selectedBrowser, null, function*(){
+ content.console.log("foobarBazBug613280");
+ });
+ waitForMessages({
+ webconsole: HUD,
+ messages: [{
+ text: "foobarBazBug613280",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(performTest.bind(null, HUD));
+ });
+ });
+}
+
+function performTest(HUD, [result]) {
+ let msg = [...result.matched][0];
+ let input = HUD.jsterm.inputNode;
+
+ let clipboardSetup = function () {
+ goDoCommand("cmd_copy");
+ };
+
+ let clipboardCopyDone = function () {
+ finishTest();
+ };
+
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled");
+
+ HUD.ui.output.selectMessage(msg);
+ HUD.outputNode.focus();
+
+ goUpdateCommand("cmd_copy");
+
+ controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+
+ // Remove new lines and whitespace since getSelection() includes
+ // a new line between message and line number, but the clipboard doesn't
+ // @see bug 1119503
+ let selectionText = (HUD.iframeWindow.getSelection() + "")
+ .replace(/\r?\n|\r| /g, "");
+ isnot(selectionText.indexOf("foobarBazBug613280"), -1,
+ "selection text includes 'foobarBazBug613280'");
+
+ waitForClipboard((str) => {
+ return selectionText.trim() === str.trim().replace(/ /g, "");
+ }, clipboardSetup, clipboardCopyDone, clipboardCopyDone);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js
new file mode 100644
index 000000000..e24ce28e2
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js
@@ -0,0 +1,119 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 613642: remember scroll location";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+ let outputNode = hud.outputNode;
+ let scrollBox = hud.ui.outputWrapper;
+
+ for (let i = 0; i < 150; i++) {
+ ContentTask.spawn(gBrowser.selectedBrowser, i, function* (num) {
+ content.console.log("test message " + num);
+ });
+ }
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test message 149",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ ok(scrollBox.scrollTop > 0, "scroll location is not at the top");
+
+ // scroll to the first node
+ outputNode.focus();
+
+ let scrolled = promise.defer();
+
+ scrollBox.onscroll = () => {
+ info("onscroll top " + scrollBox.scrollTop);
+ if (scrollBox.scrollTop != 0) {
+ // Wait for scroll to 0.
+ return;
+ }
+ scrollBox.onscroll = null;
+ is(scrollBox.scrollTop, 0, "scroll location updated (moved to top)");
+ scrolled.resolve();
+ };
+ EventUtils.synthesizeKey("VK_HOME", {}, hud.iframeWindow);
+
+ yield scrolled.promise;
+
+ // add a message and make sure scroll doesn't change
+ ContentTask.spawn(gBrowser.selectedBrowser, null,
+ "() => content.console.log('test message 150')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test message 150",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ scrolled = promise.defer();
+ scrollBox.onscroll = () => {
+ if (scrollBox.scrollTop != 0) {
+ // Wait for scroll to stabilize at the top.
+ return;
+ }
+ scrollBox.onscroll = null;
+ is(scrollBox.scrollTop, 0, "scroll location is still at the top");
+ scrolled.resolve();
+ };
+
+ // Make sure that scroll stabilizes at the top. executeSoon() is needed for
+ // the yield to work.
+ executeSoon(scrollBox.onscroll);
+
+ yield scrolled.promise;
+
+ // scroll back to the bottom
+ outputNode.lastChild.focus();
+
+ scrolled = promise.defer();
+ scrollBox.onscroll = () => {
+ if (scrollBox.scrollTop == 0) {
+ // Wait for scroll to bottom.
+ return;
+ }
+ scrollBox.onscroll = null;
+ isnot(scrollBox.scrollTop, 0, "scroll location updated (moved to bottom)");
+ scrolled.resolve();
+ };
+ EventUtils.synthesizeKey("VK_END", {});
+ yield scrolled.promise;
+
+ let oldScrollTop = scrollBox.scrollTop;
+
+ ContentTask.spawn(gBrowser.selectedBrowser, null,
+ "() => content.console.log('test message 151')");
+
+ scrolled = promise.defer();
+ scrollBox.onscroll = () => {
+ if (scrollBox.scrollTop == oldScrollTop) {
+ // Wait for scroll to change.
+ return;
+ }
+ scrollBox.onscroll = null;
+ isnot(scrollBox.scrollTop, oldScrollTop,
+ "scroll location updated (moved to bottom again)");
+ scrolled.resolve();
+ };
+ yield scrolled.promise;
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js
new file mode 100644
index 000000000..c53fe1683
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_613642_prune_scroll.js
@@ -0,0 +1,82 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 613642: maintain scroll with pruning of old messages";
+
+var hud;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+
+ let outputNode = hud.outputNode;
+
+ Services.prefs.setIntPref("devtools.hud.loglimit.console", 140);
+ let scrollBoxElement = hud.ui.outputWrapper;
+
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ for (let i = 0; i < 150; i++) {
+ content.console.log("test message " + i);
+ }
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test message 149",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let oldScrollTop = scrollBoxElement.scrollTop;
+ isnot(oldScrollTop, 0, "scroll location is not at the top");
+
+ let firstNode = outputNode.firstChild;
+ ok(firstNode, "found the first message");
+
+ let msgNode = outputNode.children[80];
+ ok(msgNode, "found the 80th message");
+
+ // scroll to the middle message node
+ msgNode.scrollIntoView(false);
+
+ isnot(scrollBoxElement.scrollTop, oldScrollTop,
+ "scroll location updated (scrolled to message)");
+
+ oldScrollTop = scrollBoxElement.scrollTop;
+
+ // add a message
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.console.log("hello world");
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "hello world",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ // Scroll location needs to change, because one message is also removed, and
+ // we need to scroll a bit towards the top, to keep the current view in sync.
+ isnot(scrollBoxElement.scrollTop, oldScrollTop,
+ "scroll location updated (added a message)");
+
+ isnot(outputNode.firstChild, firstNode,
+ "first message removed");
+
+ Services.prefs.clearUserPref("devtools.hud.loglimit.console");
+
+ hud = null;
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js b/devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js
new file mode 100644
index 000000000..ae61023a9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_614793_jsterm_scroll.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 614793: jsterm result scroll";
+
+requestLongerTimeout(2);
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ yield testScrollPosition(hud);
+});
+
+function* testScrollPosition(hud) {
+ hud.jsterm.clearOutput();
+
+ let scrollNode = hud.ui.outputWrapper;
+
+ for (let i = 0; i < 150; i++) {
+ yield ContentTask.spawn(gBrowser.selectedBrowser, i, function* (i) {
+ content.console.log("test message " + i);
+ });
+ }
+
+ let oldScrollTop = -1;
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test message 149",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ oldScrollTop = scrollNode.scrollTop;
+ isnot(oldScrollTop, 0, "scroll location is not at the top");
+
+ let msg = yield hud.jsterm.execute("'hello world'");
+
+ isnot(scrollNode.scrollTop, oldScrollTop, "scroll location updated");
+
+ oldScrollTop = scrollNode.scrollTop;
+
+ msg.scrollIntoView(false);
+
+ is(scrollNode.scrollTop, oldScrollTop, "scroll location is the same");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js b/devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js
new file mode 100644
index 000000000..439793b22
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_618078_network_exceptions.js
@@ -0,0 +1,36 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that we report JS exceptions in event handlers coming from
+// network requests, like onreadystate for XHR. See bug 618078.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 618078";
+const TEST_URI2 = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-618078-network-exceptions.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug618078exception",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js b/devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js
new file mode 100644
index 000000000..6f4248c51
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_621644_jsterm_dollar.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-621644-jsterm-dollar.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield test$(hud);
+ yield test$$(hud);
+});
+
+function* test$(HUD) {
+ let deferred = promise.defer();
+
+ HUD.jsterm.clearOutput();
+
+ HUD.jsterm.execute("$(document.body)", (msg) => {
+ ok(msg.textContent.indexOf("<p>") > -1,
+ "jsterm output is correct for $()");
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+}
+
+function test$$(HUD) {
+ let deferred = promise.defer();
+
+ HUD.jsterm.clearOutput();
+
+ HUD.jsterm.setInputValue();
+ HUD.jsterm.execute("$$(document)", (msg) => {
+ ok(msg.textContent.indexOf("621644") > -1,
+ "jsterm output is correct for $$()");
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js b/devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js
new file mode 100644
index 000000000..f4b5dca96
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_622303_persistent_filters.js
@@ -0,0 +1,149 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const prefs = {
+ "net": [
+ "network",
+ "netwarn",
+ "netxhr",
+ "networkinfo"
+ ],
+ "css": [
+ "csserror",
+ "cssparser",
+ "csslog"
+ ],
+ "js": [
+ "exception",
+ "jswarn",
+ "jslog",
+ ],
+ "logging": [
+ "error",
+ "warn",
+ "info",
+ "log",
+ "serviceworkers",
+ "sharedworkers",
+ "windowlessworkers"
+ ]
+};
+
+add_task(function* () {
+ // Set all prefs to true
+ for (let category in prefs) {
+ prefs[category].forEach(function (pref) {
+ Services.prefs.setBoolPref("devtools.webconsole.filter." + pref, true);
+ });
+ }
+
+ yield loadTab("about:blank");
+
+ let hud = yield openConsole();
+
+ let hud2 = yield onConsoleOpen(hud);
+ let hud3 = yield onConsoleReopen1(hud2);
+ yield onConsoleReopen2(hud3);
+
+ // Clear prefs
+ for (let category in prefs) {
+ prefs[category].forEach(function (pref) {
+ Services.prefs.clearUserPref("devtools.webconsole.filter." + pref);
+ });
+ }
+});
+
+function onConsoleOpen(hud) {
+ let deferred = promise.defer();
+
+ let hudBox = hud.ui.rootElement;
+
+ // Check if the filters menuitems exists and are checked
+ for (let category in prefs) {
+ let button = hudBox.querySelector(".webconsole-filter-button[category=\""
+ + category + "\"]");
+ ok(isChecked(button), "main button for " + category +
+ " category is checked");
+
+ prefs[category].forEach(function (pref) {
+ let menuitem = hudBox.querySelector("menuitem[prefKey=" + pref + "]");
+ ok(isChecked(menuitem), "menuitem for " + pref + " is checked");
+ });
+ }
+
+ // Set all prefs to false
+ for (let category in prefs) {
+ prefs[category].forEach(function (pref) {
+ hud.setFilterState(pref, false);
+ });
+ }
+
+ // Re-init the console
+ closeConsole().then(() => {
+ openConsole().then(deferred.resolve);
+ });
+
+ return deferred.promise;
+}
+
+function onConsoleReopen1(hud) {
+ info("testing after reopening once");
+ let deferred = promise.defer();
+
+ let hudBox = hud.ui.rootElement;
+
+ // Check if the filter button and menuitems are unchecked
+ for (let category in prefs) {
+ let button = hudBox.querySelector(".webconsole-filter-button[category=\""
+ + category + "\"]");
+ ok(isUnchecked(button), "main button for " + category +
+ " category is not checked");
+
+ prefs[category].forEach(function (pref) {
+ let menuitem = hudBox.querySelector("menuitem[prefKey=" + pref + "]");
+ ok(isUnchecked(menuitem), "menuitem for " + pref + " is not checked");
+ });
+ }
+
+ // Set first pref in each category to true
+ for (let category in prefs) {
+ hud.setFilterState(prefs[category][0], true);
+ }
+
+ // Re-init the console
+ closeConsole().then(() => {
+ openConsole().then(deferred.resolve);
+ });
+
+ return deferred.promise;
+}
+
+function onConsoleReopen2(hud) {
+ info("testing after reopening again");
+
+ let hudBox = hud.ui.rootElement;
+
+ // Check the main category button is checked and first menuitem is checked
+ for (let category in prefs) {
+ let button = hudBox.querySelector(".webconsole-filter-button[category=\"" +
+ category + "\"]");
+ ok(isChecked(button), category +
+ " button is checked when first pref is true");
+
+ let pref = prefs[category][0];
+ let menuitem = hudBox.querySelector("menuitem[prefKey=" + pref + "]");
+ ok(isChecked(menuitem), "first " + category + " menuitem is checked");
+ }
+}
+
+function isChecked(aNode) {
+ return aNode.getAttribute("checked") === "true";
+}
+
+function isUnchecked(aNode) {
+ return aNode.getAttribute("checked") === "false";
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js b/devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js
new file mode 100644
index 000000000..3de10774d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test for https://bugzilla.mozilla.org/show_bug.cgi?id=623749
+// Map Control + A to Select All, In the web console input, on Windows
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Test console for bug 623749";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let jsterm = hud.jsterm;
+ jsterm.setInputValue("Ignore These Four Words");
+ let inputNode = jsterm.inputNode;
+
+ // Test select all with Control + A.
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ let inputLength = inputNode.selectionEnd - inputNode.selectionStart;
+ is(inputLength, jsterm.getInputValue().length, "Select all of input");
+
+ // Test do nothing on Control + E.
+ jsterm.setInputValue("Ignore These Four Words");
+ inputNode.selectionStart = 0;
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ is(inputNode.selectionStart, 0, "Control + E does not move to end of input");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js b/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js
new file mode 100644
index 000000000..509749953
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_630733_response_redirect_headers.js
@@ -0,0 +1,120 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
+ "bug 630733";
+const TEST_URI2 = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-630733-response-redirect-headers.sjs";
+
+var lastFinishedRequests = {};
+var webConsoleClient;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield consoleOpened(hud);
+ yield getHeaders();
+ yield getContent();
+
+ performTest();
+});
+
+function consoleOpened(hud) {
+ let deferred = promise.defer();
+
+ webConsoleClient = hud.ui.webConsoleClient;
+ HUDService.lastFinishedRequest.callback = (aHttpRequest) => {
+ let status = aHttpRequest.response.status;
+ lastFinishedRequests[status] = aHttpRequest;
+ if ("301" in lastFinishedRequests &&
+ "404" in lastFinishedRequests) {
+ deferred.resolve();
+ }
+ };
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2);
+
+ return deferred.promise;
+}
+
+function getHeaders() {
+ let deferred = promise.defer();
+
+ HUDService.lastFinishedRequest.callback = null;
+
+ ok("301" in lastFinishedRequests, "request 1: 301 Moved Permanently");
+ ok("404" in lastFinishedRequests, "request 2: 404 Not found");
+
+ webConsoleClient.getResponseHeaders(lastFinishedRequests["301"].actor,
+ function (response) {
+ lastFinishedRequests["301"].response.headers = response.headers;
+
+ webConsoleClient.getResponseHeaders(lastFinishedRequests["404"].actor,
+ function (resp) {
+ lastFinishedRequests["404"].response.headers = resp.headers;
+ executeSoon(deferred.resolve);
+ });
+ });
+ return deferred.promise;
+}
+
+function getContent() {
+ let deferred = promise.defer();
+
+ webConsoleClient.getResponseContent(lastFinishedRequests["301"].actor,
+ function (response) {
+ lastFinishedRequests["301"].response.content = response.content;
+ lastFinishedRequests["301"].discardResponseBody = response.contentDiscarded;
+
+ webConsoleClient.getResponseContent(lastFinishedRequests["404"].actor,
+ function (resp) {
+ lastFinishedRequests["404"].response.content = resp.content;
+ lastFinishedRequests["404"].discardResponseBody =
+ resp.contentDiscarded;
+
+ webConsoleClient = null;
+ executeSoon(deferred.resolve);
+ });
+ });
+ return deferred.promise;
+}
+
+function performTest() {
+ function readHeader(name) {
+ for (let header of headers) {
+ if (header.name == name) {
+ return header.value;
+ }
+ }
+ return null;
+ }
+
+ let headers = lastFinishedRequests["301"].response.headers;
+ is(readHeader("Content-Type"), "text/html",
+ "we do have the Content-Type header");
+ is(readHeader("Content-Length"), 71, "Content-Length is correct");
+ is(readHeader("Location"), "/redirect-from-bug-630733",
+ "Content-Length is correct");
+ is(readHeader("x-foobar-bug630733"), "bazbaz",
+ "X-Foobar-bug630733 is correct");
+
+ let body = lastFinishedRequests["301"].response.content;
+ ok(!body.text, "body discarded for request 1");
+ ok(lastFinishedRequests["301"].discardResponseBody,
+ "body discarded for request 1 (confirmed)");
+
+ headers = lastFinishedRequests["404"].response.headers;
+ ok(!readHeader("Location"), "no Location header");
+ ok(!readHeader("x-foobar-bug630733"), "no X-Foobar-bug630733 header");
+
+ body = lastFinishedRequests["404"].response.content.text;
+ isnot(body.indexOf("404"), -1,
+ "body is correct for request 2");
+
+ lastFinishedRequests = webConsoleClient = null;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js b/devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js
new file mode 100644
index 000000000..45d1f7102
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632275_getters_document_width.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-632275-getters.html";
+
+var getterValue = null;
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(consoleOpened);
+ });
+}
+
+function consoleOpened(hud) {
+ let doc = content.wrappedJSObject.document;
+ getterValue = doc.foobar._val;
+ hud.jsterm.execute("console.dir(document)");
+
+ let onOpen = onViewOpened.bind(null, hud);
+ hud.jsterm.once("variablesview-fetched", onOpen);
+}
+
+function onViewOpened(hud, event, view) {
+ let doc = content.wrappedJSObject.document;
+
+ findVariableViewProperties(view, [
+ { name: /^(width|height)$/, dontMatch: 1 },
+ { name: "foobar._val", value: getterValue },
+ { name: "foobar.val", isGetter: true },
+ ], { webconsole: hud }).then(function () {
+ is(doc.foobar._val, getterValue, "getter did not execute");
+ is(doc.foobar.val, getterValue + 1, "getter executed");
+ is(doc.foobar._val, getterValue + 1, "getter executed (recheck)");
+
+ let textContent = hud.outputNode.textContent;
+ is(textContent.indexOf("document.body.client"), -1,
+ "no document.width/height warning displayed");
+
+ getterValue = null;
+ finishTest();
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
new file mode 100644
index 000000000..c5e672444
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632347_iterators_generators.js
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-632347-iterators-generators.html";
+
+function test() {
+ requestLongerTimeout(6);
+
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(consoleOpened);
+ });
+}
+
+function consoleOpened(HUD) {
+ let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider");
+
+ let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
+ tmp.addDebuggerToGlobal(tmp);
+ let dbg = new tmp.Debugger();
+
+ let jsterm = HUD.jsterm;
+ let win = content.wrappedJSObject;
+ let dbgWindow = dbg.addDebuggee(content);
+ let container = win._container;
+
+ // Make sure autocomplete does not walk through iterators and generators.
+ let result = container.gen1.next();
+ let completion = JSPropertyProvider(dbgWindow, null, "_container.gen1.");
+ isnot(completion.matches.length, 0, "Got matches for gen1");
+
+ is(result + 1, container.gen1.next(), "gen1.next() did not execute");
+
+ result = container.gen2.next().value;
+
+ completion = JSPropertyProvider(dbgWindow, null, "_container.gen2.");
+ isnot(completion.matches.length, 0, "Got matches for gen2");
+
+ is((result / 2 + 1) * 2, container.gen2.next().value,
+ "gen2.next() did not execute");
+
+ result = container.iter1.next();
+ is(result[0], "foo", "iter1.next() [0] is correct");
+ is(result[1], "bar", "iter1.next() [1] is correct");
+
+ completion = JSPropertyProvider(dbgWindow, null, "_container.iter1.");
+ isnot(completion.matches.length, 0, "Got matches for iter1");
+
+ result = container.iter1.next();
+ is(result[0], "baz", "iter1.next() [0] is correct");
+ is(result[1], "baaz", "iter1.next() [1] is correct");
+
+ let dbgContent = dbg.makeGlobalObjectReference(content);
+ completion = JSPropertyProvider(dbgContent, null, "_container.iter2.");
+ isnot(completion.matches.length, 0, "Got matches for iter2");
+
+ completion = JSPropertyProvider(dbgWindow, null, "window._container.");
+ ok(completion, "matches available for window._container");
+ ok(completion.matches.length, "matches available for window (length)");
+
+ dbg.removeDebuggee(content);
+ jsterm.clearOutput();
+
+ jsterm.execute("window._container", (msg) => {
+ jsterm.once("variablesview-fetched", testVariablesView.bind(null, HUD));
+ let anchor = msg.querySelector(".message-body a");
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, HUD.iframeWindow);
+ });
+}
+
+function testVariablesView(aWebconsole, aEvent, aView) {
+ findVariableViewProperties(aView, [
+ { name: "gen1", isGenerator: true },
+ { name: "gen2", isGenerator: true },
+ { name: "iter1", isIterator: true },
+ { name: "iter2", isIterator: true },
+ ], { webconsole: aWebconsole }).then(function () {
+ executeSoon(finishTest);
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_632817.js b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
new file mode 100644
index 000000000..561e3b112
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
@@ -0,0 +1,217 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that Web console messages can be filtered for NET events.
+
+"use strict";
+
+const TEST_NETWORK_REQUEST_URI =
+ "https://example.com/browser/devtools/client/webconsole/test/" +
+ "test-network-request.html";
+
+const TEST_IMG = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-image.png";
+
+const TEST_DATA_JSON_CONTENT =
+ '{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }';
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console network logging " +
+ "tests";
+
+const PAGE_REQUEST_PREDICATE =
+ ({ request }) => request.url.endsWith("test-network-request.html");
+
+const TEST_DATA_REQUEST_PREDICATE =
+ ({ request }) => request.url.endsWith("test-data.json");
+
+const XHR_WARN_REQUEST_PREDICATE =
+ ({ request }) => request.url.endsWith("sjs_cors-test-server.sjs");
+
+let hud;
+
+add_task(function*() {
+ const PREF = "devtools.webconsole.persistlog";
+ const NET_PREF = "devtools.webconsole.filter.networkinfo";
+ const NETXHR_PREF = "devtools.webconsole.filter.netxhr";
+ const MIXED_AC_PREF = "security.mixed_content.block_active_content";
+ let original = Services.prefs.getBoolPref(NET_PREF);
+ let originalXhr = Services.prefs.getBoolPref(NETXHR_PREF);
+ let originalMixedActive = Services.prefs.getBoolPref(MIXED_AC_PREF);
+ Services.prefs.setBoolPref(NET_PREF, true);
+ Services.prefs.setBoolPref(NETXHR_PREF, true);
+ Services.prefs.setBoolPref(MIXED_AC_PREF, false);
+ Services.prefs.setBoolPref(PREF, true);
+ registerCleanupFunction(() => {
+ Services.prefs.setBoolPref(NET_PREF, original);
+ Services.prefs.setBoolPref(NETXHR_PREF, originalXhr);
+ Services.prefs.setBoolPref(MIXED_AC_PREF, originalMixedActive);
+ Services.prefs.clearUserPref(PREF);
+ hud = null;
+ });
+
+ yield loadTab(TEST_URI);
+ hud = yield openConsole();
+
+ yield testPageLoad();
+ yield testXhrGet();
+ yield testXhrWarn();
+ yield testXhrPost();
+ yield testFormSubmission();
+ yield testLiveFilteringOnSearchStrings();
+});
+
+function testPageLoad() {
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_NETWORK_REQUEST_URI);
+ let lastRequest = yield waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
+
+ // Check if page load was logged correctly.
+ ok(lastRequest, "Page load was logged");
+ is(lastRequest.request.url, TEST_NETWORK_REQUEST_URI,
+ "Logged network entry is page load");
+ is(lastRequest.request.method, "GET", "Method is correct");
+}
+
+function testXhrGet() {
+ // Start the XMLHttpRequest() GET test.
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+ content.wrappedJSObject.testXhrGet();
+ });
+
+ let lastRequest = yield waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE);
+
+ ok(lastRequest, "testXhrGet() was logged");
+ is(lastRequest.request.method, "GET", "Method is correct");
+ ok(lastRequest.isXHR, "It's an XHR request");
+}
+
+function testXhrWarn() {
+ // Start the XMLHttpRequest() warn test.
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+ content.wrappedJSObject.testXhrWarn();
+ });
+
+ let lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
+ if (lastRequest.request.method == "HEAD") {
+ // in non-e10s, we get the HEAD request that priming sends, so make sure
+ // a priming request should be sent, and then get the actual request
+ is(Services.prefs.getBoolPref("security.mixed_content.send_hsts_priming"),
+ true, "Found HSTS Priming Request");
+ lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
+ }
+
+ ok(lastRequest, "testXhrWarn() was logged");
+ is(lastRequest.request.method, "GET", "Method is correct");
+ ok(lastRequest.isXHR, "It's an XHR request");
+ is(lastRequest.securityInfo, "insecure", "It's an insecure request");
+}
+
+function testXhrPost() {
+ // Start the XMLHttpRequest() POST test.
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+ content.wrappedJSObject.testXhrPost();
+ });
+
+ let lastRequest = yield waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE);
+
+ ok(lastRequest, "testXhrPost() was logged");
+ is(lastRequest.request.method, "POST", "Method is correct");
+ ok(lastRequest.isXHR, "It's an XHR request");
+}
+
+function testFormSubmission() {
+ // Start the form submission test. As the form is submitted, the page is
+ // loaded again. Bind to the load event to catch when this is done.
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+ let form = content.document.querySelector("form");
+ ok(form, "we have the HTML form");
+ form.submit();
+ });
+
+ // The form POSTs to the page URL but over https (page over http).
+ let lastRequest = yield waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
+
+ ok(lastRequest, "testFormSubmission() was logged");
+ is(lastRequest.request.method, "POST", "Method is correct");
+
+ // There should be 3 network requests pointing to the HTML file.
+ waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "test-network-request.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ count: 3,
+ },
+ {
+ text: "test-data.json",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_INFO,
+ isXhr: true,
+ count: 2,
+ },
+ {
+ text: "http://example.com/",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_WARNING,
+ isXhr: true,
+ count: 1,
+ },
+ ],
+ });
+}
+
+function testLiveFilteringOnSearchStrings() {
+ setStringFilter("http");
+ isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " +
+ "search string is set to \"http\"");
+
+ setStringFilter("HTTP");
+ isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " +
+ "search string is set to \"HTTP\"");
+
+ setStringFilter("hxxp");
+ is(countMessageNodes(), 0, "the log nodes are hidden when the search " +
+ "string is set to \"hxxp\"");
+
+ setStringFilter("ht tp");
+ isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " +
+ "search string is set to \"ht tp\"");
+
+ setStringFilter("");
+ isnot(countMessageNodes(), 0, "the log nodes are not hidden when the " +
+ "search string is removed");
+
+ setStringFilter("json");
+ is(countMessageNodes(), 2, "the log nodes show only the nodes with \"json\"");
+
+ setStringFilter("'foo'");
+ is(countMessageNodes(), 0, "the log nodes are hidden when searching for " +
+ "the string 'foo'");
+
+ setStringFilter("foo\"bar'baz\"boo'");
+ is(countMessageNodes(), 0, "the log nodes are hidden when searching for " +
+ "the string \"foo\"bar'baz\"boo'\"");
+}
+
+function countMessageNodes() {
+ let messageNodes = hud.outputNode.querySelectorAll(".message");
+ let displayedMessageNodes = 0;
+ let view = hud.iframeWindow;
+ for (let i = 0; i < messageNodes.length; i++) {
+ let computedStyle = view.getComputedStyle(messageNodes[i], null);
+ if (computedStyle.display !== "none") {
+ displayedMessageNodes++;
+ }
+ }
+
+ return displayedMessageNodes;
+}
+
+function setStringFilter(value) {
+ hud.ui.filterBox.value = value;
+ hud.ui.adjustVisibilityOnSearchStringChange();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js b/devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js
new file mode 100644
index 000000000..caaa73628
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_642108_pruneTest.js
@@ -0,0 +1,81 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console limits the number of lines displayed according to
+// the user's preferences.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>test for bug 642108.";
+const LOG_LIMIT = 20;
+
+function test() {
+ let hud;
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ let {tab} = yield loadTab(TEST_URI);
+
+ Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", LOG_LIMIT);
+ Services.prefs.setBoolPref("devtools.webconsole.filter.cssparser", true);
+
+ registerCleanupFunction(function () {
+ Services.prefs.clearUserPref("devtools.hud.loglimit.cssparser");
+ Services.prefs.clearUserPref("devtools.webconsole.filter.cssparser");
+ });
+
+ hud = yield openConsole(tab);
+
+ for (let i = 0; i < 5; i++) {
+ logCSSMessage("css log x");
+ }
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "css log x",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ repeats: 5,
+ }],
+ });
+
+ for (let i = 0; i < LOG_LIMIT + 5; i++) {
+ logCSSMessage("css log " + i);
+ }
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "css log 5",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ },
+ {
+ // LOG_LIMIT + 5
+ text: "css log 24",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ }],
+ });
+
+ is(hud.ui.outputNode.querySelectorAll(".message").length, LOG_LIMIT,
+ "number of messages");
+
+ is(Object.keys(hud.ui._repeatNodes).length, LOG_LIMIT,
+ "repeated nodes pruned from repeatNodes");
+
+ let msg = [...result.matched][0];
+ let repeats = msg.querySelector(".message-repeats");
+ is(repeats.getAttribute("value"), 1,
+ "repeated nodes pruned from repeatNodes (confirmed)");
+ }
+
+ function logCSSMessage(msg) {
+ let node = hud.ui.createMessageNode(CATEGORY_CSS, SEVERITY_WARNING, msg);
+ hud.ui.outputMessage(CATEGORY_CSS, node);
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js b/devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js
new file mode 100644
index 000000000..93063e436
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_644419_log_limits.js
@@ -0,0 +1,235 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console limits the number of lines displayed according to
+// the limit set for each category.
+
+"use strict";
+
+const INIT_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 644419: Console should " +
+ "have user-settable log limits for each message category";
+
+const TEST_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-bug-644419-log-limits.html";
+
+var hud, outputNode;
+
+add_task(function* () {
+ let { browser } = yield loadTab(INIT_URI);
+
+ hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+ outputNode = hud.outputNode;
+
+ let loaded = loadBrowser(browser);
+
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+ yield loaded;
+
+ yield testWebDevLimits();
+ yield testWebDevLimits2();
+ yield testJsLimits();
+ yield testJsLimits2();
+
+ yield testNetLimits();
+ yield loadImage();
+ yield testCssLimits();
+ yield testCssLimits2();
+
+ hud = outputNode = null;
+});
+
+function testWebDevLimits() {
+ Services.prefs.setIntPref("devtools.hud.loglimit.console", 10);
+
+ // Find the sentinel entry.
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bar is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+}
+
+function testWebDevLimits2() {
+ // Fill the log with Web Developer errors.
+ for (let i = 0; i < 11; i++) {
+ content.console.log("test message " + i);
+ }
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test message 10",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(() => {
+ testLogEntry(outputNode, "test message 0", "first message is pruned",
+ false, true);
+ findLogEntry("test message 1");
+ // Check if the sentinel entry is still there.
+ findLogEntry("bar is not defined");
+
+ Services.prefs.clearUserPref("devtools.hud.loglimit.console");
+ });
+}
+
+function testJsLimits() {
+ Services.prefs.setIntPref("devtools.hud.loglimit.exception", 10);
+
+ hud.jsterm.clearOutput();
+ content.console.log("testing JS limits");
+
+ // Find the sentinel entry.
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "testing JS limits",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+function testJsLimits2() {
+ // Fill the log with JS errors.
+ let head = content.document.getElementsByTagName("head")[0];
+ for (let i = 0; i < 11; i++) {
+ let script = content.document.createElement("script");
+ script.text = "fubar" + i + ".bogus(6);";
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ head.insertBefore(script, head.firstChild);
+ }
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "fubar10 is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ }],
+ }).then(() => {
+ testLogEntry(outputNode, "fubar0 is not defined", "first message is pruned",
+ false, true);
+ findLogEntry("fubar1 is not defined");
+ // Check if the sentinel entry is still there.
+ findLogEntry("testing JS limits");
+
+ Services.prefs.clearUserPref("devtools.hud.loglimit.exception");
+ });
+}
+
+var gCounter, gImage;
+
+function testNetLimits() {
+ Services.prefs.setIntPref("devtools.hud.loglimit.network", 10);
+
+ hud.jsterm.clearOutput();
+ content.console.log("testing Net limits");
+
+ // Find the sentinel entry.
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "testing Net limits",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(() => {
+ // Fill the log with network messages.
+ gCounter = 0;
+ });
+}
+
+function loadImage() {
+ if (gCounter < 11) {
+ let body = content.document.getElementsByTagName("body")[0];
+ gImage && gImage.removeEventListener("load", loadImage, true);
+ gImage = content.document.createElement("img");
+ gImage.src = "test-image.png?_fubar=" + gCounter;
+ body.insertBefore(gImage, body.firstChild);
+ gImage.addEventListener("load", loadImage, true);
+ gCounter++;
+ return true;
+ }
+
+ is(gCounter, 11, "loaded 11 files");
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "test-image.png",
+ url: "test-image.png?_fubar=10",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(() => {
+ let msgs = outputNode.querySelectorAll(".message[category=network]");
+ is(msgs.length, 10, "number of network messages");
+ isnot(msgs[0].url.indexOf("fubar=1"), -1, "first network message");
+ isnot(msgs[1].url.indexOf("fubar=2"), -1, "second network message");
+ findLogEntry("testing Net limits");
+
+ Services.prefs.clearUserPref("devtools.hud.loglimit.network");
+ });
+}
+
+function testCssLimits() {
+ Services.prefs.setIntPref("devtools.hud.loglimit.cssparser", 10);
+
+ hud.jsterm.clearOutput();
+ content.console.log("testing CSS limits");
+
+ // Find the sentinel entry.
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "testing CSS limits",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+function testCssLimits2() {
+ // Fill the log with CSS errors.
+ let body = content.document.getElementsByTagName("body")[0];
+ for (let i = 0; i < 11; i++) {
+ let div = content.document.createElement("div");
+ div.setAttribute("style", "-moz-foobar" + i + ": 42;");
+ body.insertBefore(div, body.firstChild);
+ }
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "-moz-foobar10",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ }],
+ }).then(() => {
+ testLogEntry(outputNode, "Unknown property \u2018-moz-foobar0\u2019",
+ "first message is pruned", false, true);
+ findLogEntry("Unknown property \u2018-moz-foobar1\u2019");
+ // Check if the sentinel entry is still there.
+ findLogEntry("testing CSS limits");
+
+ Services.prefs.clearUserPref("devtools.hud.loglimit.cssparser");
+ });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js b/devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js
new file mode 100644
index 000000000..81573e56f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_646025_console_file_location.js
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console logging methods display the method location along with
+// the output in the console.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console file location " +
+ "display test";
+const TEST_URI2 = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/" +
+ "test-bug-646025-console-file-location.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "message for level log",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ source: { url: "test-file-location.js", line: 8 },
+ },
+ {
+ text: "message for level info",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_INFO,
+ source: { url: "test-file-location.js", line: 9 },
+ },
+ {
+ text: "message for level warn",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_WARNING,
+ source: { url: "test-file-location.js", line: 10 },
+ },
+ {
+ text: "message for level error",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ source: { url: "test-file-location.js", line: 11 },
+ },
+ {
+ text: "message for level debug",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ source: { url: "test-file-location.js", line: 12 },
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js b/devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
new file mode 100644
index 000000000..233643d51
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js
@@ -0,0 +1,102 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that document.body autocompletes in the web console.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console autocompletion " +
+ "bug in document.body";
+
+var gHUD;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ gHUD = yield openConsole();
+
+ yield consoleOpened();
+ yield autocompletePopupHidden();
+ let view = yield testPropertyPanel();
+ yield onVariablesViewReady(view);
+
+ gHUD = null;
+});
+
+function consoleOpened() {
+ let deferred = promise.defer();
+
+ let jsterm = gHUD.jsterm;
+ let popup = jsterm.autocompletePopup;
+
+ ok(!popup.isOpen, "popup is not open");
+
+ popup.once("popup-opened", () => {
+ ok(popup.isOpen, "popup is open");
+
+ is(popup.itemCount, jsterm._autocompleteCache.length,
+ "popup.itemCount is correct");
+ isnot(jsterm._autocompleteCache.indexOf("addEventListener"), -1,
+ "addEventListener is in the list of suggestions");
+ isnot(jsterm._autocompleteCache.indexOf("bgColor"), -1,
+ "bgColor is in the list of suggestions");
+ isnot(jsterm._autocompleteCache.indexOf("ATTRIBUTE_NODE"), -1,
+ "ATTRIBUTE_NODE is in the list of suggestions");
+
+ popup.once("popup-closed", () => {
+ deferred.resolve();
+ });
+ EventUtils.synthesizeKey("VK_ESCAPE", {});
+ });
+
+ jsterm.setInputValue("document.body");
+ EventUtils.synthesizeKey(".", {});
+
+ return deferred.promise;
+}
+
+function autocompletePopupHidden() {
+ let deferred = promise.defer();
+
+ let jsterm = gHUD.jsterm;
+ let popup = jsterm.autocompletePopup;
+ let completeNode = jsterm.completeNode;
+
+ ok(!popup.isOpen, "popup is not open");
+
+ jsterm.once("autocomplete-updated", function () {
+ is(completeNode.value, testStr + "dy", "autocomplete shows document.body");
+ deferred.resolve();
+ });
+
+ let inputStr = "document.b";
+ jsterm.setInputValue(inputStr);
+ EventUtils.synthesizeKey("o", {});
+ let testStr = inputStr.replace(/./g, " ") + " ";
+
+ return deferred.promise;
+}
+
+function testPropertyPanel() {
+ let deferred = promise.defer();
+
+ let jsterm = gHUD.jsterm;
+ jsterm.clearOutput();
+ jsterm.execute("document", (msg) => {
+ jsterm.once("variablesview-fetched", (evt, view) => {
+ deferred.resolve(view);
+ });
+ let anchor = msg.querySelector(".message-body a");
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, gHUD.iframeWindow);
+ });
+
+ return deferred.promise;
+}
+
+function onVariablesViewReady(view) {
+ return findVariableViewProperties(view, [
+ { name: "body", value: "<body>" },
+ ], { webconsole: gHUD });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js b/devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
new file mode 100644
index 000000000..217d481e2
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
@@ -0,0 +1,109 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the $0 console helper works as intended.
+
+"use strict";
+
+var inspector, h1, outputNode;
+
+function createDocument() {
+ let doc = content.document;
+ let div = doc.createElement("div");
+ h1 = doc.createElement("h1");
+ let p1 = doc.createElement("p");
+ let p2 = doc.createElement("p");
+ let div2 = doc.createElement("div");
+ let p3 = doc.createElement("p");
+ doc.title = "Inspector Tree Selection Test";
+ h1.textContent = "Inspector Tree Selection Test";
+ p1.textContent = "This is some example text";
+ p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
+ "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
+ "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
+ "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
+ "dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
+ "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
+ "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " +
+ "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " +
+ "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " +
+ "dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
+ "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
+ "proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
+ div.appendChild(h1);
+ div.appendChild(p1);
+ div.appendChild(p2);
+ div2.appendChild(p3);
+ doc.body.appendChild(div);
+ doc.body.appendChild(div2);
+ setupHighlighterTests();
+}
+
+function setupHighlighterTests() {
+ ok(h1, "we have the header node");
+ openInspector().then(runSelectionTests);
+}
+
+var runSelectionTests = Task.async(function* (aInspector) {
+ inspector = aInspector;
+
+ let onPickerStarted = inspector.toolbox.once("picker-started");
+ inspector.toolbox.highlighterUtils.startPicker();
+ yield onPickerStarted;
+
+ info("Picker mode started, now clicking on H1 to select that node");
+ h1.scrollIntoView();
+ let onPickerStopped = inspector.toolbox.once("picker-stopped");
+ let onInspectorUpdated = inspector.once("inspector-updated");
+ EventUtils.synthesizeMouseAtCenter(h1, {}, content);
+ yield onPickerStopped;
+ yield onInspectorUpdated;
+
+ info("Picker mode stopped, H1 selected, now switching to the console");
+ let hud = yield openConsole(gBrowser.selectedTab);
+
+ performWebConsoleTests(hud);
+});
+
+function performWebConsoleTests(hud) {
+ let jsterm = hud.jsterm;
+ outputNode = hud.outputNode;
+
+ jsterm.clearOutput();
+ jsterm.execute("$0", onNodeOutput);
+
+ function onNodeOutput(node) {
+ isnot(node.textContent.indexOf("<h1>"), -1, "correct output for $0");
+
+ jsterm.clearOutput();
+ jsterm.execute("$0.textContent = 'bug653531'", onNodeUpdate);
+ }
+
+ function onNodeUpdate(node) {
+ isnot(node.textContent.indexOf("bug653531"), -1,
+ "correct output for $0.textContent");
+ is(inspector.selection.node.textContent, "bug653531",
+ "node successfully updated");
+
+ inspector = h1 = outputNode = null;
+ gBrowser.removeCurrentTab();
+ finishTest();
+ }
+}
+
+function test() {
+ waitForExplicitFinish();
+
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
+ waitForFocus(createDocument, content);
+ }, true);
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser,
+ "data:text/html;charset=utf-8,test for highlighter helper in web console");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js b/devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js
new file mode 100644
index 000000000..2c6c933fc
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_658368_time_methods.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Console API implements the time() and timeEnd() methods.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-658368-time-methods.html";
+
+const TEST_URI2 = "data:text/html;charset=utf-8,<script>" +
+ "console.timeEnd('bTimer');</script>";
+
+const TEST_URI3 = "data:text/html;charset=utf-8,<script>" +
+ "console.time('bTimer');</script>";
+
+const TEST_URI4 = "data:text/html;charset=utf-8," +
+ "<script>console.timeEnd('bTimer');</script>";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud1 = yield openConsole();
+
+ yield waitForMessages({
+ webconsole: hud1,
+ messages: [{
+ name: "aTimer started",
+ consoleTime: "aTimer",
+ }, {
+ name: "aTimer end",
+ consoleTimeEnd: "aTimer",
+ }],
+ });
+
+ // The next test makes sure that timers with the same name but in separate
+ // tabs, do not contain the same value.
+ let { browser } = yield loadTab(TEST_URI2);
+ let hud2 = yield openConsole();
+
+ testLogEntry(hud2.outputNode, "bTimer: timer started",
+ "bTimer was not started", false, true);
+
+ // The next test makes sure that timers with the same name but in separate
+ // pages, do not contain the same value.
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI3);
+
+ yield waitForMessages({
+ webconsole: hud2,
+ messages: [{
+ name: "bTimer started",
+ consoleTime: "bTimer",
+ }],
+ });
+
+ hud2.jsterm.clearOutput();
+
+ // Now the following console.timeEnd() call shouldn't display anything,
+ // if the timers in different pages are not related.
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI4);
+ yield loadBrowser(browser);
+
+ testLogEntry(hud2.outputNode, "bTimer: timer started",
+ "bTimer was not started", false, true);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js b/devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js
new file mode 100644
index 000000000..03741a249
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_659907_console_dir.js
@@ -0,0 +1,36 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console.dir works as intended.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 659907: Expand console object with a dir method";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ hud.jsterm.execute("console.dir(document)");
+
+ let varView = yield hud.jsterm.once("variablesview-fetched");
+
+ yield findVariableViewProperties(varView, [
+ {
+ name: "__proto__.__proto__.querySelectorAll",
+ value: "querySelectorAll()"
+ },
+ {
+ name: "location",
+ value: /Location \u2192 data:Web/
+ },
+ {
+ name: "__proto__.write",
+ value: "write()"
+ },
+ ], { webconsole: hud });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js b/devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js
new file mode 100644
index 000000000..5906d62d6
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_660806_history_nav.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>bug 660806 - history " +
+ "navigation must not show the autocomplete popup";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield consoleOpened(hud);
+});
+
+function consoleOpened(HUD) {
+ let deferred = promise.defer();
+
+ let jsterm = HUD.jsterm;
+ let popup = jsterm.autocompletePopup;
+ let onShown = function () {
+ ok(false, "popup shown");
+ };
+
+ jsterm.execute(`window.foobarBug660806 = {
+ 'location': 'value0',
+ 'locationbar': 'value1'
+ }`);
+
+ popup.on("popup-opened", onShown);
+
+ ok(!popup.isOpen, "popup is not open");
+
+ ok(!jsterm.lastInputValue, "no lastInputValue");
+ jsterm.setInputValue("window.foobarBug660806.location");
+ is(jsterm.lastInputValue, "window.foobarBug660806.location",
+ "lastInputValue is correct");
+
+ EventUtils.synthesizeKey("VK_RETURN", {});
+ EventUtils.synthesizeKey("VK_UP", {});
+
+ is(jsterm.lastInputValue, "window.foobarBug660806.location",
+ "lastInputValue is correct, again");
+
+ executeSoon(function () {
+ ok(!popup.isOpen, "popup is not open");
+ popup.off("popup-opened", onShown);
+ executeSoon(deferred.resolve);
+ });
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js b/devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js
new file mode 100644
index 000000000..fd510240e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_664131_console_group.js
@@ -0,0 +1,79 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console.group/groupEnd works as intended.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 664131: Expand console object with group methods";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ let jsterm = hud.jsterm;
+
+ hud.jsterm.clearOutput();
+
+ yield jsterm.execute("console.group('bug664131a')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug664131a",
+ consoleGroup: 1,
+ }],
+ });
+
+ yield jsterm.execute("console.log('bug664131a-inside')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug664131a-inside",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ groupDepth: 1,
+ }],
+ });
+
+ yield jsterm.execute('console.groupEnd("bug664131a")');
+ yield jsterm.execute('console.log("bug664131-outside")');
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug664131-outside",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ groupDepth: 0,
+ }],
+ });
+
+ yield jsterm.execute('console.groupCollapsed("bug664131b")');
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug664131b",
+ consoleGroup: 1,
+ }],
+ });
+
+ // Test that clearing the console removes the indentation.
+ hud.jsterm.clearOutput();
+ yield jsterm.execute('console.log("bug664131-cleared")');
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "bug664131-cleared",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ groupDepth: 0,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js b/devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js
new file mode 100644
index 000000000..2b1588ef9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js
@@ -0,0 +1,75 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the autocompletion results contain the names of JSTerm helpers.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test JSTerm Helpers " +
+ "autocomplete";
+
+var jsterm;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+ let popup = jsterm.autocompletePopup;
+
+ // Test if 'i' gives 'inspect'
+ input.value = "i";
+ input.setSelectionRange(1, 1);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ let newItems = popup.getItems().map(function (e) {
+ return e.label;
+ });
+ ok(newItems.indexOf("inspect") > -1,
+ "autocomplete results contain helper 'inspect'");
+
+ // Test if 'window.' does not give 'inspect'.
+ input.value = "window.";
+ input.setSelectionRange(7, 7);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ newItems = popup.getItems().map(function (e) {
+ return e.label;
+ });
+ is(newItems.indexOf("inspect"), -1,
+ "autocomplete results do not contain helper 'inspect'");
+
+ // Test if 'dump(i' gives 'inspect'
+ input.value = "dump(i";
+ input.setSelectionRange(6, 6);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ newItems = popup.getItems().map(function (e) {
+ return e.label;
+ });
+ ok(newItems.indexOf("inspect") > -1,
+ "autocomplete results contain helper 'inspect'");
+
+ // Test if 'window.dump(i' gives 'inspect'
+ input.value = "window.dump(i";
+ input.setSelectionRange(13, 13);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ newItems = popup.getItems().map(function (e) {
+ return e.label;
+ });
+ ok(newItems.indexOf("inspect") > -1,
+ "autocomplete results contain helper 'inspect'");
+
+ jsterm = null;
+});
+
+function complete(type) {
+ let updated = jsterm.once("autocomplete-updated");
+ jsterm.complete(type);
+ return updated;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_704295.js b/devtools/client/webconsole/test/browser_webconsole_bug_704295.js
new file mode 100644
index 000000000..df21232cf
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_704295.js
@@ -0,0 +1,41 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests for bug 704295
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ testCompletion(hud);
+});
+
+function testCompletion(hud) {
+ let jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+
+ // Test typing 'var d = 5;' and press RETURN
+ jsterm.setInputValue("var d = ");
+ EventUtils.synthesizeKey("5", {});
+ EventUtils.synthesizeKey(";", {});
+ is(input.value, "var d = 5;", "var d = 5;");
+ is(jsterm.completeNode.value, "", "no completion");
+ EventUtils.synthesizeKey("VK_RETURN", {});
+ is(jsterm.completeNode.value, "", "clear completion on execute()");
+
+ // Test typing 'var a = d' and press RETURN
+ jsterm.setInputValue("var a = ");
+ EventUtils.synthesizeKey("d", {});
+ is(input.value, "var a = d", "var a = d");
+ is(jsterm.completeNode.value, "", "no completion");
+ EventUtils.synthesizeKey("VK_RETURN", {});
+ is(jsterm.completeNode.value, "", "clear completion on execute()");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js b/devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js
new file mode 100644
index 000000000..af9e172c8
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/browser/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+
+ is(input.getAttribute("focused"), "true", "input has focus");
+ EventUtils.synthesizeKey("VK_TAB", {});
+ is(input.getAttribute("focused"), "", "focus moved away");
+
+ // Test user changed something
+ input.focus();
+ EventUtils.synthesizeKey("A", {});
+ EventUtils.synthesizeKey("VK_TAB", {});
+ is(input.getAttribute("focused"), "true", "input is still focused");
+
+ // Test non empty input but not changed since last focus
+ input.blur();
+ input.focus();
+ EventUtils.synthesizeKey("VK_RIGHT", {});
+ EventUtils.synthesizeKey("VK_TAB", {});
+ is(input.getAttribute("focused"), "", "input moved away");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js b/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js
new file mode 100644
index 000000000..4665af42a
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_737873_mixedcontent.js
@@ -0,0 +1,63 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console Mixed Content messages are displayed
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Web Console mixed content test";
+const TEST_HTTPS_URI = "https://example.com/browser/devtools/client/" +
+ "webconsole/test/test-bug-737873-mixedcontent.html";
+const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
+ "Mixed_content";
+
+registerCleanupFunction(function*() {
+ Services.prefs.clearUserPref("security.mixed_content.block_display_content");
+ Services.prefs.clearUserPref("security.mixed_content.block_active_content");
+});
+
+add_task(function* () {
+ Services.prefs.setBoolPref("security.mixed_content.block_display_content",
+ false);
+ Services.prefs.setBoolPref("security.mixed_content.block_active_content",
+ false);
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield testMixedContent(hud);
+});
+
+var testMixedContent = Task.async(function* (hud) {
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_HTTPS_URI);
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "example.com",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_WARNING,
+ }],
+ });
+
+ let msg = [...results[0].matched][0];
+ ok(msg, "page load logged");
+ ok(msg.classList.contains("mixed-content"), ".mixed-content element");
+
+ let link = msg.querySelector(".learn-more-link");
+ ok(link, "mixed content link element");
+ is(link.textContent, "[Mixed Content]", "link text is accurate");
+
+ yield simulateMessageLinkClick(link, LEARN_MORE_URI);
+
+ ok(!msg.classList.contains("filtered-by-type"), "message is not filtered");
+
+ hud.setFilterState("netwarn", false);
+
+ ok(msg.classList.contains("filtered-by-type"), "message is filtered");
+
+ hud.setFilterState("netwarn", true);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js b/devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js
new file mode 100644
index 000000000..85b99a79a
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js
@@ -0,0 +1,83 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that warnings about ineffective iframe sandboxing are logged to the
+// web console when necessary (and not otherwise).
+
+"use strict";
+
+requestLongerTimeout(2);
+
+const TEST_URI_WARNING = "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html";
+const TEST_URI_NOWARNING = [
+ "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html",
+ "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html",
+ "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html",
+ "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html",
+ "http://example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html"
+];
+
+const INEFFECTIVE_IFRAME_SANDBOXING_MSG = "An iframe which has both " +
+ "allow-scripts and allow-same-origin for its sandbox attribute can remove " +
+ "its sandboxing.";
+const SENTINEL_MSG = "testing ineffective sandboxing message";
+
+add_task(function* () {
+ yield testYesWarning();
+
+ for (let id = 0; id < TEST_URI_NOWARNING.length; id++) {
+ yield testNoWarning(id);
+ }
+});
+
+function* testYesWarning() {
+ yield loadTab(TEST_URI_WARNING);
+ let hud = yield openConsole();
+
+ ContentTask.spawn(gBrowser.selectedBrowser, SENTINEL_MSG, function* (msg) {
+ content.console.log(msg);
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Ineffective iframe sandboxing warning displayed successfully",
+ text: INEFFECTIVE_IFRAME_SANDBOXING_MSG,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING
+ },
+ {
+ text: SENTINEL_MSG,
+ severity: SEVERITY_LOG
+ }
+ ]
+ });
+
+ let msgs = hud.outputNode.querySelectorAll(".message[category=security]");
+ is(msgs.length, 1, "one security message");
+}
+
+function* testNoWarning(id) {
+ yield loadTab(TEST_URI_NOWARNING[id]);
+ let hud = yield openConsole();
+
+ ContentTask.spawn(gBrowser.selectedBrowser, SENTINEL_MSG, function* (msg) {
+ content.console.log(msg);
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: SENTINEL_MSG,
+ severity: SEVERITY_LOG
+ }
+ ]
+ });
+
+ let msgs = hud.outputNode.querySelectorAll(".message[category=security]");
+ is(msgs.length, 0, "no security messages (case " + id + ")");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js
new file mode 100644
index 000000000..49df4d1fc
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that errors about insecure passwords are logged to the web console.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html";
+const INSECURE_PASSWORD_MSG = "Password fields present on an insecure " +
+ "(http://) iframe. This is a security risk that allows user login " +
+ "credentials to be stolen.";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Insecure password error displayed successfully",
+ text: INSECURE_PASSWORD_MSG,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING
+ },
+ ],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js
new file mode 100644
index 000000000..00a620fc8
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js
@@ -0,0 +1,62 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ // Tests that errors about insecure passwords are logged to the web console.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-762593-insecure-passwords-web-" +
+ "console-warning.html";
+const INSECURE_PASSWORD_MSG = "Password fields present on an insecure " +
+ "(http://) page. This is a security risk that allows user " +
+ "login credentials to be stolen.";
+const INSECURE_FORM_ACTION_MSG = "Password fields present in a form with an " +
+ "insecure (http://) form action. This is a security risk " +
+ "that allows user login credentials to be stolen.";
+const INSECURE_IFRAME_MSG = "Password fields present on an insecure " +
+ "(http://) iframe. This is a security risk that allows " +
+ "user login credentials to be stolen.";
+const INSECURE_PASSWORDS_URI = "https://developer.mozilla.org/docs/Web/" +
+ "Security/Insecure_passwords" + DOCS_GA_PARAMS;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let result = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Insecure password error displayed successfully",
+ text: INSECURE_PASSWORD_MSG,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING
+ },
+ {
+ name: "Insecure iframe error displayed successfully",
+ text: INSECURE_IFRAME_MSG,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING
+ },
+ {
+ name: "Insecure form action error displayed successfully",
+ text: INSECURE_FORM_ACTION_MSG,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING
+ },
+ ],
+ });
+
+ yield testClickOpenNewTab(hud, result);
+});
+
+function testClickOpenNewTab(hud, [result]) {
+ let msg = [...result.matched][0];
+ let warningNode = msg.querySelector(".learn-more-link");
+ ok(warningNode, "learn more link");
+ return simulateMessageLinkClick(warningNode, INSECURE_PASSWORDS_URI);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js b/devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js
new file mode 100644
index 000000000..731e79d8b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_764572_output_open_url.js
@@ -0,0 +1,142 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This is a test for the Open URL context menu item
+// that is shown for network requests
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+const COMMAND_NAME = "consoleCmd_openURL";
+const CONTEXT_MENU_ID = "#menu_openURL";
+
+var HUD = null, outputNode = null, contextMenu = null;
+
+add_task(function* () {
+ Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true);
+
+ yield loadTab(TEST_URI);
+ HUD = yield openConsole();
+
+ let results = yield consoleOpened();
+ yield onConsoleMessage(results);
+
+ let results2 = yield testOnNetActivity();
+ let msg = yield onNetworkMessage(results2);
+
+ yield testOnNetActivityContextMenu(msg);
+
+ Services.prefs.clearUserPref("devtools.webconsole.filter.networkinfo");
+
+ HUD = null;
+ outputNode = null;
+ contextMenu = null;
+});
+
+function consoleOpened() {
+ outputNode = HUD.outputNode;
+ contextMenu = HUD.iframeWindow.document.getElementById("output-contextmenu");
+
+ HUD.jsterm.clearOutput();
+
+ content.console.log("bug 764572");
+
+ return waitForMessages({
+ webconsole: HUD,
+ messages: [{
+ text: "bug 764572",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+function onConsoleMessage(results) {
+ outputNode.focus();
+ outputNode.selectedItem = [...results[0].matched][0];
+
+ // Check if the command is disabled non-network messages.
+ goUpdateCommand(COMMAND_NAME);
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand(COMMAND_NAME);
+
+ let isDisabled = !controller || !controller.isCommandEnabled(COMMAND_NAME);
+ ok(isDisabled, COMMAND_NAME + " should be disabled.");
+
+ return waitForContextMenu(contextMenu, outputNode.selectedItem, () => {
+ let isHidden = contextMenu.querySelector(CONTEXT_MENU_ID).hidden;
+ ok(isHidden, CONTEXT_MENU_ID + " should be hidden.");
+ });
+}
+
+function testOnNetActivity() {
+ HUD.jsterm.clearOutput();
+
+ // Reload the url to show net activity in console.
+ content.location.reload();
+
+ return waitForMessages({
+ webconsole: HUD,
+ messages: [{
+ text: "test-console.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+function onNetworkMessage(results) {
+ let deferred = promise.defer();
+
+ outputNode.focus();
+ let msg = [...results[0].matched][0];
+ ok(msg, "network message");
+ HUD.ui.output.selectMessage(msg);
+
+ let currentTab = gBrowser.selectedTab;
+ let newTab = null;
+
+ gBrowser.tabContainer.addEventListener("TabOpen", function onOpen(evt) {
+ gBrowser.tabContainer.removeEventListener("TabOpen", onOpen, true);
+ newTab = evt.target;
+ newTab.linkedBrowser.addEventListener("load", onTabLoaded, true);
+ }, true);
+
+ function onTabLoaded() {
+ newTab.linkedBrowser.removeEventListener("load", onTabLoaded, true);
+ gBrowser.removeTab(newTab);
+ gBrowser.selectedTab = currentTab;
+ executeSoon(deferred.resolve.bind(null, msg));
+ }
+
+ // Check if the command is enabled for a network message.
+ goUpdateCommand(COMMAND_NAME);
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand(COMMAND_NAME);
+ ok(controller.isCommandEnabled(COMMAND_NAME),
+ COMMAND_NAME + " should be enabled.");
+
+ // Try to open the URL.
+ goDoCommand(COMMAND_NAME);
+
+ return deferred.promise;
+}
+
+function testOnNetActivityContextMenu(msg) {
+ let deferred = promise.defer();
+
+ outputNode.focus();
+ HUD.ui.output.selectMessage(msg);
+
+ info("net activity context menu");
+
+ waitForContextMenu(contextMenu, msg, () => {
+ let isShown = !contextMenu.querySelector(CONTEXT_MENU_ID).hidden;
+ ok(isShown, CONTEXT_MENU_ID + " should be shown.");
+ }).then(deferred.resolve);
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
new file mode 100644
index 000000000..3686fba89
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
@@ -0,0 +1,88 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that message source links for js errors and console API calls open in
+// the jsdebugger when clicked.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test" +
+ "/test-bug-766001-js-console-links.html";
+
+// Force the new debugger UI, in case this gets uplifted with the old
+// debugger still turned on
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+function test() {
+ let hud;
+
+ requestLongerTimeout(2);
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+
+ let {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+
+ let [exceptionRule, consoleRule] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "document.bar",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ {
+ text: "Blah Blah",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let exceptionMsg = [...exceptionRule.matched][0];
+ let consoleMsg = [...consoleRule.matched][0];
+ let nodes = [exceptionMsg.querySelector(".message-location > .frame-link"),
+ consoleMsg.querySelector(".message-location > .frame-link")];
+ ok(nodes[0], ".location node for the exception message");
+ ok(nodes[1], ".location node for the console message");
+
+ for (let i = 0; i < nodes.length; i++) {
+ yield checkClickOnNode(i, nodes[i]);
+ yield gDevTools.showToolbox(hud.target, "webconsole");
+ }
+
+ // check again the first node.
+ yield checkClickOnNode(0, nodes[0]);
+ }
+
+ function* checkClickOnNode(index, node) {
+ info("checking click on node index " + index);
+
+ let url = node.getAttribute("data-url");
+ ok(url, "source url found for index " + index);
+
+ let line = node.getAttribute("data-line");
+ ok(line, "found source line for index " + index);
+
+ executeSoon(() => {
+ EventUtils.sendMouseEvent({ type: "click" }, node.querySelector(".frame-link-filename"));
+ });
+
+ yield hud.ui.once("source-in-debugger-opened");
+
+ let toolbox = yield gDevTools.getToolbox(hud.target);
+ let dbg = toolbox.getPanel("jsdebugger");
+ is(dbg._selectors().getSelectedSource(dbg._getState()).get("url"),
+ url,
+ "expected source url");
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js b/devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js
new file mode 100644
index 000000000..3a7134202
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_770099_violation.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console CSP messages are displayed
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Web Console CSP violation test";
+const TEST_VIOLATION = "https://example.com/browser/devtools/client/" +
+ "webconsole/test/test_bug_770099_violation.html";
+const CSP_VIOLATION_MSG = "Content Security Policy: The page\u2019s settings " +
+ "blocked the loading of a resource at " +
+ "http://some.example.com/test.png (\u201cdefault-src " +
+ "https://example.com\u201d).";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_VIOLATION);
+ yield loaded;
+
+ yield waitForSuccess({
+ name: "CSP policy URI warning displayed successfully",
+ validator: function () {
+ return hud.outputNode.textContent.indexOf(CSP_VIOLATION_MSG) > -1;
+ }
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
new file mode 100644
index 000000000..f2efd7922
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
@@ -0,0 +1,140 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/test" +
+ "/test-bug-782653-css-errors.html";
+
+var nodes, hud, StyleEditorUI;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+
+ let styleEditor = yield testViewSource();
+ yield onStyleEditorReady(styleEditor);
+
+ nodes = hud = StyleEditorUI = null;
+});
+
+function testViewSource() {
+ let deferred = promise.defer();
+
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "\u2018font-weight\u2019",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ },
+ {
+ text: "\u2018color\u2019",
+ category: CATEGORY_CSS,
+ severity: SEVERITY_WARNING,
+ }],
+ }).then(([error1Rule, error2Rule]) => {
+ let error1Msg = [...error1Rule.matched][0];
+ let error2Msg = [...error2Rule.matched][0];
+ nodes = [error1Msg.querySelector(".message-location .frame-link"),
+ error2Msg.querySelector(".message-location .frame-link")];
+ ok(nodes[0], ".frame-link node for the first error");
+ ok(nodes[1], ".frame-link node for the second error");
+
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = gDevTools.getToolbox(target);
+ toolbox.once("styleeditor-selected", (event, panel) => {
+ StyleEditorUI = panel.UI;
+ deferred.resolve(panel);
+ });
+
+ EventUtils.sendMouseEvent({ type: "click" }, nodes[0].querySelector(".frame-link-filename"));
+ });
+
+ return deferred.promise;
+}
+
+function onStyleEditorReady(panel) {
+ let deferred = promise.defer();
+
+ let win = panel.panelWindow;
+ ok(win, "Style Editor Window is defined");
+ ok(StyleEditorUI, "Style Editor UI is defined");
+
+ function fireEvent(toolbox, href, line) {
+ toolbox.once("styleeditor-selected", function (evt) {
+ info(evt + " event fired");
+
+ checkStyleEditorForSheetAndLine(href, line - 1).then(deferred.resolve);
+ });
+
+ EventUtils.sendMouseEvent({ type: "click" }, nodes[1].querySelector(".frame-link-filename"));
+ }
+
+ waitForFocus(function () {
+ info("style editor window focused");
+
+ let href = nodes[0].getAttribute("data-url");
+ let line = nodes[0].getAttribute("data-line");
+ ok(line, "found source line");
+
+ checkStyleEditorForSheetAndLine(href, line - 1).then(function () {
+ info("first check done");
+
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = gDevTools.getToolbox(target);
+
+ href = nodes[1].getAttribute("data-url");
+ line = nodes[1].getAttribute("data-line");
+ ok(line, "found source line");
+
+ toolbox.selectTool("webconsole").then(function () {
+ info("webconsole selected");
+ fireEvent(toolbox, href, line);
+ });
+ });
+ }, win);
+
+ return deferred.promise;
+}
+
+function checkStyleEditorForSheetAndLine(href, line) {
+ let foundEditor = null;
+ for (let editor of StyleEditorUI.editors) {
+ if (editor.styleSheet.href == href) {
+ foundEditor = editor;
+ break;
+ }
+ }
+
+ ok(foundEditor, "found style editor for " + href);
+ return performLineCheck(foundEditor, line);
+}
+
+function performLineCheck(editor, line) {
+ let deferred = promise.defer();
+
+ function checkForCorrectState() {
+ is(editor.sourceEditor.getCursor().line, line,
+ "correct line is selected");
+ is(StyleEditorUI.selectedStyleSheetIndex, editor.styleSheet.styleSheetIndex,
+ "correct stylesheet is selected in the editor");
+
+ executeSoon(deferred.resolve);
+ }
+
+ info("wait for source editor to load");
+
+ // Get out of the styleeditor-selected event loop.
+ executeSoon(() => {
+ editor.getSourceEditor().then(() => {
+ // Get out of the editor's source-editor-load event loop.
+ executeSoon(checkForCorrectState);
+ });
+ });
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js b/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js
new file mode 100644
index 000000000..b040e6314
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_804845_ctrl_key_nav.js
@@ -0,0 +1,227 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test navigation of webconsole contents via ctrl-a, ctrl-e, ctrl-p, ctrl-n
+// see https://bugzilla.mozilla.org/show_bug.cgi?id=804845
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 804845 and bug 619598";
+
+var jsterm, inputNode;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ doTests(hud);
+
+ jsterm = inputNode = null;
+});
+
+function doTests(HUD) {
+ jsterm = HUD.jsterm;
+ inputNode = jsterm.inputNode;
+ ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
+ is(jsterm.inputNode.selectionStart, 0);
+ is(jsterm.inputNode.selectionEnd, 0);
+
+ testSingleLineInputNavNoHistory();
+ testMultiLineInputNavNoHistory();
+ testNavWithHistory();
+}
+
+function testSingleLineInputNavNoHistory() {
+ // Single char input
+ EventUtils.synthesizeKey("1", {});
+ is(inputNode.selectionStart, 1, "caret location after single char input");
+
+ // nav to start/end with ctrl-a and ctrl-e;
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ is(inputNode.selectionStart, 0,
+ "caret location after single char input and ctrl-a");
+
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ is(inputNode.selectionStart, 1,
+ "caret location after single char input and ctrl-e");
+
+ // Second char input
+ EventUtils.synthesizeKey("2", {});
+ // nav to start/end with up/down keys; verify behaviour using ctrl-p/ctrl-n
+ EventUtils.synthesizeKey("VK_UP", {});
+ is(inputNode.selectionStart, 0,
+ "caret location after two char input and VK_UP");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(inputNode.selectionStart, 2,
+ "caret location after two char input and VK_DOWN");
+
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ is(inputNode.selectionStart, 0,
+ "move caret to beginning of 2 char input with ctrl-a");
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ is(inputNode.selectionStart, 0,
+ "no change of caret location on repeat ctrl-a");
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(inputNode.selectionStart, 0,
+ "no change of caret location on ctrl-p from beginning of line");
+
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ is(inputNode.selectionStart, 2,
+ "move caret to end of 2 char input with ctrl-e");
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ is(inputNode.selectionStart, 2,
+ "no change of caret location on repeat ctrl-e");
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ is(inputNode.selectionStart, 2,
+ "no change of caret location on ctrl-n from end of line");
+
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(inputNode.selectionStart, 0, "ctrl-p moves to start of line");
+
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ is(inputNode.selectionStart, 2, "ctrl-n moves to end of line");
+}
+
+function testMultiLineInputNavNoHistory() {
+ let lineValues = ["one", "2", "something longer", "", "", "three!"];
+ jsterm.setInputValue("");
+ // simulate shift-return
+ for (let i = 0; i < lineValues.length; i++) {
+ jsterm.setInputValue(jsterm.getInputValue() + lineValues[i]);
+ EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true });
+ }
+ let inputValue = jsterm.getInputValue();
+ is(inputNode.selectionStart, inputNode.selectionEnd);
+ is(inputNode.selectionStart, inputValue.length,
+ "caret at end of multiline input");
+
+ // possibility newline is represented by one ('\r', '\n') or two
+ // ('\r\n') chars
+ let newlineString = inputValue.match(/(\r\n?|\n\r?)$/)[0];
+
+ // Ok, test navigating within the multi-line string!
+ EventUtils.synthesizeKey("VK_UP", {});
+ let expectedStringAfterCarat = lineValues[5] + newlineString;
+ is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat,
+ "up arrow from end of multiline");
+
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(jsterm.getInputValue().slice(inputNode.selectionStart), "",
+ "down arrow from within multiline");
+
+ // navigate up through input lines
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(jsterm.getInputValue().slice(inputNode.selectionStart), expectedStringAfterCarat,
+ "ctrl-p from end of multiline");
+
+ for (let i = 4; i >= 0; i--) {
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ expectedStringAfterCarat = lineValues[i] + newlineString +
+ expectedStringAfterCarat;
+ is(jsterm.getInputValue().slice(inputNode.selectionStart),
+ expectedStringAfterCarat, "ctrl-p from within line " + i +
+ " of multiline input");
+ }
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(inputNode.selectionStart, 0, "reached start of input");
+ is(jsterm.getInputValue(), inputValue,
+ "no change to multiline input on ctrl-p from beginning of multiline");
+
+ // navigate to end of first line
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ let caretPos = inputNode.selectionStart;
+ let expectedStringBeforeCarat = lineValues[0];
+ is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
+ "ctrl-e into multiline input");
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ is(inputNode.selectionStart, caretPos,
+ "repeat ctrl-e doesn't change caret position in multiline input");
+
+ // navigate down one line; ctrl-a to the beginning; ctrl-e to end
+ for (let i = 1; i < lineValues.length; i++) {
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ caretPos = inputNode.selectionStart;
+ expectedStringBeforeCarat += newlineString;
+ is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
+ "ctrl-a to beginning of line " + (i + 1) + " in multiline input");
+
+ EventUtils.synthesizeKey("e", { ctrlKey: true });
+ caretPos = inputNode.selectionStart;
+ expectedStringBeforeCarat += lineValues[i];
+ is(jsterm.getInputValue().slice(0, caretPos), expectedStringBeforeCarat,
+ "ctrl-e to end of line " + (i + 1) + "in multiline input");
+ }
+}
+
+function testNavWithHistory() {
+ // NOTE: Tests does NOT currently define behaviour for ctrl-p/ctrl-n with
+ // caret placed _within_ single line input
+ let values = ['"single line input"',
+ '"a longer single-line input to check caret repositioning"',
+ ['"multi-line"', '"input"', '"here!"'].join("\n"),
+ ];
+ // submit to history
+ for (let i = 0; i < values.length; i++) {
+ jsterm.setInputValue(values[i]);
+ jsterm.execute();
+ }
+ is(inputNode.selectionStart, 0, "caret location at start of empty line");
+
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(inputNode.selectionStart, values[values.length - 1].length,
+ "caret location correct at end of last history input");
+
+ // Navigate backwards history with ctrl-p
+ for (let i = values.length - 1; i > 0; i--) {
+ let match = values[i].match(/(\n)/g);
+ if (match) {
+ // multi-line inputs won't update from history unless caret at beginning
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ for (let j = 0; j < match.length; j++) {
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ }
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ } else {
+ // single-line inputs will update from history from end of line
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ }
+ is(jsterm.getInputValue(), values[i - 1],
+ "ctrl-p updates inputNode from backwards history values[" + i - 1 + "]");
+ }
+ let inputValue = jsterm.getInputValue();
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(inputNode.selectionStart, 0,
+ "ctrl-p at beginning of history moves caret location to beginning " +
+ "of line");
+ is(jsterm.getInputValue(), inputValue,
+ "no change to input value on ctrl-p from beginning of line");
+
+ // Navigate forwards history with ctrl-n
+ for (let i = 1; i < values.length; i++) {
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ is(jsterm.getInputValue(), values[i],
+ "ctrl-n updates inputNode from forwards history values[" + i + "]");
+ is(inputNode.selectionStart, values[i].length,
+ "caret location correct at end of history input for values[" + i + "]");
+ }
+ EventUtils.synthesizeKey("n", { ctrlKey: true });
+ ok(!jsterm.getInputValue(), "ctrl-n at end of history updates to empty input");
+
+ // Simulate editing multi-line
+ inputValue = "one\nlinebreak";
+ jsterm.setInputValue(inputValue);
+
+ // Attempt nav within input
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(jsterm.getInputValue(), inputValue,
+ "ctrl-p from end of multi-line does not trigger history");
+
+ EventUtils.synthesizeKey("a", { ctrlKey: true });
+ EventUtils.synthesizeKey("p", { ctrlKey: true });
+ is(jsterm.getInputValue(), values[values.length - 1],
+ "ctrl-p from start of multi-line triggers history");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js b/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js
new file mode 100644
index 000000000..ccbcb3bf3
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_817834_add_edited_input_to_history.js
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that user input that is not submitted in the command line input is not
+// lost after navigating in history.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=817834
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ testEditedInputHistory(hud);
+});
+
+function testEditedInputHistory(HUD) {
+ let jsterm = HUD.jsterm;
+ let inputNode = jsterm.inputNode;
+ ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
+ is(inputNode.selectionStart, 0);
+ is(inputNode.selectionEnd, 0);
+
+ jsterm.setInputValue('"first item"');
+ EventUtils.synthesizeKey("VK_UP", {});
+ is(jsterm.getInputValue(), '"first item"', "null test history up");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(jsterm.getInputValue(), '"first item"', "null test history down");
+
+ jsterm.execute();
+ is(jsterm.getInputValue(), "", "cleared input line after submit");
+
+ jsterm.setInputValue('"editing input 1"');
+ EventUtils.synthesizeKey("VK_UP", {});
+ is(jsterm.getInputValue(), '"first item"', "test history up");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(jsterm.getInputValue(), '"editing input 1"',
+ "test history down restores in-progress input");
+
+ jsterm.setInputValue('"second item"');
+ jsterm.execute();
+ jsterm.setInputValue('"editing input 2"');
+ EventUtils.synthesizeKey("VK_UP", {});
+ is(jsterm.getInputValue(), '"second item"', "test history up");
+ EventUtils.synthesizeKey("VK_UP", {});
+ is(jsterm.getInputValue(), '"first item"', "test history up");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(jsterm.getInputValue(), '"second item"', "test history down");
+ EventUtils.synthesizeKey("VK_DOWN", {});
+ is(jsterm.getInputValue(), '"editing input 2"',
+ "test history down restores new in-progress input again");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js b/devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js
new file mode 100644
index 000000000..0524e1c4c
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_837351_securityerrors.js
@@ -0,0 +1,42 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-837351-security-errors.html";
+
+add_task(function* () {
+ yield pushPrefEnv();
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let button = hud.ui.rootElement.querySelector(".webconsole-filter-button[category=\"security\"]");
+ ok(button, "Found security button in the web console");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Logged blocking mixed active content",
+ text: "Blocked loading mixed active content \u201chttp://example.com/\u201d",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_ERROR
+ },
+ ],
+ });
+});
+
+function pushPrefEnv() {
+ let deferred = promise.defer();
+ let options = {
+ set: [["security.mixed_content.block_active_content", true]]
+ };
+ SpecialPowers.pushPrefEnv(options, deferred.resolve);
+ return deferred.promise;
+}
+
diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js b/devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js
new file mode 100644
index 000000000..8062ffeec
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_922212_console_dirxml.js
@@ -0,0 +1,48 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console.dirxml works as intended.
+
+"use strict";
+
+const TEST_URI = `data:text/html;charset=utf-8,Web Console test for bug 922212:
+ Add console.dirxml`;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ // Should work like console.log(window)
+ hud.jsterm.execute("console.dirxml(window)");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.dirxml(window) output:",
+ text: /Window \u2192/,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ hud.jsterm.clearOutput();
+
+ hud.jsterm.execute("console.dirxml(document.body)");
+
+ // Should work like console.log(document.body);
+ [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.dirxml(document.body) output:",
+ text: "<body>",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+ let msg = [...result.matched][0];
+ yield checkLinkToInspector(true, msg);
+});
+
diff --git a/devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js b/devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js
new file mode 100644
index 000000000..fd5c4d29a
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_cached_autocomplete.js
@@ -0,0 +1,114 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the cached autocomplete results are used when the new
+// user input is a subset of the existing completion results.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test cached autocompletion " +
+ "results";
+
+var jsterm;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+ let popup = jsterm.autocompletePopup;
+
+ // Test if 'doc' gives 'document'
+ input.value = "doc";
+ input.setSelectionRange(3, 3);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ is(input.value, "doc", "'docu' completion (input.value)");
+ is(jsterm.completeNode.value, " ument", "'docu' completion (completeNode)");
+
+ // Test typing 'window.'.
+ input.value = "window.";
+ input.setSelectionRange(7, 7);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ ok(popup.getItems().length > 0, "'window.' gave a list of suggestions");
+
+ yield jsterm.execute("window.docfoobar = true");
+
+ // Test typing 'window.doc'.
+ input.value = "window.doc";
+ input.setSelectionRange(10, 10);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ let newItems = popup.getItems();
+ ok(newItems.every(function (item) {
+ return item.label != "docfoobar";
+ }), "autocomplete cached results do not contain docfoobar. list has not " +
+ "been updated");
+
+ // Test that backspace does not cause a request to the server
+ input.value = "window.do";
+ input.setSelectionRange(9, 9);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ newItems = popup.getItems();
+ ok(newItems.every(function (item) {
+ return item.label != "docfoobar";
+ }), "autocomplete cached results do not contain docfoobar. list has not " +
+ "been updated");
+
+ yield jsterm.execute("delete window.docfoobar");
+
+ // Test if 'window.getC' gives 'getComputedStyle'
+ input.value = "window.";
+ input.setSelectionRange(7, 7);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ input.value = "window.getC";
+ input.setSelectionRange(11, 11);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ newItems = popup.getItems();
+ ok(!newItems.every(function (item) {
+ return item.label != "getComputedStyle";
+ }), "autocomplete results do contain getComputedStyle");
+
+ // Test if 'dump(d' gives non-zero results
+ input.value = "dump(d";
+ input.setSelectionRange(6, 6);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ ok(popup.getItems().length > 0, "'dump(d' gives non-zero results");
+
+ // Test that 'dump(window.)' works.
+ input.value = "dump(window.)";
+ input.setSelectionRange(12, 12);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ yield jsterm.execute("window.docfoobar = true");
+
+ // Make sure 'dump(window.doc)' does not contain 'docfoobar'.
+ input.value = "dump(window.doc)";
+ input.setSelectionRange(15, 15);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ newItems = popup.getItems();
+ ok(newItems.every(function (item) {
+ return item.label != "docfoobar";
+ }), "autocomplete cached results do not contain docfoobar. list has not " +
+ "been updated");
+
+ yield jsterm.execute("delete window.docfoobar");
+
+ jsterm = null;
+});
+
+function complete(type) {
+ let updated = jsterm.once("autocomplete-updated");
+ jsterm.complete(type);
+ return updated;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_cd_iframe.js b/devtools/client/webconsole/test/browser_webconsole_cd_iframe.js
new file mode 100644
index 000000000..480c60940
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_cd_iframe.js
@@ -0,0 +1,115 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the cd() jsterm helper function works as expected. See bug 609872.
+
+"use strict";
+
+function test() {
+ let hud;
+
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug-609872-cd-iframe-parent.html";
+
+ const parentMessages = [{
+ name: "document.title in parent iframe",
+ text: "bug 609872 - iframe parent",
+ category: CATEGORY_OUTPUT,
+ }, {
+ name: "paragraph content",
+ text: "p: test for bug 609872 - iframe parent",
+ category: CATEGORY_OUTPUT,
+ }, {
+ name: "object content",
+ text: "obj: parent!",
+ category: CATEGORY_OUTPUT,
+ }];
+
+ const childMessages = [{
+ name: "document.title in child iframe",
+ text: "bug 609872 - iframe child",
+ category: CATEGORY_OUTPUT,
+ }, {
+ name: "paragraph content",
+ text: "p: test for bug 609872 - iframe child",
+ category: CATEGORY_OUTPUT,
+ }, {
+ name: "object content",
+ text: "obj: child!",
+ category: CATEGORY_OUTPUT,
+ }];
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+
+ yield executeWindowTest();
+
+ yield waitForMessages({ webconsole: hud, messages: parentMessages });
+
+ info("cd() into the iframe using a selector");
+ hud.jsterm.clearOutput();
+ yield hud.jsterm.execute("cd('iframe')");
+ yield executeWindowTest();
+
+ yield waitForMessages({ webconsole: hud, messages: childMessages });
+
+ info("cd() out of the iframe, reset to default window");
+ hud.jsterm.clearOutput();
+ yield hud.jsterm.execute("cd()");
+ yield executeWindowTest();
+
+ yield waitForMessages({ webconsole: hud, messages: parentMessages });
+
+ info("call cd() with unexpected arguments");
+ hud.jsterm.clearOutput();
+ yield hud.jsterm.execute("cd(document)");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Cannot cd()",
+ category: CATEGORY_OUTPUT,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+
+ hud.jsterm.clearOutput();
+ yield hud.jsterm.execute("cd('p')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Cannot cd()",
+ category: CATEGORY_OUTPUT,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+
+ info("cd() into the iframe using an iframe DOM element");
+ hud.jsterm.clearOutput();
+ yield hud.jsterm.execute("cd($('iframe'))");
+ yield executeWindowTest();
+
+ yield waitForMessages({ webconsole: hud, messages: childMessages });
+
+ info("cd(window.parent)");
+ hud.jsterm.clearOutput();
+ yield hud.jsterm.execute("cd(window.parent)");
+ yield executeWindowTest();
+
+ yield waitForMessages({ webconsole: hud, messages: parentMessages });
+
+ yield closeConsole(tab);
+ }
+
+ function* executeWindowTest() {
+ yield hud.jsterm.execute("document.title");
+ yield hud.jsterm.execute("'p: ' + document.querySelector('p').textContent");
+ yield hud.jsterm.execute("'obj: ' + window.foobarBug609872");
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_certificate_messages.js b/devtools/client/webconsole/test/browser_webconsole_certificate_messages.js
new file mode 100644
index 000000000..ca08d1a0f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_certificate_messages.js
@@ -0,0 +1,81 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the Web Console shows weak crypto warnings (SHA-1 Certificate)
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Web Console weak crypto " +
+ "warnings test";
+const TEST_URI_PATH = "/browser/devtools/client/webconsole/test/" +
+ "test-certificate-messages.html";
+
+var gWebconsoleTests = [
+ {url: "https://sha1ee.example.com" + TEST_URI_PATH,
+ name: "SHA1 warning displayed successfully",
+ warning: ["SHA-1"], nowarning: ["SSL 3.0", "RC4"]},
+ {url: "https://sha256ee.example.com" + TEST_URI_PATH,
+ name: "SSL warnings appropriately not present",
+ warning: [], nowarning: ["SHA-1", "SSL 3.0", "RC4"]},
+];
+const TRIGGER_MSG = "If you haven't seen ssl warnings yet, you won't";
+
+var gHud = undefined, gContentBrowser;
+var gCurrentTest;
+
+function test() {
+ registerCleanupFunction(function () {
+ gHud = gContentBrowser = null;
+ });
+
+ loadTab(TEST_URI).then(({browser}) => {
+ gContentBrowser = browser;
+ openConsole().then(runTestLoop);
+ });
+}
+
+function runTestLoop(theHud) {
+ gCurrentTest = gWebconsoleTests.shift();
+ if (!gCurrentTest) {
+ finishTest();
+ return;
+ }
+ if (!gHud) {
+ gHud = theHud;
+ }
+ gHud.jsterm.clearOutput();
+ gContentBrowser.addEventListener("load", onLoad, true);
+ if (gCurrentTest.pref) {
+ SpecialPowers.pushPrefEnv({"set": gCurrentTest.pref},
+ function () {
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.url);
+ });
+ } else {
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.url);
+ }
+}
+
+function onLoad() {
+ gContentBrowser.removeEventListener("load", onLoad, true);
+
+ waitForSuccess({
+ name: gCurrentTest.name,
+ validator: function () {
+ if (gHud.outputNode.textContent.indexOf(TRIGGER_MSG) >= 0) {
+ for (let warning of gCurrentTest.warning) {
+ if (gHud.outputNode.textContent.indexOf(warning) < 0) {
+ return false;
+ }
+ }
+ for (let nowarning of gCurrentTest.nowarning) {
+ if (gHud.outputNode.textContent.indexOf(nowarning) >= 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ }).then(runTestLoop);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_chrome.js b/devtools/client/webconsole/test/browser_webconsole_chrome.js
new file mode 100644
index 000000000..2513d1df5
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_chrome.js
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that code completion works properly in chrome tabs, like about:credits.
+
+"use strict";
+
+function test() {
+ Task.spawn(function* () {
+ const {tab} = yield loadTab("about:config");
+ ok(tab, "tab loaded");
+
+ const hud = yield openConsole(tab);
+ ok(hud, "we have a console");
+ ok(hud.iframeWindow, "we have the console UI window");
+
+ let jsterm = hud.jsterm;
+ ok(jsterm, "we have a jsterm");
+
+ let input = jsterm.inputNode;
+ ok(hud.outputNode, "we have an output node");
+
+ // Test typing 'docu'.
+ input.value = "docu";
+ input.setSelectionRange(4, 4);
+
+ let deferred = promise.defer();
+
+ jsterm.complete(jsterm.COMPLETE_HINT_ONLY, function () {
+ is(jsterm.completeNode.value, " ment", "'docu' completion");
+ deferred.resolve(null);
+ });
+
+ yield deferred.promise;
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_clear_method.js b/devtools/client/webconsole/test/browser_webconsole_clear_method.js
new file mode 100644
index 000000000..a4702980e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_clear_method.js
@@ -0,0 +1,131 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that calls to console.clear from a script delete the messages
+// previously logged.
+
+"use strict";
+
+add_task(function* () {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-clear.html";
+
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ ok(hud, "Web Console opened");
+
+ info("Check the console.clear() done on page load has been processed.");
+ yield waitForLog("Console was cleared", hud);
+ ok(hud.outputNode.textContent.includes("Console was cleared"),
+ "console.clear() message is displayed");
+ ok(!hud.outputNode.textContent.includes("log1"), "log1 not displayed");
+ ok(!hud.outputNode.textContent.includes("log2"), "log2 not displayed");
+
+ info("Logging two messages log3, log4");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.console.log("log3");
+ content.wrappedJSObject.console.log("log4");
+ });
+
+ yield waitForLog("log3", hud);
+ yield waitForLog("log4", hud);
+
+ ok(hud.outputNode.textContent.includes("Console was cleared"),
+ "console.clear() message is still displayed");
+ ok(hud.outputNode.textContent.includes("log3"), "log3 is displayed");
+ ok(hud.outputNode.textContent.includes("log4"), "log4 is displayed");
+
+ info("Open the variables view sidebar for 'objFromPage'");
+ yield openSidebar("objFromPage", { a: 1 }, hud);
+ let sidebarClosed = hud.jsterm.once("sidebar-closed");
+
+ info("Call console.clear from the page");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.console.clear();
+ });
+
+ // Cannot wait for "Console was cleared" here because such a message is
+ // already present and would yield immediately.
+ info("Wait for variables view sidebar to be closed after console.clear()");
+ yield sidebarClosed;
+
+ ok(!hud.outputNode.textContent.includes("log3"), "log3 not displayed");
+ ok(!hud.outputNode.textContent.includes("log4"), "log4 not displayed");
+ ok(hud.outputNode.textContent.includes("Console was cleared"),
+ "console.clear() message is still displayed");
+ is(hud.outputNode.textContent.split("Console was cleared").length, 2,
+ "console.clear() message is only displayed once");
+
+ info("Logging one messages log5");
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ content.wrappedJSObject.console.log("log5");
+ });
+ yield waitForLog("log5", hud);
+
+ info("Close and reopen the webconsole.");
+ yield closeConsole(gBrowser.selectedTab);
+ hud = yield openConsole();
+ yield waitForLog("Console was cleared", hud);
+
+ ok(hud.outputNode.textContent.includes("Console was cleared"),
+ "console.clear() message is still displayed");
+ ok(!hud.outputNode.textContent.includes("log1"), "log1 not displayed");
+ ok(!hud.outputNode.textContent.includes("log2"), "log1 not displayed");
+ ok(!hud.outputNode.textContent.includes("log3"), "log3 not displayed");
+ ok(!hud.outputNode.textContent.includes("log4"), "log4 not displayed");
+ ok(hud.outputNode.textContent.includes("log5"), "log5 still displayed");
+});
+
+/**
+ * Wait for a single message to be logged in the provided webconsole instance
+ * with the category CATEGORY_WEBDEV and the SEVERITY_LOG severity.
+ *
+ * @param {String} message
+ * The expected messaged.
+ * @param {WebConsole} webconsole
+ * WebConsole instance in which the message should be logged.
+ */
+function* waitForLog(message, webconsole, options) {
+ yield waitForMessages({
+ webconsole: webconsole,
+ messages: [{
+ text: message,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+/**
+ * Open the variables view sidebar for the object with the provided name objName
+ * and wait for the expected object is displayed in the variables view.
+ *
+ * @param {String} objName
+ * The name of the object to open in the sidebar.
+ * @param {Object} expectedObj
+ * The properties that should be displayed in the variables view.
+ * @param {WebConsole} webconsole
+ * WebConsole instance in which the message should be logged.
+ *
+ */
+function* openSidebar(objName, expectedObj, webconsole) {
+ let msg = yield webconsole.jsterm.execute(objName);
+ ok(msg, "output message found");
+
+ let anchor = msg.querySelector("a");
+ let body = msg.querySelector(".message-body");
+ ok(anchor, "object anchor");
+ ok(body, "message body");
+
+ yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, webconsole.iframeWindow);
+
+ let vviewVar = yield webconsole.jsterm.once("variablesview-fetched");
+ let vview = vviewVar._variablesView;
+ ok(vview, "variables view object exists");
+
+ yield findVariableViewProperties(vviewVar, [
+ expectedObj,
+ ], { webconsole: webconsole });
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_clickable_urls.js b/devtools/client/webconsole/test/browser_webconsole_clickable_urls.js
new file mode 100644
index 000000000..57d81fd05
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_clickable_urls.js
@@ -0,0 +1,103 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// When strings containing URLs are entered into the webconsole, check
+// its output and ensure that the output can be clicked to open those URLs.
+
+"use strict";
+
+const inputTests = [
+
+ // 0: URL opens page when clicked.
+ {
+ input: "'http://example.com'",
+ output: "http://example.com",
+ expectedTab: "http://example.com/",
+ },
+
+ // 1: URL opens page using https when clicked.
+ {
+ input: "'https://example.com'",
+ output: "https://example.com",
+ expectedTab: "https://example.com/",
+ },
+
+ // 2: URL with port opens page when clicked.
+ {
+ input: "'https://example.com:443'",
+ output: "https://example.com:443",
+ expectedTab: "https://example.com/",
+ },
+
+ // 3: URL containing non-empty path opens page when clicked.
+ {
+ input: "'http://example.com/foo'",
+ output: "http://example.com/foo",
+ expectedTab: "http://example.com/foo",
+ },
+
+ // 4: URL opens page when clicked, even when surrounded by non-URL tokens.
+ {
+ input: "'foo http://example.com bar'",
+ output: "foo http://example.com bar",
+ expectedTab: "http://example.com/",
+ },
+
+ // 5: URL opens page when clicked, and whitespace is be preserved.
+ {
+ input: "'foo\\nhttp://example.com\\nbar'",
+ output: "foo\nhttp://example.com\nbar",
+ expectedTab: "http://example.com/",
+ },
+
+ // 6: URL opens page when clicked when multiple links are present.
+ {
+ input: "'http://example.com http://example.com'",
+ output: "http://example.com http://example.com",
+ expectedTab: "http://example.com/",
+ },
+
+ // 7: URL without scheme does not open page when clicked.
+ {
+ input: "'example.com'",
+ output: "example.com",
+ },
+
+ // 8: URL with invalid scheme does not open page when clicked.
+ {
+ input: "'foo://example.com'",
+ output: "foo://example.com",
+ },
+
+ // 9: Shortened URL in an array
+ {
+ input: "['http://example.com/abcdefghijabcdefghij some other text']",
+ output: "Array [ \"http://example.com/abcdefghijabcdef\u2026\" ]",
+ printOutput: "http://example.com/abcdefghijabcdefghij some other text",
+ expectedTab: "http://example.com/abcdefghijabcdefghij",
+ getClickableNode: (msg) => msg.querySelectorAll("a")[1],
+ },
+
+ // 10: Shortened URL in an object
+ {
+ input: "{test: 'http://example.com/abcdefghijabcdefghij some other text'}",
+ output: "Object { test: \"http://example.com/abcdefghijabcdef\u2026\" }",
+ printOutput: "[object Object]",
+ evalOutput: "http://example.com/abcdefghijabcdefghij some other text",
+ noClick: true,
+ consoleLogClick: true,
+ expectedTab: "http://example.com/abcdefghijabcdefghij",
+ getClickableNode: (msg) => msg.querySelectorAll("a")[1],
+ },
+
+];
+
+const url = "data:text/html;charset=utf8,Bug 1005909 - Clickable URLS";
+
+add_task(function* () {
+ yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ let hud = yield openConsole();
+ yield checkOutputForInputs(hud, inputTests);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js
new file mode 100644
index 000000000..6a29d61aa
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_closure_inspection.js
@@ -0,0 +1,100 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that inspecting a closure in the variables view sidebar works when
+// execution is paused.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-closures.html";
+
+var gWebConsole, gJSTerm, gVariablesView;
+
+// Force the old debugger UI since it's directly used (see Bug 1301705)
+Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+function test() {
+ registerCleanupFunction(() => {
+ gWebConsole = gJSTerm = gVariablesView = null;
+ });
+
+ function fetchScopes(hud, toolbox, panelWin, deferred) {
+ panelWin.once(panelWin.EVENTS.FETCHED_SCOPES, () => {
+ ok(true, "Scopes were fetched");
+ toolbox.selectTool("webconsole").then(() => consoleOpened(hud));
+ deferred.resolve();
+ });
+ }
+
+ loadTab(TEST_URI).then(() => {
+ openConsole().then((hud) => {
+ openDebugger().then(({ toolbox, panelWin }) => {
+ let deferred = promise.defer();
+ fetchScopes(hud, toolbox, panelWin, deferred);
+
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ let button = content.document.querySelector("button");
+ ok(button, "button element found");
+ button.click();
+ });
+
+ return deferred.promise;
+ });
+ });
+ });
+}
+
+function consoleOpened(hud) {
+ gWebConsole = hud;
+ gJSTerm = hud.jsterm;
+ gJSTerm.execute("window.george.getName");
+
+ waitForMessages({
+ webconsole: gWebConsole,
+ messages: [{
+ text: "function _pfactory/<.getName()",
+ category: CATEGORY_OUTPUT,
+ objects: true,
+ }],
+ }).then(onExecuteGetName);
+}
+
+function onExecuteGetName(results) {
+ let clickable = results[0].clickableElements[0];
+ ok(clickable, "clickable object found");
+
+ gJSTerm.once("variablesview-fetched", onGetNameFetch);
+ let contextMenu =
+ gWebConsole.iframeWindow.document.getElementById("output-contextmenu");
+ waitForContextMenu(contextMenu, clickable, () => {
+ let openInVarView = contextMenu.querySelector("#menu_openInVarView");
+ ok(openInVarView.disabled === false,
+ "the \"Open In Variables View\" context menu item should be clickable");
+ // EventUtils.synthesizeMouseAtCenter seems to fail here in Mac OSX
+ openInVarView.click();
+ });
+}
+
+function onGetNameFetch(evt, view) {
+ gVariablesView = view._variablesView;
+ ok(gVariablesView, "variables view object");
+
+ findVariableViewProperties(view, [
+ { name: /_pfactory/, value: "" },
+ ], { webconsole: gWebConsole }).then(onExpandClosure);
+}
+
+function onExpandClosure(results) {
+ let prop = results[0].matchedProp;
+ ok(prop, "matched the name property in the variables view");
+
+ gVariablesView.window.focus();
+ gJSTerm.once("sidebar-closed", finishTest);
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, gVariablesView.window);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_column_numbers.js b/devtools/client/webconsole/test/browser_webconsole_column_numbers.js
new file mode 100644
index 000000000..8407e34d5
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_column_numbers.js
@@ -0,0 +1,46 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ // Check if console provides the right column number alongside line number
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-column.html";
+
+var hud;
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(consoleOpened);
+ });
+}
+
+function consoleOpened(aHud) {
+ hud = aHud;
+
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Error Message",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR
+ }]
+ }).then(testLocationColumn);
+}
+
+function testLocationColumn() {
+ let messages = hud.outputNode.children;
+ let expected = ["10:7", "10:39", "11:9", "12:11", "13:9", "14:7"];
+
+ for (let i = 0, len = messages.length; i < len; i++) {
+ let msg = messages[i].textContent;
+
+ is(msg.includes(expected[i]), true, "Found expected line:column of " +
+ expected[i]);
+ }
+
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_completion.js b/devtools/client/webconsole/test/browser_webconsole_completion.js
new file mode 100644
index 000000000..ee0c6809e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_completion.js
@@ -0,0 +1,106 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that code completion works properly.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test code completion";
+
+var jsterm;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+
+ // Test typing 'docu'.
+ input.value = "docu";
+ input.setSelectionRange(4, 4);
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ is(input.value, "docu", "'docu' completion (input.value)");
+ is(jsterm.completeNode.value, " ment", "'docu' completion (completeNode)");
+
+ // Test typing 'docu' and press tab.
+ input.value = "docu";
+ input.setSelectionRange(4, 4);
+ yield complete(jsterm.COMPLETE_FORWARD);
+
+ is(input.value, "document", "'docu' tab completion");
+ is(input.selectionStart, 8, "start selection is alright");
+ is(input.selectionEnd, 8, "end selection is alright");
+ is(jsterm.completeNode.value.replace(/ /g, ""), "", "'docu' completed");
+
+ // Test typing 'window.Ob' and press tab. Just 'window.O' is
+ // ambiguous: could be window.Object, window.Option, etc.
+ input.value = "window.Ob";
+ input.setSelectionRange(9, 9);
+ yield complete(jsterm.COMPLETE_FORWARD);
+
+ is(input.value, "window.Object", "'window.Ob' tab completion");
+
+ // Test typing 'document.getElem'.
+ input.value = "document.getElem";
+ input.setSelectionRange(16, 16);
+ yield complete(jsterm.COMPLETE_FORWARD);
+
+ is(input.value, "document.getElem", "'document.getElem' completion");
+ is(jsterm.completeNode.value, " entsByTagNameNS",
+ "'document.getElem' completion");
+
+ // Test pressing tab another time.
+ yield jsterm.complete(jsterm.COMPLETE_FORWARD);
+
+ is(input.value, "document.getElem", "'document.getElem' completion");
+ is(jsterm.completeNode.value, " entsByTagName",
+ "'document.getElem' another tab completion");
+
+ // Test pressing shift_tab.
+ complete(jsterm.COMPLETE_BACKWARD);
+
+ is(input.value, "document.getElem", "'document.getElem' untab completion");
+ is(jsterm.completeNode.value, " entsByTagNameNS",
+ "'document.getElem' completion");
+
+ jsterm.clearOutput();
+
+ input.value = "docu";
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ is(jsterm.completeNode.value, " ment", "'docu' completion");
+ yield jsterm.execute();
+ is(jsterm.completeNode.value, "", "clear completion on execute()");
+
+ // Test multi-line completion works
+ input.value = "console.log('one');\nconsol";
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ is(jsterm.completeNode.value, " \n e",
+ "multi-line completion");
+
+ // Test non-object autocompletion.
+ input.value = "Object.name.sl";
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ is(jsterm.completeNode.value, " ice", "non-object completion");
+
+ // Test string literal autocompletion.
+ input.value = "'Asimov'.sl";
+ yield complete(jsterm.COMPLETE_HINT_ONLY);
+
+ is(jsterm.completeNode.value, " ice", "string literal completion");
+
+ jsterm = null;
+});
+
+function complete(type) {
+ let updated = jsterm.once("autocomplete-updated");
+ jsterm.complete(type);
+ return updated;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js b/devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js
new file mode 100644
index 000000000..f8f02aa15
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_api_stackframe.js
@@ -0,0 +1,85 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the console API messages for console.error()/exception()/assert()
+// include a stackframe. See bug 920116.
+
+function test() {
+ let hud;
+
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-api-stackframe.html";
+ const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/"));
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+
+ const stack = [{
+ file: TEST_FILE,
+ fn: "thirdCall",
+ // 21,22,23
+ line: /\b2[123]\b/,
+ }, {
+ file: TEST_FILE,
+ fn: "secondCall",
+ line: 16,
+ }, {
+ file: TEST_FILE,
+ fn: "firstCall",
+ line: 12,
+ }];
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foo-log",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ collapsible: false,
+ }, {
+ text: "foo-error",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ collapsible: true,
+ stacktrace: stack,
+ }, {
+ text: "foo-exception",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ collapsible: true,
+ stacktrace: stack,
+ }, {
+ text: "foo-assert",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_ERROR,
+ collapsible: true,
+ stacktrace: stack,
+ }],
+ });
+
+ let elem = [...results[1].matched][0];
+ ok(elem, "message element");
+
+ let msg = elem._messageObject;
+ ok(msg, "message object");
+
+ ok(msg.collapsed, "message is collapsed");
+
+ msg.toggleDetails();
+
+ ok(!msg.collapsed, "message is not collapsed");
+
+ msg.toggleDetails();
+
+ ok(msg.collapsed, "message is collapsed");
+
+ yield closeConsole(tab);
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js b/devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js
new file mode 100644
index 000000000..310d4fc8b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_custom_styles.js
@@ -0,0 +1,81 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the '%c' modifier works with the console API. See bug 823097.
+
+function test() {
+ let hud;
+
+ const TEST_URI = "data:text/html;charset=utf8,<p>test for " +
+ "console.log('%ccustom styles', 'color:red')";
+
+ const checks = [{
+ // check the basics work
+ style: "color:red;font-size:1.3em",
+ props: { color: true, fontSize: true },
+ sameStyleExpected: true,
+ }, {
+ // check that the url() is not allowed
+ style: "color:blue;background-image:url('http://example.com/test')",
+ props: { color: true, fontSize: false, background: false,
+ backgroundImage: false },
+ sameStyleExpected: false,
+ }, {
+ // check that some properties are not allowed
+ style: "color:pink;position:absolute;top:10px",
+ props: { color: true, position: false, top: false },
+ sameStyleExpected: false,
+ }];
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+
+ for (let check of checks) {
+ yield checkStyle(check);
+ }
+
+ yield closeConsole(tab);
+ }
+
+ function* checkStyle(check) {
+ hud.jsterm.clearOutput();
+
+ info("checkStyle " + check.style);
+ hud.jsterm.execute("console.log('%cfoobar', \"" + check.style + "\")");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobar",
+ category: CATEGORY_WEBDEV,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ ok(msg, "message element");
+
+ let span = msg.querySelector(".message-body span[style]");
+ ok(span, "span element");
+
+ info("span textContent is: " + span.textContent);
+ isnot(span.textContent.indexOf("foobar"), -1, "span textContent check");
+
+ let outputStyle = span.getAttribute("style").replace(/\s+|;+$/g, "");
+ if (check.sameStyleExpected) {
+ is(outputStyle, check.style, "span style is correct");
+ } else {
+ isnot(outputStyle, check.style, "span style is not the same");
+ }
+
+ for (let prop of Object.keys(check.props)) {
+ is(!!span.style[prop], check.props[prop], "property check for " + prop);
+ }
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_extras.js b/devtools/client/webconsole/test/browser_webconsole_console_extras.js
new file mode 100644
index 000000000..078e33119
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_extras.js
@@ -0,0 +1,43 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that window.console functions that are not implemented yet do not
+// output anything in the web console and they do not throw any exceptions.
+// See bug 614350.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-extras.html";
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(consoleOpened);
+ });
+}
+
+function consoleOpened(hud) {
+ waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "start",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "end",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ }).then(() => {
+ let nodes = hud.outputNode.querySelectorAll(".message");
+ is(nodes.length, 2, "only two messages are displayed");
+ finishTest();
+ });
+
+ let button = content.document.querySelector("button");
+ ok(button, "we have the button");
+ EventUtils.sendMouseEvent({ type: "click" }, button, content);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_logging_api.js b/devtools/client/webconsole/test/browser_webconsole_console_logging_api.js
new file mode 100644
index 000000000..317337543
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_logging_api.js
@@ -0,0 +1,102 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the basic console.log()-style APIs and filtering work.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ let outputNode = hud.outputNode;
+
+ let methods = ["log", "info", "warn", "error", "exception", "debug"];
+ for (let method of methods) {
+ yield testMethod(method, hud, outputNode);
+ }
+});
+
+function* testMethod(method, hud, outputNode) {
+ let console = content.console;
+
+ hud.jsterm.clearOutput();
+
+ console[method]("foo-bar-baz");
+ console[method]("baar-baz");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foo-bar-baz",
+ }, {
+ text: "baar-baz",
+ }],
+ });
+
+ setStringFilter("foo", hud);
+
+ is(outputNode.querySelectorAll(".filtered-by-string").length, 1,
+ "1 hidden " + method + " node via string filtering");
+
+ hud.jsterm.clearOutput();
+
+ // now toggle the current method off - make sure no visible message
+ // TODO: move all filtering tests into a separate test file: see bug 608135
+
+ console[method]("foo-bar-baz");
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foo-bar-baz",
+ }],
+ });
+
+ setStringFilter("", hud);
+ let filter;
+ switch (method) {
+ case "debug":
+ filter = "log";
+ break;
+ case "exception":
+ filter = "error";
+ break;
+ default:
+ filter = method;
+ break;
+ }
+
+ hud.setFilterState(filter, false);
+
+ is(outputNode.querySelectorAll(".filtered-by-type").length, 1,
+ "1 message hidden for " + method + " (logging turned off)");
+
+ hud.setFilterState(filter, true);
+
+ is(outputNode.querySelectorAll(".message:not(.filtered-by-type)").length, 1,
+ "1 message shown for " + method + " (logging turned on)");
+
+ hud.jsterm.clearOutput();
+
+ // test for multiple arguments.
+ console[method]("foo", "bar");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foo bar",
+ category: CATEGORY_WEBDEV,
+ }],
+ });
+}
+
+function setStringFilter(value, hud) {
+ hud.ui.filterBox.value = value;
+ hud.ui.adjustVisibilityOnSearchStringChange();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js b/devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js
new file mode 100644
index 000000000..9575721c3
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_logging_workers_api.js
@@ -0,0 +1,39 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the basic console.log()-style APIs and filtering work for
+// sharedWorkers
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-workers.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foo-bar-shared-worker"
+ }],
+ });
+
+ hud.setFilterState("sharedworkers", false);
+
+ is(hud.outputNode.querySelectorAll(".filtered-by-type").length, 1,
+ "1 message hidden for sharedworkers (logging turned off)");
+
+ hud.setFilterState("sharedworkers", true);
+
+ is(hud.outputNode.querySelectorAll(".filtered-by-type").length, 0,
+ "1 message shown for sharedworkers (logging turned on)");
+
+ hud.setFilterState("sharedworkers", false);
+
+ hud.jsterm.clearOutput(true);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_trace_async.js b/devtools/client/webconsole/test/browser_webconsole_console_trace_async.js
new file mode 100644
index 000000000..10c3ff7a5
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_trace_async.js
@@ -0,0 +1,75 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-console-trace-async.html";
+
+add_task(function* runTest() {
+ // Async stacks aren't on by default in all builds
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["javascript.options.asyncstack", true]
+ ]}, resolve);
+ });
+
+ let {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello");
+ let hud = yield openConsole(tab);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.trace output",
+ consoleTrace: {
+ file: "test-console-trace-async.html",
+ fn: "inner",
+ },
+ }],
+ });
+
+ let node = [...result.matched][0];
+ ok(node, "found trace log node");
+ ok(node.textContent.includes("console.trace()"),
+ "trace log node includes console.trace()");
+ ok(node.textContent.includes("promise callback"),
+ "trace log node includes promise callback");
+ ok(node.textContent.includes("setTimeout handler"),
+ "trace log node includes setTimeout handler");
+
+ // The expected stack trace object.
+ let stacktrace = [
+ {
+ columnNumber: 3,
+ filename: TEST_URI,
+ functionName: "inner",
+ language: 2,
+ lineNumber: 9
+ },
+ {
+ asyncCause: "promise callback",
+ columnNumber: 3,
+ filename: TEST_URI,
+ functionName: "time1",
+ language: 2,
+ lineNumber: 13,
+ },
+ {
+ asyncCause: "setTimeout handler",
+ columnNumber: 1,
+ filename: TEST_URI,
+ functionName: "",
+ language: 2,
+ lineNumber: 18,
+ }
+ ];
+
+ let obj = node._messageObject;
+ ok(obj, "console.trace message object");
+ ok(obj._stacktrace, "found stacktrace object");
+ is(obj._stacktrace.toSource(), stacktrace.toSource(),
+ "stacktrace is correct");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js b/devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js
new file mode 100644
index 000000000..e1c6f966e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_console_trace_duplicates.js
@@ -0,0 +1,50 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug_939783_console_trace_duplicates.html";
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello");
+ const hud = yield openConsole(tab);
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
+
+ // NB: Now that stack frames include a column number multiple invocations
+ // on the same line are considered unique. ie:
+ // |foo(); foo();|
+ // will generate two distinct trace entries.
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.trace output for foo1()",
+ text: "foo1",
+ consoleTrace: {
+ file: "test-bug_939783_console_trace_duplicates.html",
+ fn: "foo3",
+ },
+ }, {
+ name: "console.trace output for foo1()",
+ text: "foo1",
+ consoleTrace: {
+ file: "test-bug_939783_console_trace_duplicates.html",
+ fn: "foo3",
+ },
+ }, {
+ name: "console.trace output for foo1b()",
+ text: "foo1b",
+ consoleTrace: {
+ file: "test-bug_939783_console_trace_duplicates.html",
+ fn: "foo3",
+ },
+ }],
+ });
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js b/devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js
new file mode 100644
index 000000000..8451ec762
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js
@@ -0,0 +1,51 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the "Open in Variables View" context menu item is enabled
+// only for objects.
+
+"use strict";
+
+const TEST_URI = `data:text/html,<script>
+ console.log("foo");
+ console.log("foo", window);
+</script>`;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ count: 2,
+ text: /foo/
+ }],
+ });
+
+ let [msgWithText, msgWithObj] = [...result.matched];
+ ok(msgWithText && msgWithObj, "Two messages should have appeared");
+
+ let contextMenu = hud.iframeWindow.document
+ .getElementById("output-contextmenu");
+ let openInVarViewItem = contextMenu.querySelector("#menu_openInVarView");
+ let obj = msgWithObj.querySelector(".cm-variable");
+ let text = msgWithText.querySelector(".console-string");
+
+ yield waitForContextMenu(contextMenu, obj, () => {
+ ok(openInVarViewItem.disabled === false, "The \"Open In Variables View\" " +
+ "context menu item should be available for objects");
+ }, () => {
+ ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " +
+ "context menu item should be disabled on popup hiding");
+ });
+
+ yield waitForContextMenu(contextMenu, text, () => {
+ ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " +
+ "context menu item should be disabled for texts");
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js b/devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js
new file mode 100644
index 000000000..4508101ee
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_context_menu_store_as_global.js
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the "Store as global variable" context menu item feature.
+// It should be work, and be enabled only for objects
+
+"use strict";
+
+const TEST_URI = `data:text/html,<script>
+ window.bar = { baz: 1 };
+ console.log("foo");
+ console.log("foo", window.bar);
+</script>`;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ count: 2,
+ text: /foo/
+ }],
+ });
+
+ let [msgWithText, msgWithObj] = [...result.matched];
+ ok(msgWithText && msgWithObj, "Two messages should have appeared");
+
+ let contextMenu = hud.iframeWindow.document
+ .getElementById("output-contextmenu");
+ let storeAsGlobalItem = contextMenu.querySelector("#menu_storeAsGlobal");
+ let obj = msgWithObj.querySelector(".cm-variable");
+ let text = msgWithText.querySelector(".console-string");
+ let onceInputSet = hud.jsterm.once("set-input-value");
+
+ info("Waiting for context menu on the object");
+ yield waitForContextMenu(contextMenu, obj, () => {
+ ok(storeAsGlobalItem.disabled === false, "The \"Store as global\" " +
+ "context menu item should be available for objects");
+ storeAsGlobalItem.click();
+ }, () => {
+ ok(storeAsGlobalItem.disabled === true, "The \"Store as global\" " +
+ "context menu item should be disabled on popup hiding");
+ });
+
+ info("Waiting for context menu on the text node");
+ yield waitForContextMenu(contextMenu, text, () => {
+ ok(storeAsGlobalItem.disabled === true, "The \"Store as global\" " +
+ "context menu item should be disabled for texts");
+ });
+
+ info("Waiting for input to be set");
+ yield onceInputSet;
+
+ is(hud.jsterm.getInputValue(), "temp0", "Input was set");
+ let executedResult = yield hud.jsterm.execute();
+
+ ok(executedResult.textContent.includes("{ baz: 1 }"),
+ "Correct variable assigned into console");
+
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_count.js b/devtools/client/webconsole/test/browser_webconsole_count.js
new file mode 100644
index 000000000..abb31a08d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_count.js
@@ -0,0 +1,77 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that console.count() counts as expected. See bug 922208.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-count.html";
+
+function test() {
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+
+ BrowserTestUtils.synthesizeMouseAtCenter("#local", {}, gBrowser.selectedBrowser);
+ let messages = [];
+ [
+ "start",
+ "<no label>: 2",
+ "console.count() testcounter: 1",
+ "console.count() testcounter: 2",
+ "console.count() testcounter: 3",
+ "console.count() testcounter: 4",
+ "end"
+ ].forEach(function (msg) {
+ messages.push({
+ text: msg,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG
+ });
+ });
+ messages.push({
+ name: "Three local counts with no label and count=1",
+ text: "<no label>: 1",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ count: 3
+ });
+ yield waitForMessages({
+ webconsole: hud,
+ messages: messages
+ });
+
+ hud.jsterm.clearOutput();
+
+ BrowserTestUtils.synthesizeMouseAtCenter("#external", {}, gBrowser.selectedBrowser);
+ messages = [];
+ [
+ "start",
+ "console.count() testcounter: 5",
+ "console.count() testcounter: 6",
+ "end"
+ ].forEach(function (msg) {
+ messages.push({
+ text: msg,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG
+ });
+ });
+ messages.push({
+ name: "Two external counts with no label and count=1",
+ text: "<no label>: 1",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ count: 2
+ });
+ yield waitForMessages({
+ webconsole: hud,
+ messages: messages
+ });
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js b/devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js
new file mode 100644
index 000000000..61ac68208
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_dont_navigate_on_doubleclick.js
@@ -0,0 +1,56 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that if a link in console is double clicked, the console frame doesn't
+// navigate to that destination (bug 975707).
+
+"use strict";
+
+function test() {
+ let originalNetPref = Services.prefs
+ .getBoolPref("devtools.webconsole.filter.networkinfo");
+ registerCleanupFunction(() => {
+ Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo",
+ originalNetPref);
+ });
+ Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true);
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const TEST_PAGE_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-console.html" + "?_uniq=" +
+ Date.now();
+
+ const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello</p>");
+ const hud = yield openConsole(tab);
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PAGE_URI);
+
+ let messages = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "Network request message",
+ url: TEST_PAGE_URI,
+ category: CATEGORY_NETWORK
+ }]
+ });
+
+ let networkEventMessage = messages[0].matched.values().next().value;
+ let urlNode = networkEventMessage.querySelector(".url");
+
+ let deferred = promise.defer();
+ urlNode.addEventListener("click", function onClick(event) {
+ urlNode.removeEventListener("click", onClick);
+ ok(event.defaultPrevented, "The default action was prevented.");
+
+ deferred.resolve();
+ });
+
+ EventUtils.synthesizeMouseAtCenter(urlNode, {clickCount: 2},
+ hud.iframeWindow);
+
+ yield deferred.promise;
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js b/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js
new file mode 100644
index 000000000..5cedfbad5
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_exception_stackframe.js
@@ -0,0 +1,104 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the console receive exceptions include a stackframe.
+// See bug 1184172.
+
+// On e10s, the exception is triggered in child process
+// and is ignored by test harness
+if (!Services.appinfo.browserTabsRemoteAutostart) {
+ SimpleTest.ignoreAllUncaughtExceptions();
+}
+
+function test() {
+ let hud;
+
+ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-exception-stackframe.html";
+ const TEST_FILE = TEST_URI.substr(TEST_URI.lastIndexOf("/"));
+
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab(TEST_URI);
+ hud = yield openConsole(tab);
+
+ const stack = [{
+ file: TEST_FILE,
+ fn: "thirdCall",
+ line: 21,
+ }, {
+ file: TEST_FILE,
+ fn: "secondCall",
+ line: 17,
+ }, {
+ file: TEST_FILE,
+ fn: "firstCall",
+ line: 12,
+ }];
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "nonExistingMethodCall is not defined",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ collapsible: true,
+ stacktrace: stack,
+ }, {
+ text: "SyntaxError: 'buggy;selector' is not a valid selector",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ collapsible: true,
+ stacktrace: [{
+ file: TEST_FILE,
+ fn: "domAPI",
+ line: 25,
+ }, {
+ file: TEST_FILE,
+ fn: "onLoadDomAPI",
+ line: 33,
+ }
+ ]
+ }, {
+ text: "DOMException",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ collapsible: true,
+ stacktrace: [{
+ file: TEST_FILE,
+ fn: "domException",
+ line: 29,
+ }, {
+ file: TEST_FILE,
+ fn: "onLoadDomException",
+ line: 36,
+ },
+
+ ]
+ }],
+ });
+
+ let elem = [...results[0].matched][0];
+ ok(elem, "message element");
+
+ let msg = elem._messageObject;
+ ok(msg, "message object");
+
+ ok(msg.collapsed, "message is collapsed");
+
+ msg.toggleDetails();
+
+ ok(!msg.collapsed, "message is not collapsed");
+
+ msg.toggleDetails();
+
+ ok(msg.collapsed, "message is collapsed");
+
+ yield closeConsole(tab);
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_execution_scope.js b/devtools/client/webconsole/test/browser_webconsole_execution_scope.js
new file mode 100644
index 000000000..78865c9b2
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_execution_scope.js
@@ -0,0 +1,37 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that commands run by the user are executed in content space.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("window.location.href;");
+
+ let [input, output] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "window.location.href;",
+ category: CATEGORY_INPUT,
+ },
+ {
+ text: TEST_URI,
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ let inputNode = [...input.matched][0];
+ let outputNode = [...output.matched][0];
+ is(inputNode.getAttribute("category"), "input",
+ "input node category is correct");
+ is(outputNode.getAttribute("category"), "output",
+ "output node category is correct");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js b/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js
new file mode 100644
index 000000000..192387e8a
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_expandable_timestamps.js
@@ -0,0 +1,57 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test for the message timestamps option: check if the preference toggles the
+// display of messages in the console output. See bug 722267.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "bug 722267 - preference for toggling timestamps in messages";
+const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
+var hud;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+ let panel = yield consoleOpened();
+
+ yield onOptionsPanelSelected(panel);
+ onPrefChanged();
+
+ Services.prefs.clearUserPref(PREF_MESSAGE_TIMESTAMP);
+ hud = null;
+});
+
+function consoleOpened() {
+ info("console opened");
+ let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
+ ok(!prefValue, "messages have no timestamp by default (pref check)");
+ ok(hud.outputNode.classList.contains("hideTimestamps"),
+ "messages have no timestamp (class name check)");
+
+ let toolbox = gDevTools.getToolbox(hud.target);
+ return toolbox.selectTool("options");
+}
+
+function onOptionsPanelSelected(panel) {
+ info("options panel opened");
+
+ let prefChanged = gDevTools.once("pref-changed", onPrefChanged);
+
+ let checkbox = panel.panelDoc.getElementById("webconsole-timestamp-messages");
+ checkbox.click();
+
+ return prefChanged;
+}
+
+function onPrefChanged() {
+ info("pref changed");
+ let prefValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
+ ok(prefValue, "messages have timestamps (pref check)");
+ ok(!hud.outputNode.classList.contains("hideTimestamps"),
+ "messages have timestamps (class name check)");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js b/devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js
new file mode 100644
index 000000000..e210bd81a
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_filter_buttons_contextmenu.js
@@ -0,0 +1,95 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the filter button context menu logic works correctly.
+
+"use strict";
+
+const TEST_URI = "http://example.com/";
+
+function test() {
+ loadTab(TEST_URI).then(() => {
+ openConsole().then(testFilterButtons);
+ });
+}
+
+function testFilterButtons(aHud) {
+ let hudBox = aHud.ui.rootElement;
+
+ testRightClick("net", hudBox, aHud)
+ .then(() => testRightClick("css", hudBox, aHud))
+ .then(() => testRightClick("js", hudBox, aHud))
+ .then(() => testRightClick("logging", hudBox, aHud))
+ .then(() => testRightClick("security", hudBox, aHud))
+ .then(finishTest);
+}
+
+function testRightClick(aCategory, hudBox, aHud) {
+ let deferred = promise.defer();
+ let selector = ".webconsole-filter-button[category=\"" + aCategory + "\"]";
+ let button = hudBox.querySelector(selector);
+ let mainButton = getMainButton(button, aHud);
+ let origCheckedState = button.getAttribute("aria-pressed");
+ let contextMenu = aHud.iframeWindow.document.getElementById(aCategory + "-contextmenu");
+
+ function verifyContextMenuIsClosed() {
+ info("verify the context menu is closed");
+ is(button.getAttribute("open"), false, "The context menu for the \"" +
+ aCategory + "\" button is closed");
+ }
+
+ function verifyOriginalCheckedState() {
+ info("verify the button has the original checked state");
+ is(button.getAttribute("aria-pressed"), origCheckedState,
+ "The button state should not have changed");
+ }
+
+ function verifyNewCheckedState() {
+ info("verify the button's checked state has changed");
+ isnot(button.getAttribute("aria-pressed"), origCheckedState,
+ "The button state should have changed");
+ }
+
+ function leftClickToClose() {
+ info("left click the button to close the contextMenu");
+ EventUtils.sendMouseEvent({type: "click"}, button);
+ executeSoon(() => {
+ verifyContextMenuIsClosed();
+ verifyOriginalCheckedState();
+ leftClickToChangeCheckedState();
+ });
+ }
+
+ function leftClickToChangeCheckedState() {
+ info("left click the mainbutton to change checked state");
+ EventUtils.sendMouseEvent({type: "click"}, mainButton);
+ executeSoon(() => {
+ verifyContextMenuIsClosed();
+ verifyNewCheckedState();
+ deferred.resolve();
+ });
+ }
+
+ verifyContextMenuIsClosed();
+ info("right click the button to open the context menu");
+ waitForContextMenu(contextMenu, mainButton, verifyOriginalCheckedState,
+ leftClickToClose);
+ return deferred.promise;
+}
+
+function getMainButton(aTargetButton, aHud) {
+ let anonymousNodes = aHud.ui.document.getAnonymousNodes(aTargetButton);
+ let subbutton;
+
+ for (let i = 0; i < anonymousNodes.length; i++) {
+ let node = anonymousNodes[i];
+ if (node.classList.contains("toolbarbutton-menubutton-button")) {
+ subbutton = node;
+ break;
+ }
+ }
+
+ return subbutton;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_for_of.js b/devtools/client/webconsole/test/browser_webconsole_for_of.js
new file mode 100644
index 000000000..83d3aaa3d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_for_of.js
@@ -0,0 +1,32 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// A for-of loop in Web Console code can loop over a content NodeList.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-for-of.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+ yield testForOf(hud);
+});
+
+function testForOf(hud) {
+ let deferred = promise.defer();
+
+ let jsterm = hud.jsterm;
+ jsterm.execute("{ let _nodes = []; for (let x of document.body.childNodes) { if (x.nodeType === 1) { _nodes.push(x.tagName); } } _nodes.join(' '); }",
+ (node) => {
+ ok(/H1 DIV H2 P/.test(node.textContent),
+ "for-of loop should find all top-level nodes");
+ deferred.resolve();
+ });
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_history.js b/devtools/client/webconsole/test/browser_webconsole_history.js
new file mode 100644
index 000000000..5ae709a4b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_history.js
@@ -0,0 +1,62 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the console history feature accessed via the up and down arrow keys.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+// Constants used for defining the direction of JSTerm input history navigation.
+const HISTORY_BACK = -1;
+const HISTORY_FORWARD = 1;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ let jsterm = hud.jsterm;
+ let input = jsterm.inputNode;
+
+ let executeList = ["document", "window", "window.location"];
+
+ for (let item of executeList) {
+ input.value = item;
+ yield jsterm.execute();
+ }
+
+ for (let x = executeList.length - 1; x != -1; x--) {
+ jsterm.historyPeruse(HISTORY_BACK);
+ is(input.value, executeList[x], "check history previous idx:" + x);
+ }
+
+ jsterm.historyPeruse(HISTORY_BACK);
+ is(input.value, executeList[0], "test that item is still index 0");
+
+ jsterm.historyPeruse(HISTORY_BACK);
+ is(input.value, executeList[0], "test that item is still still index 0");
+
+ for (let i = 1; i < executeList.length; i++) {
+ jsterm.historyPeruse(HISTORY_FORWARD);
+ is(input.value, executeList[i], "check history next idx:" + i);
+ }
+
+ jsterm.historyPeruse(HISTORY_FORWARD);
+ is(input.value, "", "check input is empty again");
+
+ // Simulate pressing Arrow_Down a few times and then if Arrow_Up shows
+ // the previous item from history again.
+ jsterm.historyPeruse(HISTORY_FORWARD);
+ jsterm.historyPeruse(HISTORY_FORWARD);
+ jsterm.historyPeruse(HISTORY_FORWARD);
+
+ is(input.value, "", "check input is still empty");
+
+ let idxLast = executeList.length - 1;
+ jsterm.historyPeruse(HISTORY_BACK);
+ is(input.value, executeList[idxLast], "check history next idx:" + idxLast);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js b/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
new file mode 100644
index 000000000..3ee33669d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_hpkp_invalid-headers.js
@@ -0,0 +1,126 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that errors about invalid HPKP security headers are logged to the web
+// console.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console HPKP invalid " +
+ "header test";
+const SJS_URL = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test_hpkp-invalid-headers.sjs";
+const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
+ "Public_Key_Pinning" + DOCS_GA_PARAMS;
+const NON_BUILTIN_ROOT_PREF = "security.cert_pinning.process_headers_from_" +
+ "non_builtin_roots";
+
+add_task(function* () {
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(NON_BUILTIN_ROOT_PREF);
+ });
+
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield* checkForMessage({
+ url: SJS_URL + "?badSyntax",
+ name: "Could not parse header error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that could not be " +
+ "parsed successfully."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?noMaxAge",
+ name: "No max-age error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that did not include " +
+ "a \u2018max-age\u2019 directive."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?invalidIncludeSubDomains",
+ name: "Invalid includeSubDomains error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that included an " +
+ "invalid \u2018includeSubDomains\u2019 directive."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?invalidMaxAge",
+ name: "Invalid max-age error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that included an " +
+ "invalid \u2018max-age\u2019 directive."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?multipleIncludeSubDomains",
+ name: "Multiple includeSubDomains error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that included " +
+ "multiple \u2018includeSubDomains\u2019 directives."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?multipleMaxAge",
+ name: "Multiple max-age error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that included " +
+ "multiple \u2018max-age\u2019 directives."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?multipleReportURIs",
+ name: "Multiple report-uri error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that included " +
+ "multiple \u2018report-uri\u2019 directives."
+ }, hud);
+
+ // The root used for mochitests is not built-in, so set the relevant pref to
+ // true to have the PKP implementation return more specific errors.
+ Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, true);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?pinsetDoesNotMatch",
+ name: "Non-matching pinset error displayed successfully",
+ text: "Public-Key-Pins: The site specified a header that did not include " +
+ "a matching pin."
+ }, hud);
+
+ Services.prefs.setBoolPref(NON_BUILTIN_ROOT_PREF, false);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?pinsetDoesNotMatch",
+ name: "Non-built-in root error displayed successfully",
+ text: "Public-Key-Pins: The certificate used by the site was not issued " +
+ "by a certificate in the default root certificate store. To " +
+ "prevent accidental breakage, the specified header was ignored."
+ }, hud);
+});
+
+function* checkForMessage(curTest, hud) {
+ hud.jsterm.clearOutput();
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, curTest.url);
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: curTest.name,
+ text: curTest.text,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ ],
+ });
+
+ yield testClickOpenNewTab(hud, results);
+}
+
+function testClickOpenNewTab(hud, results) {
+ let warningNode = results[0].clickableElements[0];
+ ok(warningNode, "link element");
+ ok(warningNode.classList.contains("learn-more-link"), "link class name");
+ return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js b/devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js
new file mode 100644
index 000000000..19cedefdb
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_hsts_invalid-headers.js
@@ -0,0 +1,92 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that errors about invalid HSTS security headers are logged
+// to the web console.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console HSTS invalid " +
+ "header test";
+const SJS_URL = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test_hsts-invalid-headers.sjs";
+const LEARN_MORE_URI = "https://developer.mozilla.org/docs/Web/Security/" +
+ "HTTP_strict_transport_security" + DOCS_GA_PARAMS;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ yield* checkForMessage({
+ url: SJS_URL + "?badSyntax",
+ name: "Could not parse header error displayed successfully",
+ text: "Strict-Transport-Security: The site specified a header that could " +
+ "not be parsed successfully."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?noMaxAge",
+ name: "No max-age error displayed successfully",
+ text: "Strict-Transport-Security: The site specified a header that did " +
+ "not include a \u2018max-age\u2019 directive."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?invalidIncludeSubDomains",
+ name: "Invalid includeSubDomains error displayed successfully",
+ text: "Strict-Transport-Security: The site specified a header that " +
+ "included an invalid \u2018includeSubDomains\u2019 directive."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?invalidMaxAge",
+ name: "Invalid max-age error displayed successfully",
+ text: "Strict-Transport-Security: The site specified a header that " +
+ "included an invalid \u2018max-age\u2019 directive."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?multipleIncludeSubDomains",
+ name: "Multiple includeSubDomains error displayed successfully",
+ text: "Strict-Transport-Security: The site specified a header that " +
+ "included multiple \u2018includeSubDomains\u2019 directives."
+ }, hud);
+
+ yield* checkForMessage({
+ url: SJS_URL + "?multipleMaxAge",
+ name: "Multiple max-age error displayed successfully",
+ text: "Strict-Transport-Security: The site specified a header that " +
+ "included multiple \u2018max-age\u2019 directives."
+ }, hud);
+});
+
+function* checkForMessage(curTest, hud) {
+ hud.jsterm.clearOutput();
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, curTest.url);
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: curTest.name,
+ text: curTest.text,
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ ],
+ });
+
+ yield testClickOpenNewTab(hud, results);
+}
+
+function testClickOpenNewTab(hud, results) {
+ let warningNode = results[0].clickableElements[0];
+ ok(warningNode, "link element");
+ ok(warningNode.classList.contains("learn-more-link"), "link class name");
+ return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js b/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js
new file mode 100644
index 000000000..2d7fda7f5
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_input_field_focus_on_panel_select.js
@@ -0,0 +1,34 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the JS input field is focused when the user switches back to the
+// web console from other tools, see bug 891581.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>hello";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ is(hud.jsterm.inputNode.hasAttribute("focused"), true,
+ "inputNode should be focused");
+
+ hud.ui.filterBox.focus();
+
+ is(hud.ui.filterBox.hasAttribute("focused"), true,
+ "filterBox should be focused");
+
+ is(hud.jsterm.inputNode.hasAttribute("focused"), false,
+ "inputNode shouldn't be focused");
+
+ yield openInspector();
+ hud = yield openConsole();
+
+ is(hud.jsterm.inputNode.hasAttribute("focused"), true,
+ "inputNode should be focused");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js b/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js
new file mode 100644
index 000000000..f79ba386f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_inspect-parsed-documents.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that dynamically created (HTML|XML|SVG)Documents can be inspected by
+// clicking on the object in console (bug 1035198).
+
+"use strict";
+
+const TEST_CASES = [
+ {
+ input: '(new DOMParser()).parseFromString("<a />", "text/html")',
+ output: "HTMLDocument",
+ inspectable: true,
+ },
+ {
+ input: '(new DOMParser()).parseFromString("<a />", "application/xml")',
+ output: "XMLDocument",
+ inspectable: true,
+ },
+ {
+ input: '(new DOMParser()).parseFromString("<svg></svg>", "image/svg+xml")',
+ output: "XMLDocument",
+ inspectable: true,
+ },
+];
+
+const TEST_URI = "data:text/html;charset=utf8," +
+ "browser_webconsole_inspect-parsed-documents.js";
+add_task(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ yield checkOutputForInputs(hud, TEST_CASES);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js b/devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js
new file mode 100644
index 000000000..7d45059fc
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_js_input_expansion.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the input box expands as the user types long lines.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ let input = hud.jsterm.inputNode;
+ input.focus();
+
+ is(input.getAttribute("multiline"), "true", "multiline is enabled");
+ // Tests if the inputNode expands.
+ input.value = "hello\nworld\n";
+ let length = input.value.length;
+ input.selectionEnd = length;
+ input.selectionStart = length;
+ function getHeight() {
+ return input.clientHeight;
+ }
+ let initialHeight = getHeight();
+ // Performs an "d". This will trigger/test for the input event that should
+ // change the "row" attribute of the inputNode.
+ EventUtils.synthesizeKey("d", {});
+ let newHeight = getHeight();
+ ok(initialHeight < newHeight, "Height changed: " + newHeight);
+
+ // Add some more rows. Tests for the 8 row limit.
+ input.value = "row1\nrow2\nrow3\nrow4\nrow5\nrow6\nrow7\nrow8\nrow9\nrow10\n";
+ length = input.value.length;
+ input.selectionEnd = length;
+ input.selectionStart = length;
+ EventUtils.synthesizeKey("d", {});
+ let newerHeight = getHeight();
+
+ ok(newerHeight > newHeight, "height changed: " + newerHeight);
+
+ // Test if the inputNode shrinks again.
+ input.value = "";
+ EventUtils.synthesizeKey("d", {});
+ let height = getHeight();
+ info("height: " + height);
+ info("initialHeight: " + initialHeight);
+ let finalHeightDifference = Math.abs(initialHeight - height);
+ ok(finalHeightDifference <= 1, "height shrank to original size within 1px");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_jsterm.js b/devtools/client/webconsole/test/browser_webconsole_jsterm.js
new file mode 100644
index 000000000..221c96fa6
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_jsterm.js
@@ -0,0 +1,195 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+var jsterm;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ jsterm = hud.jsterm;
+ yield testJSTerm(hud);
+ jsterm = null;
+});
+
+function checkResult(msg, desc) {
+ let def = promise.defer();
+ waitForMessages({
+ webconsole: jsterm.hud.owner,
+ messages: [{
+ name: desc,
+ category: CATEGORY_OUTPUT,
+ }],
+ }).then(([result]) => {
+ let node = [...result.matched][0].querySelector(".message-body");
+ if (typeof msg == "string") {
+ is(node.textContent.trim(), msg,
+ "correct message shown for " + desc);
+ } else if (typeof msg == "function") {
+ ok(msg(node), "correct message shown for " + desc);
+ }
+
+ def.resolve();
+ });
+ return def.promise;
+}
+
+function* testJSTerm(hud) {
+ const HELP_URL = "https://developer.mozilla.org/docs/Tools/" +
+ "Web_Console/Helpers";
+
+ jsterm.clearOutput();
+ yield jsterm.execute("$('#header').getAttribute('id')");
+ yield checkResult('"header"', "$() worked");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("$$('h1').length");
+ yield checkResult("1", "$$() worked");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("$x('.//*', document.body)[0] == $$('h1')[0]");
+ yield checkResult("true", "$x() worked");
+
+ // no jsterm.clearOutput() here as we clear the output using the clear() fn.
+ yield jsterm.execute("clear()");
+
+ yield waitForSuccess({
+ name: "clear() worked",
+ validator: function () {
+ return jsterm.outputNode.childNodes.length == 0;
+ }
+ });
+
+ jsterm.clearOutput();
+ yield jsterm.execute("keys({b:1})[0] == 'b'");
+ yield checkResult("true", "keys() worked", 1);
+
+ jsterm.clearOutput();
+ yield jsterm.execute("values({b:1})[0] == 1");
+ yield checkResult("true", "values() worked", 1);
+
+ jsterm.clearOutput();
+
+ let openedLinks = 0;
+ let oldOpenLink = hud.openLink;
+ hud.openLink = (url) => {
+ if (url == HELP_URL) {
+ openedLinks++;
+ }
+ };
+
+ yield jsterm.execute("help()");
+ yield jsterm.execute("help");
+ yield jsterm.execute("?");
+
+ let output = jsterm.outputNode.querySelector(".message[category='output']");
+ ok(!output, "no output for help() calls");
+ is(openedLinks, 3, "correct number of pages opened by the help calls");
+ hud.openLink = oldOpenLink;
+
+ jsterm.clearOutput();
+ yield jsterm.execute("pprint({b:2, a:1})");
+ yield checkResult("\" b: 2\n a: 1\"", "pprint()");
+
+ // check instanceof correctness, bug 599940
+ jsterm.clearOutput();
+ yield jsterm.execute("[] instanceof Array");
+ yield checkResult("true", "[] instanceof Array == true");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("({}) instanceof Object");
+ yield checkResult("true", "({}) instanceof Object == true");
+
+ // check for occurrences of Object XRayWrapper, bug 604430
+ jsterm.clearOutput();
+ yield jsterm.execute("document");
+ yield checkResult(function (node) {
+ return node.textContent.search(/\[object xraywrapper/i) == -1;
+ }, "document - no XrayWrapper");
+
+ // check that pprint(window) and keys(window) don't throw, bug 608358
+ jsterm.clearOutput();
+ yield jsterm.execute("pprint(window)");
+ yield checkResult(null, "pprint(window)");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("keys(window)");
+ yield checkResult(null, "keys(window)");
+
+ // bug 614561
+ jsterm.clearOutput();
+ yield jsterm.execute("pprint('hi')");
+ yield checkResult("\" 0: \"h\"\n 1: \"i\"\"", "pprint('hi')");
+
+ // check that pprint(function) shows function source, bug 618344
+ jsterm.clearOutput();
+ yield jsterm.execute("pprint(function() { var someCanaryValue = 42; })");
+ yield checkResult(function (node) {
+ return node.textContent.indexOf("someCanaryValue") > -1;
+ }, "pprint(function) shows source");
+
+ // check that an evaluated null produces "null", bug 650780
+ jsterm.clearOutput();
+ yield jsterm.execute("null");
+ yield checkResult("null", "null is null");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("undefined");
+ yield checkResult("undefined", "undefined is printed");
+
+ // check that thrown strings produce error messages,
+ // and the message text matches that of a stringified error object
+ // bug 1099071
+ jsterm.clearOutput();
+ yield jsterm.execute("throw '';");
+ yield checkResult((node) => {
+ return node.closest(".message").getAttribute("severity") === "error" &&
+ node.textContent === new Error("").toString();
+ }, "thrown empty string generates error message");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("throw 'tomatoes';");
+ yield checkResult((node) => {
+ return node.closest(".message").getAttribute("severity") === "error" &&
+ node.textContent === new Error("tomatoes").toString();
+ }, "thrown non-empty string generates error message");
+
+ jsterm.clearOutput();
+ yield jsterm.execute("throw { foo: 'bar' };");
+ yield checkResult((node) => {
+ return node.closest(".message").getAttribute("severity") === "error" &&
+ node.textContent === Object.prototype.toString();
+ }, "thrown object generates error message");
+
+ // check that errors with entires in errordocs.js display links
+ // alongside their messages.
+ const ErrorDocs = require("devtools/server/actors/errordocs");
+
+ const ErrorDocStatements = {
+ "JSMSG_BAD_RADIX": "(42).toString(0);",
+ "JSMSG_BAD_ARRAY_LENGTH": "([]).length = -1",
+ "JSMSG_NEGATIVE_REPETITION_COUNT": "'abc'.repeat(-1);",
+ "JSMSG_BAD_FORMAL": "var f = Function('x y', 'return x + y;');",
+ "JSMSG_PRECISION_RANGE": "77.1234.toExponential(-1);",
+ };
+
+ for (let errorMessageName of Object.keys(ErrorDocStatements)) {
+ let title = ErrorDocs.GetURL({ errorMessageName }).split("?")[0];
+
+ jsterm.clearOutput();
+ yield jsterm.execute(ErrorDocStatements[errorMessageName]);
+ yield checkResult((node) => {
+ return node.parentNode.getElementsByTagName("a")[0].title == title;
+ }, `error links to ${title}`);
+ }
+
+ // Ensure that dom errors, with error numbers outside of the range
+ // of valid js.msg errors, don't cause crashes (bug 1270721).
+ yield jsterm.execute("new Request('',{redirect:'foo'})");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js b/devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js
new file mode 100644
index 000000000..1dbfa80d9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_live_filtering_of_message_types.js
@@ -0,0 +1,56 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the message type filter checkboxes work.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ for (let i = 0; i < 50; i++) {
+ content.console.log("foobarz #" + i);
+ }
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "foobarz #49",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ is(hud.outputNode.children.length, 50, "number of messages");
+
+ hud.setFilterState("log", false);
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when the " +
+ "corresponding filter is switched off");
+
+ hud.setFilterState("log", true);
+ is(countMessageNodes(hud), 50, "the log nodes reappear when the " +
+ "corresponding filter is switched on");
+});
+
+function countMessageNodes(hud) {
+ let messageNodes = hud.outputNode.querySelectorAll(".message");
+ let displayedMessageNodes = 0;
+ let view = hud.iframeWindow;
+ for (let i = 0; i < messageNodes.length; i++) {
+ let computedStyle = view.getComputedStyle(messageNodes[i], null);
+ if (computedStyle.display !== "none") {
+ displayedMessageNodes++;
+ }
+ }
+
+ return displayedMessageNodes;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js b/devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js
new file mode 100644
index 000000000..d41d5cf2e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_live_filtering_on_search_strings.js
@@ -0,0 +1,96 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the text filter box works.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+ for (let i = 0; i < 50; i++) {
+ content.console.log("http://www.example.com/ " + i);
+ }
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "http://www.example.com/ 49",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ is(hud.outputNode.children.length, 50, "number of messages");
+
+ setStringFilter(hud, "http");
+ isnot(countMessageNodes(hud), 0, "the log nodes are not hidden when the " +
+ "search string is set to \"http\"");
+
+ setStringFilter(hud, "hxxp");
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when the search " +
+ "string is set to \"hxxp\"");
+
+ setStringFilter(hud, "ht tp");
+ isnot(countMessageNodes(hud), 0, "the log nodes are not hidden when the " +
+ "search string is set to \"ht tp\"");
+
+ setStringFilter(hud, " zzzz zzzz ");
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when the search " +
+ "string is set to \" zzzz zzzz \"");
+
+ setStringFilter(hud, "");
+ isnot(countMessageNodes(hud), 0, "the log nodes are not hidden when the " +
+ "search string is removed");
+
+ setStringFilter(hud, "\u9f2c");
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when searching " +
+ "for weasels");
+
+ setStringFilter(hud, "\u0007");
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " +
+ "the bell character");
+
+ setStringFilter(hud, '"foo"');
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " +
+ 'the string "foo"');
+
+ setStringFilter(hud, "'foo'");
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " +
+ "the string 'foo'");
+
+ setStringFilter(hud, "foo\"bar'baz\"boo'");
+ is(countMessageNodes(hud), 0, "the log nodes are hidden when searching for " +
+ "the string \"foo\"bar'baz\"boo'\"");
+});
+
+function countMessageNodes(hud) {
+ let outputNode = hud.outputNode;
+
+ let messageNodes = outputNode.querySelectorAll(".message");
+ let displayedMessageNodes = 0;
+ let view = hud.iframeWindow;
+ for (let i = 0; i < messageNodes.length; i++) {
+ let computedStyle = view.getComputedStyle(messageNodes[i], null);
+ if (computedStyle.display !== "none") {
+ displayedMessageNodes++;
+ }
+ }
+
+ return displayedMessageNodes;
+}
+
+function setStringFilter(hud, value) {
+ hud.ui.filterBox.value = value;
+ hud.ui.adjustVisibilityOnSearchStringChange();
+}
+
diff --git a/devtools/client/webconsole/test/browser_webconsole_log_file_filter.js b/devtools/client/webconsole/test/browser_webconsole_log_file_filter.js
new file mode 100644
index 000000000..d5059485f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_log_file_filter.js
@@ -0,0 +1,83 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the text filter box works to filter based on filenames
+// where the logs were generated.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-bug_923281_console_log_filter.html";
+
+var hud;
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ hud = yield openConsole();
+ yield consoleOpened();
+
+ testLiveFilteringOnSearchStrings();
+
+ hud = null;
+});
+
+function consoleOpened() {
+ let console = content.console;
+ console.log("sentinel log");
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "sentinel log",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG
+ }],
+ });
+}
+
+function testLiveFilteringOnSearchStrings() {
+ is(hud.outputNode.children.length, 4, "number of messages");
+
+ setStringFilter("random");
+ is(countMessageNodes(), 1, "the log nodes not containing string " +
+ "\"random\" are hidden");
+
+ setStringFilter("test2.js");
+ is(countMessageNodes(), 2, "show only log nodes containing string " +
+ "\"test2.js\" or log nodes created from files with filename " +
+ "containing \"test2.js\" as substring.");
+
+ setStringFilter("test1");
+ is(countMessageNodes(), 2, "show only log nodes containing string " +
+ "\"test1\" or log nodes created from files with filename " +
+ "containing \"test1\" as substring.");
+
+ setStringFilter("");
+ is(countMessageNodes(), 4, "show all log nodes on setting filter string " +
+ "as \"\".");
+}
+
+function countMessageNodes() {
+ let outputNode = hud.outputNode;
+
+ let messageNodes = outputNode.querySelectorAll(".message");
+ content.console.log(messageNodes.length);
+ let displayedMessageNodes = 0;
+ let view = hud.iframeWindow;
+ for (let i = 0; i < messageNodes.length; i++) {
+ let computedStyle = view.getComputedStyle(messageNodes[i], null);
+ if (computedStyle.display !== "none") {
+ displayedMessageNodes++;
+ }
+ }
+
+ return displayedMessageNodes;
+}
+
+function setStringFilter(value) {
+ hud.ui.filterBox.value = value;
+ hud.ui.adjustVisibilityOnSearchStringChange();
+}
+
diff --git a/devtools/client/webconsole/test/browser_webconsole_message_node_id.js b/devtools/client/webconsole/test/browser_webconsole_message_node_id.js
new file mode 100644
index 000000000..bec657740
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_message_node_id.js
@@ -0,0 +1,28 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ hud.jsterm.execute("console.log('a log message')");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "a log message",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ ok(msg.getAttribute("id"), "log message has an ID");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_multiline_input.js b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js
new file mode 100644
index 000000000..7285c2127
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_multiline_input.js
@@ -0,0 +1,70 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Tests that the console waits for more input instead of evaluating
+// when valid, but incomplete, statements are present upon pressing enter
+// -or- when the user ends a line with shift + enter.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+let SHOULD_ENTER_MULTILINE = [
+ {input: "function foo() {" },
+ {input: "var a = 1," },
+ {input: "var a = 1;", shiftKey: true },
+ {input: "function foo() { }", shiftKey: true },
+ {input: "function" },
+ {input: "(x) =>" },
+ {input: "let b = {" },
+ {input: "let a = [" },
+ {input: "{" },
+ {input: "{ bob: 3343," },
+ {input: "function x(y=" },
+ {input: "Array.from(" },
+ // shift + enter creates a new line despite parse errors
+ {input: "{2,}", shiftKey: true },
+];
+let SHOULD_EXECUTE = [
+ {input: "function foo() { }" },
+ {input: "var a = 1;" },
+ {input: "function foo() { var a = 1; }" },
+ {input: '"asdf"' },
+ {input: "99 + 3" },
+ {input: "1, 2, 3" },
+ // errors
+ {input: "function f(x) { let y = 1, }" },
+ {input: "function f(x=,) {" },
+ {input: "{2,}" },
+];
+
+add_task(function* () {
+ let { tab, browser } = yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ let inputNode = hud.jsterm.inputNode;
+
+ for (let test of SHOULD_ENTER_MULTILINE) {
+ hud.jsterm.setInputValue(test.input);
+ EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey });
+ let inputValue = hud.jsterm.getInputValue();
+ is(inputNode.selectionStart, inputNode.selectionEnd,
+ "selection is collapsed");
+ is(inputNode.selectionStart, inputValue.length,
+ "caret at end of multiline input");
+ let inputWithNewline = test.input + "\n";
+ is(inputValue, inputWithNewline, "Input value is correct");
+ }
+
+ for (let test of SHOULD_EXECUTE) {
+ hud.jsterm.setInputValue(test.input);
+ EventUtils.synthesizeKey("VK_RETURN", { shiftKey: test.shiftKey });
+ let inputValue = hud.jsterm.getInputValue();
+ is(inputNode.selectionStart, 0, "selection starts/ends at 0");
+ is(inputNode.selectionEnd, 0, "selection starts/ends at 0");
+ is(inputValue, "", "Input value is cleared");
+ }
+
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging.js b/devtools/client/webconsole/test/browser_webconsole_netlogging.js
new file mode 100644
index 000000000..63730c9b4
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging.js
@@ -0,0 +1,139 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests response logging for different request types.
+
+"use strict";
+
+// This test runs very slowly on linux32 debug - bug 1269977
+requestLongerTimeout(2);
+
+const TEST_NETWORK_REQUEST_URI =
+ "http://example.com/browser/devtools/client/webconsole/test/" +
+ "test-network-request.html";
+
+const TEST_DATA_JSON_CONTENT =
+ '{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] }';
+
+const PAGE_REQUEST_PREDICATE =
+ ({ request }) => request.url.endsWith("test-network-request.html");
+
+const TEST_DATA_REQUEST_PREDICATE =
+ ({ request }) => request.url.endsWith("test-data.json");
+
+add_task(function* testPageLoad() {
+ // Enable logging in the UI. Not needed to pass test but makes it easier
+ // to debug interactively.
+ yield new Promise(resolve => {
+ SpecialPowers.pushPrefEnv({"set":
+ [["devtools.webconsole.filter.networkinfo", true]
+ ]}, resolve);
+ });
+
+ let finishedRequest = waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
+ let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
+ let request = yield finishedRequest;
+
+ ok(request, "Page load was logged");
+
+ let client = hud.ui.webConsoleClient;
+ let args = [request.actor];
+ const postData = yield getPacket(client, "getRequestPostData", args);
+ const responseContent = yield getPacket(client, "getResponseContent", args);
+
+ is(request.request.url, TEST_NETWORK_REQUEST_URI,
+ "Logged network entry is page load");
+ is(request.request.method, "GET", "Method is correct");
+ ok(!postData.postData.text, "No request body was stored");
+ ok(!postData.postDataDiscarded,
+ "Request body was not discarded");
+ is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0,
+ "Response body's beginning is okay");
+
+ yield closeTabAndToolbox();
+});
+
+add_task(function* testXhrGet() {
+ let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
+
+ let finishedRequest = waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE);
+ content.wrappedJSObject.testXhrGet();
+ let request = yield finishedRequest;
+
+ ok(request, "testXhrGet() was logged");
+
+ let client = hud.ui.webConsoleClient;
+ let args = [request.actor];
+ const postData = yield getPacket(client, "getRequestPostData", args);
+ const responseContent = yield getPacket(client, "getResponseContent", args);
+
+ is(request.request.method, "GET", "Method is correct");
+ ok(!postData.postData.text, "No request body was sent");
+ ok(!postData.postDataDiscarded,
+ "Request body was not discarded");
+ is(responseContent.content.text, TEST_DATA_JSON_CONTENT,
+ "Response is correct");
+
+ yield closeTabAndToolbox();
+});
+
+add_task(function* testXhrPost() {
+ let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
+
+ let finishedRequest = waitForFinishedRequest(TEST_DATA_REQUEST_PREDICATE);
+ content.wrappedJSObject.testXhrPost();
+ let request = yield finishedRequest;
+
+ ok(request, "testXhrPost() was logged");
+
+ let client = hud.ui.webConsoleClient;
+ let args = [request.actor];
+ const postData = yield getPacket(client, "getRequestPostData", args);
+ const responseContent = yield getPacket(client, "getResponseContent", args);
+
+ is(request.request.method, "POST", "Method is correct");
+ is(postData.postData.text, "Hello world!", "Request body was logged");
+ is(responseContent.content.text, TEST_DATA_JSON_CONTENT,
+ "Response is correct");
+
+ yield closeTabAndToolbox();
+});
+
+add_task(function* testFormSubmission() {
+ let pageLoadRequestFinished = waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
+ let hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
+
+ info("Waiting for the page load to be finished.");
+ yield pageLoadRequestFinished;
+
+ // The form POSTs to the page URL but over https (page over http).
+ let finishedRequest = waitForFinishedRequest(PAGE_REQUEST_PREDICATE);
+ ContentTask.spawn(gBrowser.selectedBrowser, { }, `function()
+ {
+ let form = content.document.querySelector("form");
+ form.submit();
+ }`);
+ let request = yield finishedRequest;
+
+ ok(request, "testFormSubmission() was logged");
+
+ let client = hud.ui.webConsoleClient;
+ let args = [request.actor];
+ const postData = yield getPacket(client, "getRequestPostData", args);
+ const responseContent = yield getPacket(client, "getResponseContent", args);
+
+ is(request.request.method, "POST", "Method is correct");
+ isnot(postData.postData.text
+ .indexOf("Content-Type: application/x-www-form-urlencoded"), -1,
+ "Content-Type is correct");
+ isnot(postData.postData.text
+ .indexOf("Content-Length: 20"), -1, "Content-length is correct");
+ isnot(postData.postData.text
+ .indexOf("name=foo+bar&age=144"), -1, "Form data is correct");
+ is(responseContent.content.text.indexOf("<!DOCTYPE HTML>"), 0,
+ "Response body's beginning is okay");
+
+ yield closeTabAndToolbox();
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js b/devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js
new file mode 100644
index 000000000..c6fa12401
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_basic.js
@@ -0,0 +1,44 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the page's resources are displayed in the console as they're
+// loaded
+
+"use strict";
+
+const TEST_NETWORK_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-network.html" + "?_date=" +
+ Date.now();
+
+add_task(function* () {
+ yield loadTab("data:text/html;charset=utf-8,Web Console basic network " +
+ "logging test");
+ let hud = yield openConsole();
+
+ yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_NETWORK_URI);
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "running network console",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-network.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "testscript.js",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-image.png",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js b/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js
new file mode 100644
index 000000000..b44b49453
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_panel.js
@@ -0,0 +1,30 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that network log messages bring up the network panel.
+
+"use strict";
+
+const TEST_NETWORK_REQUEST_URI =
+ "http://example.com/browser/devtools/client/webconsole/test/" +
+ "test-network-request.html";
+
+add_task(function* () {
+ let finishedRequest = waitForFinishedRequest(({ request }) => {
+ return request.url.endsWith("test-network-request.html");
+ });
+
+ const hud = yield loadPageAndGetHud(TEST_NETWORK_REQUEST_URI);
+ let request = yield finishedRequest;
+
+ yield hud.ui.openNetworkPanel(request.actor);
+ let toolbox = gDevTools.getToolbox(hud.target);
+ is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
+ let panel = toolbox.getCurrentPanel();
+ let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
+ is(selected.attachment.method, request.request.method,
+ "The correct request is selected");
+ is(selected.attachment.url, request.request.url,
+ "The correct request is definitely selected");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
new file mode 100644
index 000000000..265bc7c00
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_netlogging_reset_filter.js
@@ -0,0 +1,95 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that network log messages bring up the network panel and select the
+// right request even if it was previously filtered off.
+
+"use strict";
+
+const TEST_FILE_URI =
+ "http://example.com/browser/devtools/client/webconsole/test/" +
+ "test-network.html";
+const TEST_URI = "data:text/html;charset=utf8,<p>test file URI";
+
+var hud;
+
+add_task(function* () {
+ let Actions = require("devtools/client/netmonitor/actions/index");
+
+ let requests = [];
+ let { browser } = yield loadTab(TEST_URI);
+
+ yield pushPrefEnv();
+ hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ HUDService.lastFinishedRequest.callback = request => requests.push(request);
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_FILE_URI);
+ yield loaded;
+
+ yield testMessages();
+ let htmlRequest = requests.find(e => e.request.url.endsWith("html"));
+ ok(htmlRequest, "htmlRequest was a html");
+
+ yield hud.ui.openNetworkPanel(htmlRequest.actor);
+ let toolbox = gDevTools.getToolbox(hud.target);
+ is(toolbox.currentToolId, "netmonitor", "Network panel was opened");
+
+ let panel = toolbox.getCurrentPanel();
+ let selected = panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
+ is(selected.attachment.method, htmlRequest.request.method,
+ "The correct request is selected");
+ is(selected.attachment.url, htmlRequest.request.url,
+ "The correct request is definitely selected");
+
+ // Filter out the HTML request.
+ panel.panelWin.gStore.dispatch(Actions.toggleFilterType("js"));
+
+ yield toolbox.selectTool("webconsole");
+ is(toolbox.currentToolId, "webconsole", "Web console was selected");
+ yield hud.ui.openNetworkPanel(htmlRequest.actor);
+
+ panel.panelWin.NetMonitorView.RequestsMenu.selectedItem;
+ is(selected.attachment.method, htmlRequest.request.method,
+ "The correct request is selected");
+ is(selected.attachment.url, htmlRequest.request.url,
+ "The correct request is definitely selected");
+
+ // All tests are done. Shutdown.
+ HUDService.lastFinishedRequest.callback = null;
+ htmlRequest = browser = requests = hud = null;
+});
+
+function testMessages() {
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "running network console logging tests",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "test-network.html",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "testscript.js",
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+}
+
+function pushPrefEnv() {
+ let deferred = promise.defer();
+ let options = {
+ set: [["devtools.webconsole.filter.networkinfo", true]]
+ };
+ SpecialPowers.pushPrefEnv(options, deferred.resolve);
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_notifications.js b/devtools/client/webconsole/test/browser_webconsole_notifications.js
new file mode 100644
index 000000000..4bda9192f
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_notifications.js
@@ -0,0 +1,77 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
+ "notifications";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let consoleOpened = promise.defer();
+ let gotEvents = waitForEvents(consoleOpened.promise);
+ yield openConsole().then(() => {
+ consoleOpened.resolve();
+ });
+
+ yield gotEvents;
+});
+
+function waitForEvents(onConsoleOpened) {
+ let deferred = promise.defer();
+
+ function webConsoleCreated(id) {
+ Services.obs.removeObserver(observer, "web-console-created");
+ ok(HUDService.getHudReferenceById(id), "We have a hud reference");
+ content.wrappedJSObject.console.log("adding a log message");
+ }
+
+ function webConsoleDestroyed(id) {
+ Services.obs.removeObserver(observer, "web-console-destroyed");
+ ok(!HUDService.getHudReferenceById(id), "We do not have a hud reference");
+ executeSoon(deferred.resolve);
+ }
+
+ function webConsoleMessage(id, nodeID) {
+ Services.obs.removeObserver(observer, "web-console-message-created");
+ ok(id, "we have a console ID");
+ is(typeof nodeID, "string", "message node id is a string");
+ onConsoleOpened.then(closeConsole);
+ }
+
+ let observer = {
+
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+ observe: function observe(subject, topic, data) {
+ subject = subject.QueryInterface(Ci.nsISupportsString);
+
+ switch (topic) {
+ case "web-console-created":
+ webConsoleCreated(subject.data);
+ break;
+ case "web-console-destroyed":
+ webConsoleDestroyed(subject.data);
+ break;
+ case "web-console-message-created":
+ webConsoleMessage(subject, data);
+ break;
+ default:
+ break;
+ }
+ },
+
+ init: function init() {
+ Services.obs.addObserver(this, "web-console-created", false);
+ Services.obs.addObserver(this, "web-console-destroyed", false);
+ Services.obs.addObserver(this, "web-console-message-created", false);
+ }
+ };
+
+ observer.init();
+
+ return deferred.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js b/devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js
new file mode 100644
index 000000000..ae11305de
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_open-links-without-callback.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that if a link without an onclick callback is clicked the link is
+// opened in a new tab and no exception occurs (bug 999236).
+
+"use strict";
+
+function test() {
+ function* runner() {
+ const TEST_EVAL_STRING = "document";
+ const TEST_PAGE_URI = "http://example.com/browser/devtools/client/" +
+ "webconsole/test/test-console.html";
+ const {tab} = yield loadTab(TEST_PAGE_URI);
+ const hud = yield openConsole(tab);
+
+ hud.jsterm.execute(TEST_EVAL_STRING);
+
+ const EXPECTED_OUTPUT = new RegExp("HTMLDocument \.+");
+
+ let messages = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "JS eval output",
+ text: EXPECTED_OUTPUT,
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ let messageNode = messages[0].matched.values().next().value;
+
+ // The correct anchor is second in the message node; the first anchor has
+ // class .cm-variable. Ignore the first one by not matching anchors that
+ // have the class .cm-variable.
+ let urlNode = messageNode.querySelector("a:not(.cm-variable)");
+
+ let linkOpened = false;
+ let oldOpenUILinkIn = window.openUILinkIn;
+ window.openUILinkIn = function (aLink) {
+ if (aLink == TEST_PAGE_URI) {
+ linkOpened = true;
+ }
+ };
+
+ EventUtils.synthesizeMouseAtCenter(urlNode, {}, hud.iframeWindow);
+
+ ok(linkOpened, "Clicking the URL opens the desired page");
+ window.openUILinkIn = oldOpenUILinkIn;
+ }
+
+ Task.spawn(runner).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_01.js b/devtools/client/webconsole/test/browser_webconsole_output_01.js
new file mode 100644
index 000000000..c75577ea7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_01.js
@@ -0,0 +1,122 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+
+"use strict";
+
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
+
+// Test the webconsole output for various types of objects.
+
+const TEST_URI = "data:text/html;charset=utf8,test for console output - 01";
+
+var {DebuggerServer} = require("devtools/server/main");
+
+var longString = (new Array(DebuggerServer.LONG_STRING_LENGTH + 4)).join("a");
+var initialString = longString.substring(0, DebuggerServer.LONG_STRING_INITIAL_LENGTH);
+
+var inputTests = [
+ // 0
+ {
+ input: "'hello \\nfrom \\rthe \\\"string world!'",
+ output: "\"hello \nfrom \rthe \"string world!\"",
+ consoleOutput: "hello \nfrom \rthe \"string world!",
+ },
+
+ // 1
+ {
+ // unicode test
+ input: "'\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165'",
+ output: "\"\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165\"",
+ consoleOutput: "\xFA\u1E47\u0129\xE7\xF6d\xEA \u021B\u0115\u0219\u0165",
+ },
+
+ // 2
+ {
+ input: "'" + longString + "'",
+ output: '"' + initialString + "\"[\u2026]",
+ consoleOutput: initialString + "[\u2026]",
+ printOutput: initialString,
+ },
+
+ // 3
+ {
+ input: "''",
+ output: '""',
+ consoleOutput: "",
+ printOutput: '""',
+ },
+
+ // 4
+ {
+ input: "0",
+ output: "0",
+ },
+
+ // 5
+ {
+ input: "'0'",
+ output: '"0"',
+ consoleOutput: "0",
+ },
+
+ // 6
+ {
+ input: "42",
+ output: "42",
+ },
+
+ // 7
+ {
+ input: "'42'",
+ output: '"42"',
+ consoleOutput: "42",
+ },
+
+ // 8
+ {
+ input: "/foobar/",
+ output: "/foobar/",
+ inspectable: true,
+ },
+
+ // 9
+ {
+ input: "Symbol()",
+ output: "Symbol()"
+ },
+
+ // 10
+ {
+ input: "Symbol('foo')",
+ output: "Symbol(foo)"
+ },
+
+ // 11
+ {
+ input: "Symbol.iterator",
+ output: "Symbol(Symbol.iterator)"
+ },
+];
+
+longString = initialString = null;
+
+function test() {
+ requestLongerTimeout(2);
+
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ return checkOutputForInputs(hud, inputTests);
+ }).then(finishUp);
+}
+
+function finishUp() {
+ longString = initialString = inputTests = null;
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_02.js b/devtools/client/webconsole/test/browser_webconsole_output_02.js
new file mode 100644
index 000000000..8018669a9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_02.js
@@ -0,0 +1,183 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the webconsole output for various types of objects.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-02.html";
+
+var inputTests = [
+ // 0 - native named function
+ {
+ input: "document.getElementById",
+ output: "function getElementById()",
+ printOutput: "function getElementById() {\n [native code]\n}",
+ inspectable: true,
+ variablesViewLabel: "getElementById()",
+ },
+
+ // 1 - anonymous function
+ {
+ input: "(function() { return 42; })",
+ output: "function ()",
+ printOutput: "function () { return 42; }",
+ suppressClick: true
+ },
+
+ // 2 - named function
+ {
+ input: "window.testfn1",
+ output: "function testfn1()",
+ printOutput: "function testfn1() { return 42; }",
+ suppressClick: true
+ },
+
+ // 3 - anonymous function, but spidermonkey gives us an inferred name.
+ {
+ input: "testobj1.testfn2",
+ output: "function testobj1.testfn2()",
+ printOutput: "function () { return 42; }",
+ suppressClick: true
+ },
+
+ // 4 - named function with custom display name
+ {
+ input: "window.testfn3",
+ output: "function testfn3DisplayName()",
+ printOutput: "function testfn3() { return 42; }",
+ suppressClick: true
+ },
+
+ // 5 - basic array
+ {
+ input: "window.array1",
+ output: 'Array [ 1, 2, 3, "a", "b", "c", "4", "5" ]',
+ printOutput: "1,2,3,a,b,c,4,5",
+ inspectable: true,
+ variablesViewLabel: "Array[8]",
+ },
+
+ // 6 - array with objects
+ {
+ input: "window.array2",
+ output: 'Array [ "a", HTMLDocument \u2192 test-console-output-02.html, ' +
+ "<body>, DOMStringMap[0], DOMTokenList[0] ]",
+ printOutput: '"a,[object HTMLDocument],[object HTMLBodyElement],' +
+ '[object DOMStringMap],"',
+ inspectable: true,
+ variablesViewLabel: "Array[5]",
+ },
+
+ // 7 - array with more than 10 elements
+ {
+ input: "window.array3",
+ output: "Array [ 1, Window \u2192 test-console-output-02.html, null, " +
+ '"a", "b", undefined, false, "", -Infinity, ' +
+ "testfn3DisplayName(), 3 more\u2026 ]",
+ printOutput: '"1,[object Window],,a,b,,false,,-Infinity,' +
+ 'function testfn3() { return 42; },[object Object],foo,bar"',
+ inspectable: true,
+ variablesViewLabel: "Array[13]",
+ },
+
+ // 8 - array with holes and a cyclic reference
+ {
+ input: "window.array4",
+ output: 'Array [ <5 empty slots>, "test", Array[7] ]',
+ printOutput: '",,,,,test,"',
+ inspectable: true,
+ variablesViewLabel: "Array[7]",
+ },
+
+ // 9
+ {
+ input: "window.typedarray1",
+ output: "Int32Array [ 1, 287, 8651, 40983, 8754 ]",
+ printOutput: "1,287,8651,40983,8754",
+ inspectable: true,
+ variablesViewLabel: "Int32Array[5]",
+ },
+
+ // 10 - Set with cyclic reference
+ {
+ input: "window.set1",
+ output: 'Set [ 1, 2, null, Array[13], "a", "b", undefined, <head>, ' +
+ "Set[9] ]",
+ printOutput: "[object Set]",
+ inspectable: true,
+ variablesViewLabel: "Set[9]",
+ },
+
+ // 11 - Object with cyclic reference and a getter
+ {
+ input: "window.testobj2",
+ output: 'Object { a: "b", c: "d", e: 1, f: "2", foo: Object, ' +
+ "bar: Object, getterTest: Getter }",
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 12 - Object with more than 10 properties
+ {
+ input: "window.testobj3",
+ output: 'Object { a: "b", c: "d", e: 1, f: "2", g: true, h: null, ' +
+ 'i: undefined, j: "", k: StyleSheetList[0], l: NodeList[5], ' +
+ "2 more\u2026 }",
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 13 - Object with a non-enumerable property that we do not show
+ {
+ input: "window.testobj4",
+ output: 'Object { a: "b", c: "d", 1 more\u2026 }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 14 - Map with cyclic references
+ {
+ input: "window.map1",
+ output: 'Map { a: "b", HTMLCollection[2]: Object, Map[3]: Set[9] }',
+ printOutput: "[object Map]",
+ inspectable: true,
+ variablesViewLabel: "Map[3]",
+ },
+
+ // 15 - WeakSet
+ {
+ input: "window.weakset",
+ // Need a regexp because the order may vary.
+ output: new RegExp("WeakSet \\[ (String, <head>|<head>, String) \\]"),
+ printOutput: "[object WeakSet]",
+ inspectable: true,
+ variablesViewLabel: "WeakSet[2]",
+ },
+
+ // 16 - WeakMap
+ {
+ input: "window.weakmap",
+ // Need a regexp because the order may vary.
+ output: new RegExp("WeakMap { (String: 23, HTMLCollection\\[2\\]: Object|HTMLCollection\\[2\\]: Object, String: 23) }"),
+ printOutput: "[object WeakMap]",
+ inspectable: true,
+ variablesViewLabel: "WeakMap[2]",
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+ yield checkOutputForInputs(hud, inputTests);
+ inputTests = null;
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_03.js b/devtools/client/webconsole/test/browser_webconsole_output_03.js
new file mode 100644
index 000000000..bd77c2a4d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_03.js
@@ -0,0 +1,168 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the webconsole output for various types of objects.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-03.html";
+
+var inputTests = [
+
+ // 0
+ {
+ input: "document",
+ output: "HTMLDocument \u2192 " + TEST_URI,
+ printOutput: "[object HTMLDocument]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 1
+ {
+ input: "window",
+ output: "Window \u2192 " + TEST_URI,
+ printOutput: "[object Window",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 2
+ {
+ input: "document.body",
+ output: "<body>",
+ printOutput: "[object HTMLBodyElement]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 3
+ {
+ input: "document.body.dataset",
+ output: "DOMStringMap { }",
+ printOutput: "[object DOMStringMap]",
+ inspectable: true,
+ variablesViewLabel: "DOMStringMap[0]",
+ },
+
+ // 4
+ {
+ input: "document.body.classList",
+ output: "DOMTokenList [ ]",
+ printOutput: '""',
+ inspectable: true,
+ variablesViewLabel: "DOMTokenList[0]",
+ },
+
+ // 5
+ {
+ input: "window.location.href",
+ output: '"' + TEST_URI + '"',
+ noClick: true,
+ },
+
+ // 6
+ {
+ input: "window.location",
+ output: "Location \u2192 " + TEST_URI,
+ printOutput: TEST_URI,
+ inspectable: true,
+ variablesViewLabel: "Location \u2192 test-console-output-03.html",
+ },
+
+ // 7
+ {
+ input: "document.body.attributes",
+ output: "NamedNodeMap [ ]",
+ printOutput: "[object NamedNodeMap]",
+ inspectable: true,
+ variablesViewLabel: "NamedNodeMap[0]",
+ },
+
+ // 8
+ {
+ input: "document.styleSheets",
+ output: "StyleSheetList [ ]",
+ printOutput: "[object StyleSheetList",
+ inspectable: true,
+ variablesViewLabel: "StyleSheetList[0]",
+ },
+
+ // 9
+ {
+ input: "testBodyClassName()",
+ output: '<body class="test1 tezt2">',
+ printOutput: "[object HTMLBodyElement]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 10
+ {
+ input: "testBodyID()",
+ output: '<body class="test1 tezt2" id="foobarid">',
+ printOutput: "[object HTMLBodyElement]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 11
+ {
+ input: "document.body.classList",
+ output: 'DOMTokenList [ "test1", "tezt2" ]',
+ printOutput: '"test1 tezt2"',
+ inspectable: true,
+ variablesViewLabel: "DOMTokenList[2]",
+ },
+
+ // 12
+ {
+ input: "testBodyDataset()",
+ output: '<body class="test1 tezt2" id="foobarid"' +
+ ' data-preview="zuzu&quot;&lt;a&gt;foo">',
+ printOutput: "[object HTMLBodyElement]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 13
+ {
+ input: "document.body.dataset",
+ output: 'DOMStringMap { preview: "zuzu"<a>foo" }',
+ printOutput: "[object DOMStringMap]",
+ inspectable: true,
+ variablesViewLabel: "DOMStringMap[1]",
+ },
+
+ // 14
+ {
+ input: "document.body.attributes",
+ output: 'NamedNodeMap [ class="test1 tezt2", id="foobarid", ' +
+ 'data-preview="zuzu&quot;&lt;a&gt;foo" ]',
+ printOutput: "[object NamedNodeMap]",
+ inspectable: true,
+ variablesViewLabel: "NamedNodeMap[3]",
+ },
+
+ // 15
+ {
+ input: "document.body.attributes[0]",
+ output: 'class="test1 tezt2"',
+ printOutput: "[object Attr]",
+ inspectable: true,
+ variablesViewLabel: 'class="test1 tezt2"',
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+ yield checkOutputForInputs(hud, inputTests);
+ inputTests = null;
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_04.js b/devtools/client/webconsole/test/browser_webconsole_output_04.js
new file mode 100644
index 000000000..d829594a7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_04.js
@@ -0,0 +1,129 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+//
+
+"use strict";
+
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
+
+// Test the webconsole output for various types of objects.
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-04.html";
+
+var inputTests = [
+ // 0
+ {
+ input: "testTextNode()",
+ output: '#text "hello world!"',
+ printOutput: "[object Text]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 1
+ {
+ input: "testCommentNode()",
+ output: /<!--\s+- Any copyright /,
+ printOutput: "[object Comment]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 2
+ {
+ input: "testDocumentFragment()",
+ output: "DocumentFragment [ <div#foo1.bar>, <div#foo3> ]",
+ printOutput: "[object DocumentFragment]",
+ inspectable: true,
+ variablesViewLabel: "DocumentFragment[2]",
+ },
+
+ // 3
+ {
+ input: "testError()",
+ output: "TypeError: window.foobar is not a function\n" +
+ "Stack trace:\n" +
+ "testError@" + TEST_URI + ":44",
+ printOutput: '"TypeError: window.foobar is not a function"',
+ inspectable: true,
+ variablesViewLabel: "TypeError",
+ },
+
+ // 4
+ {
+ input: "testDOMException()",
+ output: `DOMException [SyntaxError: "'foo;()bar!' is not a valid selector"`,
+ printOutput: `"SyntaxError: 'foo;()bar!' is not a valid selector"`,
+ inspectable: true,
+ variablesViewLabel: "SyntaxError",
+ },
+
+ // 5
+ {
+ input: "testCSSStyleDeclaration()",
+ output: 'CSS2Properties { color: "green", font-size: "2em" }',
+ printOutput: "[object CSS2Properties]",
+ inspectable: true,
+ noClick: true,
+ },
+
+ // 6
+ {
+ input: "testStyleSheetList()",
+ output: "StyleSheetList [ CSSStyleSheet ]",
+ printOutput: "[object StyleSheetList",
+ inspectable: true,
+ variablesViewLabel: "StyleSheetList[1]",
+ },
+
+ // 7
+ {
+ input: "document.styleSheets[0]",
+ output: "CSSStyleSheet",
+ printOutput: "[object CSSStyleSheet]",
+ inspectable: true,
+ },
+
+ // 8
+ {
+ input: "document.styleSheets[0].cssRules",
+ output: "CSSRuleList [ CSSStyleRule, CSSMediaRule ]",
+ printOutput: "[object CSSRuleList",
+ inspectable: true,
+ variablesViewLabel: "CSSRuleList[2]",
+ },
+
+ // 9
+ {
+ input: "document.styleSheets[0].cssRules[0]",
+ output: 'CSSStyleRule "p, div"',
+ printOutput: "[object CSSStyleRule",
+ inspectable: true,
+ variablesViewLabel: "CSSStyleRule",
+ },
+
+ // 10
+ {
+ input: "document.styleSheets[0].cssRules[1]",
+ output: 'CSSMediaRule "print"',
+ printOutput: "[object CSSMediaRule",
+ inspectable: true,
+ variablesViewLabel: "CSSMediaRule",
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ const hud = yield openConsole(tab);
+ yield checkOutputForInputs(hud, inputTests);
+ inputTests = null;
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_05.js b/devtools/client/webconsole/test/browser_webconsole_output_05.js
new file mode 100644
index 000000000..53bfd768c
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_05.js
@@ -0,0 +1,177 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the webconsole output for various types of objects.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,test for console output - 05";
+const {ELLIPSIS} = require("devtools/shared/l10n");
+
+// March, 1960: The first implementation of Lisp. From Wikipedia:
+//
+// > Lisp was first implemented by Steve Russell on an IBM 704 computer. Russell
+// > had read McCarthy's paper, and realized (to McCarthy's surprise) that the
+// > Lisp eval function could be implemented in machine code. The result was a
+// > working Lisp interpreter which could be used to run Lisp programs, or more
+// > properly, 'evaluate Lisp expressions.'
+var testDate = -310435200000;
+
+var inputTests = [
+ // 0
+ {
+ input: "/foo?b*\\s\"ar/igym",
+ output: "/foo?b*\\s\"ar/gimy",
+ printOutput: "/foo?b*\\s\"ar/gimy",
+ inspectable: true,
+ },
+
+ // 1
+ {
+ input: "null",
+ output: "null",
+ },
+
+ // 2
+ {
+ input: "undefined",
+ output: "undefined",
+ },
+
+ // 3
+ {
+ input: "true",
+ output: "true",
+ },
+
+ // 4
+ {
+ input: "new Boolean(false)",
+ output: "Boolean { false }",
+ printOutput: "false",
+ inspectable: true,
+ variablesViewLabel: "Boolean { false }"
+ },
+
+ // 5
+ {
+ input: "new Date(" + testDate + ")",
+ output: "Date " + (new Date(testDate)).toISOString(),
+ printOutput: (new Date(testDate)).toString(),
+ inspectable: true,
+ },
+
+ // 6
+ {
+ input: "new Date('test')",
+ output: "Invalid Date",
+ printOutput: "Invalid Date",
+ inspectable: true,
+ variablesViewLabel: "Invalid Date",
+ },
+
+ // 7
+ {
+ input: "Date.prototype",
+ output: /Object \{.*\}/,
+ printOutput: "Invalid Date",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 8
+ {
+ input: "new Number(43)",
+ output: "Number { 43 }",
+ printOutput: "43",
+ inspectable: true,
+ variablesViewLabel: "Number { 43 }"
+ },
+
+ // 9
+ {
+ input: "new String('hello')",
+ output: /String { "hello", 6 more.* }/,
+ printOutput: "hello",
+ inspectable: true,
+ variablesViewLabel: "String"
+ },
+
+ // 10
+ {
+ input: "(function () { var s = new String('hello'); s.whatever = 23; " +
+ " return s;})()",
+ output: /String { "hello", whatever: 23, 6 more.* }/,
+ printOutput: "hello",
+ inspectable: true,
+ variablesViewLabel: "String"
+ },
+
+ // 11
+ {
+ input: "(function () { var s = new String('hello'); s[8] = 'x'; " +
+ " return s;})()",
+ output: /String { "hello", 8: "x", 6 more.* }/,
+ printOutput: "hello",
+ inspectable: true,
+ variablesViewLabel: "String"
+ },
+
+ // 12
+ {
+ // XXX: Can't test fulfilled and rejected promises, because promises get
+ // settled on the next tick of the event loop.
+ input: "new Promise(function () {})",
+ output: 'Promise { <state>: "pending" }',
+ printOutput: "[object Promise]",
+ inspectable: true,
+ variablesViewLabel: "Promise"
+ },
+
+ // 13
+ {
+ input: "(function () { var p = new Promise(function () {}); " +
+ "p.foo = 1; return p; }())",
+ output: 'Promise { <state>: "pending", foo: 1 }',
+ printOutput: "[object Promise]",
+ inspectable: true,
+ variablesViewLabel: "Promise"
+ },
+
+ // 14
+ {
+ input: "new Object({1: 'this\\nis\\nsupposed\\nto\\nbe\\na\\nvery" +
+ "\\nlong\\nstring\\n,shown\\non\\na\\nsingle\\nline', " +
+ "2: 'a shorter string', 3: 100})",
+ output: '[ <1 empty slot>, "this is supposed to be a very long ' + ELLIPSIS +
+ '", "a shorter string", 100 ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[4]"
+ },
+
+ // 15
+ {
+ input: "new Proxy({a:1},[1,2,3])",
+ output: 'Proxy { <target>: Object, <handler>: Array[3] }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Proxy"
+ }
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ return checkOutputForInputs(hud, inputTests);
+ }).then(finishUp);
+}
+
+function finishUp() {
+ inputTests = testDate = null;
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_06.js b/devtools/client/webconsole/test/browser_webconsole_output_06.js
new file mode 100644
index 000000000..ad69b3908
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_06.js
@@ -0,0 +1,283 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test the webconsole output for various arrays.
+
+const TEST_URI = "data:text/html;charset=utf8,test for console output - 06";
+const {ELLIPSIS} = require("devtools/shared/l10n");
+
+const testStrIn = "SHOW\\nALL\\nOF\\nTHIS\\nON\\nA\\nSINGLE" +
+ "\\nLINE ONLY. ESCAPE ALL NEWLINE";
+const testStrOut = "SHOW ALL OF THIS ON A SINGLE LINE O" + ELLIPSIS;
+
+var inputTests = [
+ // 1 - array with empty slots only
+ {
+ input: "Array(5)",
+ output: "Array [ <5 empty slots> ]",
+ printOutput: ",,,,",
+ inspectable: true,
+ variablesViewLabel: "Array[5]",
+ },
+ // 2 - array with one empty slot at the beginning
+ {
+ input: "[,1,2,3]",
+ output: "Array [ <1 empty slot>, 1, 2, 3 ]",
+ printOutput: ",1,2,3",
+ inspectable: true,
+ variablesViewLabel: "Array[4]",
+ },
+ // 3 - array with multiple consecutive empty slots at the beginning
+ {
+ input: "[,,,3,4,5]",
+ output: "Array [ <3 empty slots>, 3, 4, 5 ]",
+ printOutput: ",,,3,4,5",
+ inspectable: true,
+ variablesViewLabel: "Array[6]",
+ },
+ // 4 - array with one empty slot at the middle
+ {
+ input: "[0,1,,3,4,5]",
+ output: "Array [ 0, 1, <1 empty slot>, 3, 4, 5 ]",
+ printOutput: "0,1,,3,4,5",
+ inspectable: true,
+ variablesViewLabel: "Array[6]",
+ },
+ // 5 - array with multiple successive empty slots at the middle
+ {
+ input: "[0,1,,,,5]",
+ output: "Array [ 0, 1, <3 empty slots>, 5 ]",
+ printOutput: "0,1,,,,5",
+ inspectable: true,
+ variablesViewLabel: "Array[6]",
+ },
+ // 6 - array with multiple non successive single empty slots
+ {
+ input: "[0,,2,,4,5]",
+ output: "Array [ 0, <1 empty slot>, 2, <1 empty slot>, 4, 5 ]",
+ printOutput: "0,,2,,4,5",
+ inspectable: true,
+ variablesViewLabel: "Array[6]",
+ },
+ // 7 - array with multiple multi-slot holes
+ {
+ input: "[0,,,3,,,,7,8]",
+ output: "Array [ 0, <2 empty slots>, 3, <3 empty slots>, 7, 8 ]",
+ printOutput: "0,,,3,,,,7,8",
+ inspectable: true,
+ variablesViewLabel: "Array[9]",
+ },
+ // 8 - array with a single slot hole at the end
+ {
+ input: "[0,1,2,3,4,,]",
+ output: "Array [ 0, 1, 2, 3, 4, <1 empty slot> ]",
+ printOutput: "0,1,2,3,4,",
+ inspectable: true,
+ variablesViewLabel: "Array[6]",
+ },
+ // 9 - array with multiple consecutive empty slots at the end
+ {
+ input: "[0,1,2,,,,]",
+ output: "Array [ 0, 1, 2, <3 empty slots> ]",
+ printOutput: "0,1,2,,,",
+ inspectable: true,
+ variablesViewLabel: "Array[6]",
+ },
+
+ // 10 - array with members explicitly set to null
+ {
+ input: "[0,null,null,3,4,5]",
+ output: "Array [ 0, null, null, 3, 4, 5 ]",
+ printOutput: "0,,,3,4,5",
+ inspectable: true,
+ variablesViewLabel: "Array[6]"
+ },
+
+ // 11 - array with members explicitly set to undefined
+ {
+ input: "[0,undefined,undefined,3,4,5]",
+ output: "Array [ 0, undefined, undefined, 3, 4, 5 ]",
+ printOutput: "0,,,3,4,5",
+ inspectable: true,
+ variablesViewLabel: "Array[6]"
+ },
+
+ // 12 - array with long strings as elements
+ {
+ input: '["' + testStrIn + '", "' + testStrIn + '", "' + testStrIn + '"]',
+ output: 'Array [ "' + testStrOut + '", "' + testStrOut + '", "' +
+ testStrOut + '" ]',
+ inspectable: true,
+ printOutput: "SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. ESCAPE " +
+ "ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\nLINE ONLY. " +
+ "ESCAPE ALL NEWLINE,SHOW\nALL\nOF\nTHIS\nON\nA\nSINGLE\n" +
+ "LINE ONLY. ESCAPE ALL NEWLINE",
+ variablesViewLabel: "Array[3]"
+ },
+
+ // 13
+ {
+ input: '({0: "a", 1: "b"})',
+ output: 'Object [ "a", "b" ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[2]",
+ },
+
+ // 14
+ {
+ input: '({0: "a", 42: "b"})',
+ output: '[ "a", <9 empty slots>, 33 more\u2026 ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[43]",
+ },
+
+ // 15
+ {
+ input: '({0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", ' +
+ '7: "h", 8: "i", 9: "j", 10: "k", 11: "l"})',
+ output: 'Object [ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", ' +
+ "2 more\u2026 ]",
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[12]",
+ },
+
+ // 16
+ {
+ input: '({0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", 6: "g", ' +
+ '7: "h", 8: "i", 9: "j", 10: "k", 11: "l", m: "n"})',
+ output: 'Object { 0: "a", 1: "b", 2: "c", 3: "d", 4: "e", 5: "f", ' +
+ '6: "g", 7: "h", 8: "i", 9: "j", 3 more\u2026 }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 17
+ {
+ input: '({" ": "a"})',
+ output: 'Object { : "a" }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 18
+ {
+ input: '({})',
+ output: 'Object { }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 19
+ {
+ input: '({length: 0})',
+ output: 'Object [ ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[0]",
+ },
+
+ // 20
+ {
+ input: '({length: 1})',
+ output: '[ <1 empty slot> ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[1]",
+ },
+
+ // 21
+ {
+ input: '({0: "a", 1: "b", length: 1})',
+ output: 'Object { 0: "a", 1: "b", length: 1 }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 22
+ {
+ input: '({0: "a", 1: "b", length: 2})',
+ output: 'Object [ "a", "b" ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[2]",
+ },
+
+ // 23
+ {
+ input: '({0: "a", 1: "b", length: 3})',
+ output: '[ "a", "b", <1 empty slot> ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[3]",
+ },
+
+ // 24
+ {
+ input: '({0: "a", 2: "b", length: 2})',
+ output: 'Object { 0: "a", 2: "b", length: 2 }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 25
+ {
+ input: '({0: "a", 2: "b", length: 3})',
+ output: '[ "a", <1 empty slot>, "b" ]',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object[3]",
+ },
+
+ // 26
+ {
+ input: '({0: "a", b: "b", length: 1})',
+ output: 'Object { 0: "a", b: "b", length: 1 }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 27
+ {
+ input: '({0: "a", b: "b", length: 2})',
+ output: 'Object { 0: "a", b: "b", length: 2 }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+
+ // 28
+ {
+ input: '({42: "a"})',
+ output: 'Object { 42: "a" }',
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object",
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ return checkOutputForInputs(hud, inputTests);
+ }).then(finishUp);
+}
+
+function finishUp() {
+ inputTests = null;
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js b/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js
new file mode 100644
index 000000000..22de843f9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js
@@ -0,0 +1,72 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that multiple messages are copied into the clipboard and that they are
+// separated by new lines. See bug 916997.
+
+"use strict";
+
+add_task(function* () {
+ const TEST_URI = "data:text/html;charset=utf8,<p>hello world, bug 916997";
+ let clipboardValue = "";
+
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+ hud.jsterm.clearOutput();
+
+ let controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled");
+
+ content.console.log("Hello world! bug916997a");
+ content.console.log("Hello world 2! bug916997b");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Hello world! bug916997a",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }, {
+ text: "Hello world 2! bug916997b",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ hud.ui.output.selectAllMessages();
+ hud.outputNode.focus();
+
+ goUpdateCommand("cmd_copy");
+ controller = top.document.commandDispatcher
+ .getControllerForCommand("cmd_copy");
+ is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+
+ let selection = hud.iframeWindow.getSelection() + "";
+ info("selection '" + selection + "'");
+
+ waitForClipboard((str) => {
+ clipboardValue = str;
+ return str.indexOf("bug916997a") > -1 && str.indexOf("bug916997b") > -1;
+ },
+ () => {
+ goDoCommand("cmd_copy");
+ },
+ () => {
+ info("clipboard value '" + clipboardValue + "'");
+ let lines = clipboardValue.trim().split("\n");
+ is(hud.outputNode.children.length, 2, "number of messages");
+ is(lines.length, hud.outputNode.children.length, "number of lines");
+ isnot(lines[0].indexOf("bug916997a"), -1,
+ "first message text includes 'bug916997a'");
+ isnot(lines[1].indexOf("bug916997b"), -1,
+ "second message text includes 'bug916997b'");
+ is(lines[0].indexOf("bug916997b"), -1,
+ "first message text does not include 'bug916997b'");
+ },
+ () => {
+ info("last clipboard value: '" + clipboardValue + "'");
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js
new file mode 100644
index 000000000..097eb3b37
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_01.js
@@ -0,0 +1,122 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejections should be fixed.
+
+"use strict";
+
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(null);
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed(
+ "TypeError: this.toolbox is null");
+
+// Test the webconsole output for various types of DOM Nodes.
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-dom-elements.html";
+
+var inputTests = [
+ {
+ input: "testBodyNode()",
+ output: '<body class="body-class" id="body-id">',
+ printOutput: "[object HTMLBodyElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: true
+ },
+
+ {
+ input: "testDocumentElement()",
+ output: '<html dir="ltr" lang="en-US">',
+ printOutput: "[object HTMLHtmlElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: true
+ },
+
+ {
+ input: "testDocument()",
+ output: "HTMLDocument \u2192 " + TEST_URI,
+ printOutput: "[object HTMLDocument]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: false
+ },
+
+ {
+ input: "testNode()",
+ output: '<p some-attribute="some-value">',
+ printOutput: "[object HTMLParagraphElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: true
+ },
+
+ {
+ input: "testNodeList()",
+ output: "NodeList [ <p>, <p#lots-of-attributes>, <iframe>, " +
+ "<div.some.classname.here.with.more.classnames.here>, " +
+ "<svg>, <clipPath>, <rect>, <script> ]",
+ printOutput: "[object NodeList]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: true
+ },
+
+ {
+ input: "testNodeInIframe()",
+ output: "<p>",
+ printOutput: "[object HTMLParagraphElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: true
+ },
+
+ {
+ input: "testLotsOfAttributes()",
+ output: '<p id="lots-of-attributes" a="" b="" c="" d="" e="" f="" g="" ' +
+ 'h="" i="" j="" k="" l="" m="" n="">',
+ printOutput: "[object HTMLParagraphElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: true
+ },
+
+ {
+ input: "testDocumentFragment()",
+ output: "DocumentFragment [ <span.foo>, <div#fragdiv> ]",
+ printOutput: "[object DocumentFragment]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: false
+ },
+
+ {
+ input: "testNodeInDocumentFragment()",
+ output: '<span class="foo" data-lolz="hehe">',
+ printOutput: "[object HTMLSpanElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: false
+ },
+
+ {
+ input: "testUnattachedNode()",
+ output: '<p class="such-class" data-data="such-data">',
+ printOutput: "[object HTMLParagraphElement]",
+ inspectable: true,
+ noClick: true,
+ inspectorIcon: false
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ yield checkOutputForInputs(hud, inputTests);
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js
new file mode 100644
index 000000000..51fe89e01
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_02.js
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the inspector links in the webconsole output for DOM Nodes actually
+// open the inspector and select the right node.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-dom-elements.html";
+
+const TEST_DATA = [
+ {
+ // The first test shouldn't be returning the body element as this is the
+ // default selected node, so re-selecting it won't fire the
+ // inspector-updated event
+ input: "testNode()",
+ output: '<p some-attribute="some-value">',
+ displayName: "p",
+ attrs: [{name: "some-attribute", value: "some-value"}]
+ },
+ {
+ input: "testBodyNode()",
+ output: '<body class="body-class" id="body-id">',
+ displayName: "body",
+ attrs: [
+ {
+ name: "class", value: "body-class"
+ },
+ {
+ name: "id", value: "body-id"
+ }
+ ]
+ },
+ {
+ input: "testNodeInIframe()",
+ output: "<p>",
+ displayName: "p",
+ attrs: []
+ },
+ {
+ input: "testDocumentElement()",
+ output: '<html dir="ltr" lang="en-US">',
+ displayName: "html",
+ attrs: [
+ {
+ name: "dir",
+ value: "ltr"
+ },
+ {
+ name: "lang",
+ value: "en-US"
+ }
+ ]
+ }
+];
+
+function test() {
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ yield checkDomElementHighlightingForInputs(hud, TEST_DATA);
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js
new file mode 100644
index 000000000..b5dd125d1
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_03.js
@@ -0,0 +1,70 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that inspector links in webconsole outputs for DOM Nodes highlight
+// the actual DOM Nodes on hover
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-dom-elements.html";
+
+function test() {
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ let toolbox = gDevTools.getToolbox(hud.target);
+
+ // Loading the inspector panel at first, to make it possible to listen for
+ // new node selections
+ yield toolbox.loadTool("inspector");
+ toolbox.getPanel("inspector");
+
+ info("Executing 'testNode()' in the web console to output a DOM Node");
+ let [result] = yield jsEval("testNode()", hud, {
+ text: '<p some-attribute="some-value">'
+ });
+
+ let elementNodeWidget = yield getWidget(result);
+
+ let nodeFront = yield hoverOverWidget(elementNodeWidget, toolbox);
+ let attrs = nodeFront.attributes;
+ is(nodeFront.tagName, "P", "The correct node was highlighted");
+ is(attrs[0].name, "some-attribute", "The correct node was highlighted");
+ is(attrs[0].value, "some-value", "The correct node was highlighted");
+ }).then(finishTest);
+}
+
+function jsEval(input, hud, message) {
+ hud.jsterm.execute(input);
+ return waitForMessages({
+ webconsole: hud,
+ messages: [message]
+ });
+}
+
+function* getWidget(result) {
+ info("Getting the output ElementNode widget");
+
+ let msg = [...result.matched][0];
+ let elementNodeWidget = [...msg._messageObject.widgets][0];
+ ok(elementNodeWidget, "ElementNode widget found in the output");
+
+ info("Waiting for the ElementNode widget to be linked to the inspector");
+ yield elementNodeWidget.linkToInspector();
+
+ return elementNodeWidget;
+}
+
+function* hoverOverWidget(widget, toolbox) {
+ info("Hovering over the output to highlight the node");
+
+ let onHighlight = toolbox.once("node-highlight");
+ EventUtils.sendMouseEvent({type: "mouseover"}, widget.element,
+ widget.element.ownerDocument.defaultView);
+ let nodeFront = yield onHighlight;
+ ok(true, "The highlighter was shown on a node");
+ return nodeFront;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js
new file mode 100644
index 000000000..c7eb94902
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_04.js
@@ -0,0 +1,113 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that inspector links in the webconsole output for DOM Nodes do not try
+// to highlight or select nodes once they have been detached
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-dom-elements.html";
+
+const TEST_DATA = [
+ {
+ // The first test shouldn't be returning the body element as this is the
+ // default selected node, so re-selecting it won't fire the
+ // inspector-updated event
+ input: "testNode()",
+ output: '<p some-attribute="some-value">'
+ },
+ {
+ input: "testSvgNode()",
+ output: '<clipPath>'
+ },
+ {
+ input: "testBodyNode()",
+ output: '<body class="body-class" id="body-id">'
+ },
+ {
+ input: "testNodeInIframe()",
+ output: "<p>"
+ },
+ {
+ input: "testDocumentElement()",
+ output: '<html dir="ltr" lang="en-US">'
+ }
+];
+
+const PREF = "devtools.webconsole.persistlog";
+
+function test() {
+ Services.prefs.setBoolPref(PREF, true);
+ registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
+
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ let toolbox = gDevTools.getToolbox(hud.target);
+
+ info("Executing the test data");
+ let widgets = [];
+ for (let data of TEST_DATA) {
+ let [result] = yield jsEval(data.input, hud, {text: data.output});
+ let {widget} = yield getWidgetAndMessage(result);
+ widgets.push(widget);
+ }
+
+ info("Reloading the page");
+ yield reloadPage();
+
+ info("Iterating over the ElementNode widgets");
+ for (let widget of widgets) {
+ // Verify that openNodeInInspector rejects since the associated dom node
+ // doesn't exist anymore
+ yield widget.openNodeInInspector().then(() => {
+ ok(false, "The openNodeInInspector promise resolved");
+ }, () => {
+ ok(true, "The openNodeInInspector promise rejected as expected");
+ });
+ yield toolbox.selectTool("webconsole");
+
+ // Verify that highlightDomNode rejects too, for the same reason
+ yield widget.highlightDomNode().then(() => {
+ ok(false, "The highlightDomNode promise resolved");
+ }, () => {
+ ok(true, "The highlightDomNode promise rejected as expected");
+ });
+ }
+ }).then(finishTest);
+}
+
+function jsEval(input, hud, message) {
+ info("Executing '" + input + "' in the web console");
+ hud.jsterm.execute(input);
+ return waitForMessages({
+ webconsole: hud,
+ messages: [message]
+ });
+}
+
+function* getWidgetAndMessage(result) {
+ info("Getting the output ElementNode widget");
+
+ let msg = [...result.matched][0];
+ let widget = [...msg._messageObject.widgets][0];
+ ok(widget, "ElementNode widget found in the output");
+
+ info("Waiting for the ElementNode widget to be linked to the inspector");
+ yield widget.linkToInspector();
+
+ return {widget: widget, msg: msg};
+}
+
+function reloadPage() {
+ let def = promise.defer();
+ gBrowser.selectedBrowser.addEventListener("load", function onload() {
+ gBrowser.selectedBrowser.removeEventListener("load", onload, true);
+ def.resolve();
+ }, true);
+ content.location.reload();
+ return def.promise;
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js
new file mode 100644
index 000000000..9d35ef984
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_dom_elements_05.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test the inspector links in the webconsole output for namespaced elements
+// actually open the inspector and select the right node.
+
+const XHTML = `
+ <!DOCTYPE html>
+ <html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <body>
+ <svg:svg width="100" height="100">
+ <svg:clipPath id="clip">
+ <svg:rect id="rectangle" x="0" y="0" width="10" height="5"></svg:rect>
+ </svg:clipPath>
+ <svg:circle cx="0" cy="0" r="5"></svg:circle>
+ </svg:svg>
+ </body>
+ </html>
+`;
+
+const TEST_URI = "data:application/xhtml+xml;charset=utf-8," + encodeURI(XHTML);
+
+const TEST_DATA = [
+ {
+ input: 'document.querySelector("clipPath")',
+ output: '<svg:clipPath id="clip">',
+ displayName: "svg:clipPath"
+ },
+ {
+ input: 'document.querySelector("circle")',
+ output: '<svg:circle cx="0" cy="0" r="5">',
+ displayName: "svg:circle"
+ },
+];
+
+function test() {
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ yield checkDomElementHighlightingForInputs(hud, TEST_DATA);
+ }).then(finishTest);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_events.js b/devtools/client/webconsole/test/browser_webconsole_output_events.js
new file mode 100644
index 000000000..9bd04bfc7
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_events.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Whitelisting this test.
+// As part of bug 1077403, the leaking uncaught rejection should be fixed.
+
+"use strict";
+
+thisTestLeaksUncaughtRejectionsAndShouldBeFixed("null");
+
+// Test the webconsole output for DOM events.
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-events.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("testDOMEvents()");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "testDOMEvents() output",
+ text: "undefined",
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log() output for mousemove",
+ text: /eventLogger mousemove { target: .+, buttons: 0, clientX: \d+, clientY: \d+, layerX: \d+, layerY: \d+ }/,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log() output for keypress",
+ text: /eventLogger keypress Shift { target: .+, key: .+, charCode: \d+, keyCode: \d+ }/,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_order.js b/devtools/client/webconsole/test/browser_webconsole_output_order.js
new file mode 100644
index 000000000..66fa74cb0
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_order.js
@@ -0,0 +1,47 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that any output created from calls to the console API comes before the
+// echoed JavaScript.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console.html";
+
+add_task(function* () {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole();
+
+ let jsterm = hud.jsterm;
+
+ jsterm.clearOutput();
+ jsterm.execute("console.log('foo', 'bar');");
+
+ let [functionCall, consoleMessage, result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "console.log('foo', 'bar');",
+ category: CATEGORY_INPUT,
+ },
+ {
+ text: "foo bar",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ },
+ {
+ text: "undefined",
+ category: CATEGORY_OUTPUT,
+ }]
+ });
+
+ let fncallNode = [...functionCall.matched][0];
+ let consoleMessageNode = [...consoleMessage.matched][0];
+ let resultNode = [...result.matched][0];
+ is(fncallNode.nextElementSibling, consoleMessageNode,
+ "console.log() is followed by 'foo' 'bar'");
+ is(consoleMessageNode.nextElementSibling, resultNode,
+ "'foo' 'bar' is followed by undefined");
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_regexp.js b/devtools/client/webconsole/test/browser_webconsole_output_regexp.js
new file mode 100644
index 000000000..2d6e767e9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_regexp.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the webconsole output for various types of objects.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-output-regexp.html";
+
+var inputTests = [
+ // 0
+ {
+ input: "/foo/igym",
+ output: "/foo/gimy",
+ printOutput: "Error: source called",
+ inspectable: true,
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ return checkOutputForInputs(hud, inputTests);
+ }).then(finishUp);
+}
+
+function finishUp() {
+ inputTests = null;
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_output_table.js b/devtools/client/webconsole/test/browser_webconsole_output_table.js
new file mode 100644
index 000000000..372afb28d
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_output_table.js
@@ -0,0 +1,199 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that console.table() works as intended.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/test-console-table.html";
+
+const TEST_DATA = [
+ {
+ command: "console.table(languages1)",
+ data: [
+ { _index: 0, name: "\"JavaScript\"", fileExtension: "Array[1]" },
+ { _index: 1, name: "Object", fileExtension: "\".ts\"" },
+ { _index: 2, name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+ ],
+ columns: { _index: "(index)", name: "name", fileExtension: "fileExtension" }
+ },
+ {
+ command: "console.table(languages1, 'name')",
+ data: [
+ { _index: 0, name: "\"JavaScript\"", fileExtension: "Array[1]" },
+ { _index: 1, name: "Object", fileExtension: "\".ts\"" },
+ { _index: 2, name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+ ],
+ columns: { _index: "(index)", name: "name" }
+ },
+ {
+ command: "console.table(languages1, ['name'])",
+ data: [
+ { _index: 0, name: "\"JavaScript\"", fileExtension: "Array[1]" },
+ { _index: 1, name: "Object", fileExtension: "\".ts\"" },
+ { _index: 2, name: "\"CoffeeScript\"", fileExtension: "\".coffee\"" }
+ ],
+ columns: { _index: "(index)", name: "name" }
+ },
+ {
+ command: "console.table(languages2)",
+ data: [
+ { _index: "csharp", name: "\"C#\"", paradigm: "\"object-oriented\"" },
+ { _index: "fsharp", name: "\"F#\"", paradigm: "\"functional\"" }
+ ],
+ columns: { _index: "(index)", name: "name", paradigm: "paradigm" }
+ },
+ {
+ command: "console.table([[1, 2], [3, 4]])",
+ data: [
+ { _index: 0, 0: "1", 1: "2" },
+ { _index: 1, 0: "3", 1: "4" }
+ ],
+ columns: { _index: "(index)", 0: "0", 1: "1" }
+ },
+ {
+ command: "console.table({a: [1, 2], b: [3, 4]})",
+ data: [
+ { _index: "a", 0: "1", 1: "2" },
+ { _index: "b", 0: "3", 1: "4" }
+ ],
+ columns: { _index: "(index)", 0: "0", 1: "1" }
+ },
+ {
+ command: "console.table(family)",
+ data: [
+ { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"",
+ age: "32" },
+ { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"",
+ age: "33" },
+ { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"",
+ age: "5" },
+ { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+ ],
+ columns: { _index: "(index)", firstName: "firstName", lastName: "lastName",
+ age: "age" }
+ },
+ {
+ command: "console.table(family, [])",
+ data: [
+ { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"",
+ age: "32" },
+ { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"",
+ age: "33" },
+ { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"",
+ age: "5" },
+ { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+ ],
+ columns: { _index: "(index)" }
+ },
+ {
+ command: "console.table(family, ['firstName', 'lastName'])",
+ data: [
+ { _index: "mother", firstName: "\"Susan\"", lastName: "\"Doyle\"",
+ age: "32" },
+ { _index: "father", firstName: "\"John\"", lastName: "\"Doyle\"",
+ age: "33" },
+ { _index: "daughter", firstName: "\"Lily\"", lastName: "\"Doyle\"",
+ age: "5" },
+ { _index: "son", firstName: "\"Mike\"", lastName: "\"Doyle\"", age: "8" },
+ ],
+ columns: { _index: "(index)", firstName: "firstName", lastName: "lastName" }
+ },
+ {
+ command: "console.table(mySet)",
+ data: [
+ { _index: 0, _value: "1" },
+ { _index: 1, _value: "5" },
+ { _index: 2, _value: "\"some text\"" },
+ { _index: 3, _value: "null" },
+ { _index: 4, _value: "undefined" }
+ ],
+ columns: { _index: "(iteration index)", _value: "Values" }
+ },
+ {
+ command: "console.table(myMap)",
+ data: [
+ { _index: 0, _key: "\"a string\"",
+ _value: "\"value associated with 'a string'\"" },
+ { _index: 1, _key: "5", _value: "\"value associated with 5\"" },
+ ],
+ columns: { _index: "(iteration index)", _key: "Key", _value: "Values" }
+ },
+ {
+ command: "console.table(weakset)",
+ data: [
+ { _value: "String" },
+ { _value: "String" },
+ ],
+ columns: { _index: "(iteration index)", _value: "Values" },
+ couldBeOutOfOrder: true,
+ },
+ {
+ command: "console.table(weakmap)",
+ data: [
+ { _key: "String", _value: "\"oh no\"" },
+ { _key: "String", _value: "23" },
+ ],
+ columns: { _index: "(iteration index)", _key: "Key", _value: "Values" },
+ couldBeOutOfOrder: true,
+ },
+];
+
+add_task(function* () {
+ const {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+
+ for (let testdata of TEST_DATA) {
+ hud.jsterm.clearOutput();
+
+ info("Executing " + testdata.command);
+
+ let onTableRender = once(hud.ui, "messages-table-rendered");
+ hud.jsterm.execute(testdata.command);
+ yield onTableRender;
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: testdata.command + " output",
+ consoleTable: true
+ }],
+ });
+
+ let node = [...result.matched][0];
+ ok(node, "found trace log node");
+
+ let obj = node._messageObject;
+ ok(obj, "console.trace message object");
+
+ ok(obj._data, "found table data object");
+
+ let data = obj._data.map(entries => {
+ let entryResult = {};
+
+ for (let key of Object.keys(entries)) {
+ // If the results can be out of order, then ignore _index.
+ if (!testdata.couldBeOutOfOrder || key !== "_index") {
+ entryResult[key] = entries[key] instanceof HTMLElement ?
+ entries[key].textContent : entries[key];
+ }
+ }
+
+ return entryResult;
+ });
+
+ if (testdata.couldBeOutOfOrder) {
+ data = data.map(e => e.toSource()).sort().join(",");
+ let expected = testdata.data.map(e => e.toSource()).sort().join(",");
+ is(data, expected, "table data is correct");
+ } else {
+ is(data.toSource(), testdata.data.toSource(), "table data is correct");
+ }
+ ok(obj._columns, "found table column object");
+ is(obj._columns.toSource(), testdata.columns.toSource(),
+ "table column is correct");
+ }
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_promise.js b/devtools/client/webconsole/test/browser_webconsole_promise.js
new file mode 100644
index 000000000..59cd287ca
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_promise.js
@@ -0,0 +1,35 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1148759 - Test the webconsole can display promises inside objects.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,test for console and promises";
+
+var inputTests = [
+ // 0
+ {
+ input: "({ x: Promise.resolve() })",
+ output: "Object { x: Promise }",
+ printOutput: "[object Object]",
+ inspectable: true,
+ variablesViewLabel: "Object"
+ },
+];
+
+function test() {
+ requestLongerTimeout(2);
+
+ Task.spawn(function* () {
+ let {tab} = yield loadTab(TEST_URI);
+ let hud = yield openConsole(tab);
+ return checkOutputForInputs(hud, inputTests);
+ }).then(finishUp);
+}
+
+function finishUp() {
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_property_provider.js b/devtools/client/webconsole/test/browser_webconsole_property_provider.js
new file mode 100644
index 000000000..0c9b4c4e3
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_property_provider.js
@@ -0,0 +1,46 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the property provider, which is part of the code completion
+// infrastructure.
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test the JS property provider";
+
+function test() {
+ loadTab(TEST_URI).then(testPropertyProvider);
+}
+
+function testPropertyProvider({browser}) {
+ browser.removeEventListener("load", testPropertyProvider, true);
+ let {JSPropertyProvider} = require("devtools/shared/webconsole/js-property-provider");
+
+ let tmp = Cu.import("resource://gre/modules/jsdebugger.jsm", {});
+ tmp.addDebuggerToGlobal(tmp);
+ let dbg = new tmp.Debugger();
+ let dbgWindow = dbg.addDebuggee(content);
+
+ let completion = JSPropertyProvider(dbgWindow, null, "thisIsNotDefined");
+ is(completion.matches.length, 0, "no match for 'thisIsNotDefined");
+
+ // This is a case the PropertyProvider can't handle. Should return null.
+ completion = JSPropertyProvider(dbgWindow, null, "window[1].acb");
+ is(completion, null, "no match for 'window[1].acb");
+
+ // A very advanced completion case.
+ let strComplete =
+ "function a() { }document;document.getElementById(window.locatio";
+ completion = JSPropertyProvider(dbgWindow, null, strComplete);
+ ok(completion.matches.length == 2, "two matches found");
+ ok(completion.matchProp == "locatio", "matching part is 'test'");
+ let matches = completion.matches;
+ matches.sort();
+ ok(matches[0] == "location", "the first match is 'location'");
+ ok(matches[1] == "locationbar", "the second match is 'locationbar'");
+
+ dbg.removeDebuggee(content);
+ finishTest();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_reflow.js b/devtools/client/webconsole/test/browser_webconsole_reflow.js
new file mode 100644
index 000000000..86caa10e0
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_reflow.js
@@ -0,0 +1,33 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for " +
+ "reflow activity";
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ function onReflowListenersReady() {
+ browser.contentDocument.body.style.display = "none";
+ browser.contentDocument.body.clientTop;
+ }
+
+ Services.prefs.setBoolPref("devtools.webconsole.filter.csslog", true);
+ hud.ui._updateReflowActivityListener(onReflowListenersReady);
+ Services.prefs.clearUserPref("devtools.webconsole.filter.csslog");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: /reflow: /,
+ category: CATEGORY_CSS,
+ severity: SEVERITY_LOG,
+ }],
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js b/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js
new file mode 100644
index 000000000..566af8d42
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js
@@ -0,0 +1,76 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,<p>test Scratchpad panel " +
+ "linking</p>";
+
+var { Tools } = require("devtools/client/definitions");
+var { isTargetSupported } = Tools.scratchpad;
+
+function pushPrefEnv() {
+ let deferred = promise.defer();
+ let options = {"set":
+ [["devtools.scratchpad.enabled", true]
+ ]};
+ SpecialPowers.pushPrefEnv(options, deferred.resolve);
+ return deferred.promise;
+}
+
+add_task(function* () {
+ waitForExplicitFinish();
+
+ yield pushPrefEnv();
+
+ yield loadTab(TEST_URI);
+
+ info("Opening toolbox with Scratchpad panel");
+
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ let toolbox = yield gDevTools.showToolbox(target, "scratchpad", "window");
+
+ let scratchpadPanel = toolbox.getPanel("scratchpad");
+ let { scratchpad } = scratchpadPanel;
+ is(toolbox.getCurrentPanel(), scratchpadPanel,
+ "Scratchpad is currently selected panel");
+
+ info("Switching to webconsole panel");
+
+ let webconsolePanel = yield toolbox.selectTool("webconsole");
+ let { hud } = webconsolePanel;
+ is(toolbox.getCurrentPanel(), webconsolePanel,
+ "Webconsole is currently selected panel");
+
+ info("console.log()ing from Scratchpad");
+
+ scratchpad.setText("console.log('foobar-from-scratchpad')");
+ scratchpad.run();
+ let messages = yield waitForMessages({
+ webconsole: hud,
+ messages: [{ text: "foobar-from-scratchpad" }]
+ });
+
+ info("Clicking link to switch to and focus Scratchpad");
+
+ let [matched] = [...messages[0].matched];
+ ok(matched, "Found logged message from Scratchpad");
+ let anchor = matched.querySelector(".message-location .frame-link-filename");
+
+ toolbox.on("scratchpad-selected", function selected() {
+ toolbox.off("scratchpad-selected", selected);
+
+ is(toolbox.getCurrentPanel(), scratchpadPanel,
+ "Clicking link switches to Scratchpad panel");
+
+ is(Services.ww.activeWindow, toolbox.win.parent,
+ "Scratchpad's toolbox is focused");
+
+ Tools.scratchpad.isTargetSupported = isTargetSupported;
+ finish();
+ });
+
+ EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js
new file mode 100644
index 000000000..779d80376
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Ensure that [Learn More] links appear alongside any errors listed
+// in "errordocs.js". Note: this only tests script execution.
+
+"use strict";
+
+const ErrorDocs = require("devtools/server/actors/errordocs");
+
+function makeURIData(script) {
+ return `data:text/html;charset=utf8,<script>${script}</script>`;
+}
+
+const TestData = [
+ {
+ jsmsg: "JSMSG_READ_ONLY",
+ script: "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;",
+ isException: true,
+ },
+ {
+ jsmsg: "JSMSG_STMT_AFTER_RETURN",
+ script: "function a() { return; 1 + 1; };",
+ isException: false,
+ }
+];
+
+add_task(function* () {
+ yield loadTab("data:text/html;charset=utf8,errordoc tests");
+
+ let hud = yield openConsole();
+
+ for (let i = 0; i < TestData.length; i++) {
+ yield testScriptError(hud, TestData[i]);
+ }
+});
+
+function* testScriptError(hud, testData) {
+ if (testData.isException === true) {
+ expectUncaughtException();
+ }
+
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, makeURIData(testData.script));
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ category: CATEGORY_JS
+ }
+ ]
+ });
+
+ // grab the most current error doc URL
+ let url = ErrorDocs.GetURL({ errorMessageName: testData.jsmsg });
+
+ let hrefs = {};
+ for (let link of hud.jsterm.outputNode.querySelectorAll("a")) {
+ hrefs[link.href] = true;
+ }
+
+ ok(url in hrefs, `Expected a link to ${url}.`);
+
+ hud.jsterm.clearOutput();
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js b/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js
new file mode 100644
index 000000000..43cb96bdc
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_show_subresource_security_errors.js
@@ -0,0 +1,39 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Ensure non-toplevel security errors are displayed
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console subresource STS " +
+ "warning test";
+const TEST_DOC = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test_bug1092055_shouldwarn.html";
+const SAMPLE_MSG = "specified a header that could not be parsed successfully.";
+
+add_task(function* setup() {
+ yield SpecialPowers.pushPrefEnv({
+ set: [["dom.ipc.processCount", 1]]
+ });
+});
+
+add_task(function* () {
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ hud.jsterm.clearOutput();
+
+ let loaded = loadBrowser(browser);
+ BrowserTestUtils.loadURI(browser, TEST_DOC);
+ yield loaded;
+
+ yield waitForSuccess({
+ name: "Subresource STS warning displayed successfully",
+ validator: function () {
+ return hud.outputNode.textContent.indexOf(SAMPLE_MSG) > -1;
+ }
+ });
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js b/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js
new file mode 100644
index 000000000..b66d5afff
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_shows_reqs_in_netmonitor.js
@@ -0,0 +1,73 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf8,Test that the web console " +
+ "displays requests that have been recorded in the " +
+ "netmonitor, even if the console hadn't opened yet.";
+
+const TEST_FILE = "test-network-request.html";
+const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/" +
+ "test/" + TEST_FILE;
+
+const NET_PREF = "devtools.webconsole.filter.networkinfo";
+Services.prefs.setBoolPref(NET_PREF, true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(NET_PREF);
+});
+
+add_task(function* () {
+ let { tab, browser } = yield loadTab(TEST_URI);
+
+ let target = TargetFactory.forTab(tab);
+ let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
+ info("Network panel is open.");
+
+ yield loadDocument(browser);
+ info("Document loaded.");
+
+ // Test that the request appears in the network panel.
+ testNetmonitor(toolbox);
+
+ // Test that the request appears in the console.
+ let hud = yield openConsole();
+ info("Web console is open");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "network message",
+ text: TEST_FILE,
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG
+ }
+ ]
+ });
+});
+
+function loadDocument(browser) {
+ let deferred = promise.defer();
+
+ browser.addEventListener("load", function onLoad() {
+ browser.removeEventListener("load", onLoad, true);
+ deferred.resolve();
+ }, true);
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_PATH);
+
+ return deferred.promise;
+}
+
+function testNetmonitor(toolbox) {
+ let monitor = toolbox.getCurrentPanel();
+ let { RequestsMenu } = monitor.panelWin.NetMonitorView;
+ RequestsMenu.lazyUpdate = false;
+ is(RequestsMenu.itemCount, 1, "Network request appears in the network panel");
+
+ let item = RequestsMenu.getItemAtIndex(0);
+ is(item.attachment.method, "GET", "The attached method is correct.");
+ is(item.attachment.url, TEST_PATH, "The attached url is correct.");
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_split.js b/devtools/client/webconsole/test/browser_webconsole_split.js
new file mode 100644
index 000000000..0242d94b4
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_split.js
@@ -0,0 +1,268 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,Web Console test for splitting";
+
+function test() {
+ waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 1]]}, runTest);
+}
+
+function runTest() {
+ // Test is slow on Linux EC2 instances - Bug 962931
+ requestLongerTimeout(2);
+
+ let {Toolbox} = require("devtools/client/framework/toolbox");
+ let toolbox;
+
+ loadTab(TEST_URI).then(testConsoleLoadOnDifferentPanel);
+
+ function testConsoleLoadOnDifferentPanel() {
+ info("About to check console loads even when non-webconsole panel is open");
+
+ openPanel("inspector").then(() => {
+ toolbox.on("webconsole-ready", () => {
+ ok(true, "Webconsole has been triggered as loaded while another tool " +
+ "is active");
+ testKeyboardShortcuts();
+ });
+
+ // Opens split console.
+ toolbox.toggleSplitConsole();
+ });
+ }
+
+ function testKeyboardShortcuts() {
+ info("About to check that panel responds to ESCAPE keyboard shortcut");
+
+ toolbox.once("split-console", () => {
+ ok(true, "Split console has been triggered via ESCAPE keypress");
+ checkAllTools();
+ });
+
+ // Closes split console.
+ EventUtils.sendKey("ESCAPE", toolbox.win);
+ }
+
+ function checkAllTools() {
+ info("About to check split console with each panel individually.");
+
+ Task.spawn(function* () {
+ yield openAndCheckPanel("jsdebugger");
+ yield openAndCheckPanel("inspector");
+ yield openAndCheckPanel("styleeditor");
+ yield openAndCheckPanel("performance");
+ yield openAndCheckPanel("netmonitor");
+
+ yield checkWebconsolePanelOpened();
+ testBottomHost();
+ });
+ }
+
+ function getCurrentUIState() {
+ let win = toolbox.win;
+ let deck = toolbox.doc.querySelector("#toolbox-deck");
+ let webconsolePanel = toolbox.webconsolePanel;
+ let splitter = toolbox.doc.querySelector("#toolbox-console-splitter");
+
+ let containerHeight = parseFloat(win.getComputedStyle(deck.parentNode)
+ .getPropertyValue("height"));
+ let deckHeight = parseFloat(win.getComputedStyle(deck)
+ .getPropertyValue("height"));
+ let webconsoleHeight = parseFloat(win.getComputedStyle(webconsolePanel)
+ .getPropertyValue("height"));
+ let splitterVisibility = !splitter.getAttribute("hidden");
+ let openedConsolePanel = toolbox.currentToolId === "webconsole";
+ let cmdButton = toolbox.doc.querySelector("#command-button-splitconsole");
+
+ return {
+ deckHeight: deckHeight,
+ containerHeight: containerHeight,
+ webconsoleHeight: webconsoleHeight,
+ splitterVisibility: splitterVisibility,
+ openedConsolePanel: openedConsolePanel,
+ buttonSelected: cmdButton.hasAttribute("checked")
+ };
+ }
+
+ function checkWebconsolePanelOpened() {
+ info("About to check special cases when webconsole panel is open.");
+
+ let deferred = promise.defer();
+
+ // Start with console split, so we can test for transition to main panel.
+ toolbox.toggleSplitConsole();
+
+ let currentUIState = getCurrentUIState();
+
+ ok(currentUIState.splitterVisibility,
+ "Splitter is visible when console is split");
+ ok(currentUIState.deckHeight > 0,
+ "Deck has a height > 0 when console is split");
+ ok(currentUIState.webconsoleHeight > 0,
+ "Web console has a height > 0 when console is split");
+ ok(!currentUIState.openedConsolePanel,
+ "The console panel is not the current tool");
+ ok(currentUIState.buttonSelected, "The command button is selected");
+
+ openPanel("webconsole").then(() => {
+ currentUIState = getCurrentUIState();
+
+ ok(!currentUIState.splitterVisibility,
+ "Splitter is hidden when console is opened.");
+ is(currentUIState.deckHeight, 0,
+ "Deck has a height == 0 when console is opened.");
+ is(currentUIState.webconsoleHeight, currentUIState.containerHeight,
+ "Web console is full height.");
+ ok(currentUIState.openedConsolePanel,
+ "The console panel is the current tool");
+ ok(currentUIState.buttonSelected,
+ "The command button is still selected.");
+
+ // Make sure splitting console does nothing while webconsole is opened
+ toolbox.toggleSplitConsole();
+
+ currentUIState = getCurrentUIState();
+
+ ok(!currentUIState.splitterVisibility,
+ "Splitter is hidden when console is opened.");
+ is(currentUIState.deckHeight, 0,
+ "Deck has a height == 0 when console is opened.");
+ is(currentUIState.webconsoleHeight, currentUIState.containerHeight,
+ "Web console is full height.");
+ ok(currentUIState.openedConsolePanel,
+ "The console panel is the current tool");
+ ok(currentUIState.buttonSelected,
+ "The command button is still selected.");
+
+ // Make sure that split state is saved after opening another panel
+ openPanel("inspector").then(() => {
+ currentUIState = getCurrentUIState();
+ ok(currentUIState.splitterVisibility,
+ "Splitter is visible when console is split");
+ ok(currentUIState.deckHeight > 0,
+ "Deck has a height > 0 when console is split");
+ ok(currentUIState.webconsoleHeight > 0,
+ "Web console has a height > 0 when console is split");
+ ok(!currentUIState.openedConsolePanel,
+ "The console panel is not the current tool");
+ ok(currentUIState.buttonSelected,
+ "The command button is still selected.");
+
+ toolbox.toggleSplitConsole();
+ deferred.resolve();
+ });
+ });
+ return deferred.promise;
+ }
+
+ function openPanel(toolId) {
+ let deferred = promise.defer();
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ gDevTools.showToolbox(target, toolId).then(function (box) {
+ toolbox = box;
+ deferred.resolve();
+ }).then(null, console.error);
+ return deferred.promise;
+ }
+
+ function openAndCheckPanel(toolId) {
+ let deferred = promise.defer();
+ openPanel(toolId).then(() => {
+ info("Checking toolbox for " + toolId);
+ checkToolboxUI(toolbox.getCurrentPanel());
+ deferred.resolve();
+ });
+ return deferred.promise;
+ }
+
+ function checkToolboxUI() {
+ let currentUIState = getCurrentUIState();
+
+ ok(!currentUIState.splitterVisibility, "Splitter is hidden by default");
+ is(currentUIState.deckHeight, currentUIState.containerHeight,
+ "Deck has a height > 0 by default");
+ is(currentUIState.webconsoleHeight, 0,
+ "Web console is collapsed by default");
+ ok(!currentUIState.openedConsolePanel,
+ "The console panel is not the current tool");
+ ok(!currentUIState.buttonSelected, "The command button is not selected.");
+
+ toolbox.toggleSplitConsole();
+
+ currentUIState = getCurrentUIState();
+
+ ok(currentUIState.splitterVisibility,
+ "Splitter is visible when console is split");
+ ok(currentUIState.deckHeight > 0,
+ "Deck has a height > 0 when console is split");
+ ok(currentUIState.webconsoleHeight > 0,
+ "Web console has a height > 0 when console is split");
+ is(Math.round(currentUIState.deckHeight + currentUIState.webconsoleHeight),
+ currentUIState.containerHeight,
+ "Everything adds up to container height");
+ ok(!currentUIState.openedConsolePanel,
+ "The console panel is not the current tool");
+ ok(currentUIState.buttonSelected, "The command button is selected.");
+
+ toolbox.toggleSplitConsole();
+
+ currentUIState = getCurrentUIState();
+
+ ok(!currentUIState.splitterVisibility, "Splitter is hidden after toggling");
+ is(currentUIState.deckHeight, currentUIState.containerHeight,
+ "Deck has a height > 0 after toggling");
+ is(currentUIState.webconsoleHeight, 0,
+ "Web console is collapsed after toggling");
+ ok(!currentUIState.openedConsolePanel,
+ "The console panel is not the current tool");
+ ok(!currentUIState.buttonSelected, "The command button is not selected.");
+ }
+
+ function testBottomHost() {
+ checkHostType(Toolbox.HostType.BOTTOM);
+
+ checkToolboxUI();
+
+ toolbox.switchHost(Toolbox.HostType.SIDE).then(testSidebarHost);
+ }
+
+ function testSidebarHost() {
+ checkHostType(Toolbox.HostType.SIDE);
+
+ checkToolboxUI();
+
+ toolbox.switchHost(Toolbox.HostType.WINDOW).then(testWindowHost);
+ }
+
+ function testWindowHost() {
+ checkHostType(Toolbox.HostType.WINDOW);
+
+ checkToolboxUI();
+
+ toolbox.switchHost(Toolbox.HostType.BOTTOM).then(testDestroy);
+ }
+
+ function checkHostType(hostType) {
+ is(toolbox.hostType, hostType, "host type is " + hostType);
+
+ let pref = Services.prefs.getCharPref("devtools.toolbox.host");
+ is(pref, hostType, "host pref is " + hostType);
+ }
+
+ function testDestroy() {
+ toolbox.destroy().then(function () {
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ gDevTools.showToolbox(target).then(finish);
+ });
+ }
+
+ function finish() {
+ toolbox = null;
+ finishTest();
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_split_escape_key.js b/devtools/client/webconsole/test/browser_webconsole_split_escape_key.js
new file mode 100644
index 000000000..f71efb99e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_split_escape_key.js
@@ -0,0 +1,158 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ "use strict";
+
+ function test() {
+ info("Test various cases where the escape key should hide the split console.");
+
+ let toolbox;
+ let hud;
+ let jsterm;
+ let hudMessages;
+ let variablesView;
+
+ Task.spawn(runner).then(finish);
+
+ function* runner() {
+ let {tab} = yield loadTab("data:text/html;charset=utf-8,<p>Web Console " +
+ "test for splitting");
+ let target = TargetFactory.forTab(tab);
+ toolbox = yield gDevTools.showToolbox(target, "inspector");
+
+ yield testCreateSplitConsoleAfterEscape();
+
+ yield showAutoCompletePopoup();
+
+ yield testHideAutoCompletePopupAfterEscape();
+
+ yield executeJS();
+ yield clickMessageAndShowVariablesView();
+ jsterm.focus();
+
+ yield testHideVariablesViewAfterEscape();
+
+ yield clickMessageAndShowVariablesView();
+ yield startPropertyEditor();
+
+ yield testCancelPropertyEditorAfterEscape();
+ yield testHideVariablesViewAfterEscape();
+ yield testHideSplitConsoleAfterEscape();
+ }
+
+ function testCreateSplitConsoleAfterEscape() {
+ let result = toolbox.once("webconsole-ready", () => {
+ hud = toolbox.getPanel("webconsole").hud;
+ jsterm = hud.jsterm;
+ ok(toolbox.splitConsole, "Split console is created.");
+ });
+
+ let contentWindow = toolbox.win;
+ contentWindow.focus();
+ EventUtils.sendKey("ESCAPE", contentWindow);
+
+ return result;
+ }
+
+ function testHideSplitConsoleAfterEscape() {
+ let result = toolbox.once("split-console", () => {
+ ok(!toolbox.splitConsole, "Split console is hidden.");
+ });
+ EventUtils.sendKey("ESCAPE", toolbox.win);
+
+ return result;
+ }
+
+ function testHideVariablesViewAfterEscape() {
+ let result = jsterm.once("sidebar-closed", () => {
+ ok(!hud.ui.jsterm.sidebar,
+ "Variables view is hidden.");
+ ok(toolbox.splitConsole,
+ "Split console is open after hiding the variables view.");
+ });
+ EventUtils.sendKey("ESCAPE", toolbox.win);
+
+ return result;
+ }
+
+ function testHideAutoCompletePopupAfterEscape() {
+ let deferred = promise.defer();
+ let popup = jsterm.autocompletePopup;
+
+ popup.once("popup-closed", () => {
+ ok(!popup.isOpen,
+ "Auto complete popup is hidden.");
+ ok(toolbox.splitConsole,
+ "Split console is open after hiding the autocomplete popup.");
+
+ deferred.resolve();
+ });
+
+ EventUtils.sendKey("ESCAPE", toolbox.win);
+
+ return deferred.promise;
+ }
+
+ function testCancelPropertyEditorAfterEscape() {
+ EventUtils.sendKey("ESCAPE", variablesView.window);
+ ok(hud.ui.jsterm.sidebar,
+ "Variables view is open after canceling property editor.");
+ ok(toolbox.splitConsole,
+ "Split console is open after editing.");
+ }
+
+ function* executeJS() {
+ jsterm.execute("var foo = { bar: \"baz\" }; foo;");
+ hudMessages = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Object { bar: \"baz\" }",
+ category: CATEGORY_OUTPUT,
+ objects: true
+ }],
+ });
+ }
+
+ function clickMessageAndShowVariablesView() {
+ let result = jsterm.once("variablesview-fetched", (event, vview) => {
+ variablesView = vview;
+ });
+
+ let clickable = hudMessages[0].clickableElements[0];
+ EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+
+ return result;
+ }
+
+ function* startPropertyEditor() {
+ let results = yield findVariableViewProperties(variablesView, [
+ {name: "bar", value: "baz"}
+ ], {webconsole: hud});
+ results[0].matchedProp.focus();
+ EventUtils.synthesizeKey("VK_RETURN", variablesView.window);
+ }
+
+ function showAutoCompletePopoup() {
+ let onPopupShown = jsterm.autocompletePopup.once("popup-opened");
+
+ jsterm.focus();
+ jsterm.setInputValue("document.location.");
+ EventUtils.sendKey("TAB", hud.iframeWindow);
+
+ return onPopupShown;
+ }
+
+ function finish() {
+ toolbox.destroy().then(() => {
+ toolbox = null;
+ hud = null;
+ jsterm = null;
+ hudMessages = null;
+ variablesView = null;
+
+ finishTest();
+ });
+ }
+ }
diff --git a/devtools/client/webconsole/test/browser_webconsole_split_focus.js b/devtools/client/webconsole/test/browser_webconsole_split_focus.js
new file mode 100644
index 000000000..ff65229c9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_split_focus.js
@@ -0,0 +1,66 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ "use strict";
+
+ function test() {
+ info("Test that the split console state is persisted");
+
+ let toolbox;
+ let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
+ "splitting</p>";
+
+ Task.spawn(runner).then(finish);
+
+ function* runner() {
+ info("Opening a tab while there is no user setting on split console pref");
+ let {tab} = yield loadTab(TEST_URI);
+ let target = TargetFactory.forTab(tab);
+ toolbox = yield gDevTools.showToolbox(target, "inspector");
+
+ ok(!toolbox.splitConsole, "Split console is hidden by default");
+
+ info("Focusing the search box before opening the split console");
+ let inspector = toolbox.getPanel("inspector");
+ inspector.searchBox.focus();
+
+ let activeElement = getActiveElement(inspector.panelDoc);
+ is(activeElement, inspector.searchBox, "Search box is focused");
+
+ yield toolbox.openSplitConsole();
+
+ ok(toolbox.splitConsole, "Split console is now visible");
+
+ // Use the binding element since jsterm.inputNode is a XUL textarea element.
+ activeElement = getActiveElement(toolbox.doc);
+ activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
+ let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
+ is(activeElement, inputNode, "Split console input is focused by default");
+
+ yield toolbox.closeSplitConsole();
+
+ info("Making sure that the search box is refocused after closing the " +
+ "split console");
+ activeElement = getActiveElement(inspector.panelDoc);
+ is(activeElement, inspector.searchBox, "Search box is focused");
+
+ yield toolbox.destroy();
+ }
+
+ function getActiveElement(doc) {
+ let activeElement = doc.activeElement;
+ while (activeElement && activeElement.contentDocument) {
+ activeElement = activeElement.contentDocument.activeElement;
+ }
+ return activeElement;
+ }
+
+ function finish() {
+ toolbox = TEST_URI = null;
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
+ finishTest();
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_split_persist.js b/devtools/client/webconsole/test/browser_webconsole_split_persist.js
new file mode 100644
index 000000000..e11bd4811
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_split_persist.js
@@ -0,0 +1,119 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ "use strict";
+
+ function test() {
+ info("Test that the split console state is persisted");
+
+ let toolbox;
+ let TEST_URI = "data:text/html;charset=utf-8,<p>Web Console test for " +
+ "splitting</p>";
+
+ Task.spawn(runner).then(finish);
+
+ function* runner() {
+ info("Opening a tab while there is no user setting on split console pref");
+ let {tab} = yield loadTab(TEST_URI);
+ let target = TargetFactory.forTab(tab);
+ toolbox = yield gDevTools.showToolbox(target, "inspector");
+
+ ok(!toolbox.splitConsole, "Split console is hidden by default.");
+ ok(!isCommandButtonChecked(), "Split console button is unchecked by " +
+ "default.");
+ yield toggleSplitConsoleWithEscape();
+ ok(toolbox.splitConsole, "Split console is now visible.");
+ ok(isCommandButtonChecked(), "Split console button is now checked.");
+ ok(getVisiblePrefValue(), "Visibility pref is true");
+
+ is(getHeightPrefValue(), toolbox.webconsolePanel.height,
+ "Panel height matches the pref");
+ toolbox.webconsolePanel.height = 200;
+
+ yield toolbox.destroy();
+
+ info("Opening a tab while there is a true user setting on split console " +
+ "pref");
+ ({tab} = yield loadTab(TEST_URI));
+ target = TargetFactory.forTab(tab);
+ toolbox = yield gDevTools.showToolbox(target, "inspector");
+
+ ok(toolbox.splitConsole, "Split console is visible by default.");
+ ok(isCommandButtonChecked(), "Split console button is checked by default.");
+ is(getHeightPrefValue(), 200, "Height is set based on panel height after " +
+ "closing");
+
+ // Use the binding element since jsterm.inputNode is a XUL textarea element.
+ let activeElement = getActiveElement(toolbox.doc);
+ activeElement = activeElement.ownerDocument.getBindingParent(activeElement);
+ let inputNode = toolbox.getPanel("webconsole").hud.jsterm.inputNode;
+ is(activeElement, inputNode, "Split console input is focused by default");
+
+ toolbox.webconsolePanel.height = 1;
+ ok(toolbox.webconsolePanel.clientHeight > 1,
+ "The actual height of the console is bound with a min height");
+
+ toolbox.webconsolePanel.height = 10000;
+ ok(toolbox.webconsolePanel.clientHeight < 10000,
+ "The actual height of the console is bound with a max height");
+
+ yield toggleSplitConsoleWithEscape();
+ ok(!toolbox.splitConsole, "Split console is now hidden.");
+ ok(!isCommandButtonChecked(), "Split console button is now unchecked.");
+ ok(!getVisiblePrefValue(), "Visibility pref is false");
+
+ yield toolbox.destroy();
+
+ is(getHeightPrefValue(), 10000,
+ "Height is set based on panel height after closing");
+
+ info("Opening a tab while there is a false user setting on split " +
+ "console pref");
+ ({tab} = yield loadTab(TEST_URI));
+ target = TargetFactory.forTab(tab);
+ toolbox = yield gDevTools.showToolbox(target, "inspector");
+
+ ok(!toolbox.splitConsole, "Split console is hidden by default.");
+ ok(!getVisiblePrefValue(), "Visibility pref is false");
+
+ yield toolbox.destroy();
+ }
+
+ function getActiveElement(doc) {
+ let activeElement = doc.activeElement;
+ while (activeElement && activeElement.contentDocument) {
+ activeElement = activeElement.contentDocument.activeElement;
+ }
+ return activeElement;
+ }
+
+ function getVisiblePrefValue() {
+ return Services.prefs.getBoolPref("devtools.toolbox.splitconsoleEnabled");
+ }
+
+ function getHeightPrefValue() {
+ return Services.prefs.getIntPref("devtools.toolbox.splitconsoleHeight");
+ }
+
+ function isCommandButtonChecked() {
+ return toolbox.doc.querySelector("#command-button-splitconsole")
+ .hasAttribute("checked");
+ }
+
+ function toggleSplitConsoleWithEscape() {
+ let onceSplitConsole = toolbox.once("split-console");
+ let contentWindow = toolbox.win;
+ contentWindow.focus();
+ EventUtils.sendKey("ESCAPE", contentWindow);
+ return onceSplitConsole;
+ }
+
+ function finish() {
+ toolbox = TEST_URI = null;
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
+ finishTest();
+ }
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js b/devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js
new file mode 100644
index 000000000..a10acf9b2
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_start_netmon_first.js
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the webconsole works if the network monitor is first opened, then
+// the user switches to the webconsole. See bug 970914.
+
+"use strict";
+
+function test() {
+ Task.spawn(runner).then(finishTest);
+
+ function* runner() {
+ const {tab} = yield loadTab("data:text/html;charset=utf8,<p>hello");
+
+ const hud = yield openConsole(tab);
+
+ hud.jsterm.execute("console.log('foobar bug970914')");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log",
+ text: "foobar bug970914",
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let text = hud.outputNode.textContent;
+ isnot(text.indexOf("foobar bug970914"), -1,
+ "console.log message confirmed");
+ ok(!/logging API|disabled by a script/i.test(text),
+ "no warning about disabled console API");
+ }
+}
+
diff --git a/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js b/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js
new file mode 100644
index 000000000..c8f2200f9
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_strict_mode_errors.js
@@ -0,0 +1,83 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that "use strict" JS errors generate errors, not warnings.
+
+"use strict";
+
+add_task(function* () {
+ // On e10s, the exception is triggered in child process
+ // and is ignored by test harness
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ yield loadTab("data:text/html;charset=utf8,<script>'use strict';var arguments;</script>");
+
+ let hud = yield openConsole();
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "SyntaxError: 'arguments' can't be defined or assigned to in strict mode code",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ ],
+ });
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "data:text/html;charset="
+ + "utf8,<script>'use strict';function f(a, a) {};</script>");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "SyntaxError: duplicate formal argument a",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ ],
+ });
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "data:text/html;charset="
+ + "utf8,<script>'use strict';var o = {get p() {}};o.p = 1;</script>");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "TypeError: setting a property that has only a getter",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ ],
+ });
+
+ if (!Services.appinfo.browserTabsRemoteAutostart) {
+ expectUncaughtException();
+ }
+ BrowserTestUtils.loadURI(gBrowser.selectedBrowser,
+ "data:text/html;charset=utf8,<script>'use strict';v = 1;</script>");
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ text: "ReferenceError: assignment to undeclared variable v",
+ category: CATEGORY_JS,
+ severity: SEVERITY_ERROR,
+ },
+ ],
+ });
+
+ hud = null;
+});
diff --git a/devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js b/devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js
new file mode 100644
index 000000000..eafeee18e
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_trackingprotection_errors.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Load a page with tracking elements that get blocked and make sure that a
+// 'learn more' link shows up in the webconsole.
+
+"use strict";
+
+const TEST_URI = "http://tracking.example.org/browser/devtools/client/" +
+ "webconsole/test/test-trackingprotection-securityerrors.html";
+const LEARN_MORE_URI = "https://developer.mozilla.org/Firefox/Privacy/" +
+ "Tracking_Protection" + DOCS_GA_PARAMS;
+const PREF = "privacy.trackingprotection.enabled";
+
+const {UrlClassifierTestUtils} = Cu.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
+
+registerCleanupFunction(function () {
+ Services.prefs.clearUserPref(PREF);
+ UrlClassifierTestUtils.cleanupTestTrackers();
+});
+
+add_task(function* testMessagesAppear() {
+ yield UrlClassifierTestUtils.addTestTrackers();
+ Services.prefs.setBoolPref(PREF, true);
+
+ let { browser } = yield loadTab(TEST_URI);
+
+ let hud = yield openConsole();
+
+ let results = yield waitForMessages({
+ webconsole: hud,
+ messages: [
+ {
+ name: "Was blocked because tracking protection is enabled",
+ text: "The resource at \u201chttp://tracking.example.com/\u201d was " +
+ "blocked because tracking protection is enabled",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_WARNING,
+ objects: true,
+ },
+ ],
+ });
+
+ yield testClickOpenNewTab(hud, results[0]);
+});
+
+function testClickOpenNewTab(hud, match) {
+ let warningNode = match.clickableElements[0];
+ ok(warningNode, "link element");
+ ok(warningNode.classList.contains("learn-more-link"), "link class name");
+ return simulateMessageLinkClick(warningNode, LEARN_MORE_URI);
+}
diff --git a/devtools/client/webconsole/test/browser_webconsole_view_source.js b/devtools/client/webconsole/test/browser_webconsole_view_source.js
new file mode 100644
index 000000000..a81b58acc
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_webconsole_view_source.js
@@ -0,0 +1,52 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that source URLs in the Web Console can be clicked to display the
+// standard View Source window. As JS exceptions and console.log() messages always
+// have their locations opened in Debugger, we need to test a security message in
+// order to have it opened in the standard View Source window.
+
+"use strict";
+
+const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
+ "test/test-mixedcontent-securityerrors.html";
+
+add_task(function* () {
+ yield actuallyTest();
+});
+
+add_task(function* () {
+ Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
+ yield actuallyTest();
+ Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
+});
+
+var actuallyTest = Task.async(function*() {
+ yield loadTab(TEST_URI);
+ let hud = yield openConsole(null);
+ info("console opened");
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: "Blocked loading mixed active content",
+ category: CATEGORY_SECURITY,
+ severity: SEVERITY_ERROR,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ ok(msg, "error message");
+ let locationNode = msg.querySelector(".message-location .frame-link-filename");
+ ok(locationNode, "location node");
+
+ let onTabOpen = waitForTab();
+
+ EventUtils.sendMouseEvent({ type: "click" }, locationNode);
+
+ let tab = yield onTabOpen;
+ ok(true, "the view source tab was opened in response to clicking the location node");
+ gBrowser.removeTab(tab);
+});
diff --git a/devtools/client/webconsole/test/head.js b/devtools/client/webconsole/test/head.js
new file mode 100644
index 000000000..519cb78b0
--- /dev/null
+++ b/devtools/client/webconsole/test/head.js
@@ -0,0 +1,1844 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* import-globals-from ../../framework/test/shared-head.js */
+"use strict";
+
+// shared-head.js handles imports, constants, and utility functions
+Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
+
+var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
+var {Messages} = require("devtools/client/webconsole/console-output");
+const asyncStorage = require("devtools/shared/async-storage");
+const HUDService = require("devtools/client/webconsole/hudservice");
+
+// Services.prefs.setBoolPref("devtools.debugger.log", true);
+
+var gPendingOutputTest = 0;
+
+// The various categories of messages.
+const CATEGORY_NETWORK = 0;
+const CATEGORY_CSS = 1;
+const CATEGORY_JS = 2;
+const CATEGORY_WEBDEV = 3;
+const CATEGORY_INPUT = 4;
+const CATEGORY_OUTPUT = 5;
+const CATEGORY_SECURITY = 6;
+const CATEGORY_SERVER = 7;
+
+// The possible message severities.
+const SEVERITY_ERROR = 0;
+const SEVERITY_WARNING = 1;
+const SEVERITY_INFO = 2;
+const SEVERITY_LOG = 3;
+
+// The indent of a console group in pixels.
+const GROUP_INDENT = 12;
+
+const WEBCONSOLE_STRINGS_URI = "devtools/client/locales/webconsole.properties";
+var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
+
+const DOCS_GA_PARAMS = "?utm_source=mozilla" +
+ "&utm_medium=firefox-console-errors" +
+ "&utm_campaign=default";
+
+flags.testing = true;
+
+function loadTab(url) {
+ let deferred = promise.defer();
+
+ let tab = gBrowser.selectedTab = gBrowser.addTab(url);
+ let browser = gBrowser.getBrowserForTab(tab);
+
+ browser.addEventListener("load", function onLoad() {
+ browser.removeEventListener("load", onLoad, true);
+ deferred.resolve({tab: tab, browser: browser});
+ }, true);
+
+ return deferred.promise;
+}
+
+function loadBrowser(browser) {
+ return BrowserTestUtils.browserLoaded(browser);
+}
+
+function closeTab(tab) {
+ let deferred = promise.defer();
+
+ let container = gBrowser.tabContainer;
+
+ container.addEventListener("TabClose", function onTabClose() {
+ container.removeEventListener("TabClose", onTabClose, true);
+ deferred.resolve(null);
+ }, true);
+
+ gBrowser.removeTab(tab);
+
+ return deferred.promise;
+}
+
+/**
+ * Load the page and return the associated HUD.
+ *
+ * @param string uri
+ * The URI of the page to load.
+ * @param string consoleType [optional]
+ * The console type, either "browserConsole" or "webConsole". Defaults to
+ * "webConsole".
+ * @return object
+ * The HUD associated with the console
+ */
+function* loadPageAndGetHud(uri, consoleType) {
+ let { browser } = yield loadTab("data:text/html;charset=utf-8,Loading tab for tests");
+
+ let hud;
+ if (consoleType === "browserConsole") {
+ hud = yield HUDService.openBrowserConsoleOrFocus();
+ } else {
+ hud = yield openConsole();
+ }
+
+ ok(hud, "Console was opened");
+
+ let loaded = loadBrowser(browser);
+ yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser, uri);
+ yield loaded;
+
+ yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ text: uri,
+ category: CATEGORY_NETWORK,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ return hud;
+}
+
+function afterAllTabsLoaded(callback, win) {
+ win = win || window;
+
+ let stillToLoad = 0;
+
+ function onLoad() {
+ this.removeEventListener("load", onLoad, true);
+ stillToLoad--;
+ if (!stillToLoad) {
+ callback();
+ }
+ }
+
+ for (let a = 0; a < win.gBrowser.tabs.length; a++) {
+ let browser = win.gBrowser.tabs[a].linkedBrowser;
+ if (browser.webProgress.isLoadingDocument) {
+ stillToLoad++;
+ browser.addEventListener("load", onLoad, true);
+ }
+ }
+
+ if (!stillToLoad) {
+ callback();
+ }
+}
+
+/**
+ * Check if a log entry exists in the HUD output node.
+ *
+ * @param {Element} outputNode
+ * the HUD output node.
+ * @param {string} matchString
+ * the string you want to check if it exists in the output node.
+ * @param {string} msg
+ * the message describing the test
+ * @param {boolean} [onlyVisible=false]
+ * find only messages that are visible, not hidden by the filter.
+ * @param {boolean} [failIfFound=false]
+ * fail the test if the string is found in the output node.
+ * @param {string} cssClass [optional]
+ * find only messages with the given CSS class.
+ */
+function testLogEntry(outputNode, matchString, msg, onlyVisible,
+ failIfFound, cssClass) {
+ let selector = ".message";
+ // Skip entries that are hidden by the filter.
+ if (onlyVisible) {
+ selector += ":not(.filtered-by-type):not(.filtered-by-string)";
+ }
+ if (cssClass) {
+ selector += "." + aClass;
+ }
+
+ let msgs = outputNode.querySelectorAll(selector);
+ let found = false;
+ for (let i = 0, n = msgs.length; i < n; i++) {
+ let message = msgs[i].textContent.indexOf(matchString);
+ if (message > -1) {
+ found = true;
+ break;
+ }
+ }
+
+ is(found, !failIfFound, msg);
+}
+
+/**
+ * A convenience method to call testLogEntry().
+ *
+ * @param str string
+ * The string to find.
+ */
+function findLogEntry(str) {
+ testLogEntry(outputNode, str, "found " + str);
+}
+
+/**
+ * Open the Web Console for the given tab.
+ *
+ * @param nsIDOMElement [tab]
+ * Optional tab element for which you want open the Web Console. The
+ * default tab is taken from the global variable |tab|.
+ * @param function [callback]
+ * Optional function to invoke after the Web Console completes
+ * initialization (web-console-created).
+ * @return object
+ * A promise that is resolved once the web console is open.
+ */
+var openConsole = function (tab) {
+ let webconsoleOpened = promise.defer();
+ let target = TargetFactory.forTab(tab || gBrowser.selectedTab);
+ gDevTools.showToolbox(target, "webconsole").then(toolbox => {
+ let hud = toolbox.getCurrentPanel().hud;
+ hud.jsterm._lazyVariablesView = false;
+ webconsoleOpened.resolve(hud);
+ });
+ return webconsoleOpened.promise;
+};
+
+/**
+ * Close the Web Console for the given tab.
+ *
+ * @param nsIDOMElement [tab]
+ * Optional tab element for which you want close the Web Console. The
+ * default tab is taken from the global variable |tab|.
+ * @param function [callback]
+ * Optional function to invoke after the Web Console completes
+ * closing (web-console-destroyed).
+ * @return object
+ * A promise that is resolved once the web console is closed.
+ */
+var closeConsole = Task.async(function* (tab) {
+ let target = TargetFactory.forTab(tab || gBrowser.selectedTab);
+ let toolbox = gDevTools.getToolbox(target);
+ if (toolbox) {
+ yield toolbox.destroy();
+ }
+});
+
+/**
+ * Listen for a new tab to open and return a promise that resolves when one
+ * does and completes the load event.
+ * @return a promise that resolves to the tab object
+ */
+var waitForTab = Task.async(function* () {
+ info("Waiting for a tab to open");
+ yield once(gBrowser.tabContainer, "TabOpen");
+ let tab = gBrowser.selectedTab;
+ let browser = tab.linkedBrowser;
+ yield once(browser, "load", true);
+ info("The tab load completed");
+ return tab;
+});
+
+/**
+ * Dump the output of all open Web Consoles - used only for debugging purposes.
+ */
+function dumpConsoles() {
+ if (gPendingOutputTest) {
+ console.log("dumpConsoles start");
+ for (let [, hud] of HUDService.consoles) {
+ if (!hud.outputNode) {
+ console.debug("no output content for", hud.hudId);
+ continue;
+ }
+
+ console.debug("output content for", hud.hudId);
+ for (let elem of hud.outputNode.childNodes) {
+ dumpMessageElement(elem);
+ }
+ }
+ console.log("dumpConsoles end");
+
+ gPendingOutputTest = 0;
+ }
+}
+
+/**
+ * Dump to output debug information for the given webconsole message.
+ *
+ * @param nsIDOMNode message
+ * The message element you want to display.
+ */
+function dumpMessageElement(message) {
+ let text = message.textContent;
+ let repeats = message.querySelector(".message-repeats");
+ if (repeats) {
+ repeats = repeats.getAttribute("value");
+ }
+ console.debug("id", message.getAttribute("id"),
+ "date", message.timestamp,
+ "class", message.className,
+ "category", message.category,
+ "severity", message.severity,
+ "repeats", repeats,
+ "clipboardText", message.clipboardText,
+ "text", text);
+}
+
+var finishTest = Task.async(function* () {
+ dumpConsoles();
+
+ let browserConsole = HUDService.getBrowserConsole();
+ if (browserConsole) {
+ if (browserConsole.jsterm) {
+ browserConsole.jsterm.clearOutput(true);
+ }
+ yield HUDService.toggleBrowserConsole();
+ }
+
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ yield gDevTools.closeToolbox(target);
+
+ finish();
+});
+
+// Always use the 'old' frontend for tests that rely on it
+Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
+registerCleanupFunction(function* () {
+ Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
+});
+
+registerCleanupFunction(function* () {
+ flags.testing = false;
+
+ // Remove stored console commands in between tests
+ yield asyncStorage.removeItem("webConsoleHistory");
+
+ dumpConsoles();
+
+ let browserConsole = HUDService.getBrowserConsole();
+ if (browserConsole) {
+ if (browserConsole.jsterm) {
+ browserConsole.jsterm.clearOutput(true);
+ }
+ yield HUDService.toggleBrowserConsole();
+ }
+
+ let target = TargetFactory.forTab(gBrowser.selectedTab);
+ yield gDevTools.closeToolbox(target);
+
+ while (gBrowser.tabs.length > 1) {
+ gBrowser.removeCurrentTab();
+ }
+});
+
+waitForExplicitFinish();
+
+/**
+ * Polls a given function waiting for it to become true.
+ *
+ * @param object options
+ * Options object with the following properties:
+ * - validator
+ * A validator function that returns a boolean. This is called every few
+ * milliseconds to check if the result is true. When it is true, the
+ * promise is resolved and polling stops. If validator never returns
+ * true, then polling timeouts after several tries and the promise is
+ * rejected.
+ * - name
+ * Name of test. This is used to generate the success and failure
+ * messages.
+ * - timeout
+ * Timeout for validator function, in milliseconds. Default is 5000.
+ * @return object
+ * A Promise object that is resolved based on the validator function.
+ */
+function waitForSuccess(options) {
+ let deferred = promise.defer();
+ let start = Date.now();
+ let timeout = options.timeout || 5000;
+ let {validator} = options;
+
+ function wait() {
+ if ((Date.now() - start) > timeout) {
+ // Log the failure.
+ ok(false, "Timed out while waiting for: " + options.name);
+ deferred.reject(null);
+ return;
+ }
+
+ if (validator(options)) {
+ ok(true, options.name);
+ deferred.resolve(null);
+ } else {
+ setTimeout(wait, 100);
+ }
+ }
+
+ setTimeout(wait, 100);
+
+ return deferred.promise;
+}
+
+var openInspector = Task.async(function* (tab = gBrowser.selectedTab) {
+ let target = TargetFactory.forTab(tab);
+ let toolbox = yield gDevTools.showToolbox(target, "inspector");
+ return toolbox.getCurrentPanel();
+});
+
+/**
+ * Find variables or properties in a VariablesView instance.
+ *
+ * @param object view
+ * The VariablesView instance.
+ * @param array rules
+ * The array of rules you want to match. Each rule is an object with:
+ * - name (string|regexp): property name to match.
+ * - value (string|regexp): property value to match.
+ * - isIterator (boolean): check if the property is an iterator.
+ * - isGetter (boolean): check if the property is a getter.
+ * - isGenerator (boolean): check if the property is a generator.
+ * - dontMatch (boolean): make sure the rule doesn't match any property.
+ * @param object options
+ * Options for matching:
+ * - webconsole: the WebConsole instance we work with.
+ * @return object
+ * A promise object that is resolved when all the rules complete
+ * matching. The resolved callback is given an array of all the rules
+ * you wanted to check. Each rule has a new property: |matchedProp|
+ * which holds a reference to the Property object instance from the
+ * VariablesView. If the rule did not match, then |matchedProp| is
+ * undefined.
+ */
+function findVariableViewProperties(view, rules, options) {
+ // Initialize the search.
+ function init() {
+ // Separate out the rules that require expanding properties throughout the
+ // view.
+ let expandRules = [];
+ let filterRules = rules.filter((rule) => {
+ if (typeof rule.name == "string" && rule.name.indexOf(".") > -1) {
+ expandRules.push(rule);
+ return false;
+ }
+ return true;
+ });
+
+ // Search through the view those rules that do not require any properties to
+ // be expanded. Build the array of matchers, outstanding promises to be
+ // resolved.
+ let outstanding = [];
+ finder(filterRules, view, outstanding);
+
+ // Process the rules that need to expand properties.
+ let lastStep = processExpandRules.bind(null, expandRules);
+
+ // Return the results - a promise resolved to hold the updated rules array.
+ let returnResults = onAllRulesMatched.bind(null, rules);
+
+ return promise.all(outstanding).then(lastStep).then(returnResults);
+ }
+
+ function onMatch(prop, rule, matched) {
+ if (matched && !rule.matchedProp) {
+ rule.matchedProp = prop;
+ }
+ }
+
+ function finder(rules, vars, promises) {
+ for (let [, prop] of vars) {
+ for (let rule of rules) {
+ let matcher = matchVariablesViewProperty(prop, rule, options);
+ promises.push(matcher.then(onMatch.bind(null, prop, rule)));
+ }
+ }
+ }
+
+ function processExpandRules(rules) {
+ let rule = rules.shift();
+ if (!rule) {
+ return promise.resolve(null);
+ }
+
+ let deferred = promise.defer();
+ let expandOptions = {
+ rootVariable: view,
+ expandTo: rule.name,
+ webconsole: options.webconsole,
+ };
+
+ variablesViewExpandTo(expandOptions).then(function onSuccess(prop) {
+ let name = rule.name;
+ let lastName = name.split(".").pop();
+ rule.name = lastName;
+
+ let matched = matchVariablesViewProperty(prop, rule, options);
+ return matched.then(onMatch.bind(null, prop, rule)).then(function () {
+ rule.name = name;
+ });
+ }, function onFailure() {
+ return promise.resolve(null);
+ }).then(processExpandRules.bind(null, rules)).then(function () {
+ deferred.resolve(null);
+ });
+
+ return deferred.promise;
+ }
+
+ function onAllRulesMatched(rules) {
+ for (let rule of rules) {
+ let matched = rule.matchedProp;
+ if (matched && !rule.dontMatch) {
+ ok(true, "rule " + rule.name + " matched for property " + matched.name);
+ } else if (matched && rule.dontMatch) {
+ ok(false, "rule " + rule.name + " should not match property " +
+ matched.name);
+ } else {
+ ok(rule.dontMatch, "rule " + rule.name + " did not match any property");
+ }
+ }
+ return rules;
+ }
+
+ return init();
+}
+
+/**
+ * Check if a given Property object from the variables view matches the given
+ * rule.
+ *
+ * @param object prop
+ * The variable's view Property instance.
+ * @param object rule
+ * Rules for matching the property. See findVariableViewProperties() for
+ * details.
+ * @param object options
+ * Options for matching. See findVariableViewProperties().
+ * @return object
+ * A promise that is resolved when all the checks complete. Resolution
+ * result is a boolean that tells your promise callback the match
+ * result: true or false.
+ */
+function matchVariablesViewProperty(prop, rule, options) {
+ function resolve(result) {
+ return promise.resolve(result);
+ }
+
+ if (rule.name) {
+ let match = rule.name instanceof RegExp ?
+ rule.name.test(prop.name) :
+ prop.name == rule.name;
+ if (!match) {
+ return resolve(false);
+ }
+ }
+
+ if (rule.value) {
+ let displayValue = prop.displayValue;
+ if (prop.displayValueClassName == "token-string") {
+ displayValue = displayValue.substring(1, displayValue.length - 1);
+ }
+
+ let match = rule.value instanceof RegExp ?
+ rule.value.test(displayValue) :
+ displayValue == rule.value;
+ if (!match) {
+ info("rule " + rule.name + " did not match value, expected '" +
+ rule.value + "', found '" + displayValue + "'");
+ return resolve(false);
+ }
+ }
+
+ if ("isGetter" in rule) {
+ let isGetter = !!(prop.getter && prop.get("get"));
+ if (rule.isGetter != isGetter) {
+ info("rule " + rule.name + " getter test failed");
+ return resolve(false);
+ }
+ }
+
+ if ("isGenerator" in rule) {
+ let isGenerator = prop.displayValue == "Generator";
+ if (rule.isGenerator != isGenerator) {
+ info("rule " + rule.name + " generator test failed");
+ return resolve(false);
+ }
+ }
+
+ let outstanding = [];
+
+ if ("isIterator" in rule) {
+ let isIterator = isVariableViewPropertyIterator(prop, options.webconsole);
+ outstanding.push(isIterator.then((result) => {
+ if (result != rule.isIterator) {
+ info("rule " + rule.name + " iterator test failed");
+ }
+ return result == rule.isIterator;
+ }));
+ }
+
+ outstanding.push(promise.resolve(true));
+
+ return promise.all(outstanding).then(function _onMatchDone(results) {
+ let ruleMatched = results.indexOf(false) == -1;
+ return resolve(ruleMatched);
+ });
+}
+
+/**
+ * Check if the given variables view property is an iterator.
+ *
+ * @param object prop
+ * The Property instance you want to check.
+ * @param object webConsole
+ * The WebConsole instance to work with.
+ * @return object
+ * A promise that is resolved when the check completes. The resolved
+ * callback is given a boolean: true if the property is an iterator, or
+ * false otherwise.
+ */
+function isVariableViewPropertyIterator(prop, webConsole) {
+ if (prop.displayValue == "Iterator") {
+ return promise.resolve(true);
+ }
+
+ let deferred = promise.defer();
+
+ variablesViewExpandTo({
+ rootVariable: prop,
+ expandTo: "__proto__.__iterator__",
+ webconsole: webConsole,
+ }).then(function onSuccess() {
+ deferred.resolve(true);
+ }, function onFailure() {
+ deferred.resolve(false);
+ });
+
+ return deferred.promise;
+}
+
+/**
+ * Recursively expand the variables view up to a given property.
+ *
+ * @param options
+ * Options for view expansion:
+ * - rootVariable: start from the given scope/variable/property.
+ * - expandTo: string made up of property names you want to expand.
+ * For example: "body.firstChild.nextSibling" given |rootVariable:
+ * document|.
+ * - webconsole: a WebConsole instance. If this is not provided all
+ * property expand() calls will be considered sync. Things may fail!
+ * @return object
+ * A promise that is resolved only when the last property in |expandTo|
+ * is found, and rejected otherwise. Resolution reason is always the
+ * last property - |nextSibling| in the example above. Rejection is
+ * always the last property that was found.
+ */
+function variablesViewExpandTo(options) {
+ let root = options.rootVariable;
+ let expandTo = options.expandTo.split(".");
+ let jsterm = (options.webconsole || {}).jsterm;
+ let lastDeferred = promise.defer();
+
+ function fetch(prop) {
+ if (!prop.onexpand) {
+ ok(false, "property " + prop.name + " cannot be expanded: !onexpand");
+ return promise.reject(prop);
+ }
+
+ let deferred = promise.defer();
+
+ if (prop._fetched || !jsterm) {
+ executeSoon(function () {
+ deferred.resolve(prop);
+ });
+ } else {
+ jsterm.once("variablesview-fetched", function _onFetchProp() {
+ executeSoon(() => deferred.resolve(prop));
+ });
+ }
+
+ prop.expand();
+
+ return deferred.promise;
+ }
+
+ function getNext(prop) {
+ let name = expandTo.shift();
+ let newProp = prop.get(name);
+
+ if (expandTo.length > 0) {
+ ok(newProp, "found property " + name);
+ if (newProp) {
+ fetch(newProp).then(getNext, fetchError);
+ } else {
+ lastDeferred.reject(prop);
+ }
+ } else if (newProp) {
+ lastDeferred.resolve(newProp);
+ } else {
+ lastDeferred.reject(prop);
+ }
+ }
+
+ function fetchError(prop) {
+ lastDeferred.reject(prop);
+ }
+
+ if (!root._fetched) {
+ fetch(root).then(getNext, fetchError);
+ } else {
+ getNext(root);
+ }
+
+ return lastDeferred.promise;
+}
+
+/**
+ * Update the content of a property in the variables view.
+ *
+ * @param object options
+ * Options for the property update:
+ * - property: the property you want to change.
+ * - field: string that tells what you want to change:
+ * - use "name" to change the property name,
+ * - or "value" to change the property value.
+ * - string: the new string to write into the field.
+ * - webconsole: reference to the Web Console instance we work with.
+ * @return object
+ * A Promise object that is resolved once the property is updated.
+ */
+var updateVariablesViewProperty = Task.async(function* (options) {
+ let view = options.property._variablesView;
+ view.window.focus();
+ options.property.focus();
+
+ switch (options.field) {
+ case "name":
+ EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true }, view.window);
+ break;
+ case "value":
+ EventUtils.synthesizeKey("VK_RETURN", {}, view.window);
+ break;
+ default:
+ throw new Error("options.field is incorrect");
+ }
+
+ let deferred = promise.defer();
+
+ executeSoon(() => {
+ EventUtils.synthesizeKey("A", { accelKey: true }, view.window);
+
+ for (let c of options.string) {
+ EventUtils.synthesizeKey(c, {}, view.window);
+ }
+
+ if (options.webconsole) {
+ options.webconsole.jsterm.once("variablesview-fetched")
+ .then((varView) => deferred.resolve(varView));
+ }
+
+ EventUtils.synthesizeKey("VK_RETURN", {}, view.window);
+
+ if (!options.webconsole) {
+ executeSoon(() => {
+ deferred.resolve(null);
+ });
+ }
+ });
+
+ return deferred.promise;
+});
+
+/**
+ * Open the JavaScript debugger.
+ *
+ * @param object options
+ * Options for opening the debugger:
+ * - tab: the tab you want to open the debugger for.
+ * @return object
+ * A promise that is resolved once the debugger opens, or rejected if
+ * the open fails. The resolution callback is given one argument, an
+ * object that holds the following properties:
+ * - target: the Target object for the Tab.
+ * - toolbox: the Toolbox instance.
+ * - panel: the jsdebugger panel instance.
+ * - panelWin: the window object of the panel iframe.
+ */
+function openDebugger(options = {}) {
+ if (!options.tab) {
+ options.tab = gBrowser.selectedTab;
+ }
+
+ let deferred = promise.defer();
+
+ let target = TargetFactory.forTab(options.tab);
+ let toolbox = gDevTools.getToolbox(target);
+ let dbgPanelAlreadyOpen = toolbox && toolbox.getPanel("jsdebugger");
+
+ gDevTools.showToolbox(target, "jsdebugger").then(function onSuccess(tool) {
+ let panel = tool.getCurrentPanel();
+ let panelWin = panel.panelWin;
+
+ panel._view.Variables.lazyEmpty = false;
+
+ let resolveObject = {
+ target: target,
+ toolbox: tool,
+ panel: panel,
+ panelWin: panelWin,
+ };
+
+ if (dbgPanelAlreadyOpen) {
+ deferred.resolve(resolveObject);
+ } else {
+ panelWin.DebuggerController.waitForSourcesLoaded().then(() => {
+ deferred.resolve(resolveObject);
+ });
+ }
+ }, function onFailure(reason) {
+ console.debug("failed to open the toolbox for 'jsdebugger'", reason);
+ deferred.reject(reason);
+ });
+
+ return deferred.promise;
+}
+
+/**
+ * Returns true if the caret in the debugger editor is placed at the specified
+ * position.
+ * @param panel The debugger panel.
+ * @param {number} line The line number.
+ * @param {number} [col] The column number.
+ * @returns {boolean}
+ */
+function isDebuggerCaretPos(panel, line, col = 1) {
+ let editor = panel.panelWin.DebuggerView.editor;
+ let cursor = editor.getCursor();
+
+ // Source editor starts counting line and column numbers from 0.
+ info("Current editor caret position: " + (cursor.line + 1) + ", " +
+ (cursor.ch + 1));
+ return cursor.line == (line - 1) && cursor.ch == (col - 1);
+}
+
+/**
+ * Wait for messages in the Web Console output.
+ *
+ * @param object options
+ * Options for what you want to wait for:
+ * - webconsole: the webconsole instance you work with.
+ * - matchCondition: "any" or "all". Default: "all". The promise
+ * returned by this function resolves when all of the messages are
+ * matched, if the |matchCondition| is "all". If you set the condition to
+ * "any" then the promise is resolved by any message rule that matches,
+ * irrespective of order - waiting for messages stops whenever any rule
+ * matches.
+ * - messages: an array of objects that tells which messages to wait for.
+ * Properties:
+ * - text: string or RegExp to match the textContent of each new
+ * message.
+ * - noText: string or RegExp that must not match in the message
+ * textContent.
+ * - repeats: the number of message repeats, as displayed by the Web
+ * Console.
+ * - category: match message category. See CATEGORY_* constants at
+ * the top of this file.
+ * - severity: match message severity. See SEVERITY_* constants at
+ * the top of this file.
+ * - count: how many unique web console messages should be matched by
+ * this rule.
+ * - consoleTrace: boolean, set to |true| to match a console.trace()
+ * message. Optionally this can be an object of the form
+ * { file, fn, line } that can match the specified file, function
+ * and/or line number in the trace message.
+ * - consoleTime: string that matches a console.time() timer name.
+ * Provide this if you want to match a console.time() message.
+ * - consoleTimeEnd: same as above, but for console.timeEnd().
+ * - consoleDir: boolean, set to |true| to match a console.dir()
+ * message.
+ * - consoleGroup: boolean, set to |true| to match a console.group()
+ * message.
+ * - consoleTable: boolean, set to |true| to match a console.table()
+ * message.
+ * - longString: boolean, set to |true} to match long strings in the
+ * message.
+ * - collapsible: boolean, set to |true| to match messages that can
+ * be collapsed/expanded.
+ * - type: match messages that are instances of the given object. For
+ * example, you can point to Messages.NavigationMarker to match any
+ * such message.
+ * - objects: boolean, set to |true| if you expect inspectable
+ * objects in the message.
+ * - source: object of the shape { url, line }. This is used to
+ * match the source URL and line number of the error message or
+ * console API call.
+ * - prefix: prefix text to check for in the prefix element.
+ * - stacktrace: array of objects of the form { file, fn, line } that
+ * can match frames in the stacktrace associated with the message.
+ * - groupDepth: number used to check the depth of the message in
+ * a group.
+ * - url: URL to match for network requests.
+ * @return object
+ * A promise object is returned once the messages you want are found.
+ * The promise is resolved with the array of rule objects you give in
+ * the |messages| property. Each objects is the same as provided, with
+ * additional properties:
+ * - matched: a Set of web console messages that matched the rule.
+ * - clickableElements: a list of inspectable objects. This is available
+ * if any of the following properties are present in the rule:
+ * |consoleTrace| or |objects|.
+ * - longStrings: a list of long string ellipsis elements you can click
+ * in the message element, to expand a long string. This is available
+ * only if |longString| is present in the matching rule.
+ */
+function waitForMessages(options) {
+ info("Waiting for messages...");
+
+ gPendingOutputTest++;
+ let webconsole = options.webconsole;
+ let rules = WebConsoleUtils.cloneObject(options.messages, true);
+ let rulesMatched = 0;
+ let listenerAdded = false;
+ let deferred = promise.defer();
+ options.matchCondition = options.matchCondition || "all";
+
+ function checkText(rule, text) {
+ let result = false;
+ if (Array.isArray(rule)) {
+ result = rule.every((s) => checkText(s, text));
+ } else if (typeof rule == "string") {
+ result = text.indexOf(rule) > -1;
+ } else if (rule instanceof RegExp) {
+ result = rule.test(text);
+ } else {
+ result = rule == text;
+ }
+ return result;
+ }
+
+ function checkConsoleTable(rule, element) {
+ let elemText = element.textContent;
+
+ if (!checkText("console.table():", elemText)) {
+ return false;
+ }
+
+ rule.category = CATEGORY_WEBDEV;
+ rule.severity = SEVERITY_LOG;
+ rule.type = Messages.ConsoleTable;
+
+ return true;
+ }
+
+ function checkConsoleTrace(rule, element) {
+ let elemText = element.textContent;
+ let trace = rule.consoleTrace;
+
+ if (!checkText("console.trace():", elemText)) {
+ return false;
+ }
+
+ rule.category = CATEGORY_WEBDEV;
+ rule.severity = SEVERITY_LOG;
+ rule.type = Messages.ConsoleTrace;
+
+ if (!rule.stacktrace && typeof trace == "object" && trace !== true) {
+ if (Array.isArray(trace)) {
+ rule.stacktrace = trace;
+ } else {
+ rule.stacktrace = [trace];
+ }
+ }
+
+ return true;
+ }
+
+ function checkConsoleTime(rule, element) {
+ let elemText = element.textContent;
+ let time = rule.consoleTime;
+
+ if (!checkText(time + ": timer started", elemText)) {
+ return false;
+ }
+
+ rule.category = CATEGORY_WEBDEV;
+ rule.severity = SEVERITY_LOG;
+
+ return true;
+ }
+
+ function checkConsoleTimeEnd(rule, element) {
+ let elemText = element.textContent;
+ let time = rule.consoleTimeEnd;
+ let regex = new RegExp(time + ": -?\\d+([,.]\\d+)?ms");
+
+ if (!checkText(regex, elemText)) {
+ return false;
+ }
+
+ rule.category = CATEGORY_WEBDEV;
+ rule.severity = SEVERITY_LOG;
+
+ return true;
+ }
+
+ function checkConsoleDir(rule, element) {
+ if (!element.classList.contains("inlined-variables-view")) {
+ return false;
+ }
+
+ let elemText = element.textContent;
+ if (!checkText(rule.consoleDir, elemText)) {
+ return false;
+ }
+
+ let iframe = element.querySelector("iframe");
+ if (!iframe) {
+ ok(false, "console.dir message has no iframe");
+ return false;
+ }
+
+ return true;
+ }
+
+ function checkConsoleGroup(rule) {
+ if (!isNaN(parseInt(rule.consoleGroup, 10))) {
+ rule.groupDepth = rule.consoleGroup;
+ }
+ rule.category = CATEGORY_WEBDEV;
+ rule.severity = SEVERITY_LOG;
+
+ return true;
+ }
+
+ function checkSource(rule, element) {
+ let location = getRenderedSource(element);
+ if (!location) {
+ return false;
+ }
+
+ if (!checkText(rule.source.url, location.url)) {
+ return false;
+ }
+
+ if ("line" in rule.source && location.line != rule.source.line) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function checkCollapsible(rule, element) {
+ let msg = element._messageObject;
+ if (!msg || !!msg.collapsible != rule.collapsible) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function checkStacktrace(rule, element) {
+ let stack = rule.stacktrace;
+ let frames = element.querySelectorAll(".stacktrace > .stack-trace > .frame-link");
+ if (!frames.length) {
+ return false;
+ }
+
+ for (let i = 0; i < stack.length; i++) {
+ let frame = frames[i];
+ let expected = stack[i];
+ if (!frame) {
+ ok(false, "expected frame #" + i + " but didnt find it");
+ return false;
+ }
+
+ if (expected.file) {
+ let url = frame.getAttribute("data-url");
+ if (!checkText(expected.file, url)) {
+ ok(false, "frame #" + i + " does not match file name: " +
+ expected.file + " != " + url);
+ displayErrorContext(rule, element);
+ return false;
+ }
+ }
+
+ if (expected.fn) {
+ let fn = frame.querySelector(".frame-link-function-display-name").textContent;
+ if (!checkText(expected.fn, fn)) {
+ ok(false, "frame #" + i + " does not match the function name: " +
+ expected.fn + " != " + fn);
+ displayErrorContext(rule, element);
+ return false;
+ }
+ }
+
+ if (expected.line) {
+ let line = frame.getAttribute("data-line");
+ if (!checkText(expected.line, line)) {
+ ok(false, "frame #" + i + " does not match the line number: " +
+ expected.line + " != " + line);
+ displayErrorContext(rule, element);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ function hasXhrLabel(element) {
+ let xhr = element.querySelector(".xhr");
+ if (!xhr) {
+ return false;
+ }
+ return true;
+ }
+
+ function checkMessage(rule, element) {
+ let elemText = element.textContent;
+
+ if (rule.text && !checkText(rule.text, elemText)) {
+ return false;
+ }
+
+ if (rule.noText && checkText(rule.noText, elemText)) {
+ return false;
+ }
+
+ if (rule.consoleTable && !checkConsoleTable(rule, element)) {
+ return false;
+ }
+
+ if (rule.consoleTrace && !checkConsoleTrace(rule, element)) {
+ return false;
+ }
+
+ if (rule.consoleTime && !checkConsoleTime(rule, element)) {
+ return false;
+ }
+
+ if (rule.consoleTimeEnd && !checkConsoleTimeEnd(rule, element)) {
+ return false;
+ }
+
+ if (rule.consoleDir && !checkConsoleDir(rule, element)) {
+ return false;
+ }
+
+ if (rule.consoleGroup && !checkConsoleGroup(rule, element)) {
+ return false;
+ }
+
+ if (rule.source && !checkSource(rule, element)) {
+ return false;
+ }
+
+ if ("collapsible" in rule && !checkCollapsible(rule, element)) {
+ return false;
+ }
+
+ if (rule.isXhr && !hasXhrLabel(element)) {
+ return false;
+ }
+
+ if (!rule.isXhr && hasXhrLabel(element)) {
+ return false;
+ }
+
+ let partialMatch = !!(rule.consoleTrace || rule.consoleTime ||
+ rule.consoleTimeEnd);
+
+ // The rule tries to match the newer types of messages, based on their
+ // object constructor.
+ if (rule.type) {
+ if (!element._messageObject ||
+ !(element._messageObject instanceof rule.type)) {
+ if (partialMatch) {
+ ok(false, "message type for rule: " + displayRule(rule));
+ displayErrorContext(rule, element);
+ }
+ return false;
+ }
+ partialMatch = true;
+ }
+
+ if ("category" in rule && element.category != rule.category) {
+ if (partialMatch) {
+ is(element.category, rule.category,
+ "message category for rule: " + displayRule(rule));
+ displayErrorContext(rule, element);
+ }
+ return false;
+ }
+
+ if ("severity" in rule && element.severity != rule.severity) {
+ if (partialMatch) {
+ is(element.severity, rule.severity,
+ "message severity for rule: " + displayRule(rule));
+ displayErrorContext(rule, element);
+ }
+ return false;
+ }
+
+ if (rule.text) {
+ partialMatch = true;
+ }
+
+ if (rule.stacktrace && !checkStacktrace(rule, element)) {
+ if (partialMatch) {
+ ok(false, "failed to match stacktrace for rule: " + displayRule(rule));
+ displayErrorContext(rule, element);
+ }
+ return false;
+ }
+
+ if (rule.category == CATEGORY_NETWORK && "url" in rule &&
+ !checkText(rule.url, element.url)) {
+ return false;
+ }
+
+ if ("repeats" in rule) {
+ let repeats = element.querySelector(".message-repeats");
+ if (!repeats || repeats.getAttribute("value") != rule.repeats) {
+ return false;
+ }
+ }
+
+ if ("groupDepth" in rule) {
+ let indentNode = element.querySelector(".indent");
+ let indent = (GROUP_INDENT * rule.groupDepth) + "px";
+ if (!indentNode || indentNode.style.width != indent) {
+ is(indentNode.style.width, indent,
+ "group depth check failed for message rule: " + displayRule(rule));
+ return false;
+ }
+ }
+
+ if ("longString" in rule) {
+ let longStrings = element.querySelectorAll(".longStringEllipsis");
+ if (rule.longString != !!longStrings[0]) {
+ if (partialMatch) {
+ is(!!longStrings[0], rule.longString,
+ "long string existence check failed for message rule: " +
+ displayRule(rule));
+ displayErrorContext(rule, element);
+ }
+ return false;
+ }
+ rule.longStrings = longStrings;
+ }
+
+ if ("objects" in rule) {
+ let clickables = element.querySelectorAll(".message-body a");
+ if (rule.objects != !!clickables[0]) {
+ if (partialMatch) {
+ is(!!clickables[0], rule.objects,
+ "objects existence check failed for message rule: " +
+ displayRule(rule));
+ displayErrorContext(rule, element);
+ }
+ return false;
+ }
+ rule.clickableElements = clickables;
+ }
+
+ if ("prefix" in rule) {
+ let prefixNode = element.querySelector(".prefix");
+ is(prefixNode && prefixNode.textContent, rule.prefix, "Check prefix");
+ }
+
+ let count = rule.count || 1;
+ if (!rule.matched) {
+ rule.matched = new Set();
+ }
+ rule.matched.add(element);
+
+ return rule.matched.size == count;
+ }
+
+ function onMessagesAdded(event, newMessages) {
+ for (let msg of newMessages) {
+ let elem = msg.node;
+ let location = getRenderedSource(elem);
+ if (location && location.url) {
+ let url = location.url;
+ // Prevent recursion with the browser console and any potential
+ // messages coming from head.js.
+ if (url.indexOf("devtools/client/webconsole/test/head.js") != -1) {
+ continue;
+ }
+ }
+
+ for (let rule of rules) {
+ if (rule._ruleMatched) {
+ continue;
+ }
+
+ let matched = checkMessage(rule, elem);
+ if (matched) {
+ rule._ruleMatched = true;
+ rulesMatched++;
+ ok(1, "matched rule: " + displayRule(rule));
+ if (maybeDone()) {
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ function allRulesMatched() {
+ return options.matchCondition == "all" && rulesMatched == rules.length ||
+ options.matchCondition == "any" && rulesMatched > 0;
+ }
+
+ function maybeDone() {
+ if (allRulesMatched()) {
+ if (listenerAdded) {
+ webconsole.ui.off("new-messages", onMessagesAdded);
+ }
+ gPendingOutputTest--;
+ deferred.resolve(rules);
+ return true;
+ }
+ return false;
+ }
+
+ function testCleanup() {
+ if (allRulesMatched()) {
+ return;
+ }
+
+ if (webconsole.ui) {
+ webconsole.ui.off("new-messages", onMessagesAdded);
+ }
+
+ for (let rule of rules) {
+ if (!rule._ruleMatched) {
+ ok(false, "failed to match rule: " + displayRule(rule));
+ }
+ }
+ }
+
+ function displayRule(rule) {
+ return rule.name || rule.text;
+ }
+
+ function displayErrorContext(rule, element) {
+ console.log("error occured during rule " + displayRule(rule));
+ console.log("while checking the following message");
+ dumpMessageElement(element);
+ }
+
+ executeSoon(() => {
+ let messages = [];
+ for (let elem of webconsole.outputNode.childNodes) {
+ messages.push({
+ node: elem,
+ update: false,
+ });
+ }
+
+ onMessagesAdded("new-messages", messages);
+
+ if (!allRulesMatched()) {
+ listenerAdded = true;
+ registerCleanupFunction(testCleanup);
+ webconsole.ui.on("new-messages", onMessagesAdded);
+ }
+ });
+
+ return deferred.promise;
+}
+
+function whenDelayedStartupFinished(win, callback) {
+ Services.obs.addObserver(function observer(subject, topic) {
+ if (win == subject) {
+ Services.obs.removeObserver(observer, topic);
+ executeSoon(callback);
+ }
+ }, "browser-delayed-startup-finished", false);
+}
+
+/**
+ * Check the web console output for the given inputs. Each input is checked for
+ * the expected JS eval result, the result of calling print(), the result of
+ * console.log(). The JS eval result is also checked if it opens the variables
+ * view on click.
+ *
+ * @param object hud
+ * The web console instance to work with.
+ * @param array inputTests
+ * An array of input tests. An input test element is an object. Each
+ * object has the following properties:
+ * - input: string, JS input value to execute.
+ *
+ * - output: string|RegExp, expected JS eval result.
+ *
+ * - inspectable: boolean, when true, the test runner expects the JS eval
+ * result is an object that can be clicked for inspection.
+ *
+ * - noClick: boolean, when true, the test runner does not click the JS
+ * eval result. Some objects, like |window|, have a lot of properties and
+ * opening vview for them is very slow (they can cause timeouts in debug
+ * builds).
+ *
+ * - consoleOutput: string|RegExp, optional, expected consoleOutput
+ * If not provided consoleOuput = output;
+ *
+ * - printOutput: string|RegExp, optional, expected output for
+ * |print(input)|. If this is not provided, printOutput = output.
+ *
+ * - variablesViewLabel: string|RegExp, optional, the expected variables
+ * view label when the object is inspected. If this is not provided, then
+ * |output| is used.
+ *
+ * - inspectorIcon: boolean, when true, the test runner expects the
+ * result widget to contain an inspectorIcon element (className
+ * open-inspector).
+ *
+ * - expectedTab: string, optional, the full URL of the new tab which
+ * must open. If this is not provided, any new tabs that open will cause
+ * a test failure.
+ */
+function checkOutputForInputs(hud, inputTests) {
+ let container = gBrowser.tabContainer;
+
+ function* runner() {
+ for (let [i, entry] of inputTests.entries()) {
+ info("checkInput(" + i + "): " + entry.input);
+ yield checkInput(entry);
+ }
+ container = null;
+ }
+
+ function* checkInput(entry) {
+ yield checkConsoleLog(entry);
+ yield checkPrintOutput(entry);
+ yield checkJSEval(entry);
+ }
+
+ function* checkConsoleLog(entry) {
+ info("Logging");
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("console.log(" + entry.input + ")");
+
+ let consoleOutput = "consoleOutput" in entry ?
+ entry.consoleOutput : entry.output;
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "console.log() output: " + consoleOutput,
+ text: consoleOutput,
+ category: CATEGORY_WEBDEV,
+ severity: SEVERITY_LOG,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+
+ if (entry.consoleLogClick) {
+ yield checkObjectClick(entry, msg);
+ }
+
+ if (typeof entry.inspectorIcon == "boolean") {
+ info("Checking Inspector Link");
+ yield checkLinkToInspector(entry.inspectorIcon, msg);
+ }
+ }
+
+ function checkPrintOutput(entry) {
+ info("Printing");
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute("print(" + entry.input + ")");
+
+ let printOutput = entry.printOutput || entry.output;
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "print() output: " + printOutput,
+ text: printOutput,
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+ }
+
+ function* checkJSEval(entry) {
+ info("Evaluating");
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute(entry.input);
+
+ let evalOutput = entry.evalOutput || entry.output;
+
+ let [result] = yield waitForMessages({
+ webconsole: hud,
+ messages: [{
+ name: "JS eval output: " + entry.evalOutput,
+ text: entry.evalOutput,
+ category: CATEGORY_OUTPUT,
+ }],
+ });
+
+ let msg = [...result.matched][0];
+ if (!entry.noClick) {
+ yield checkObjectClick(entry, msg);
+ }
+ if (typeof entry.inspectorIcon == "boolean") {
+ info("Checking Inspector Link: " + entry.input);
+ yield checkLinkToInspector(entry.inspectorIcon, msg);
+ }
+ }
+
+ function* checkObjectClick(entry, msg) {
+ info("Clicking");
+ let body;
+ if (entry.getClickableNode) {
+ body = entry.getClickableNode(msg);
+ } else {
+ body = msg.querySelector(".message-body a") ||
+ msg.querySelector(".message-body");
+ }
+ ok(body, "the message body");
+
+ let deferredVariablesView = promise.defer();
+ entry._onVariablesViewOpen = onVariablesViewOpen.bind(null, entry,
+ deferredVariablesView);
+ hud.jsterm.on("variablesview-open", entry._onVariablesViewOpen);
+
+ let deferredTab = promise.defer();
+ entry._onTabOpen = onTabOpen.bind(null, entry, deferredTab);
+ container.addEventListener("TabOpen", entry._onTabOpen, true);
+
+ body.scrollIntoView();
+
+ if (!entry.suppressClick) {
+ EventUtils.synthesizeMouse(body, 2, 2, {}, hud.iframeWindow);
+ }
+
+ if (entry.inspectable) {
+ info("message body tagName '" + body.tagName + "' className '" +
+ body.className + "'");
+ yield deferredVariablesView.promise;
+ } else {
+ hud.jsterm.off("variablesview-open", entry._onVariablesView);
+ entry._onVariablesView = null;
+ }
+
+ if (entry.expectedTab) {
+ yield deferredTab.promise;
+ } else {
+ container.removeEventListener("TabOpen", entry._onTabOpen, true);
+ entry._onTabOpen = null;
+ }
+
+ yield promise.resolve(null);
+ }
+
+ function onVariablesViewOpen(entry, {resolve, reject}, event, view, options) {
+ info("Variables view opened");
+ let label = entry.variablesViewLabel || entry.output;
+ if (typeof label == "string" && options.label != label) {
+ return;
+ }
+ if (label instanceof RegExp && !label.test(options.label)) {
+ return;
+ }
+
+ hud.jsterm.off("variablesview-open", entry._onVariablesViewOpen);
+ entry._onVariablesViewOpen = null;
+ ok(entry.inspectable, "variables view was shown");
+
+ resolve(null);
+ }
+
+ function onTabOpen(entry, {resolve, reject}, event) {
+ container.removeEventListener("TabOpen", entry._onTabOpen, true);
+ entry._onTabOpen = null;
+ let tab = event.target;
+ let browser = gBrowser.getBrowserForTab(tab);
+
+ Task.spawn(function* () {
+ yield loadBrowser(browser);
+ let uri = yield ContentTask.spawn(browser, {}, function* () {
+ return content.location.href;
+ });
+ ok(entry.expectedTab && entry.expectedTab == uri,
+ "opened tab '" + uri + "', expected tab '" + entry.expectedTab + "'");
+ yield closeTab(tab);
+ }).then(resolve, reject);
+ }
+
+ return Task.spawn(runner);
+}
+
+/**
+ * Check the web console DOM element output for the given inputs.
+ * Each input is checked for the expected JS eval result. The JS eval result is
+ * also checked if it opens the inspector with the correct node selected on
+ * inspector icon click
+ *
+ * @param object hud
+ * The web console instance to work with.
+ * @param array inputTests
+ * An array of input tests. An input test element is an object. Each
+ * object has the following properties:
+ * - input: string, JS input value to execute.
+ *
+ * - output: string, expected JS eval result.
+ *
+ * - displayName: string, expected NodeFront's displayName.
+ *
+ * - attr: Array, expected NodeFront's attributes
+ */
+function checkDomElementHighlightingForInputs(hud, inputs) {
+ function* runner() {
+ let toolbox = gDevTools.getToolbox(hud.target);
+
+ // Loading the inspector panel at first, to make it possible to listen for
+ // new node selections
+ yield toolbox.selectTool("inspector");
+ let inspector = toolbox.getCurrentPanel();
+ yield toolbox.selectTool("webconsole");
+
+ info("Iterating over the test data");
+ for (let data of inputs) {
+ let [result] = yield jsEval(data.input, {text: data.output});
+ let {msg} = yield checkWidgetAndMessage(result);
+ yield checkNodeHighlight(toolbox, inspector, msg, data);
+ }
+ }
+
+ function jsEval(input, message) {
+ info("Executing '" + input + "' in the web console");
+
+ hud.jsterm.clearOutput();
+ hud.jsterm.execute(input);
+
+ return waitForMessages({
+ webconsole: hud,
+ messages: [message]
+ });
+ }
+
+ function* checkWidgetAndMessage(result) {
+ info("Getting the output ElementNode widget");
+
+ let msg = [...result.matched][0];
+ let widget = [...msg._messageObject.widgets][0];
+ ok(widget, "ElementNode widget found in the output");
+
+ info("Waiting for the ElementNode widget to be linked to the inspector");
+ yield widget.linkToInspector();
+
+ return {widget, msg};
+ }
+
+ function* checkNodeHighlight(toolbox, inspector, msg, testData) {
+ let inspectorIcon = msg.querySelector(".open-inspector");
+ ok(inspectorIcon, "Inspector icon found in the ElementNode widget");
+
+ info("Clicking on the inspector icon and waiting for the " +
+ "inspector to be selected");
+ let onInspectorSelected = toolbox.once("inspector-selected");
+ let onInspectorUpdated = inspector.once("inspector-updated");
+ let onNewNode = toolbox.selection.once("new-node-front");
+ let onNodeHighlight = toolbox.once("node-highlight");
+
+ EventUtils.synthesizeMouseAtCenter(inspectorIcon, {},
+ inspectorIcon.ownerDocument.defaultView);
+ yield onInspectorSelected;
+ yield onInspectorUpdated;
+ yield onNodeHighlight;
+ let nodeFront = yield onNewNode;
+
+ ok(true, "Inspector selected and new node got selected");
+
+ is(nodeFront.displayName, testData.displayName,
+ "The correct node was highlighted");
+
+ if (testData.attrs) {
+ let attrs = nodeFront.attributes;
+ for (let i in testData.attrs) {
+ is(attrs[i].name, testData.attrs[i].name,
+ "Expected attribute's name is present");
+ is(attrs[i].value, testData.attrs[i].value,
+ "Expected attribute's value is present");
+ }
+ }
+
+ info("Unhighlight the node by moving away from the markup view");
+ let onNodeUnhighlight = toolbox.once("node-unhighlight");
+ let btn = inspector.toolbox.doc.querySelector(".toolbox-dock-button");
+ EventUtils.synthesizeMouseAtCenter(btn, {type: "mousemove"},
+ inspector.toolbox.win);
+ yield onNodeUnhighlight;
+
+ info("Switching back to the console");
+ yield toolbox.selectTool("webconsole");
+ }
+
+ return Task.spawn(runner);
+}
+
+/**
+ * Finish the request and resolve with the request object.
+ *
+ * @param {Function} predicate A predicate function that takes the request
+ * object as an argument and returns true if the request was the expected one,
+ * false otherwise. The returned promise is resolved ONLY if the predicate
+ * matches a request. Defaults to accepting any request.
+ * @return promise
+ * @resolves The request object.
+ */
+function waitForFinishedRequest(predicate = () => true) {
+ registerCleanupFunction(function () {
+ HUDService.lastFinishedRequest.callback = null;
+ });
+
+ return new Promise(resolve => {
+ HUDService.lastFinishedRequest.callback = request => {
+ // Check if this is the expected request
+ if (predicate(request)) {
+ // Match found. Clear the listener.
+ HUDService.lastFinishedRequest.callback = null;
+
+ resolve(request);
+ } else {
+ info(`Ignoring unexpected request ${JSON.stringify(request, null, 2)}`);
+ }
+ };
+ });
+}
+
+/**
+ * Wait for eventName on target.
+ * @param {Object} target An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Boolean} useCapture Optional for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function once(target, eventName, useCapture = false) {
+ info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+ let deferred = promise.defer();
+
+ for (let [add, remove] of [
+ ["addEventListener", "removeEventListener"],
+ ["addListener", "removeListener"],
+ ["on", "off"]
+ ]) {
+ if ((add in target) && (remove in target)) {
+ target[add](eventName, function onEvent(...aArgs) {
+ target[remove](eventName, onEvent, useCapture);
+ deferred.resolve.apply(deferred, aArgs);
+ }, useCapture);
+ break;
+ }
+ }
+
+ return deferred.promise;
+}
+
+/**
+ * Checks a link to the inspector
+ *
+ * @param {boolean} hasLinkToInspector Set to true if the message should
+ * link to the inspector panel.
+ * @param {element} msg The message to test.
+ */
+function checkLinkToInspector(hasLinkToInspector, msg) {
+ let elementNodeWidget = [...msg._messageObject.widgets][0];
+ if (!elementNodeWidget) {
+ ok(!hasLinkToInspector, "The message has no ElementNode widget");
+ return true;
+ }
+
+ return elementNodeWidget.linkToInspector().then(() => {
+ // linkToInspector resolved, check for the .open-inspector element
+ if (hasLinkToInspector) {
+ ok(msg.querySelectorAll(".open-inspector").length,
+ "The ElementNode widget is linked to the inspector");
+ } else {
+ ok(!msg.querySelectorAll(".open-inspector").length,
+ "The ElementNode widget isn't linked to the inspector");
+ }
+ }, () => {
+ // linkToInspector promise rejected, node not linked to inspector
+ ok(!hasLinkToInspector,
+ "The ElementNode widget isn't linked to the inspector");
+ });
+}
+
+function getSourceActor(sources, URL) {
+ let item = sources.getItemForAttachment(a => a.source.url === URL);
+ return item && item.value;
+}
+
+/**
+ * Make a request against an actor and resolve with the packet.
+ * @param object client
+ * The client to use when making the request.
+ * @param function requestType
+ * The client request function to run.
+ * @param array args
+ * The arguments to pass into the function.
+ */
+function getPacket(client, requestType, args) {
+ return new Promise(resolve => {
+ client[requestType](...args, packet => resolve(packet));
+ });
+}
+
+/**
+ * Verify that clicking on a link from a popup notification message tries to
+ * open the expected URL.
+ */
+function simulateMessageLinkClick(element, expectedLink) {
+ let deferred = promise.defer();
+
+ // Invoke the click event and check if a new tab would
+ // open to the correct page.
+ let oldOpenUILinkIn = window.openUILinkIn;
+ window.openUILinkIn = function (link) {
+ if (link == expectedLink) {
+ ok(true, "Clicking the message link opens the desired page");
+ window.openUILinkIn = oldOpenUILinkIn;
+ deferred.resolve();
+ }
+ };
+
+ let event = new MouseEvent("click", {
+ detail: 1,
+ button: 0,
+ bubbles: true,
+ cancelable: true
+ });
+ element.dispatchEvent(event);
+
+ return deferred.promise;
+}
+
+function getRenderedSource(root) {
+ let location = root.querySelector(".message-location .frame-link");
+ return location ? {
+ url: location.getAttribute("data-url"),
+ line: location.getAttribute("data-line"),
+ column: location.getAttribute("data-column"),
+ } : null;
+}
diff --git a/devtools/client/webconsole/test/test-autocomplete-in-stackframe.html b/devtools/client/webconsole/test/test-autocomplete-in-stackframe.html
new file mode 100644
index 000000000..ba5212de3
--- /dev/null
+++ b/devtools/client/webconsole/test/test-autocomplete-in-stackframe.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+ <head>
+ <meta charset="utf8">
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <title>Test for bug 842682 - use the debugger API for web console autocomplete</title>
+ <script>
+ var foo1 = "globalFoo";
+
+ var foo1Obj = {
+ prop1: "111",
+ prop2: {
+ prop21: "212121"
+ }
+ };
+
+ function firstCall()
+ {
+ var foo2 = "fooFirstCall";
+
+ var foo2Obj = {
+ prop1: {
+ prop11: "111111"
+ }
+ };
+
+ secondCall();
+ }
+
+ function secondCall()
+ {
+ var foo3 = "fooSecondCall";
+
+ var foo3Obj = {
+ prop1: {
+ prop11: "313131"
+ }
+ };
+
+ debugger;
+ }
+ </script>
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-585956-console-trace.html b/devtools/client/webconsole/test/test-bug-585956-console-trace.html
new file mode 100644
index 000000000..e658ba633
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-585956-console-trace.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head><meta charset="utf-8">
+ <title>Web Console test for bug 585956 - console.trace()</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript">
+window.foobar585956c = function(a) {
+ console.trace();
+ return a+"c";
+};
+
+function foobar585956b(a) {
+ return foobar585956c(a+"b");
+}
+
+function foobar585956a(omg) {
+ return foobar585956b(omg + "a");
+}
+
+foobar585956a("omg");
+</script>
+ </head>
+ <body>
+ <p>Web Console test for bug 585956 - console.trace().</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html
new file mode 100644
index 000000000..ebf9c515f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>WebConsole test: iframe associated to the wrong HUD</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>WebConsole test: iframe associated to the wrong HUD.</p>
+ <p>This is the iframe!</p>
+ </body>
+ </html>
diff --git a/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html
new file mode 100644
index 000000000..8e47cf20f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>WebConsole test: iframe associated to the wrong HUD</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>WebConsole test: iframe associated to the wrong HUD.</p>
+ <iframe
+ src="http://example.com/browser/devtools/client/webconsole/test/test-bug-593003-iframe-wrong-hud-iframe.html"></iframe>
+ </body>
+ </html>
diff --git a/devtools/client/webconsole/test/test-bug-595934-canvas-css.html b/devtools/client/webconsole/test/test-bug-595934-canvas-css.html
new file mode 100644
index 000000000..3c9cf03a5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-canvas-css.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: CSS Parser (with
+ Canvas)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript"
+ src="test-bug-595934-canvas-css.js"></script>
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "CSS Parser" (with
+ Canvas).</p>
+ <p><canvas width="200" height="200">Canvas support is required!</canvas></p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-595934-canvas-css.js b/devtools/client/webconsole/test/test-bug-595934-canvas-css.js
new file mode 100644
index 000000000..ee1ebd425
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-canvas-css.js
@@ -0,0 +1,10 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+window.addEventListener("DOMContentLoaded", function () {
+ var canvas = document.querySelector("canvas");
+ var context = canvas.getContext("2d");
+ context.strokeStyle = "foobarCanvasCssParser";
+}, false);
diff --git a/devtools/client/webconsole/test/test-bug-595934-css-loader.css b/devtools/client/webconsole/test/test-bug-595934-css-loader.css
new file mode 100644
index 000000000..b4224430f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-css-loader.css
@@ -0,0 +1,10 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+body {
+ color: #0f0;
+ font-weight: bold;
+}
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^ b/devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^
new file mode 100644
index 000000000..e7be84a71
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-css-loader.css^headers^
@@ -0,0 +1 @@
+Content-Type: image/png
diff --git a/devtools/client/webconsole/test/test-bug-595934-css-loader.html b/devtools/client/webconsole/test/test-bug-595934-css-loader.html
new file mode 100644
index 000000000..6bb0d54c5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-css-loader.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: CSS Loader</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <link rel="stylesheet" href="test-bug-595934-css-loader.css">
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "CSS Loader".</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-595934-css-parser.css b/devtools/client/webconsole/test/test-bug-595934-css-parser.css
new file mode 100644
index 000000000..f6db82398
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-css-parser.css
@@ -0,0 +1,10 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+p {
+ color: #0f0;
+ foobarCssParser: failure;
+}
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-css-parser.html b/devtools/client/webconsole/test/test-bug-595934-css-parser.html
new file mode 100644
index 000000000..a4ea74ba3
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-css-parser.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: CSS Parser</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <link rel="stylesheet" type="text/css"
+ href="test-bug-595934-css-parser.css">
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "CSS Parser".</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html
new file mode 100644
index 000000000..a70f9011b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: DOM.
+ (empty getElementById())</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript"
+ src="test-bug-595934-empty-getelementbyid.js"></script>
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "DOM"
+ (empty getElementById()).</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js
new file mode 100644
index 000000000..bf9ccece9
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-empty-getelementbyid.js
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+window.addEventListener("load", function () {
+ document.getElementById("");
+}, false);
diff --git a/devtools/client/webconsole/test/test-bug-595934-html.html b/devtools/client/webconsole/test/test-bug-595934-html.html
new file mode 100644
index 000000000..fe35afef6
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-html.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: HTML</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "HTML".</p>
+ <form action="?" enctype="multipart/form-data">
+ <p><label>Input <input type="text" value="test value"></label></p>
+ </form>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-image.html b/devtools/client/webconsole/test/test-bug-595934-image.html
new file mode 100644
index 000000000..312ecd49f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-image.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: Image</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category Image.</p>
+ <p><img src="test-bug-595934-image.jpg" alt="corrupted image"></p>
+ </body>
+</html>
+
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-image.jpg b/devtools/client/webconsole/test/test-bug-595934-image.jpg
new file mode 100644
index 000000000..947e5f11b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-image.jpg
Binary files differ
diff --git a/devtools/client/webconsole/test/test-bug-595934-imagemap.html b/devtools/client/webconsole/test/test-bug-595934-imagemap.html
new file mode 100644
index 000000000..007c3c01b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-imagemap.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: ImageMap</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "ImageMap".</p>
+ <p><img src="test-image.png" usemap="#testMap" alt="Test image"></p>
+ <map name="testMap">
+ <area shape="rect" coords="0,0,10,10,5" href="#" alt="Test area" />
+ </map>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html
new file mode 100644
index 000000000..2fd8beac5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: malformed-xml.
+ (external file)</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript"><!--
+ var req = new XMLHttpRequest();
+ req.open("GET", "test-bug-595934-malformedxml-external.xml", true);
+ req.send(null);
+ // --></script>
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "malformed-xml"
+ (external file).</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml
new file mode 100644
index 000000000..4812786f1
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-malformedxml-external.xml
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "malformed-xml".</p>
+ </body>
diff --git a/devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml b/devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml
new file mode 100644
index 000000000..62689c567
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-malformedxml.xhtml
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Web Console test for bug 595934 - category: malformed-xml</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "malformed-xml".</p>
+ </body>
diff --git a/devtools/client/webconsole/test/test-bug-595934-svg.xhtml b/devtools/client/webconsole/test/test-bug-595934-svg.xhtml
new file mode 100644
index 000000000..572382c64
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-svg.xhtml
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ <head>
+ <title>Web Console test for bug 595934 - category: SVG</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - category "SVG".</p>
+ <svg version="1.1" width="120" height="fooBarSVG"
+ xmlns="http://www.w3.org/2000/svg">
+ <ellipse fill="#0f0" stroke="#000" cx="50%"
+ cy="50%" rx="50%" ry="50%" />
+ </svg>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-workers.html b/devtools/client/webconsole/test/test-bug-595934-workers.html
new file mode 100644
index 000000000..baf5a6215
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-workers.html
@@ -0,0 +1,18 @@
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 595934 - category: DOM Worker
+ javascript</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p id="foobar">Web Console test for bug 595934 - category "DOM Worker
+ javascript".</p>
+ <script type="text/javascript">
+ var myWorker = new Worker("test-bug-595934-workers.js");
+ myWorker.postMessage("hello world");
+ </script>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-595934-workers.js b/devtools/client/webconsole/test/test-bug-595934-workers.js
new file mode 100644
index 000000000..d23f080af
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-595934-workers.js
@@ -0,0 +1,14 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global fooBarWorker*/
+/* eslint-disable no-unused-vars*/
+
+"use strict";
+
+var onmessage = function () {
+ fooBarWorker();
+};
+
diff --git a/devtools/client/webconsole/test/test-bug-597136-external-script-errors.html b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.html
new file mode 100644
index 000000000..25bdeecc5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+<!--
+ ***** BEGIN LICENSE BLOCK *****
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * Contributor(s):
+ * Patrick Walton <pcwalton@mozilla.com>
+ *
+ * ***** END LICENSE BLOCK *****
+ -->
+ <title>Test for bug 597136: external script errors</title>
+ </head>
+ <body>
+ <h1>Test for bug 597136: external script errors</h1>
+ <p><button onclick="f()">Click me</button</p>
+
+ <script type="text/javascript"
+ src="test-bug-597136-external-script-errors.js"></script>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-597136-external-script-errors.js b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.js
new file mode 100644
index 000000000..00821e38e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-597136-external-script-errors.js
@@ -0,0 +1,9 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function f() {
+ bogus.g();
+}
+
diff --git a/devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html b/devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html
new file mode 100644
index 000000000..68e19e677
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-597756-reopen-closed-tab.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Bug 597756: test error logging after tab close and reopen</title>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <h1>Bug 597756: test error logging after tab close and reopen.</h1>
+
+ <script type="text/javascript"><!--
+ fooBug597756_error.bar();
+ // --></script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-599725-response-headers.sjs b/devtools/client/webconsole/test/test-bug-599725-response-headers.sjs
new file mode 100644
index 000000000..2e78d6b7b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-599725-response-headers.sjs
@@ -0,0 +1,25 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response)
+{
+ var Etag = '"4c881ab-b03-435f0a0f9ef00"';
+ var IfNoneMatch = request.hasHeader("If-None-Match")
+ ? request.getHeader("If-None-Match")
+ : "";
+
+ var page = "<!DOCTYPE html><html><body><p>hello world!</p></body></html>";
+
+ response.setHeader("Etag", Etag, false);
+
+ if (IfNoneMatch == Etag) {
+ response.setStatusLine(request.httpVersion, "304", "Not Modified");
+ }
+ else {
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Content-Length", page.length + "", false);
+ response.write(page);
+ }
+}
diff --git a/devtools/client/webconsole/test/test-bug-600183-charset.html b/devtools/client/webconsole/test/test-bug-600183-charset.html
new file mode 100644
index 000000000..040490a6b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-600183-charset.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="gb2312">
+ <title>Console HTTP test page (chinese)</title>
+ </head>
+ <body>
+ <p>µÄÎʺò!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-600183-charset.html^headers^ b/devtools/client/webconsole/test/test-bug-600183-charset.html^headers^
new file mode 100644
index 000000000..9f3e2302f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-600183-charset.html^headers^
@@ -0,0 +1 @@
+Content-Type: text/html; charset=gb2312
diff --git a/devtools/client/webconsole/test/test-bug-601177-log-levels.html b/devtools/client/webconsole/test/test-bug-601177-log-levels.html
new file mode 100644
index 000000000..a59213907
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-601177-log-levels.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 601177: log levels</title>
+ <script src="test-bug-601177-log-levels.js" type="text/javascript"></script>
+ <script type="text/javascript"><!--
+ window.undefinedPropertyBug601177;
+ // --></script>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <h1>Web Console test for bug 601177: log levels</h1>
+ <img src="test-image.png?bug601177">
+ <img src="foobar-known-to-fail.png?bug601177">
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-601177-log-levels.js b/devtools/client/webconsole/test/test-bug-601177-log-levels.js
new file mode 100644
index 000000000..afeb13ff6
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-601177-log-levels.js
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+foobarBug601177strictError = "strict error";
+
+window.foobarBug601177exception();
diff --git a/devtools/client/webconsole/test/test-bug-603750-websocket.html b/devtools/client/webconsole/test/test-bug-603750-websocket.html
new file mode 100644
index 000000000..f0097dd77
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-603750-websocket.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 603750 - Web Socket errors</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 595934 - Web Socket errors.</p>
+ <iframe src="data:text/html;charset=utf-8,hello world!"></iframe>
+ <script type="text/javascript" src="test-bug-603750-websocket.js"></script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-603750-websocket.js b/devtools/client/webconsole/test/test-bug-603750-websocket.js
new file mode 100644
index 000000000..3d92c506b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-603750-websocket.js
@@ -0,0 +1,20 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+window.addEventListener("load", function () {
+ let ws1 = new WebSocket("ws://0.0.0.0:81");
+ ws1.onopen = function () {
+ ws1.send("test 1");
+ ws1.close();
+ };
+
+ let ws2 = new window.frames[0].WebSocket("ws://0.0.0.0:82");
+ ws2.onopen = function () {
+ ws2.send("test 2");
+ ws2.close();
+ };
+}, false);
diff --git a/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html
new file mode 100644
index 000000000..451eba21e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>test for bug 609872 - iframe child</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>test for bug 609872 - iframe child</p>
+ <script>window.foobarBug609872 = 'child!';</script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html
new file mode 100644
index 000000000..fdb636b97
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-609872-cd-iframe-parent.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>test for bug 609872 - iframe parent</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>test for bug 609872 - iframe parent</p>
+ <script>window.foobarBug609872 = 'parent!';</script>
+ <iframe src="test-bug-609872-cd-iframe-child.html"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html b/devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html
new file mode 100644
index 000000000..edf40e80e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-613013-console-api-iframe.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>test for bug 613013</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>test for bug 613013</p>
+ <script type="text/javascript"><!--
+ (function () {
+ var iframe = document.createElement('iframe');
+ iframe.src = 'data:text/html;charset=utf-8,little iframe';
+ document.body.appendChild(iframe);
+
+ console.log("foobarBug613013");
+ })();
+ // --></script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-618078-network-exceptions.html b/devtools/client/webconsole/test/test-bug-618078-network-exceptions.html
new file mode 100644
index 000000000..ac755e1b9
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-618078-network-exceptions.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 618078 - exception in async network request
+ callback</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript">
+ var req = new XMLHttpRequest();
+ req.open('GET', 'http://example.com', true);
+ req.onreadystatechange = function() {
+ if (req.readyState == 4) {
+ bug618078exception();
+ }
+ };
+ req.send(null);
+ </script>
+ </head>
+ <body>
+ <p>Web Console test for bug 618078 - exception in async network request
+ callback.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html b/devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html
new file mode 100644
index 000000000..09c986703
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-621644-jsterm-dollar.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 621644</title>
+ <script>
+ function $(elem) {
+ return elem.innerHTML;
+ }
+ function $$(doc) {
+ return doc.title;
+ }
+ </script>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <h1>Web Console test for bug 621644</h1>
+ <p>hello world!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs b/devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs
new file mode 100644
index 000000000..f92e0fe65
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-630733-response-redirect-headers.sjs
@@ -0,0 +1,16 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response)
+{
+ var page = "<!DOCTYPE html><html><body><p>hello world! bug 630733</p></body></html>";
+
+ response.setStatusLine(request.httpVersion, "301", "Moved Permanently");
+ response.setHeader("Content-Type", "text/html", false);
+ response.setHeader("Content-Length", page.length + "", false);
+ response.setHeader("x-foobar-bug630733", "bazbaz", false);
+ response.setHeader("Location", "/redirect-from-bug-630733", false);
+ response.write(page);
+}
diff --git a/devtools/client/webconsole/test/test-bug-632275-getters.html b/devtools/client/webconsole/test/test-bug-632275-getters.html
new file mode 100644
index 000000000..349c301f3
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-632275-getters.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 632275 - getters</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<script type="application/javascript;version=1.8">
+ document.foobar = {
+ _val: 5,
+ get val() { return ++this._val; }
+ };
+</script>
+
+ </head>
+ <body>
+ <p>Web Console test for bug 632275 - getters.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html b/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html
new file mode 100644
index 000000000..1eddcf350
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-632347-iterators-generators.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 632347 - iterators and generators</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript;version=1.8">
+(function(){
+function genFunc() {
+ var a = 5;
+ while (a < 10) {
+ yield a++;
+ }
+}
+
+window._container = {};
+
+_container.gen1 = genFunc();
+_container.gen1.next();
+
+var obj = { foo: "bar", baz: "baaz", hay: "stack" };
+_container.iter1 = Iterator(obj);
+
+function Range(low, high) {
+ this.low = low;
+ this.high = high;
+}
+
+function RangeIterator(range) {
+ this.range = range;
+ this.current = this.range.low;
+}
+
+RangeIterator.prototype.next = function() {
+ if (this.current > this.range.high) {
+ throw StopIteration;
+ } else {
+ return this.current++;
+ }
+}
+
+Range.prototype.__iterator__ = function() {
+ return new RangeIterator(this);
+}
+
+_container.iter2 = new Range(3, 15);
+
+_container.gen2 = (function* () { for (let i in _container.iter2) yield i * 2; })();
+})();
+</script>
+ </head>
+ <body>
+ <p>Web Console test for bug 632347 - iterators and generators.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-644419-log-limits.html b/devtools/client/webconsole/test/test-bug-644419-log-limits.html
new file mode 100644
index 000000000..21d99ba14
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-644419-log-limits.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for bug 644419: console log limits</title>
+ </head>
+ <body>
+ <h1>Test for bug 644419: Console should have user-settable log limits for
+ each message category</h1>
+
+ <script type="text/javascript">
+ function foo() {
+ bar.baz();
+ }
+ foo();
+ </script>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-646025-console-file-location.html b/devtools/client/webconsole/test/test-bug-646025-console-file-location.html
new file mode 100644
index 000000000..7c80f1446
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-646025-console-file-location.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console file location test</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script src="test-file-location.js"></script>
+ </head>
+ <body>
+ <h1>Web Console File Location Test Page</h1>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-658368-time-methods.html b/devtools/client/webconsole/test/test-bug-658368-time-methods.html
new file mode 100644
index 000000000..cc50b6313
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-658368-time-methods.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for bug 658368: Expand console object with time and timeEnd
+ methods</title>
+ </head>
+ <body>
+ <h1>Test for bug 658368: Expand console object with time and timeEnd
+ methods</h1>
+
+ <script type="text/javascript">
+ function foo() {
+ console.timeEnd("aTimer");
+ }
+ console.time("aTimer");
+ foo();
+ console.time("bTimer");
+ </script>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-737873-mixedcontent.html b/devtools/client/webconsole/test/test-bug-737873-mixedcontent.html
new file mode 100644
index 000000000..db83274f0
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-737873-mixedcontent.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf8">
+ <title>Mixed Content test - http on https</title>
+ <script src="testscript.js"></script>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <iframe src = "http://example.com"></iframe>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html
new file mode 100644
index 000000000..ccb363ed9
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>I am sandboxed and want to escape.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html
new file mode 100644
index 000000000..b9939fe83
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe
+src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html
new file mode 100644
index 000000000..7678d15fe
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe
+src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts allow-same-origin"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html
new file mode 100644
index 000000000..233a6cb70
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively (allow-scripts, allow-same-origin)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe src="test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts allow-same-origin"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html
new file mode 100644
index 000000000..da0d58819
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively (allow-scripts, no allow-same-origin)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe src="test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html
new file mode 100644
index 000000000..f33f0a6dc
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html
@@ -0,0 +1,13 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively (no allow-scripts, allow-same-origin)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe src="test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-same-origin"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html
new file mode 100644
index 000000000..c0ff6994a
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively (allow-scripts, allow-same-origin)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe
+src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html" sandbox="allow-scripts allow-same-origin"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html
new file mode 100644
index 000000000..84e0b6c72
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively (allow-scripts, allow-same-origin, nested)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe
+src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html" sandbox="allow-scripts allow-same-origin"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html
new file mode 100644
index 000000000..72d86931a
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 752559 - print warning to error console when iframe sandbox
+ is being used ineffectively (nested, allow-scripts, allow-same-origin)</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <iframe
+src="http://www.example.com/browser/devtools/client/webconsole/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html
new file mode 100644
index 000000000..d7bcd45d6
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 762593 - Add warning/error Message to Web Console when the
+ page includes Insecure Password fields</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+ <!-- This test tests the scenario where a javascript adds password fields to
+ an about:blank iframe inside an insecure web page. It ensures that
+ insecure password fields like those are detected and a warning is sent to
+ the web console. -->
+ </head>
+ <body>
+ <p>This insecure page is served with an about:blank iframe. A script then adds a
+ password field to it.</p>
+ <iframe id = "myiframe" width = "300" height="300" >
+ </iframe>
+ <script>
+ var doc = window.document;
+ var myIframe = doc.getElementById("myiframe");
+ myIframe.contentDocument.open();
+ myIframe.contentDocument.write("<form><input type = 'password' name='pwd' value='test'> </form>");
+ myIframe.contentDocument.close();
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html
new file mode 100644
index 000000000..f473303f4
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-762593-insecure-passwords-web-console-warning.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf8">
+ <title>Bug 762593 - Add warning/error Message to Web Console when the
+ page includes Insecure Password fields</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>This page is served with an iframe with insecure password field.</p>
+ <iframe src
+ ="http://example.com/browser/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html">
+ </iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-766001-console-log.js b/devtools/client/webconsole/test/test-bug-766001-console-log.js
new file mode 100644
index 000000000..a4be0cb15
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-766001-console-log.js
@@ -0,0 +1,10 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function onLoad123() {
+ console.log("Blah Blah");
+}
+
+window.addEventListener("load", onLoad123, false);
diff --git a/devtools/client/webconsole/test/test-bug-766001-js-console-links.html b/devtools/client/webconsole/test/test-bug-766001-js-console-links.html
new file mode 100644
index 000000000..6a6ac6008
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-766001-js-console-links.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 766001 : Open JS/Console call Links in Debugger</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript" src="test-bug-766001-js-errors.js"></script>
+ <script type="text/javascript" src="test-bug-766001-console-log.js"></script>
+ </head>
+ <body>
+ <p>Web Console test for bug 766001 : Open JS/Console call Links in Debugger.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-766001-js-errors.js b/devtools/client/webconsole/test/test-bug-766001-js-errors.js
new file mode 100644
index 000000000..85321813a
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-766001-js-errors.js
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+window.addEventListener("load", function () {
+ document.bar();
+}, false);
diff --git a/devtools/client/webconsole/test/test-bug-782653-css-errors-1.css b/devtools/client/webconsole/test/test-bug-782653-css-errors-1.css
new file mode 100644
index 000000000..ad7fd1999
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-782653-css-errors-1.css
@@ -0,0 +1,10 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+body {
+ color: #0f0;
+ font-weight: green;
+}
+
diff --git a/devtools/client/webconsole/test/test-bug-782653-css-errors-2.css b/devtools/client/webconsole/test/test-bug-782653-css-errors-2.css
new file mode 100644
index 000000000..91b14137a
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-782653-css-errors-2.css
@@ -0,0 +1,10 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+body {
+ color: #0fl;
+ font-weight: bold;
+}
+
diff --git a/devtools/client/webconsole/test/test-bug-782653-css-errors.html b/devtools/client/webconsole/test/test-bug-782653-css-errors.html
new file mode 100644
index 000000000..7ca11fc34
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-782653-css-errors.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 782653 : Open CSS Links in Style Editor</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <link rel="stylesheet" href="test-bug-782653-css-errors-1.css">
+ <link rel="stylesheet" href="test-bug-782653-css-errors-2.css">
+ </head>
+ <body>
+ <p>Web Console test for bug 782653 : Open CSS Links in Style Editor.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-837351-security-errors.html b/devtools/client/webconsole/test/test-bug-837351-security-errors.html
new file mode 100644
index 000000000..db83274f0
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-837351-security-errors.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf8">
+ <title>Mixed Content test - http on https</title>
+ <script src="testscript.js"></script>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <iframe src = "http://example.com"></iframe>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-859170-longstring-hang.html b/devtools/client/webconsole/test/test-bug-859170-longstring-hang.html
new file mode 100644
index 000000000..51bc0de28
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-859170-longstring-hang.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head><meta charset="utf-8">
+ <title>Web Console test for bug 859170 - very long strings hang the browser</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript">
+(function() {
+var longString = "abbababazomglolztest";
+for (var i = 0; i < 10; i++) {
+ longString += longString + longString;
+}
+
+longString = "foobar" + (new Array(9000)).join("a") + "foobaz" +
+ longString + "boom!";
+console.log(longString);
+})();
+</script>
+ </head>
+ <body>
+ <p>Web Console test for bug 859170 - very long strings hang the browser.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-869003-iframe.html b/devtools/client/webconsole/test/test-bug-869003-iframe.html
new file mode 100644
index 000000000..5a29728e5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-869003-iframe.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 869003</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript"><!--
+ window.onload = function testConsoleLogging()
+ {
+ var o = { hello: "world!", bug: 869003 };
+ console.log("foobar", o);
+ };
+ // --></script>
+ </head>
+ <body>
+ <p>Make sure users can inspect objects from cross-domain iframes.</p>
+ <p>Iframe window.</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-869003-top-window.html b/devtools/client/webconsole/test/test-bug-869003-top-window.html
new file mode 100644
index 000000000..a2da438f6
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-869003-top-window.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 869003</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Make sure users can inspect objects from cross-domain iframes.</p>
+ <p>Top window.</p>
+ <iframe src="http://example.org/browser/devtools/client/webconsole/test/test-bug-869003-iframe.html"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html b/devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html
new file mode 100644
index 000000000..de297d9b5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-952277-highlight-nodes-in-vview.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p>
+ <p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p>
+ <p>Web Console test for bug 952277 - Highlighting and selecting nodes from the variablesview</p>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-bug-989025-iframe-parent.html b/devtools/client/webconsole/test/test-bug-989025-iframe-parent.html
new file mode 100644
index 000000000..54a4e9038
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug-989025-iframe-parent.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>test for bug 989025 - iframe parent</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>test for bug 989025 - iframe parent</p>
+ <iframe src="http://mochi.test:8888/browser/devtools/client/webconsole/test/test-bug-609872-cd-iframe-child.html"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html
new file mode 100644
index 000000000..912e301f0
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Click on function should point to source</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript" src="test-bug_1050691_click_function_to_source.js"></script>
+ </head>
+ <body></body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js
new file mode 100644
index 000000000..1eddf0d6e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug_1050691_click_function_to_source.js
@@ -0,0 +1,10 @@
+/**
+ * this
+ * is
+ * a
+ * function
+ */
+function foo() {
+ console.log(foo);
+}
+
diff --git a/devtools/client/webconsole/test/test-bug_923281_console_log_filter.html b/devtools/client/webconsole/test/test-bug_923281_console_log_filter.html
new file mode 100644
index 000000000..f2d650a5d
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug_923281_console_log_filter.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Console test</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript" src="test-bug_923281_test1.js"></script>
+ <script type="text/javascript" src="test-bug_923281_test2.js"></script>
+ </head>
+ <body></body>
+</html>
diff --git a/devtools/client/webconsole/test/test-bug_923281_test1.js b/devtools/client/webconsole/test/test-bug_923281_test1.js
new file mode 100644
index 000000000..1c07f1155
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug_923281_test1.js
@@ -0,0 +1,7 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+console.log("Sample log.");
+console.log("This log should be filtered when filtered for test2.js.");
diff --git a/devtools/client/webconsole/test/test-bug_923281_test2.js b/devtools/client/webconsole/test/test-bug_923281_test2.js
new file mode 100644
index 000000000..7ac85b387
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug_923281_test2.js
@@ -0,0 +1,8 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+console.log("This is a random text.");
diff --git a/devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html b/devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html
new file mode 100644
index 000000000..ab44de09f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-bug_939783_console_trace_duplicates.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test for bug 939783 - different console.trace() calls
+ wrongly filtered as duplicates</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript">
+function foo1() {
+ foo2();
+}
+
+function foo1b() {
+ foo2();
+}
+
+function foo2() {
+ foo3();
+}
+
+function foo3() {
+ console.trace();
+}
+
+foo1(); foo1();
+foo1b();
+
+</script>
+ </head>
+ <body>
+ <p>Web Console test for bug 939783 - different console.trace() calls
+ wrongly filtered as duplicates</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-certificate-messages.html b/devtools/client/webconsole/test/test-certificate-messages.html
new file mode 100644
index 000000000..b0419a6fc
--- /dev/null
+++ b/devtools/client/webconsole/test/test-certificate-messages.html
@@ -0,0 +1,22 @@
+<!--
+ Bug 1068949 - Log crypto warnings to the security pane in the webconsole
+-->
+
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf8">
+ <title>Security warning test - no violations</title>
+ <!-- ensure no subresource errors so window re-use doesn't cause failures -->
+ <link rel="icon" href="data:;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC">
+ <script>
+ console.log("If you haven't seen ssl warnings yet, you won't");
+ </script>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-closure-optimized-out.html b/devtools/client/webconsole/test/test-closure-optimized-out.html
new file mode 100644
index 000000000..3ad4e8fc0
--- /dev/null
+++ b/devtools/client/webconsole/test/test-closure-optimized-out.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Debugger Test for Inspecting Optimized-Out Variables</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript">
+ window.addEventListener("load", function onload() {
+ window.removeEventListener("load", onload);
+ function clickHandler(event) {
+ button.removeEventListener("click", clickHandler, false);
+ function outer(arg) {
+ var upvar = arg * 2;
+ // The inner lambda only aliases arg, so the frontend alias analysis decides
+ // that upvar is not aliased and is not in the CallObject.
+ return function () {
+ arg += 2;
+ };
+ }
+
+ var f = outer(42);
+ f();
+ }
+ var button = document.querySelector("button");
+ button.addEventListener("click", clickHandler, false);
+ });
+ </script>
+
+ </head>
+ <body>
+ <button>Click me!</button>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-closures.html b/devtools/client/webconsole/test/test-closures.html
new file mode 100644
index 000000000..4fadade20
--- /dev/null
+++ b/devtools/client/webconsole/test/test-closures.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset='utf-8'/>
+ <title>Console Test for Closure Inspection</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript">
+ function injectPerson() {
+ var PersonFactory = function _pfactory(name) {
+ var foo = 10;
+ return {
+ getName: function() { return name; },
+ getFoo: function() { foo = Date.now(); return foo; }
+ };
+ };
+ window.george = new PersonFactory("George");
+ debugger;
+ }
+ </script>
+
+ </head>
+ <body>
+ <button onclick="injectPerson()">Test</button>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-api-stackframe.html b/devtools/client/webconsole/test/test-console-api-stackframe.html
new file mode 100644
index 000000000..df7fef9b1
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-api-stackframe.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+ <head>
+ <meta charset="utf8">
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <title>Test for bug 920116 - stacktraces for console API messages</title>
+ <script>
+ function firstCall() {
+ secondCall();
+ }
+
+ function secondCall() {
+ thirdCall();
+ }
+
+ function thirdCall() {
+ console.log("foo-log");
+ console.error("foo-error");
+ console.exception("foo-exception");
+ console.assert("red" == "blue", "foo-assert");
+ }
+
+ window.onload = firstCall;
+ </script>
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-assert.html b/devtools/client/webconsole/test/test-console-assert.html
new file mode 100644
index 000000000..b104d72d4
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-assert.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <meta charset="utf-8">
+ <title>console.assert() test</title>
+ <script type="text/javascript">
+ function test() {
+ console.log("start");
+ console.assert(false, "false assert");
+ console.assert(0, "falsy assert");
+ console.assert(true, "true assert");
+ console.log("end");
+ }
+ </script>
+ </head>
+ <body>
+ <p>test console.assert()</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-clear.html b/devtools/client/webconsole/test/test-console-clear.html
new file mode 100644
index 000000000..8009db858
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-clear.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console.clear() tests</title>
+ <script type="text/javascript">
+ console.log("log1");
+ console.log("log2");
+ console.clear();
+
+ window.objFromPage = { a: 1 };
+ </script>
+ </head>
+ <body>
+ <h1 id="header">Clear Demo</h1>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-column.html b/devtools/client/webconsole/test/test-console-column.html
new file mode 100644
index 000000000..ff9cc81e1
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-column.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <meta charset="utf-8">
+ <title>Console test</title>
+
+ <script type="text/javascript">
+ console.info("INLINE SCRIPT:"); console.log('Further');
+ console.warn("I'm warning you, he will eat up all yr bacon.");
+ console.error("Error Message");
+ console.log('Rainbooooww');
+ console.log('NYAN CATZ');
+ </script>
+ </head>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-count-external-file.js b/devtools/client/webconsole/test/test-console-count-external-file.js
new file mode 100644
index 000000000..cca9e2f10
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-count-external-file.js
@@ -0,0 +1,11 @@
+/* eslint-disable no-unused-vars */
+
+"use strict";
+
+function counterExternalFile() {
+ console.count("console.count() testcounter");
+}
+function externalCountersWithoutLabel() {
+ console.count();
+ console.count();
+}
diff --git a/devtools/client/webconsole/test/test-console-count.html b/devtools/client/webconsole/test/test-console-count.html
new file mode 100644
index 000000000..e6db0ebb0
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-count.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <meta charset="utf-8">
+ <title>console.count() test</title>
+ <script src="test-console-count-external-file.js"></script>
+ <script tyoe="text/javascript">
+ function counterSeperateScriptTag() {
+ console.count("console.count() testcounter");
+ }
+ </script>
+ <script type="text/javascript">
+ function counterNoLabel() {
+ console.count();
+ }
+ function countersWithoutLabel() {
+ console.count();
+ console.count();
+ }
+ function counterWithLabel() {
+ console.count("console.count() testcounter");
+ }
+ function testLocal() {
+ console.log("start");
+ counterNoLabel();
+ counterNoLabel();
+ countersWithoutLabel();
+ counterWithLabel();
+ counterWithLabel();
+ counterSeperateScriptTag();
+ counterSeperateScriptTag();
+ console.log("end");
+ }
+ function testExternal() {
+ console.log("start");
+ counterExternalFile();
+ counterExternalFile();
+ externalCountersWithoutLabel();
+ console.log("end");
+ }
+ </script>
+ </head>
+ <body>
+ <p>test console.count()</p>
+ <button id="local" onclick="testLocal();">
+ test local console.count() calls
+ </button>
+ <button id="external" onclick="testExternal();">
+ test external console.count() calls
+ </button>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-extras.html b/devtools/client/webconsole/test/test-console-extras.html
new file mode 100644
index 000000000..8685b1a80
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-extras.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console extended API test</title>
+ <script type="text/javascript">
+ function test() {
+ console.log("start");
+ console.clear();
+ console.log("end");
+ }
+ </script>
+ </head>
+ <body>
+ <h1 id="header">Heads Up Display Demo</h1>
+ <button onclick="test();">Test Extended API</button>
+ <div id="myDiv"></div>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-output-02.html b/devtools/client/webconsole/test/test-console-output-02.html
new file mode 100644
index 000000000..ad90f0ebf
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-output-02.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+ <meta charset="utf-8">
+ <title>Test the web console output - 02</title>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body>
+ <p>hello world!</p>
+ <script type="text/javascript">
+function testfn1() { return 42; }
+
+var testobj1 = {
+ testfn2: function() { return 42; },
+};
+
+function testfn3() { return 42; }
+testfn3.displayName = "testfn3DisplayName";
+
+var array1 = [1, 2, 3, "a", "b", "c", "4", "5"];
+
+var array2 = ["a", document, document.body, document.body.dataset,
+ document.body.classList];
+
+var array3 = [1, window, null, "a", "b", undefined, false, "", -Infinity, testfn3, testobj1, "foo", "bar"];
+
+var array4 = new Array(5);
+array4.push("test");
+array4.push(array4);
+
+var typedarray1 = new Int32Array([1, 287, 8651, 40983, 8754]);
+
+var set1 = new Set([1, 2, null, array3, "a", "b", undefined, document.head]);
+set1.add(set1);
+
+var bunnies = new String("bunnies")
+var weakset = new WeakSet([bunnies, document.head]);
+
+var testobj2 = {a: "b", c: "d", e: 1, f: "2"};
+testobj2.foo = testobj1;
+testobj2.bar = testobj2;
+Object.defineProperty(testobj2, "getterTest", {
+ enumerable: true,
+ get: function() {
+ return 42;
+ },
+});
+
+var testobj3 = {a: "b", c: "d", e: 1, f: "2", g: true, h: null, i: undefined,
+ j: "", k: document.styleSheets, l: document.body.childNodes,
+ o: new Array(125), m: document.head};
+
+var testobj4 = {a: "b", c: "d"};
+Object.defineProperty(testobj4, "nonEnumerable", { value: "hello world" });
+
+var map1 = new Map([["a", "b"], [document.body.children, testobj2]]);
+map1.set(map1, set1);
+
+var weakmap = new WeakMap([[bunnies, 23], [document.body.children, testobj2]]);
+
+ </script>
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-output-03.html b/devtools/client/webconsole/test/test-console-output-03.html
new file mode 100644
index 000000000..9dcf051a6
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-output-03.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+ <meta charset="utf-8">
+ <title>Test the web console output - 03</title>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body>
+ <p>hello world!</p>
+ <script type="text/javascript">
+function testBodyClassName() {
+ document.body.className = "test1 tezt2";
+ return document.body;
+}
+
+function testBodyID() {
+ document.body.id = 'foobarid';
+ return document.body;
+}
+
+function testBodyDataset() {
+ document.body.dataset.preview = 'zuzu"<a>foo';
+ return document.body;
+}
+ </script>
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-output-04.html b/devtools/client/webconsole/test/test-console-output-04.html
new file mode 100644
index 000000000..bb4345277
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-output-04.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+ <meta charset="utf-8">
+ <title>Test the web console output - 04</title>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body>
+ <p>hello world!</p>
+ <script type="text/javascript">
+function testTextNode() {
+ return document.querySelector("p").childNodes[0];
+}
+
+function testCommentNode() {
+ return document.head.childNodes[5];
+}
+
+function testDocumentFragment() {
+ var frag = document.createDocumentFragment();
+
+ var div = document.createElement("div");
+ div.id = "foo1";
+ div.className = "bar";
+ frag.appendChild(div);
+
+ var span = document.createElement("span");
+ span.id = "foo2";
+ span.textContent = "hello world";
+ div.appendChild(span);
+
+ var div2 = document.createElement("div");
+ div2.id = "foo3";
+ frag.appendChild(div2);
+
+ return frag;
+}
+
+function testError() {
+ try {
+ window.foobar("a");
+ } catch (ex) {
+ return ex;
+ }
+ return null;
+}
+
+function testDOMException() {
+ try {
+ var foo = document.querySelector("foo;()bar!");
+ } catch (ex) {
+ return ex;
+ }
+ return null;
+}
+
+function testCSSStyleDeclaration() {
+ document.body.style = 'color: green; font-size: 2em';
+ return document.body.style;
+}
+
+function testStyleSheetList() {
+ var style = document.querySelector("style");
+ if (!style) {
+ style = document.createElement("style");
+ style.textContent = "p, div { color: blue; font-weight: bold }\n" +
+ "@media print { p { background-color: yellow } }";
+ document.head.appendChild(style);
+ }
+ return document.styleSheets;
+}
+ </script>
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-output-dom-elements.html b/devtools/client/webconsole/test/test-console-output-dom-elements.html
new file mode 100644
index 000000000..5acabfa3f
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-output-dom-elements.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+ <meta charset="utf-8">
+ <title>Test the web console output - dom elements</title>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body class="body-class" id="body-id">
+ <p some-attribute="some-value">hello world!</p>
+ <p id="lots-of-attributes" a b c d e f g h i j k l m n></p>
+ <!--
+ Be sure we have a charset in our iframe's data URI, otherwise we get the following extra
+ console output message:
+ "The character encoding of a framed document was not declared. The document may appear different if viewed without the document framing it."
+ This wouldn't be a big deal, but when we look for a "<p>" in our `waitForMessage` helper,
+ this extra encoding warning line contains the data URI source, returning a message
+ that was unexpected
+ -->
+ <iframe src="data:text/html;charset=US-ASCII,<p>hello from iframe</p>"></iframe>
+ <div class="some classname here with more classnames here"></div>
+ <svg>
+ <clipPath>
+ <rect x="0" y="0" width="10" height="5"></rect>
+ </clipPath>
+ </svg>
+ <script type="text/javascript">
+function testBodyNode() {
+ return document.body;
+}
+
+function testDocumentElement() {
+ return document.documentElement;
+}
+
+function testLotsOfAttributes() {
+ return document.querySelector("#lots-of-attributes");
+}
+
+function testDocument() {
+ return document;
+}
+
+function testNode() {
+ return document.querySelector("p");
+}
+
+function testSvgNode() {
+ return document.querySelector("clipPath");
+}
+
+function testNodeList() {
+ return document.querySelectorAll("body *");
+}
+
+function testNodeInIframe() {
+ return document.querySelector("iframe").contentWindow.document.querySelector("p");
+}
+
+function testDocumentFragment() {
+ var frag = document.createDocumentFragment();
+
+ var span = document.createElement("span");
+ span.className = 'foo';
+ span.dataset.lolz = 'hehe';
+
+ var div = document.createElement('div')
+ div.id = 'fragdiv';
+
+ frag.appendChild(span);
+ frag.appendChild(div);
+
+ return frag;
+}
+
+function testNodeInDocumentFragment() {
+ var frag = testDocumentFragment();
+ return frag.firstChild;
+}
+
+function testUnattachedNode() {
+ var p = document.createElement("p");
+ p.className = "such-class";
+ p.dataset.data = "such-data";
+ return p;
+}
+ </script>
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-output-events.html b/devtools/client/webconsole/test/test-console-output-events.html
new file mode 100644
index 000000000..908a86fab
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-output-events.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+ <meta charset="utf-8">
+ <title>Test the web console output for DOM events</title>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body>
+ <p>hello world!</p>
+
+ <script type="text/javascript">
+function testDOMEvents() {
+ function eventLogger(ev) {
+ console.log("eventLogger", ev);
+ }
+ document.addEventListener("mousemove", eventLogger);
+ document.addEventListener("keypress", eventLogger);
+
+ synthesizeMouseMove();
+ synthesizeKeyPress("a", {shiftKey: true});
+}
+
+function synthesizeMouseMove(element) {
+ var mouseEvent = document.createEvent("MouseEvent");
+ mouseEvent.initMouseEvent("mousemove", true, true, window, 0, 0, 0, 0, 0,
+ false, false, false, false, 0, null);
+
+ document.dispatchEvent(mouseEvent);
+}
+
+function synthesizeKeyPress(key, options) {
+ var keyboardEvent = document.createEvent("KeyboardEvent");
+ keyboardEvent.initKeyEvent("keypress", true, true, window, false, false,
+ options.shiftKey, false, key.charCodeAt(0), 0);
+ document.dispatchEvent(keyboardEvent);
+}
+ </script>
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-output-regexp.html b/devtools/client/webconsole/test/test-console-output-regexp.html
new file mode 100644
index 000000000..e62680d90
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-output-regexp.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en-US">
+<head>
+ <meta charset="utf-8">
+ <title>Test the web console output for RegExp</title>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body>
+ <p>hello world!</p>
+
+ <script type="text/javascript">
+Object.defineProperty(RegExp.prototype, "flags", {
+ get: function() { throw Error("flags called"); }
+})
+Object.defineProperty(RegExp.prototype, "source", {
+ get: function() { throw Error("source called"); },
+})
+ </script>
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-replaced-api.html b/devtools/client/webconsole/test/test-console-replaced-api.html
new file mode 100644
index 000000000..2b05d023a
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-replaced-api.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console test replaced API</title>
+ </head>
+ <body>
+ <h1 id="header">Web Console Replace API Test</h1>
+ <script type="text/javascript">
+ window.console = {log: function (msg){}, info: function (msg){}, warn: function (msg){}, error: function (msg){}};
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-server-logging-array.sjs b/devtools/client/webconsole/test/test-console-server-logging-array.sjs
new file mode 100644
index 000000000..bba394264
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-server-logging-array.sjs
@@ -0,0 +1,32 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response)
+{
+ var page = "<!DOCTYPE html><html>" +
+ "<head><meta charset='utf-8'></head>" +
+ "<body><p>hello world!</p></body>" +
+ "</html>";
+
+ var data = {
+ "version": "4.1.0",
+ "columns": ["log", "backtrace", "type"],
+ "rows":[[
+ [{ "best": "Firefox", "reckless": "Chrome", "new_ie": "Safari", "new_new_ie": "Edge"}],
+ "C:\\src\\www\\serverlogging\\test7.php:4:1",
+ ""
+ ]],
+ };
+
+ // Put log into headers.
+ var value = b64EncodeUnicode(JSON.stringify(data));
+ response.setHeader("X-ChromeLogger-Data", value, false);
+
+ response.write(page);
+}
+
+function b64EncodeUnicode(str) {
+ return btoa(unescape(encodeURIComponent(str)));
+}
diff --git a/devtools/client/webconsole/test/test-console-server-logging.sjs b/devtools/client/webconsole/test/test-console-server-logging.sjs
new file mode 100644
index 000000000..7177e7185
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-server-logging.sjs
@@ -0,0 +1,32 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function handleRequest(request, response)
+{
+ var page = "<!DOCTYPE html><html>" +
+ "<head><meta charset='utf-8'></head>" +
+ "<body><p>hello world!</p></body>" +
+ "</html>";
+
+ var data = {
+ "version": "4.1.0",
+ "columns": ["log", "backtrace", "type"],
+ "rows": [[
+ ["values: %s %o %i %f %s","string",{"a":10,"___class_name":"Object"},123,1.12, "\u2713"],
+ "C:\\src\\www\\serverlogging\\test7.php:4:1",
+ ""
+ ]]
+ };
+
+ // Put log into headers.
+ var value = b64EncodeUnicode(JSON.stringify(data));
+ response.setHeader("X-ChromeLogger-Data", value, false);
+
+ response.write(page);
+}
+
+function b64EncodeUnicode(str) {
+ return btoa(unescape(encodeURIComponent(str)));
+}
diff --git a/devtools/client/webconsole/test/test-console-table.html b/devtools/client/webconsole/test/test-console-table.html
new file mode 100644
index 000000000..461dedcde
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-table.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+ <head>
+ <meta charset="utf8">
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <title>Test for Bug 899753 - console.table support</title>
+ <script>
+ var languages1 = [
+ { name: "JavaScript", fileExtension: [".js"] },
+ { name: { a: "TypeScript" }, fileExtension: ".ts" },
+ { name: "CoffeeScript", fileExtension: ".coffee" }
+ ];
+
+ var languages2 = {
+ csharp: { name: "C#", paradigm: "object-oriented" },
+ fsharp: { name: "F#", paradigm: "functional" }
+ };
+
+ function Person(firstName, lastName, age)
+ {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.age = age;
+ }
+
+ var family = {};
+ family.mother = new Person("Susan", "Doyle", 32);
+ family.father = new Person("John", "Doyle", 33);
+ family.daughter = new Person("Lily", "Doyle", 5);
+ family.son = new Person("Mike", "Doyle", 8);
+
+ var myMap = new Map();
+
+ myMap.set("a string", "value associated with 'a string'");
+ myMap.set(5, "value associated with 5");
+
+ var mySet = new Set();
+
+ mySet.add(1);
+ mySet.add(5);
+ mySet.add("some text");
+ mySet.add(null);
+ mySet.add(undefined);
+
+ // These are globals and so won't be reclaimed by the GC.
+ var bunnies = new String("bunnies");
+ var lizards = new String("lizards");
+
+ var weakmap = new WeakMap();
+ weakmap.set(bunnies, 23);
+ weakmap.set(lizards, "oh no");
+
+ var weakset = new WeakSet([bunnies, lizards]);
+
+ </script>
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-trace-async.html b/devtools/client/webconsole/test/test-console-trace-async.html
new file mode 100644
index 000000000..c7b895455
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-trace-async.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head><meta charset="utf-8">
+ <title>Web Console test for bug 1200832 - console.trace() async stacks</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<script type="application/javascript">
+function inner() {
+ console.trace();
+}
+
+function time1() {
+ new Promise(function(resolve, reject) {
+ setTimeout(resolve, 10);
+ }).then(inner);
+}
+
+setTimeout(time1, 10);
+</script>
+ </head>
+ <body>
+ <p>Web Console test for bug 1200832 - console.trace() async stacks</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console-workers.html b/devtools/client/webconsole/test/test-console-workers.html
new file mode 100644
index 000000000..f4b286ae5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console-workers.html
@@ -0,0 +1,13 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console test</title>
+ </head>
+ <body>
+ <script type="text/javascript">
+var sw = new SharedWorker('data:application/javascript,console.log("foo-bar-shared-worker");');
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-console.html b/devtools/client/webconsole/test/test-console.html
new file mode 100644
index 000000000..b294a3ba1
--- /dev/null
+++ b/devtools/client/webconsole/test/test-console.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console test</title>
+ <script type="text/javascript">
+ var fooObj = {
+ testProp: "testValue"
+ };
+
+ function test() {
+ var str = "Dolske Digs Bacon, Now and Forevermore."
+ for (var i=0; i < 5; i++) {
+ console.log(str);
+ }
+ }
+
+ function testTrace() {
+ console.log("bug 1100562");
+ console.trace();
+ }
+
+ console.info("INLINE SCRIPT:");
+ test();
+ console.warn("I'm warning you, he will eat up all yr bacon.");
+ console.error("Error Message");
+ </script>
+ </head>
+ <body>
+ <h1 id="header">Heads Up Display Demo</h1>
+ <button onclick="test();">Log stuff about Dolske</button>
+ <button id="testTrace" onclick="testTrace();">Log stuff with stacktrace</button>
+ <div id="myDiv"></div>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-consoleiframes.html b/devtools/client/webconsole/test/test-consoleiframes.html
new file mode 100644
index 000000000..a8176f93a
--- /dev/null
+++ b/devtools/client/webconsole/test/test-consoleiframes.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+ <script>
+ console.log("main file");
+ </script>
+</head>
+<body>
+<h1>iframe console test</h1>
+<iframe src="test-iframe1.html"></iframe>
+<iframe src="test-iframe2.html"></iframe>
+<iframe src="test-iframe3.html"></iframe>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test-cu-reporterror.js b/devtools/client/webconsole/test/test-cu-reporterror.js
new file mode 100644
index 000000000..6e2f9d262
--- /dev/null
+++ b/devtools/client/webconsole/test/test-cu-reporterror.js
@@ -0,0 +1,4 @@
+function a() {
+ Components.utils.reportError("bug1141222");
+}
+a();
diff --git a/devtools/client/webconsole/test/test-data.json b/devtools/client/webconsole/test/test-data.json
new file mode 100644
index 000000000..471d240b5
--- /dev/null
+++ b/devtools/client/webconsole/test/test-data.json
@@ -0,0 +1 @@
+{ id: "test JSON data", myArray: [ "foo", "bar", "baz", "biff" ] } \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test-data.json^headers^ b/devtools/client/webconsole/test/test-data.json^headers^
new file mode 100644
index 000000000..7b5e82d4b
--- /dev/null
+++ b/devtools/client/webconsole/test/test-data.json^headers^
@@ -0,0 +1 @@
+Content-Type: application/json
diff --git a/devtools/client/webconsole/test/test-duplicate-error.html b/devtools/client/webconsole/test/test-duplicate-error.html
new file mode 100644
index 000000000..1b2691672
--- /dev/null
+++ b/devtools/client/webconsole/test/test-duplicate-error.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Console duplicate error test</title>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+
+ See https://bugzilla.mozilla.org/show_bug.cgi?id=582201
+ -->
+ </head>
+ <body>
+ <h1>Heads Up Display - duplicate error test</h1>
+
+ <script type="text/javascript"><!--
+ fooDuplicateError1.bar();
+ // --></script>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-encoding-ISO-8859-1.html b/devtools/client/webconsole/test/test-encoding-ISO-8859-1.html
new file mode 100644
index 000000000..cf19629f4
--- /dev/null
+++ b/devtools/client/webconsole/test/test-encoding-ISO-8859-1.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="ISO-8859-1">
+</head>
+<body>üöä</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test-error.html b/devtools/client/webconsole/test/test-error.html
new file mode 100644
index 000000000..abf62a3f1
--- /dev/null
+++ b/devtools/client/webconsole/test/test-error.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Console error test</title>
+ </head>
+ <body>
+ <h1>Heads Up Display - error test</h1>
+ <p><button>generate error</button></p>
+
+ <script type="text/javascript"><!--
+ var button = document.getElementsByTagName("button")[0];
+
+ button.addEventListener("click", function clicker () {
+ button.removeEventListener("click", clicker, false);
+ fooBazBaz.bar();
+ }, false);
+ // --></script>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-eval-in-stackframe.html b/devtools/client/webconsole/test/test-eval-in-stackframe.html
new file mode 100644
index 000000000..ec1bf3f30
--- /dev/null
+++ b/devtools/client/webconsole/test/test-eval-in-stackframe.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+ <head>
+ <meta charset="utf8">
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <title>Test for bug 783499 - use the debugger API in the web console</title>
+ <script>
+ var foo = "globalFooBug783499";
+ var fooObj = {
+ testProp: "testValue",
+ };
+
+ function firstCall()
+ {
+ var foo = "fooFirstCall";
+ var foo3 = "foo3FirstCall";
+ secondCall();
+ }
+
+ function secondCall()
+ {
+ var foo2 = "foo2SecondCall";
+ var fooObj = {
+ testProp2: "testValue2",
+ };
+ var fooObj2 = {
+ testProp22: "testValue22",
+ };
+ debugger;
+ }
+ </script>
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-exception-stackframe.html b/devtools/client/webconsole/test/test-exception-stackframe.html
new file mode 100644
index 000000000..0a6dea4ca
--- /dev/null
+++ b/devtools/client/webconsole/test/test-exception-stackframe.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html dir="ltr" lang="en">
+ <head>
+ <meta charset="utf8">
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ <title>Test for bug 1184172 - stacktraces for exceptions</title>
+ <script>
+ function firstCall() {
+ secondCall();
+ }
+
+ // Check anonymous functions
+ var secondCall = function () {
+ thirdCall();
+ }
+
+ function thirdCall() {
+ nonExistingMethodCall();
+ }
+
+ function domAPI() {
+ document.querySelector("buggy;selector");
+ }
+
+ function domException() {
+ throw new DOMException("DOMException");
+ }
+ window.addEventListener("load", firstCall);
+ window.addEventListener("load", function onLoadDomAPI() {
+ domAPI();
+ });
+ window.addEventListener("load", function onLoadDomException() {
+ domException();
+ });
+ </script>
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-file-location.js b/devtools/client/webconsole/test/test-file-location.js
new file mode 100644
index 000000000..d9879a356
--- /dev/null
+++ b/devtools/client/webconsole/test/test-file-location.js
@@ -0,0 +1,12 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+console.log("message for level log");
+console.info("message for level info");
+console.warn("message for level warn");
+console.error("message for level error");
+console.debug("message for level debug");
diff --git a/devtools/client/webconsole/test/test-filter.html b/devtools/client/webconsole/test/test-filter.html
new file mode 100644
index 000000000..219177bb2
--- /dev/null
+++ b/devtools/client/webconsole/test/test-filter.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console test</title>
+ <script type="text/javascript">
+ </script>
+ </head>
+ <body>
+ <h1>Heads Up Display Filter Test Page</h1>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-for-of.html b/devtools/client/webconsole/test/test-for-of.html
new file mode 100644
index 000000000..876010c9e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-for-of.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<body>
+<h1>a</h1>
+<div><p>b</p></div>
+<h2>c</h2>
+<p>d</p>
diff --git a/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html b/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html
new file mode 100644
index 000000000..d14b5cdd7
--- /dev/null
+++ b/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+ <head>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <h1>iframe 2</h1>
+ <p>This frame contains a password field inside a form with insecure action.</p>
+ <form action="http://test">
+ <input type="password" name="pwd">
+ </form>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html b/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html
new file mode 100644
index 000000000..dde47a78e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-iframe-762593-insecure-frame.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<html>
+ <head>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <h1>iframe 1</h1>
+ <p>This frame is served with an insecure password field.</p>
+ <iframe src=
+ "http://example.com/browser/devtools/client/webconsole/test/test-iframe-762593-insecure-form-action.html">
+ </iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-iframe1.html b/devtools/client/webconsole/test/test-iframe1.html
new file mode 100644
index 000000000..4dd4eddfe
--- /dev/null
+++ b/devtools/client/webconsole/test/test-iframe1.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <script>
+ console.log("iframe 1");
+ </script>
+</head>
+<body>
+<h1>iframe 1</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test-iframe2.html b/devtools/client/webconsole/test/test-iframe2.html
new file mode 100644
index 000000000..c15884795
--- /dev/null
+++ b/devtools/client/webconsole/test/test-iframe2.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <script>
+ console.log("iframe 2");
+ blah;
+ </script>
+</head>
+<body>
+<h1>iframe 2</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test-iframe3.html b/devtools/client/webconsole/test/test-iframe3.html
new file mode 100644
index 000000000..f0df8b669
--- /dev/null
+++ b/devtools/client/webconsole/test/test-iframe3.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+ <script>
+ console.log("iframe 3");
+ </script>
+</head>
+<body>
+<h1>iframe 3</h1>
+<iframe src="test-iframe1.html"></iframe>
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test-image.png b/devtools/client/webconsole/test/test-image.png
new file mode 100644
index 000000000..769c63634
--- /dev/null
+++ b/devtools/client/webconsole/test/test-image.png
Binary files differ
diff --git a/devtools/client/webconsole/test/test-mixedcontent-securityerrors.html b/devtools/client/webconsole/test/test-mixedcontent-securityerrors.html
new file mode 100644
index 000000000..cb8cfdaaf
--- /dev/null
+++ b/devtools/client/webconsole/test/test-mixedcontent-securityerrors.html
@@ -0,0 +1,21 @@
+<!--
+ Bug 875456 - Log mixed content messages from the Mixed Content Blocker to the
+ Security Pane in the Web Console
+-->
+
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf8">
+ <title>Mixed Content test - http on https</title>
+ <script src="testscript.js"></script>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <iframe src="http://example.com"></iframe>
+ <img src="http://example.com/tests/image/test/mochitest/blue.png"></img>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-mutation.html b/devtools/client/webconsole/test/test-mutation.html
new file mode 100644
index 000000000..e80933b06
--- /dev/null
+++ b/devtools/client/webconsole/test/test-mutation.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Console mutation test</title>
+ <script>
+ window.onload = function (){
+ var node = document.createElement("div");
+ document.body.appendChild(node);
+ };
+ </script>
+ </head>
+ <body>
+ <h1>Heads Up Display DOM Mutation Test Page</h1>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-network-request.html b/devtools/client/webconsole/test/test-network-request.html
new file mode 100644
index 000000000..7cb736296
--- /dev/null
+++ b/devtools/client/webconsole/test/test-network-request.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>Console HTTP test page</title>
+ <script type="text/javascript"><!--
+ function makeXhr(aMethod, aUrl, aRequestBody, aCallback) {
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open(aMethod, aUrl, true);
+ xmlhttp.onreadystatechange = function() {
+ if (aCallback && xmlhttp.readyState == 4) {
+ aCallback();
+ }
+ };
+ xmlhttp.send(aRequestBody);
+ }
+
+ function testXhrGet(aCallback) {
+ makeXhr('get', 'test-data.json', null, aCallback);
+ }
+
+ function testXhrWarn(aCallback) {
+ makeXhr('get', 'http://example.com/browser/devtools/client/netmonitor/test/sjs_cors-test-server.sjs', null, aCallback);
+ }
+
+ function testXhrPost(aCallback) {
+ makeXhr('post', 'test-data.json', "Hello world!", aCallback);
+ }
+ // --></script>
+ </head>
+ <body>
+ <h1>Heads Up Display HTTP Logging Testpage</h1>
+ <h2>This page is used to test the HTTP logging.</h2>
+
+ <form action="https://example.com/browser/devtools/client/webconsole/test/test-network-request.html" method="post">
+ <input name="name" type="text" value="foo bar"><br>
+ <input name="age" type="text" value="144"><br>
+ </form>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-network.html b/devtools/client/webconsole/test/test-network.html
new file mode 100644
index 000000000..69d3422e3
--- /dev/null
+++ b/devtools/client/webconsole/test/test-network.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console network test</title>
+ <script src="testscript.js?foo"></script>
+ </head>
+ <body>
+ <h1>Heads Up Display Network Test Page</h1>
+ <img src="test-image.png"></img>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-observe-http-ajax.html b/devtools/client/webconsole/test/test-observe-http-ajax.html
new file mode 100644
index 000000000..5abcefdad
--- /dev/null
+++ b/devtools/client/webconsole/test/test-observe-http-ajax.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Console HTTP test page</title>
+ <script type="text/javascript">
+ function test() {
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.open('get', 'test-data.json', false);
+ xmlhttp.send(null);
+ }
+ </script>
+ </head>
+ <body onload="test();">
+ <h1>Heads Up Display HTTP & AJAX Test Page</h1>
+ <h2>This page fires an ajax request so we can see the http logging of the console</h2>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-own-console.html b/devtools/client/webconsole/test/test-own-console.html
new file mode 100644
index 000000000..d1d18ebc2
--- /dev/null
+++ b/devtools/client/webconsole/test/test-own-console.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+<head>
+<meta charset="utf-8">
+<script>
+ var _console = {
+ foo: "bar"
+ }
+
+ window.console = _console;
+
+ function loadIFrame() {
+ var iframe = document.body.querySelector("iframe");
+ iframe.addEventListener("load", function() {
+ iframe.removeEventListener("load", arguments.callee, true);
+ }, true);
+
+ iframe.setAttribute("src", "test-console.html");
+ }
+</script>
+</head>
+<body>
+ <iframe></iframe>
+</body>
diff --git a/devtools/client/webconsole/test/test-property-provider.html b/devtools/client/webconsole/test/test-property-provider.html
new file mode 100644
index 000000000..532b00f44
--- /dev/null
+++ b/devtools/client/webconsole/test/test-property-provider.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US"><head>
+ <meta charset="utf-8">
+ <title>Property provider test</title>
+ <script>
+ var testObj = {
+ testProp: 'testValue'
+ };
+ </script>
+ </head>
+ <body>
+ <h1>Heads Up Property Provider Test Page</h1>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-repeated-messages.html b/devtools/client/webconsole/test/test-repeated-messages.html
new file mode 100644
index 000000000..b19c9485e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-repeated-messages.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf8">
+ <title>Test for bugs 720180, 800510, 865288 and 1218089</title>
+ <script>
+ function testConsole() {
+ // same line and column number
+ for(var i = 0; i < 2; i++) {
+ console.log("foo repeat");
+ }
+ console.log("foo repeat");
+ console.error("foo repeat");
+ }
+ function testConsoleObjects() {
+ for (var i = 0; i < 3; i++) {
+ var o = { id: "abba" + i };
+ console.log("abba", o);
+ }
+ }
+ function testConsoleFalsyValues(){
+ [NaN, undefined, null].forEach(function(item, index){
+ console.log(item);
+ });
+ [NaN, NaN].forEach(function(item, index){
+ console.log(item);
+ });
+ [undefined, undefined].forEach(function(item, index){
+ console.log(item);
+ });
+ [null, null].forEach(function(item, index){
+ console.log(item);
+ });
+ }
+ </script>
+ <style>
+ body {
+ background-image: foobarz;
+ }
+ p {
+ background-image: foobarz;
+ }
+ </style>
+ <!--
+ - Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+ </head>
+ <body>
+ <p>Hello world!</p>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test-result-format-as-string.html b/devtools/client/webconsole/test/test-result-format-as-string.html
new file mode 100644
index 000000000..c3ab78ee7
--- /dev/null
+++ b/devtools/client/webconsole/test/test-result-format-as-string.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Web Console test: jsterm eval format as a string</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ </head>
+ <body>
+ <p>Make sure js eval results are formatted as strings.</p>
+ <script>
+ document.querySelector("p").toSource = function() {
+ var element = document.createElement("div");
+ element.id = "foobar";
+ element.textContent = "bug772506_content";
+ element.setAttribute("onmousemove",
+ "(function () {" +
+ " gBrowser._bug772506 = 'foobar';" +
+ "})();"
+ );
+ return element;
+ };
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-trackingprotection-securityerrors.html b/devtools/client/webconsole/test/test-trackingprotection-securityerrors.html
new file mode 100644
index 000000000..17f0e459e
--- /dev/null
+++ b/devtools/client/webconsole/test/test-trackingprotection-securityerrors.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<!-- 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/. -->
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf8">
+ </head>
+ <body>
+ <iframe src="http://tracking.example.com/"></iframe>
+ </body>
+</html>
diff --git a/devtools/client/webconsole/test/test-webconsole-error-observer.html b/devtools/client/webconsole/test/test-webconsole-error-observer.html
new file mode 100644
index 000000000..8466bc6f2
--- /dev/null
+++ b/devtools/client/webconsole/test/test-webconsole-error-observer.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+ <head>
+ <meta charset="utf-8">
+ <title>WebConsoleErrorObserver test - bug 611032</title>
+ <!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+ <script type="text/javascript">
+ console.log("log Bazzle");
+ console.info("info Bazzle");
+ console.warn("warn Bazzle");
+ console.error("error Bazzle");
+
+ var foo = {};
+ foo.bazBug611032();
+ </script>
+ <style type="text/css">
+ .foo { color: cssColorBug611032; }
+ </style>
+ </head>
+ <body>
+ <h1>WebConsoleErrorObserver test</h1>
+ </body>
+</html>
+
diff --git a/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html
new file mode 100644
index 000000000..bf63601bf
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Bug 1045902 - CSP: Log console message for ‘reflected-xss’</title>
+</head>
+<body>
+Bug 1045902 - CSP: Log console message for ‘reflected-xss’
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^ b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
new file mode 100644
index 000000000..0b234f0e8
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: reflected-xss filter;
diff --git a/devtools/client/webconsole/test/test_bug1092055_shouldwarn.html b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.html
new file mode 100644
index 000000000..ebb7773cb
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Bug 1092055 - Log console messages for non-top-level security errors</title>
+ <script src="test_bug1092055_shouldwarn.js"></script>
+ <!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+ -->
+</head>
+<body>
+Bug 1092055 - Log console messages for non-top-level security errors
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js
new file mode 100644
index 000000000..c7d5cec14
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js
@@ -0,0 +1,2 @@
+// It doesn't matter what this script does, but the broken HSTS header sent
+// with it should result in warnings in the webconsole
diff --git a/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^
new file mode 100644
index 000000000..f99377fc6
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug1092055_shouldwarn.js^headers^
@@ -0,0 +1 @@
+Strict-Transport-Security: some complete nonsense
diff --git a/devtools/client/webconsole/test/test_bug_1010953_cspro.html b/devtools/client/webconsole/test/test_bug_1010953_cspro.html
new file mode 100644
index 000000000..83ac6391f
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug_1010953_cspro.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Test for Bug 1010953 - Verify that CSP and CSPRO log different console
+messages.</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1010953">Mozilla Bug 1010953</a>
+
+
+<!-- this script file allowed by the CSP header (but not by the report-only header) -->
+<script src="http://some.example.com/test_bug_1010953_cspro.js"></script>
+
+<!-- this image allowed only be the CSP report-only header. -->
+<img src="http://some.example.com/test.png">
+</body>
+</html> \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^ b/devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^
new file mode 100644
index 000000000..03056e2cb
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug_1010953_cspro.html^headers^
@@ -0,0 +1,2 @@
+Content-Security-Policy: default-src 'self'; img-src 'self'; script-src some.example.com;
+Content-Security-Policy-Report-Only: default-src 'self'; img-src some.example.com; script-src 'self'; report-uri https://example.com/ignored/; \ No newline at end of file
diff --git a/devtools/client/webconsole/test/test_bug_1247459_violation.html b/devtools/client/webconsole/test/test_bug_1247459_violation.html
new file mode 100644
index 000000000..fdda4eb26
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug_1247459_violation.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta http-equiv="Content-Security-Policy" content="img-src https://example.com"></meta>
+ <meta http-equiv="Content-Security-Policy" content="img-src https://example.com"></meta>
+ <meta charset="UTF-8">
+ <title>Test for Bug 1247459 - policy violations for header and META are displayed separately</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247459">Mozilla Bug 1247459</a>
+<img src="http://some.example.com/test.png">
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test_bug_770099_violation.html b/devtools/client/webconsole/test/test_bug_770099_violation.html
new file mode 100644
index 000000000..ccbded87a
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug_770099_violation.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>Test for Bug 770099 - policy violation</title>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770099">Mozilla Bug 770099</a>
+<img src="http://some.example.com/test.png">
+</body>
+</html>
diff --git a/devtools/client/webconsole/test/test_bug_770099_violation.html^headers^ b/devtools/client/webconsole/test/test_bug_770099_violation.html^headers^
new file mode 100644
index 000000000..4c6fa3c26
--- /dev/null
+++ b/devtools/client/webconsole/test/test_bug_770099_violation.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: default-src 'self'
diff --git a/devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs b/devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs
new file mode 100644
index 000000000..cd0e18523
--- /dev/null
+++ b/devtools/client/webconsole/test/test_hpkp-invalid-headers.sjs
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+
+ let issue;
+ switch (request.queryString) {
+ case "badSyntax":
+ response.setHeader("Public-Key-Pins", "\"");
+ issue = "is not syntactically correct.";
+ break;
+ case "noMaxAge":
+ response.setHeader("Public-Key-Pins", "max-age444");
+ issue = "does not include a max-age directive.";
+ break;
+ case "invalidIncludeSubDomains":
+ response.setHeader("Public-Key-Pins", "includeSubDomains=abc");
+ issue = "includes an invalid includeSubDomains directive.";
+ break;
+ case "invalidMaxAge":
+ response.setHeader("Public-Key-Pins", "max-age=abc");
+ issue = "includes an invalid max-age directive.";
+ break;
+ case "multipleIncludeSubDomains":
+ response.setHeader("Public-Key-Pins",
+ "includeSubDomains; includeSubDomains");
+ issue = "includes multiple includeSubDomains directives.";
+ break;
+ case "multipleMaxAge":
+ response.setHeader("Public-Key-Pins",
+ "max-age=444; max-age=999");
+ issue = "includes multiple max-age directives.";
+ break;
+ case "multipleReportURIs":
+ response.setHeader("Public-Key-Pins",
+ 'report-uri="http://example.com"; ' +
+ 'report-uri="http://example.com"');
+ issue = "includes multiple report-uri directives.";
+ break;
+ case "pinsetDoesNotMatch":
+ response.setHeader(
+ "Public-Key-Pins",
+ 'max-age=999; ' +
+ 'pin-sha256="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; ' +
+ 'pin-sha256="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB="');
+ issue = "does not include a matching pin.";
+ break;
+ }
+
+ response.write("This page is served with a PKP header that " + issue);
+}
diff --git a/devtools/client/webconsole/test/test_hsts-invalid-headers.sjs b/devtools/client/webconsole/test/test_hsts-invalid-headers.sjs
new file mode 100644
index 000000000..9e3ea7624
--- /dev/null
+++ b/devtools/client/webconsole/test/test_hsts-invalid-headers.sjs
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+
+ let issue;
+ switch (request.queryString) {
+ case "badSyntax":
+ response.setHeader("Strict-Transport-Security", "\"");
+ issue = "is not syntactically correct.";
+ break;
+ case "noMaxAge":
+ response.setHeader("Strict-Transport-Security", "max-age444");
+ issue = "does not include a max-age directive.";
+ break;
+ case "invalidIncludeSubDomains":
+ response.setHeader("Strict-Transport-Security", "includeSubDomains=abc");
+ issue = "includes an invalid includeSubDomains directive.";
+ break;
+ case "invalidMaxAge":
+ response.setHeader("Strict-Transport-Security", "max-age=abc");
+ issue = "includes an invalid max-age directive.";
+ break;
+ case "multipleIncludeSubDomains":
+ response.setHeader("Strict-Transport-Security",
+ "includeSubDomains; includeSubDomains");
+ issue = "includes multiple includeSubDomains directives.";
+ break;
+ case "multipleMaxAge":
+ response.setHeader("Strict-Transport-Security",
+ "max-age=444; max-age=999");
+ issue = "includes multiple max-age directives.";
+ break;
+ }
+
+ response.write("This page is served with a STS header that " + issue);
+}
diff --git a/devtools/client/webconsole/test/testscript.js b/devtools/client/webconsole/test/testscript.js
new file mode 100644
index 000000000..849b03d86
--- /dev/null
+++ b/devtools/client/webconsole/test/testscript.js
@@ -0,0 +1,2 @@
+"use strict";
+console.log("running network console logging tests");