Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Sat, 14 Apr 2018 00:59:38 +0300
changeset 466860 79dd1948f4bd3c731ab60aa59571bd9db10f09f1
parent 466859 f36928548891d9029477f0733c71bb8e093e95f6 (current diff)
parent 466810 6547c27303bc4d8961b11e656751e839807d65c7 (diff)
child 466861 676c6c0096afa3e77d32ace4cfd4b21f65b53d15
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge on a CLOSED TREE
devtools/client/locales/en-US/webConsole.dtd
devtools/client/webconsole/old/console-output.js
devtools/client/webconsole/old/jsterm.js
devtools/client/webconsole/old/moz.build
devtools/client/webconsole/old/net/.eslintrc.js
devtools/client/webconsole/old/net/components/cookies-tab.js
devtools/client/webconsole/old/net/components/headers-tab.js
devtools/client/webconsole/old/net/components/moz.build
devtools/client/webconsole/old/net/components/net-info-body.css
devtools/client/webconsole/old/net/components/net-info-body.js
devtools/client/webconsole/old/net/components/net-info-group-list.js
devtools/client/webconsole/old/net/components/net-info-group.css
devtools/client/webconsole/old/net/components/net-info-group.js
devtools/client/webconsole/old/net/components/net-info-params.css
devtools/client/webconsole/old/net/components/net-info-params.js
devtools/client/webconsole/old/net/components/params-tab.js
devtools/client/webconsole/old/net/components/post-tab.js
devtools/client/webconsole/old/net/components/response-tab.css
devtools/client/webconsole/old/net/components/response-tab.js
devtools/client/webconsole/old/net/components/size-limit.css
devtools/client/webconsole/old/net/components/size-limit.js
devtools/client/webconsole/old/net/components/spinner.js
devtools/client/webconsole/old/net/components/stacktrace-tab.js
devtools/client/webconsole/old/net/data-provider.js
devtools/client/webconsole/old/net/main.js
devtools/client/webconsole/old/net/moz.build
devtools/client/webconsole/old/net/net-request.css
devtools/client/webconsole/old/net/net-request.js
devtools/client/webconsole/old/net/test/mochitest/.eslintrc.js
devtools/client/webconsole/old/net/test/mochitest/browser.ini
devtools/client/webconsole/old/net/test/mochitest/browser_net_basic.js
devtools/client/webconsole/old/net/test/mochitest/browser_net_cookies.js
devtools/client/webconsole/old/net/test/mochitest/browser_net_headers.js
devtools/client/webconsole/old/net/test/mochitest/browser_net_params.js
devtools/client/webconsole/old/net/test/mochitest/browser_net_post.js
devtools/client/webconsole/old/net/test/mochitest/browser_net_response.js
devtools/client/webconsole/old/net/test/mochitest/head.js
devtools/client/webconsole/old/net/test/mochitest/page_basic.html
devtools/client/webconsole/old/net/test/mochitest/test-cookies.json
devtools/client/webconsole/old/net/test/mochitest/test-cookies.json^headers^
devtools/client/webconsole/old/net/test/mochitest/test.json
devtools/client/webconsole/old/net/test/mochitest/test.json^headers^
devtools/client/webconsole/old/net/test/mochitest/test.txt
devtools/client/webconsole/old/net/test/mochitest/test.xml
devtools/client/webconsole/old/net/test/mochitest/test.xml^headers^
devtools/client/webconsole/old/net/test/unit/.eslintrc.js
devtools/client/webconsole/old/net/test/unit/test_json-utils.js
devtools/client/webconsole/old/net/test/unit/test_net-utils.js
devtools/client/webconsole/old/net/test/unit/xpcshell.ini
devtools/client/webconsole/old/net/utils/events.js
devtools/client/webconsole/old/net/utils/json.js
devtools/client/webconsole/old/net/utils/moz.build
devtools/client/webconsole/old/net/utils/net.js
devtools/client/webconsole/old/test/.eslintrc.js
devtools/client/webconsole/old/test/browser.ini
devtools/client/webconsole/old/test/browser_bug1045902_console_csp_ignore_reflected_xss_message.js
devtools/client/webconsole/old/test/browser_bug664688_sandbox_update_after_navigation.js
devtools/client/webconsole/old/test/browser_bug_638949_copy_link_location.js
devtools/client/webconsole/old/test/browser_bug_862916_console_dir_and_filter_off.js
devtools/client/webconsole/old/test/browser_bug_865288_repeat_different_objects.js
devtools/client/webconsole/old/test/browser_bug_865871_variables_view_close_on_esc_key.js
devtools/client/webconsole/old/test/browser_bug_869003_inspect_cross_domain_object.js
devtools/client/webconsole/old/test/browser_bug_871156_ctrlw_close_tab.js
devtools/client/webconsole/old/test/browser_cached_messages.js
devtools/client/webconsole/old/test/browser_console.js
devtools/client/webconsole/old/test/browser_console_certificate_imminent_distrust.js
devtools/client/webconsole/old/test/browser_console_clear_method.js
devtools/client/webconsole/old/test/browser_console_clear_on_reload.js
devtools/client/webconsole/old/test/browser_console_click_focus.js
devtools/client/webconsole/old/test/browser_console_consolejsm_output.js
devtools/client/webconsole/old/test/browser_console_copy_command.js
devtools/client/webconsole/old/test/browser_console_copy_entire_message_context_menu.js
devtools/client/webconsole/old/test/browser_console_dead_objects.js
devtools/client/webconsole/old/test/browser_console_devtools_loader_exception.js
devtools/client/webconsole/old/test/browser_console_error_source_click.js
devtools/client/webconsole/old/test/browser_console_filters.js
devtools/client/webconsole/old/test/browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js
devtools/client/webconsole/old/test/browser_console_history_persist.js
devtools/client/webconsole/old/test/browser_console_iframe_messages.js
devtools/client/webconsole/old/test/browser_console_keyboard_accessibility.js
devtools/client/webconsole/old/test/browser_console_log_inspectable_object.js
devtools/client/webconsole/old/test/browser_console_native_getters.js
devtools/client/webconsole/old/test/browser_console_navigation_marker.js
devtools/client/webconsole/old/test/browser_console_netlogging.js
devtools/client/webconsole/old/test/browser_console_nsiconsolemessage.js
devtools/client/webconsole/old/test/browser_console_open_or_focus.js
devtools/client/webconsole/old/test/browser_console_optimized_out_vars.js
devtools/client/webconsole/old/test/browser_console_private_browsing.js
devtools/client/webconsole/old/test/browser_console_restore.js
devtools/client/webconsole/old/test/browser_console_server_logging.js
devtools/client/webconsole/old/test/browser_console_variables_view.js
devtools/client/webconsole/old/test/browser_console_variables_view_dom_nodes.js
devtools/client/webconsole/old/test/browser_console_variables_view_dont_sort_non_sortable_classes_properties.js
devtools/client/webconsole/old/test/browser_console_variables_view_filter.js
devtools/client/webconsole/old/test/browser_console_variables_view_highlighter.js
devtools/client/webconsole/old/test/browser_console_variables_view_special_names.js
devtools/client/webconsole/old/test/browser_console_variables_view_while_debugging.js
devtools/client/webconsole/old/test/browser_console_variables_view_while_debugging_and_inspecting.js
devtools/client/webconsole/old/test/browser_eval_in_debugger_stackframe.js
devtools/client/webconsole/old/test/browser_eval_in_debugger_stackframe2.js
devtools/client/webconsole/old/test/browser_jsterm_inspect.js
devtools/client/webconsole/old/test/browser_longstring_hang.js
devtools/client/webconsole/old/test/browser_netmonitor_shows_reqs_in_webconsole.js
devtools/client/webconsole/old/test/browser_output_breaks_after_console_dir_uninspectable.js
devtools/client/webconsole/old/test/browser_output_longstring_expand.js
devtools/client/webconsole/old/test/browser_repeated_messages_accuracy.js
devtools/client/webconsole/old/test/browser_result_format_as_string.js
devtools/client/webconsole/old/test/browser_warn_user_about_replaced_api.js
devtools/client/webconsole/old/test/browser_webconsole_allow_mixedcontent_securityerrors.js
devtools/client/webconsole/old/test/browser_webconsole_assert.js
devtools/client/webconsole/old/test/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
devtools/client/webconsole/old/test/browser_webconsole_autocomplete_accessibility.js
devtools/client/webconsole/old/test/browser_webconsole_autocomplete_and_selfxss.js
devtools/client/webconsole/old/test/browser_webconsole_autocomplete_crossdomain_iframe.js
devtools/client/webconsole/old/test/browser_webconsole_autocomplete_in_debugger_stackframe.js
devtools/client/webconsole/old/test/browser_webconsole_autocomplete_popup_close_on_tab_switch.js
devtools/client/webconsole/old/test/browser_webconsole_block_mixedcontent_securityerrors.js
devtools/client/webconsole/old/test/browser_webconsole_bug_1006027_message_timestamps_incorrect.js
devtools/client/webconsole/old/test/browser_webconsole_bug_1010953_cspro.js
devtools/client/webconsole/old/test/browser_webconsole_bug_1050691_click_function_to_source.js
devtools/client/webconsole/old/test/browser_webconsole_bug_1247459_violation.js
devtools/client/webconsole/old/test/browser_webconsole_bug_578437_page_reload.js
devtools/client/webconsole/old/test/browser_webconsole_bug_579412_input_focus.js
devtools/client/webconsole/old/test/browser_webconsole_bug_580001_closing_after_completion.js
devtools/client/webconsole/old/test/browser_webconsole_bug_580030_errors_after_page_reload.js
devtools/client/webconsole/old/test/browser_webconsole_bug_582201_duplicate_errors.js
devtools/client/webconsole/old/test/browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js
devtools/client/webconsole/old/test/browser_webconsole_bug_585237_line_limit.js
devtools/client/webconsole/old/test/browser_webconsole_bug_585956_console_trace.js
devtools/client/webconsole/old/test/browser_webconsole_bug_585991_autocomplete_keys.js
devtools/client/webconsole/old/test/browser_webconsole_bug_585991_autocomplete_popup.js
devtools/client/webconsole/old/test/browser_webconsole_bug_586388_select_all.js
devtools/client/webconsole/old/test/browser_webconsole_bug_587617_output_copy.js
devtools/client/webconsole/old/test/browser_webconsole_bug_588342_document_focus.js
devtools/client/webconsole/old/test/browser_webconsole_bug_588730_text_node_insertion.js
devtools/client/webconsole/old/test/browser_webconsole_bug_588967_input_expansion.js
devtools/client/webconsole/old/test/browser_webconsole_bug_589162_css_filter.js
devtools/client/webconsole/old/test/browser_webconsole_bug_592442_closing_brackets.js
devtools/client/webconsole/old/test/browser_webconsole_bug_593003_iframe_wrong_hud.js
devtools/client/webconsole/old/test/browser_webconsole_bug_594497_history_arrow_keys.js
devtools/client/webconsole/old/test/browser_webconsole_bug_595223_file_uri.js
devtools/client/webconsole/old/test/browser_webconsole_bug_595350_multiple_windows_and_tabs.js
devtools/client/webconsole/old/test/browser_webconsole_bug_595934_message_categories.js
devtools/client/webconsole/old/test/browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js
devtools/client/webconsole/old/test/browser_webconsole_bug_597136_external_script_errors.js
devtools/client/webconsole/old/test/browser_webconsole_bug_597136_network_requests_from_chrome.js
devtools/client/webconsole/old/test/browser_webconsole_bug_597460_filter_scroll.js
devtools/client/webconsole/old/test/browser_webconsole_bug_597756_reopen_closed_tab.js
devtools/client/webconsole/old/test/browser_webconsole_bug_599725_response_headers.js
devtools/client/webconsole/old/test/browser_webconsole_bug_600183_charset.js
devtools/client/webconsole/old/test/browser_webconsole_bug_601177_log_levels.js
devtools/client/webconsole/old/test/browser_webconsole_bug_601352_scroll.js
devtools/client/webconsole/old/test/browser_webconsole_bug_601667_filter_buttons.js
devtools/client/webconsole/old/test/browser_webconsole_bug_603750_websocket.js
devtools/client/webconsole/old/test/browser_webconsole_bug_611795.js
devtools/client/webconsole/old/test/browser_webconsole_bug_613013_console_api_iframe.js
devtools/client/webconsole/old/test/browser_webconsole_bug_613280_jsterm_copy.js
devtools/client/webconsole/old/test/browser_webconsole_bug_613642_maintain_scroll.js
devtools/client/webconsole/old/test/browser_webconsole_bug_613642_prune_scroll.js
devtools/client/webconsole/old/test/browser_webconsole_bug_614793_jsterm_scroll.js
devtools/client/webconsole/old/test/browser_webconsole_bug_618078_network_exceptions.js
devtools/client/webconsole/old/test/browser_webconsole_bug_621644_jsterm_dollar.js
devtools/client/webconsole/old/test/browser_webconsole_bug_622303_persistent_filters.js
devtools/client/webconsole/old/test/browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js
devtools/client/webconsole/old/test/browser_webconsole_bug_630733_response_redirect_headers.js
devtools/client/webconsole/old/test/browser_webconsole_bug_632275_getters_document_width.js
devtools/client/webconsole/old/test/browser_webconsole_bug_632347_iterators_generators.js
devtools/client/webconsole/old/test/browser_webconsole_bug_632817.js
devtools/client/webconsole/old/test/browser_webconsole_bug_642108_pruneTest.js
devtools/client/webconsole/old/test/browser_webconsole_bug_644419_log_limits.js
devtools/client/webconsole/old/test/browser_webconsole_bug_646025_console_file_location.js
devtools/client/webconsole/old/test/browser_webconsole_bug_651501_document_body_autocomplete.js
devtools/client/webconsole/old/test/browser_webconsole_bug_653531_highlighter_console_helper.js
devtools/client/webconsole/old/test/browser_webconsole_bug_658368_time_methods.js
devtools/client/webconsole/old/test/browser_webconsole_bug_659907_console_dir.js
devtools/client/webconsole/old/test/browser_webconsole_bug_660806_history_nav.js
devtools/client/webconsole/old/test/browser_webconsole_bug_664131_console_group.js
devtools/client/webconsole/old/test/browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js
devtools/client/webconsole/old/test/browser_webconsole_bug_704295.js
devtools/client/webconsole/old/test/browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js
devtools/client/webconsole/old/test/browser_webconsole_bug_737873_mixedcontent.js
devtools/client/webconsole/old/test/browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js
devtools/client/webconsole/old/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js
devtools/client/webconsole/old/test/browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js
devtools/client/webconsole/old/test/browser_webconsole_bug_764572_output_open_url.js
devtools/client/webconsole/old/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js
devtools/client/webconsole/old/test/browser_webconsole_bug_770099_violation.js
devtools/client/webconsole/old/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
devtools/client/webconsole/old/test/browser_webconsole_bug_804845_ctrl_key_nav.js
devtools/client/webconsole/old/test/browser_webconsole_bug_817834_add_edited_input_to_history.js
devtools/client/webconsole/old/test/browser_webconsole_bug_837351_securityerrors.js
devtools/client/webconsole/old/test/browser_webconsole_bug_922212_console_dirxml.js
devtools/client/webconsole/old/test/browser_webconsole_cached_autocomplete.js
devtools/client/webconsole/old/test/browser_webconsole_cd_iframe.js
devtools/client/webconsole/old/test/browser_webconsole_certificate_messages.js
devtools/client/webconsole/old/test/browser_webconsole_chrome.js
devtools/client/webconsole/old/test/browser_webconsole_clear_method.js
devtools/client/webconsole/old/test/browser_webconsole_clickable_urls.js
devtools/client/webconsole/old/test/browser_webconsole_closure_inspection.js
devtools/client/webconsole/old/test/browser_webconsole_column_numbers.js
devtools/client/webconsole/old/test/browser_webconsole_completion.js
devtools/client/webconsole/old/test/browser_webconsole_console_api_stackframe.js
devtools/client/webconsole/old/test/browser_webconsole_console_custom_styles.js
devtools/client/webconsole/old/test/browser_webconsole_console_extras.js
devtools/client/webconsole/old/test/browser_webconsole_console_logging_api.js
devtools/client/webconsole/old/test/browser_webconsole_console_logging_workers_api.js
devtools/client/webconsole/old/test/browser_webconsole_console_trace_async.js
devtools/client/webconsole/old/test/browser_webconsole_console_trace_duplicates.js
devtools/client/webconsole/old/test/browser_webconsole_context_menu_open_in_var_view.js
devtools/client/webconsole/old/test/browser_webconsole_context_menu_store_as_global.js
devtools/client/webconsole/old/test/browser_webconsole_count.js
devtools/client/webconsole/old/test/browser_webconsole_dont_navigate_on_doubleclick.js
devtools/client/webconsole/old/test/browser_webconsole_exception_stackframe.js
devtools/client/webconsole/old/test/browser_webconsole_execution_scope.js
devtools/client/webconsole/old/test/browser_webconsole_expandable_timestamps.js
devtools/client/webconsole/old/test/browser_webconsole_filter_buttons_contextmenu.js
devtools/client/webconsole/old/test/browser_webconsole_for_of.js
devtools/client/webconsole/old/test/browser_webconsole_history.js
devtools/client/webconsole/old/test/browser_webconsole_hpkp_invalid-headers.js
devtools/client/webconsole/old/test/browser_webconsole_hsts_invalid-headers.js
devtools/client/webconsole/old/test/browser_webconsole_input_field_focus_on_panel_select.js
devtools/client/webconsole/old/test/browser_webconsole_inspect-parsed-documents.js
devtools/client/webconsole/old/test/browser_webconsole_js_input_expansion.js
devtools/client/webconsole/old/test/browser_webconsole_jsterm.js
devtools/client/webconsole/old/test/browser_webconsole_live_filtering_of_message_types.js
devtools/client/webconsole/old/test/browser_webconsole_live_filtering_on_search_strings.js
devtools/client/webconsole/old/test/browser_webconsole_log_file_filter.js
devtools/client/webconsole/old/test/browser_webconsole_message_node_id.js
devtools/client/webconsole/old/test/browser_webconsole_multiline_input.js
devtools/client/webconsole/old/test/browser_webconsole_netlogging.js
devtools/client/webconsole/old/test/browser_webconsole_netlogging_basic.js
devtools/client/webconsole/old/test/browser_webconsole_netlogging_panel.js
devtools/client/webconsole/old/test/browser_webconsole_netlogging_reset_filter.js
devtools/client/webconsole/old/test/browser_webconsole_notifications.js
devtools/client/webconsole/old/test/browser_webconsole_open-links-without-callback.js
devtools/client/webconsole/old/test/browser_webconsole_output_01.js
devtools/client/webconsole/old/test/browser_webconsole_output_02.js
devtools/client/webconsole/old/test/browser_webconsole_output_03.js
devtools/client/webconsole/old/test/browser_webconsole_output_04.js
devtools/client/webconsole/old/test/browser_webconsole_output_05.js
devtools/client/webconsole/old/test/browser_webconsole_output_06.js
devtools/client/webconsole/old/test/browser_webconsole_output_copy_newlines.js
devtools/client/webconsole/old/test/browser_webconsole_output_dom_elements_01.js
devtools/client/webconsole/old/test/browser_webconsole_output_dom_elements_02.js
devtools/client/webconsole/old/test/browser_webconsole_output_dom_elements_03.js
devtools/client/webconsole/old/test/browser_webconsole_output_dom_elements_04.js
devtools/client/webconsole/old/test/browser_webconsole_output_dom_elements_05.js
devtools/client/webconsole/old/test/browser_webconsole_output_events.js
devtools/client/webconsole/old/test/browser_webconsole_output_order.js
devtools/client/webconsole/old/test/browser_webconsole_output_regexp.js
devtools/client/webconsole/old/test/browser_webconsole_output_table.js
devtools/client/webconsole/old/test/browser_webconsole_promise.js
devtools/client/webconsole/old/test/browser_webconsole_reflow.js
devtools/client/webconsole/old/test/browser_webconsole_scratchpad_panel_link.js
devtools/client/webconsole/old/test/browser_webconsole_script_errordoc_urls.js
devtools/client/webconsole/old/test/browser_webconsole_show_subresource_security_errors.js
devtools/client/webconsole/old/test/browser_webconsole_shows_reqs_in_netmonitor.js
devtools/client/webconsole/old/test/browser_webconsole_split.js
devtools/client/webconsole/old/test/browser_webconsole_split_escape_key.js
devtools/client/webconsole/old/test/browser_webconsole_split_focus.js
devtools/client/webconsole/old/test/browser_webconsole_split_persist.js
devtools/client/webconsole/old/test/browser_webconsole_start_netmon_first.js
devtools/client/webconsole/old/test/browser_webconsole_strict_mode_errors.js
devtools/client/webconsole/old/test/browser_webconsole_trackingprotection_errors.js
devtools/client/webconsole/old/test/browser_webconsole_view_source.js
devtools/client/webconsole/old/test/head.js
devtools/client/webconsole/old/test/test-autocomplete-in-stackframe.html
devtools/client/webconsole/old/test/test-bug-585956-console-trace.html
devtools/client/webconsole/old/test/test-bug-593003-iframe-wrong-hud-iframe.html
devtools/client/webconsole/old/test/test-bug-593003-iframe-wrong-hud.html
devtools/client/webconsole/old/test/test-bug-595934-canvas-css.html
devtools/client/webconsole/old/test/test-bug-595934-canvas-css.js
devtools/client/webconsole/old/test/test-bug-595934-css-loader.css
devtools/client/webconsole/old/test/test-bug-595934-css-loader.css^headers^
devtools/client/webconsole/old/test/test-bug-595934-css-loader.html
devtools/client/webconsole/old/test/test-bug-595934-css-parser.css
devtools/client/webconsole/old/test/test-bug-595934-css-parser.html
devtools/client/webconsole/old/test/test-bug-595934-empty-getelementbyid.html
devtools/client/webconsole/old/test/test-bug-595934-empty-getelementbyid.js
devtools/client/webconsole/old/test/test-bug-595934-html.html
devtools/client/webconsole/old/test/test-bug-595934-image.html
devtools/client/webconsole/old/test/test-bug-595934-image.jpg
devtools/client/webconsole/old/test/test-bug-595934-imagemap.html
devtools/client/webconsole/old/test/test-bug-595934-malformedxml-external.html
devtools/client/webconsole/old/test/test-bug-595934-malformedxml-external.xml
devtools/client/webconsole/old/test/test-bug-595934-malformedxml.xhtml
devtools/client/webconsole/old/test/test-bug-595934-svg.xhtml
devtools/client/webconsole/old/test/test-bug-595934-workers.html
devtools/client/webconsole/old/test/test-bug-595934-workers.js
devtools/client/webconsole/old/test/test-bug-597136-external-script-errors.html
devtools/client/webconsole/old/test/test-bug-597136-external-script-errors.js
devtools/client/webconsole/old/test/test-bug-597756-reopen-closed-tab.html
devtools/client/webconsole/old/test/test-bug-599725-response-headers.sjs
devtools/client/webconsole/old/test/test-bug-600183-charset.html
devtools/client/webconsole/old/test/test-bug-600183-charset.html^headers^
devtools/client/webconsole/old/test/test-bug-601177-log-levels.html
devtools/client/webconsole/old/test/test-bug-601177-log-levels.js
devtools/client/webconsole/old/test/test-bug-603750-websocket.html
devtools/client/webconsole/old/test/test-bug-603750-websocket.js
devtools/client/webconsole/old/test/test-bug-609872-cd-iframe-child.html
devtools/client/webconsole/old/test/test-bug-609872-cd-iframe-parent.html
devtools/client/webconsole/old/test/test-bug-613013-console-api-iframe.html
devtools/client/webconsole/old/test/test-bug-618078-network-exceptions.html
devtools/client/webconsole/old/test/test-bug-621644-jsterm-dollar.html
devtools/client/webconsole/old/test/test-bug-630733-response-redirect-headers.sjs
devtools/client/webconsole/old/test/test-bug-632275-getters.html
devtools/client/webconsole/old/test/test-bug-632347-iterators-generators.html
devtools/client/webconsole/old/test/test-bug-644419-log-limits.html
devtools/client/webconsole/old/test/test-bug-646025-console-file-location.html
devtools/client/webconsole/old/test/test-bug-658368-time-methods.html
devtools/client/webconsole/old/test/test-bug-737873-mixedcontent.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning-inner.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning0.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning1.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning2.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning3.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning4.html
devtools/client/webconsole/old/test/test-bug-752559-ineffective-iframe-sandbox-warning5.html
devtools/client/webconsole/old/test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html
devtools/client/webconsole/old/test/test-bug-762593-insecure-passwords-web-console-warning.html
devtools/client/webconsole/old/test/test-bug-766001-console-log.js
devtools/client/webconsole/old/test/test-bug-766001-js-console-links.html
devtools/client/webconsole/old/test/test-bug-766001-js-errors.js
devtools/client/webconsole/old/test/test-bug-782653-css-errors-1.css
devtools/client/webconsole/old/test/test-bug-782653-css-errors-2.css
devtools/client/webconsole/old/test/test-bug-782653-css-errors.html
devtools/client/webconsole/old/test/test-bug-837351-security-errors.html
devtools/client/webconsole/old/test/test-bug-859170-longstring-hang.html
devtools/client/webconsole/old/test/test-bug-869003-iframe.html
devtools/client/webconsole/old/test/test-bug-869003-top-window.html
devtools/client/webconsole/old/test/test-bug-952277-highlight-nodes-in-vview.html
devtools/client/webconsole/old/test/test-bug-989025-iframe-parent.html
devtools/client/webconsole/old/test/test-bug_1050691_click_function_to_source.html
devtools/client/webconsole/old/test/test-bug_1050691_click_function_to_source.js
devtools/client/webconsole/old/test/test-bug_923281_console_log_filter.html
devtools/client/webconsole/old/test/test-bug_923281_test1.js
devtools/client/webconsole/old/test/test-bug_923281_test2.js
devtools/client/webconsole/old/test/test-bug_939783_console_trace_duplicates.html
devtools/client/webconsole/old/test/test-certificate-messages.html
devtools/client/webconsole/old/test/test-closure-optimized-out.html
devtools/client/webconsole/old/test/test-closures.html
devtools/client/webconsole/old/test/test-console-api-stackframe.html
devtools/client/webconsole/old/test/test-console-assert.html
devtools/client/webconsole/old/test/test-console-clear.html
devtools/client/webconsole/old/test/test-console-column.html
devtools/client/webconsole/old/test/test-console-count-external-file.js
devtools/client/webconsole/old/test/test-console-count.html
devtools/client/webconsole/old/test/test-console-extras.html
devtools/client/webconsole/old/test/test-console-output-02.html
devtools/client/webconsole/old/test/test-console-output-03.html
devtools/client/webconsole/old/test/test-console-output-04.html
devtools/client/webconsole/old/test/test-console-output-dom-elements.html
devtools/client/webconsole/old/test/test-console-output-events.html
devtools/client/webconsole/old/test/test-console-replaced-api.html
devtools/client/webconsole/old/test/test-console-server-logging-array.sjs
devtools/client/webconsole/old/test/test-console-server-logging-backtrace.sjs
devtools/client/webconsole/old/test/test-console-server-logging.sjs
devtools/client/webconsole/old/test/test-console-table.html
devtools/client/webconsole/old/test/test-console-trace-async.html
devtools/client/webconsole/old/test/test-console-workers.html
devtools/client/webconsole/old/test/test-console.html
devtools/client/webconsole/old/test/test-consoleiframes.html
devtools/client/webconsole/old/test/test-cu-reporterror.js
devtools/client/webconsole/old/test/test-data.json
devtools/client/webconsole/old/test/test-data.json^headers^
devtools/client/webconsole/old/test/test-duplicate-error.html
devtools/client/webconsole/old/test/test-encoding-ISO-8859-1.html
devtools/client/webconsole/old/test/test-error.html
devtools/client/webconsole/old/test/test-eval-in-stackframe.html
devtools/client/webconsole/old/test/test-exception-stackframe.html
devtools/client/webconsole/old/test/test-file-location.js
devtools/client/webconsole/old/test/test-filter.html
devtools/client/webconsole/old/test/test-for-of.html
devtools/client/webconsole/old/test/test-iframe-762593-insecure-form-action.html
devtools/client/webconsole/old/test/test-iframe-762593-insecure-frame.html
devtools/client/webconsole/old/test/test-iframe1.html
devtools/client/webconsole/old/test/test-iframe2.html
devtools/client/webconsole/old/test/test-iframe3.html
devtools/client/webconsole/old/test/test-image.png
devtools/client/webconsole/old/test/test-mixedcontent-securityerrors.html
devtools/client/webconsole/old/test/test-mutation.html
devtools/client/webconsole/old/test/test-network-request.html
devtools/client/webconsole/old/test/test-network.html
devtools/client/webconsole/old/test/test-observe-http-ajax.html
devtools/client/webconsole/old/test/test-own-console.html
devtools/client/webconsole/old/test/test-property-provider.html
devtools/client/webconsole/old/test/test-repeated-messages.html
devtools/client/webconsole/old/test/test-result-format-as-string.html
devtools/client/webconsole/old/test/test-trackingprotection-securityerrors.html
devtools/client/webconsole/old/test/test-webconsole-error-observer.html
devtools/client/webconsole/old/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html
devtools/client/webconsole/old/test/test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
devtools/client/webconsole/old/test/test_bug1092055_shouldwarn.html
devtools/client/webconsole/old/test/test_bug1092055_shouldwarn.js
devtools/client/webconsole/old/test/test_bug1092055_shouldwarn.js^headers^
devtools/client/webconsole/old/test/test_bug_1010953_cspro.html
devtools/client/webconsole/old/test/test_bug_1010953_cspro.html^headers^
devtools/client/webconsole/old/test/test_bug_1247459_violation.html
devtools/client/webconsole/old/test/test_bug_770099_violation.html
devtools/client/webconsole/old/test/test_bug_770099_violation.html^headers^
devtools/client/webconsole/old/test/test_hpkp-invalid-headers.sjs
devtools/client/webconsole/old/test/test_hsts-invalid-headers.sjs
devtools/client/webconsole/old/test/testscript.js
devtools/client/webconsole/old/webconsole.js
devtools/client/webconsole/old/webconsole.xul
dom/base/crashtests/1118764.html
dom/tests/mochitest/webcomponents/test_shadow_element.html
layout/base/crashtests/1404789-1.html
layout/base/crashtests/1409088.html
layout/reftests/css-display/display-contents-shadow-dom-1.html
layout/reftests/forms/legend/shadow-dom.html
layout/style/crashtests/1415021.html
layout/style/crashtests/1415663.html
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -621,20 +621,28 @@ html|input.urlbar-input[textoverflow]:no
 
 /* For non-action items, hide the action text; for action items, hide the URL
    text. Don't show the separator for keyword results. */
 #PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-type-icon,
 #PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-site-icon,
 #PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-tags:not([empty]),
 #PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-separator:not([type=keyword]),
 #PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-url:not([actiontype]),
-#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-action[actiontype] {
+#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-action[actiontype],
+#PopupAutoCompleteRichResult > richlistbox > richlistitem[selected] > .ac-url[actiontype=remotetab],
+#PopupAutoCompleteRichResult > richlistbox > richlistitem:hover > .ac-url[actiontype=remotetab]
+{
   display: -moz-box;
 }
 
+#PopupAutoCompleteRichResult > richlistbox > richlistitem[selected] > .ac-action[actiontype=remotetab],
+#PopupAutoCompleteRichResult > richlistbox > richlistitem:hover > .ac-action[actiontype=remotetab] {
+  display: none;
+}
+
 /* Only show the "Search with" label on hover or selection. */
 #PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover) > .ac-action[actiontype=searchengine],
 #PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover) > .ac-separator[actiontype=searchengine] {
   display: none;
 }
 
 #PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-site-icon {
   margin-inline-start: 0;
--- a/browser/components/enterprisepolicies/tests/moz.build
+++ b/browser/components/enterprisepolicies/tests/moz.build
@@ -1,17 +1,14 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-with Files("**"):
-    BUG_COMPONENT = ("Firefox", "General")
-
 BROWSER_CHROME_MANIFESTS += [
     'browser/browser.ini',
     'browser/disable_app_update/browser.ini',
     'browser/disable_default_bookmarks/browser.ini',
     'browser/disable_developer_tools/browser.ini',
     'browser/disable_forget_button/browser.ini',
     'browser/disable_fxscreenshots/browser.ini',
 ]
--- a/browser/components/extensions/schemas/geckoProfiler.json
+++ b/browser/components/extensions/schemas/geckoProfiler.json
@@ -25,16 +25,17 @@
         "enum": [
           "java",
           "js",
           "leaf",
           "mainthreadio",
           "memory",
           "privacy",
           "restyle",
+          "screenshots",
           "stackwalk",
           "tasktracer",
           "threads",
           "trackopts"
         ]
       }
     ],
     "functions": [
--- a/browser/components/preferences/in-content/findInPage.js
+++ b/browser/components/preferences/in-content/findInPage.js
@@ -478,17 +478,17 @@ var gSearchResultsPane = {
       // building a string of concatenated translated strings out of it.
       let keywords = messages.map((msg, i) => {
         let [refId, refAttr] = refs[i];
         if (!msg) {
           console.error(`Missing search l10n id "${refId}"`);
           return null;
         }
         if (refAttr) {
-          let attr = msg.attrs.find(a => a.name === refAttr);
+          let attr = msg.attributes && msg.attributes.find(a => a.name === refAttr);
           if (!attr) {
             console.error(`Missing search l10n id "${refId}.${refAttr}"`);
             return null;
           }
           if (attr.value === "") {
             console.error(`Empty value added to search-l10n-ids "${refId}.${refAttr}"`);
           }
           return attr.value;
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -453,17 +453,17 @@ var gMainPane = {
     let archResource = Services.appinfo.is64Bit
       ? "aboutDialog.architecture.sixtyFourBit"
       : "aboutDialog.architecture.thirtyTwoBit";
     let arch = bundle.GetStringFromName(archResource);
     version += ` (${arch})`;
 
     document.l10n.setAttributes(
       document.getElementById("updateAppInfo"),
-      "update-application-info",
+      "update-application-version",
       { version }
     );
 
     // Show a release notes link if we have a URL.
     let relNotesLink = document.getElementById("releasenotes");
     let relNotesPrefType = Services.prefs.getPrefType("app.releaseNotesURL");
     if (relNotesPrefType != Services.prefs.PREF_INVALID) {
       let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
--- a/browser/components/preferences/in-content/main.xul
+++ b/browser/components/preferences/in-content/main.xul
@@ -437,17 +437,17 @@
 <!-- Update -->
 <groupbox id="updateApp" data-category="paneGeneral" hidden="true">
   <caption class="search-header" hidden="true"><label data-l10n-id="update-application-title"/></caption>
 
   <label data-l10n-id="update-application-description"/>
   <hbox align="center">
     <vbox flex="1">
       <description id="updateAppInfo">
-        <html:a id="releasenotes" class="learnMore text-link" hidden="true"/>
+        <html:a id="releasenotes" data-l10n-name="learn-more" class="learnMore text-link" hidden="true"/>
       </description>
       <description id="distribution" class="text-blurb" hidden="true"/>
       <description id="distributionId" class="text-blurb" hidden="true"/>
     </vbox>
 #ifdef MOZ_UPDATER
     <spacer flex="1"/>
     <!-- Please don't remove the wrapping hbox/vbox/box for these elements. It's used to properly compute the search tooltip position. -->
     <vbox>
@@ -610,18 +610,18 @@
           <menuitem label="4" value="4"/>
           <menuitem label="5" value="5"/>
           <menuitem label="6" value="6"/>
           <menuitem label="7" value="7"/>
         </menupopup>
       </menulist>
     </hbox>
     <description id="contentProcessCountEnabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-enabled-desc"/>
-    <description id="contentProcessCountDisabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-disabled-desc">
-      <html:a class="text-link" href="https://wiki.mozilla.org/Electrolysis"/>
+    <description id="contentProcessCountDisabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-blocked-desc">
+      <html:a class="text-link" data-l10n-name="learn-more" href="https://wiki.mozilla.org/Electrolysis"/>
     </description>
   </vbox>
 </groupbox>
 
 <hbox id="browsingCategory"
       class="subcategory"
       hidden="true"
       data-category="paneGeneral">
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -276,18 +276,18 @@
 </groupbox>
 
 <!-- Tracking -->
 <groupbox id="trackingGroup" data-category="panePrivacy" hidden="true">
   <caption><label data-l10n-id="tracking-header"/></caption>
   <vbox>
     <hbox align="start">
       <vbox flex="1">
-        <description data-l10n-id="tracking-description">
-          <a id="trackingProtectionLearnMore" target="_blank" class="learnMore text-link"/>
+        <description data-l10n-id="tracking-desc">
+          <a id="trackingProtectionLearnMore" data-l10n-name="learn-more" target="_blank" class="learnMore text-link"/>
         </description>
       </vbox>
       <spacer flex="1"/>
     </hbox>
     <hbox>
       <vbox id="trackingProtectionBox" flex="1" hidden="true">
         <vbox>
           <hbox id="trackingProtectionExtensionContentLabel" align="center" hidden="true">
--- a/browser/components/preferences/in-content/searchResults.xul
+++ b/browser/components/preferences/in-content/searchResults.xul
@@ -10,19 +10,19 @@
   <label class="header-name" flex="1" data-l10n-id="search-results-header" />
 </hbox>
 
 <groupbox id="no-results-message"
           data-hidden-from-search="true"
           data-category="paneSearchResults"
           hidden="true">
   <vbox class="no-results-container">
-    <label id="sorry-message" data-l10n-id="search-results-sorry-message">
-      <html:span id="sorry-message-query"/>
+    <label id="sorry-message" data-l10n-id="search-results-empty-message">
+      <html:span data-l10n-name="query" id="sorry-message-query"/>
     </label>
-    <label id="need-help" data-l10n-id="search-results-need-help">
-      <a class="text-link" target="_blank"></a>
+    <label id="need-help" data-l10n-id="search-results-help-link">
+      <a class="text-link" data-l10n-name="url" target="_blank"></a>
     </label>
   </vbox>
   <vbox class="no-results-container" align="center">
     <image></image>
   </vbox>
 </groupbox>
--- a/browser/components/preferences/in-content/tests/browser_fluent.js
+++ b/browser/components/preferences/in-content/tests/browser_fluent.js
@@ -34,15 +34,15 @@ add_task(async function() {
     ["performance-default-content-process-count", { num: defaultProcessCount }]
   ]);
 
   let elem = doc.querySelector(
     `#contentProcessCount > menupopup > menuitem[value="${defaultProcessCount}"]`);
 
   Assert.deepEqual(msg, {
     value: null,
-    attrs: [
+    attributes: [
       {name: "label", value: elem.getAttribute("label")}
     ]
   });
 
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
--- a/browser/extensions/webcompat-reporter/test/browser/browser_button_state.js
+++ b/browser/extensions/webcompat-reporter/test/browser/browser_button_state.js
@@ -1,15 +1,17 @@
 const REPORTABLE_PAGE = "http://example.com/";
 const REPORTABLE_PAGE2 = "https://example.com/";
 const NONREPORTABLE_PAGE = "about:mozilla";
 
 /* Test that the Report Site Issue button is enabled for http and https tabs,
    on page load, or TabSelect, and disabled for everything else. */
 add_task(async function test_button_state_disabled() {
+  await SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENABLED, true]]});
+
   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE);
   await openPageActions();
   is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on tab load");
 
   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, NONREPORTABLE_PAGE);
   await openPageActions();
   is(isButtonDisabled(), true, "Check that button is disabled for non-reportable schemes on tab load");
 
@@ -20,16 +22,18 @@ add_task(async function test_button_stat
   BrowserTestUtils.removeTab(tab1);
   BrowserTestUtils.removeTab(tab2);
   BrowserTestUtils.removeTab(tab3);
 });
 
 /* Test that the button is enabled or disabled when we expected it to be, when
    pinned to the URL bar. */
 add_task(async function test_button_state_in_urlbar() {
+  await SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENABLED, true]]});
+
   pinToURLBar();
   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE);
   is(isURLButtonEnabled(), true, "Check that button (in urlbar) is enabled for reportable schemes on tab load");
 
   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, NONREPORTABLE_PAGE);
   is(isURLButtonEnabled(), false, "Check that button (in urlbar) is hidden for non-reportable schemes on tab load");
 
   let tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE2);
--- a/browser/extensions/webcompat-reporter/test/browser/browser_report_site_issue.js
+++ b/browser/extensions/webcompat-reporter/test/browser/browser_report_site_issue.js
@@ -1,15 +1,18 @@
 /* Test that clicking on the Report Site Issue button opens a new tab
    and sends a postMessaged blob to it. */
 add_task(async function test_opened_page() {
   requestLongerTimeout(2);
 
   // ./head.js sets the value for PREF_WC_REPORTER_ENDPOINT
-  await SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENDPOINT, NEW_ISSUE_PAGE]]});
+  await SpecialPowers.pushPrefEnv({set: [
+    [PREF_WC_REPORTER_ENABLED, true],
+    [PREF_WC_REPORTER_ENDPOINT, NEW_ISSUE_PAGE]
+  ]});
 
   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);
 
   await openPageActions();
   let webcompatButton = document.getElementById(WC_PAGE_ACTION_ID);
   ok(webcompatButton, "Report Site Issue button exists.");
 
   let screenshotPromise;
--- a/browser/locales/en-US/browser/preferences/preferences.ftl
+++ b/browser/locales/en-US/browser/preferences/preferences.ftl
@@ -75,24 +75,24 @@ should-restart-title = Restart { -brand-
 should-restart-ok = Restart { -brand-short-name } now
 cancel-no-restart-button = Cancel
 restart-later = Restart Later
 
 ## Preferences UI Search Results
 
 search-results-header = Search Results
 
-# `<span></span>` will be replaced by the search term.
-search-results-sorry-message =
+# `<span data-l10n-name="query"></span>` will be replaced by the search term.
+search-results-empty-message =
     { PLATFORM() ->
-        [windows] Sorry! There are no results in Options for “<span></span>”.
-       *[other] Sorry! There are no results in Preferences for “<span></span>”.
+        [windows] Sorry! There are no results in Options for “<span data-l10n-name="query"></span>”.
+       *[other] Sorry! There are no results in Preferences for “<span data-l10n-name="query"></span>”.
     }
 
-search-results-need-help = Need help? Visit <a>{ -brand-short-name } Support</a>
+search-results-help-link = Need help? Visit <a data-l10n-name="url">{ -brand-short-name } Support</a>
 
 ## General Section
 
 startup-header = Startup
 
 # { -brand-short-name } will be 'Firefox Developer Edition',
 # since this setting is only exposed in Firefox Developer Edition
 separate-profile-mode =
@@ -262,17 +262,17 @@ play-drm-content =
     .accesskey = P
 
 play-drm-content-learn-more = Learn more
 
 update-application-title = { -brand-short-name } Updates
 
 update-application-description = Keep { -brand-short-name } up to date for the best performance, stability, and security.
 
-update-application-info = Version { $version } <a>What's new</a>
+update-application-version = Version { $version } <a data-l10n-name="learn-more">What’s new</a>
 
 update-history =
     .label = Show Update History…
     .accesskey = p
 
 update-application-allow-description = Allow { -brand-short-name } to
 
 update-application-auto =
@@ -310,17 +310,17 @@ performance-settings-learn-more = Learn 
 performance-allow-hw-accel =
     .label = Use hardware acceleration when available
     .accesskey = r
 
 performance-limit-content-process-option = Content process limit
     .accesskey = l
 
 performance-limit-content-process-enabled-desc = Additional content processes can improve performance when using multiple tabs, but will also use more memory.
-performance-limit-content-process-disabled-desc = Modifying the number of content processes is only possible with multiprocess { -brand-short-name }. <a>Learn how to check if multiprocess is enabled</a>
+performance-limit-content-process-blocked-desc = Modifying the number of content processes is only possible with multiprocess { -brand-short-name }. <a data-l10n-name="learn-more">Learn how to check if multiprocess is enabled</a>
 
 # Variables:
 #   $num - default value of the `dom.ipc.processCount` pref.
 performance-default-content-process-count =
     .label = { $num } (default)
 
 ## General Section - Browsing
 
@@ -693,17 +693,17 @@ addressbar-locbar-openpage-option =
     .accesskey = O
 
 addressbar-suggestions-settings = Change preferences for search engine suggestions
 
 ## Privacy Section - Tracking
 
 tracking-header = Tracking Protection
 
-tracking-description = Tracking Protection blocks online trackers that collect your browsing data across multiple websites. <a>Learn more about Tracking Protection and your privacy</a>
+tracking-desc = Tracking Protection blocks online trackers that collect your browsing data across multiple websites. <a data-l10n-name="learn-more">Learn more about Tracking Protection and your privacy</a>
 
 tracking-mode-label = Use Tracking Protection to block known trackers
 
 tracking-mode-always =
     .label = Always
     .accesskey = y
 tracking-mode-private =
     .label = Only in private windows
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -94,24 +94,18 @@ Tools.inspector = {
   build: function(iframeWindow, toolbox) {
     return new InspectorPanel(iframeWindow, toolbox);
   }
 };
 Tools.webConsole = {
   id: "webconsole",
   accesskey: l10n("webConsoleCmd.accesskey"),
   ordinal: 2,
-  oldWebConsoleURL: "chrome://devtools/content/webconsole/old/webconsole.xul",
-  newWebConsoleURL: "chrome://devtools/content/webconsole/webconsole.html",
-  get browserConsoleURL() {
-    if (Services.prefs.getBoolPref("devtools.browserconsole.new-frontend-enabled")) {
-      return "chrome://devtools/content/webconsole/browserconsole.xul";
-    }
-    return Tools.webConsole.oldWebConsoleURL;
-  },
+  url: "chrome://devtools/content/webconsole/webconsole.html",
+  browserConsoleURL: "chrome://devtools/content/webconsole/browserconsole.xul",
   icon: "chrome://devtools/skin/images/tool-webconsole.svg",
   label: l10n("ToolboxTabWebconsole.label"),
   menuLabel: l10n("MenuWebconsole.label"),
   panelLabel: l10n("ToolboxWebConsole.panelLabel"),
   get tooltip() {
     return l10n("ToolboxWebconsole.tooltip2",
     (osString == "Darwin" ? "Cmd+Opt+" : "Ctrl+Shift+") +
     l10n("webconsole.commandkey"));
@@ -131,29 +125,16 @@ Tools.webConsole = {
 
   isTargetSupported: function() {
     return true;
   },
   build: function(iframeWindow, toolbox) {
     return new WebConsolePanel(iframeWindow, toolbox);
   }
 };
-function switchWebconsole() {
-  if (Services.prefs.getBoolPref("devtools.webconsole.new-frontend-enabled")) {
-    Tools.webConsole.url = Tools.webConsole.newWebConsoleURL;
-  } else {
-    Tools.webConsole.url = Tools.webConsole.oldWebConsoleURL;
-  }
-}
-switchWebconsole();
-
-Services.prefs.addObserver(
-  "devtools.webconsole.new-frontend-enabled",
-  { observe: switchWebconsole }
-);
 
 Tools.jsdebugger = {
   id: "jsdebugger",
   accesskey: l10n("debuggerMenu.accesskey"),
   ordinal: 3,
   icon: "chrome://devtools/skin/images/tool-debugger.svg",
   url: "chrome://devtools/content/debugger/debugger.xul",
   label: l10n("ToolboxDebugger.label"),
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -100,16 +100,17 @@ skip-if = e10s # Bug 1069044 - destroyIn
 [browser_toolbox_selectionchanged_event.js]
 [browser_toolbox_sidebar.js]
 [browser_toolbox_sidebar_events.js]
 [browser_toolbox_sidebar_existing_tabs.js]
 [browser_toolbox_sidebar_overflow_menu.js]
 [browser_toolbox_split_console.js]
 [browser_toolbox_target.js]
 [browser_toolbox_tabsswitch_shortcuts.js]
+[browser_toolbox_telemetry_close.js]
 [browser_toolbox_textbox_context_menu.js]
 [browser_toolbox_theme.js]
 [browser_toolbox_theme_registration.js]
 [browser_toolbox_toggle.js]
 [browser_toolbox_tool_ready.js]
 [browser_toolbox_tool_remote_reopen.js]
 [browser_toolbox_toolbar_overflow.js]
 [browser_toolbox_tools_per_toolbox_registration.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/framework/test/browser_toolbox_telemetry_close.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Toolbox } = require("devtools/client/framework/toolbox");
+
+const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js";
+const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
+const { SIDE, BOTTOM } = Toolbox.HostType;
+const DATA = [
+  {
+    timestamp: null,
+    category: "devtools.main",
+    method: "close",
+    object: "tools",
+    value: null,
+    extra: {
+      host: "side",
+      width: "1440"
+    }
+  },
+  {
+    timestamp: null,
+    category: "devtools.main",
+    method: "close",
+    object: "tools",
+    value: null,
+    extra: {
+      host: "bottom",
+      width: "1440"
+    }
+  }
+];
+
+add_task(async function() {
+  // Let's reset the counts.
+  Services.telemetry.clearEvents();
+
+  // Ensure no events have been logged
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  ok(!snapshot.parent, "No events have been logged for the main process");
+
+  await openAndCloseToolbox("webconsole", SIDE);
+  await openAndCloseToolbox("webconsole", BOTTOM);
+
+  checkResults();
+});
+
+async function openAndCloseToolbox(toolId, host) {
+  const tab = await addTab(URL);
+  const target = TargetFactory.forTab(tab);
+  const toolbox = await gDevTools.showToolbox(target, toolId);
+
+  await toolbox.switchHost(host);
+  await toolbox.destroy();
+}
+
+function checkResults() {
+  const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
+  const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
+                                                 event[2] === "close" &&
+                                                 event[3] === "tools" &&
+                                                 event[4] === null
+  );
+
+  for (let i in DATA) {
+    const [ timestamp, category, method, object, value, extra ] = events[i];
+    const expected = DATA[i];
+
+    // ignore timestamp
+    ok(timestamp > 0, "timestamp is greater than 0");
+    is(category, expected.category, "category is correct");
+    is(method, expected.method, "method is correct");
+    is(object, expected.object, "object is correct");
+    is(value, expected.value, "value is correct");
+
+    is(extra.host, expected.extra.host, "host is correct");
+    ok(extra.width > 0, "width is greater than 0");
+  }
+}
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -362,21 +362,16 @@ OptionsPanel.prototype = {
     let isNightly = AppConstants.NIGHTLY_BUILD;
     if (!isNightly) {
       return;
     }
 
     // Labels for these new buttons are nightly only and mostly intended for working on
     // devtools.
     let prefDefinitions = [{
-      pref: "devtools.webconsole.new-frontend-enabled",
-      label: L10N.getStr("toolbox.options.enableNewConsole.label"),
-      id: "devtools-new-webconsole",
-      parentId: "webconsole-options"
-    }, {
       pref: "devtools.debugger.new-debugger-frontend",
       label: L10N.getStr("toolbox.options.enableNewDebugger.label"),
       id: "devtools-new-debugger",
       parentId: "debugger-options"
     }, {
       pref: "devtools.performance.new-panel-enabled",
       label: "Enable new performance recorder (then re-open DevTools)",
       id: "devtools-new-performance",
--- a/devtools/client/framework/toolbox-process-window.js
+++ b/devtools/client/framework/toolbox-process-window.js
@@ -97,17 +97,16 @@ function setPrefDefaults() {
   Services.prefs.setBoolPref("devtools.performance.ui.show-platform-data", true);
   Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
   Services.prefs.setBoolPref("browser.dom.window.dump.enabled", true);
   Services.prefs.setBoolPref("devtools.command-button-noautohide.enabled", true);
   Services.prefs.setBoolPref("devtools.scratchpad.enabled", true);
   // Bug 1225160 - Using source maps with browser debugging can lead to a crash
   Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
   Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
-  Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
   Services.prefs.setBoolPref("devtools.preference.new-panel-enabled", false);
   Services.prefs.setBoolPref("layout.css.emulate-moz-box-with-flex", false);
 }
 window.addEventListener("load", async function() {
   let cmdClose = document.getElementById("toolbox-cmd-close");
   cmdClose.addEventListener("command", onCloseCommand);
   setPrefDefaults();
   // Reveal status message if connecting is slow or if an error occurs.
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -2755,16 +2755,20 @@ Toolbox.prototype = {
         button.teardown();
       }
     });
 
     // We need to grab a reference to win before this._host is destroyed.
     let win = this.win;
 
     this._telemetry.toolClosed("toolbox");
+    this._telemetry.recordEvent("devtools.main", "close", "tools", null, {
+      host: this._getTelemetryHostString(),
+      width: Math.ceil(win.outerWidth / 50) * 50
+    });
     this._telemetry.destroy();
 
     // Finish all outstanding tasks (which means finish destroying panels and
     // then destroying the host, successfully or not) before destroying the
     // target.
     deferred.resolve(settleAll(outstanding)
         .catch(console.error)
         .then(() => {
--- a/devtools/client/inspector/markup/test/doc_markup_anonymous.html
+++ b/devtools/client/inspector/markup/test/doc_markup_anonymous.html
@@ -20,15 +20,13 @@
 
   <div id="shadow">light dom</div>
 
   <div id="native"><video controls></video></div>
 
   <script>
   "use strict";
   var host = document.querySelector("#shadow");
-  if (host.createShadowRoot) {
-    var root = host.createShadowRoot();
-    root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
-  }
+  var root = host.attachShadow({ mode: "open" });
+  root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
   </script>
 </body>
 </html>
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -5,17 +5,16 @@
 devtools.jar:
 %   content devtools %content/
     content/shared/vendor/d3.js (shared/vendor/d3.js)
     content/shared/vendor/dagre-d3.js (shared/vendor/dagre-d3.js)
     content/shared/widgets/widgets.css (shared/widgets/widgets.css)
     content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
     content/webconsole/webconsole.html (webconsole/webconsole.html)
     content/webconsole/browserconsole.xul (webconsole/browserconsole.xul)
-*   content/webconsole/old/webconsole.xul (webconsole/old/webconsole.xul)
 *   content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul)
     content/scratchpad/scratchpad.js (scratchpad/scratchpad.js)
     content/shared/splitview.css (shared/splitview.css)
     content/shared/theme-switching.js (shared/theme-switching.js)
 *   content/styleeditor/styleeditor.xul (styleeditor/styleeditor.xul)
 *   content/storage/storage.xul (storage/storage.xul)
     content/inspector/inspector.js (inspector/inspector.js)
     content/inspector/markup/markup.xhtml (inspector/markup/markup.xhtml)
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -202,12 +202,8 @@ toolbox.sourceMapFailure=Source map erro
 # The text of the error: %1$S
 # The URL of the source: %2$S
 toolbox.sourceMapSourceFailure=Error while fetching an original source: %1$S\nSource URL: %2$S
 
 # LOCALIZATION NOTE (toolbox.options.enableNewDebugger.label): Label of the options panel
 # checkbox to enable the new debugger frontend. Displayed only in Nightly and local
 # builds.
 toolbox.options.enableNewDebugger.label=Enable new debugger frontend
-
-# LOCALIZATION NOTE (toolbox.options.enableNewConsole.label): Label of the options panel
-# checkbox to enable the new console frontend. Displayed only in Nightly and local builds.
-toolbox.options.enableNewConsole.label=Enable new console frontend
deleted file mode 100644
--- a/devtools/client/locales/en-US/webConsole.dtd
+++ /dev/null
@@ -1,96 +0,0 @@
-<!-- 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/. -->
-<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
-  - keep it in English, or another language commonly spoken among web developers.
-  - You want to make that choice consistent across the developer tools.
-  - A good criteria is the language in which you'd find the best
-  - documentation on web development on the web. -->
-<!ENTITY window.title "Web Console">
-<!-- LOCALIZATION NOTE (openURL.label): You can see this string in the Web
-   - Console context menu. -->
-<!ENTITY openURL.label     "Open URL in New Tab">
-<!ENTITY openURL.accesskey "T">
-<!-- LOCALIZATION NOTE (btnPageNet.label): This string is used for the menu
-  -  button that allows users to toggle the network logging output.
-  -  This string and the following strings toggle various kinds of output
-  -  filters. -->
-<!ENTITY btnPageNet.label   "Net">
-<!ENTITY btnPageNet.tooltip "Log network access">
-<!ENTITY btnPageNet.accesskey "N">
-<!-- LOCALIZATION NOTE (btnPageNet.accesskeyMacOSX): This string is used as
-  -  access key for the menu button that allows users to toggle the network
-  -  logging output. On MacOSX accesskeys are available with Ctrl-*. Please make
-  -  sure you do not use the following letters: A, E, N and P. These are used
-  -  for editing commands in text inputs. -->
-<!ENTITY btnPageNet.accesskeyMacOSX "t">
-<!ENTITY btnPageCSS.label   "CSS">
-<!ENTITY btnPageCSS.tooltip2 "Log CSS errors and warnings">
-<!ENTITY btnPageCSS.accesskey "C">
-<!ENTITY btnPageJS.label    "JS">
-<!ENTITY btnPageJS.tooltip  "Log JavaScript exceptions">
-<!ENTITY btnPageJS.accesskey  "J">
-<!ENTITY btnPageSecurity.label "Security">
-<!ENTITY btnPageSecurity.tooltip "Log security errors and warnings">
-<!ENTITY btnPageSecurity.accesskey "u">
-
-<!-- LOCALIZATION NOTE (btnPageLogging): This is used as the text of the
-  -  the toolbar. It shows or hides messages that the web developer inserted on
-  -  the page for debugging purposes, using calls such console.log() and
-  -  console.error(). -->
-<!ENTITY btnPageLogging.label   "Logging">
-<!ENTITY btnPageLogging.tooltip "Log messages sent to the window.console object">
-<!ENTITY btnPageLogging.accesskey3 "L">
-<!ENTITY btnConsoleErrors       "Errors">
-<!ENTITY btnConsoleInfo         "Info">
-<!ENTITY btnConsoleWarnings     "Warnings">
-<!ENTITY btnConsoleLog          "Log">
-<!ENTITY btnConsoleXhr          "XHR">
-<!ENTITY btnConsoleReflows      "Reflows">
-
-<!-- LOCALIZATION NOTE (btnServerLogging): This is used as the text of the
-  -  the toolbar. It shows or hides messages that the web developer inserted on
-  -  the page for debugging purposes, using calls on the HTTP server. -->
-<!ENTITY btnServerLogging.label       "Server">
-<!ENTITY btnServerLogging.tooltip     "Log messages received from a web server">
-<!ENTITY btnServerLogging.accesskey   "S">
-<!ENTITY btnServerErrors              "Errors">
-<!ENTITY btnServerInfo                "Info">
-<!ENTITY btnServerWarnings            "Warnings">
-<!ENTITY btnServerLog                 "Log">
-
-<!-- LOCALIZATION NODE (btnConsoleSharedWorkers) the term "Shared Workers"
-  -  should not be translated. -->
-<!ENTITY btnConsoleSharedWorkers "Shared Workers">
-<!-- LOCALIZATION NODE (btnConsoleServiceWorkers) the term "Service Workers"
-  -  should not be translated. -->
-<!ENTITY btnConsoleServiceWorkers "Service Workers">
-<!-- LOCALIZATION NODE (btnConsoleWindowlessWorkers) the term "Workers"
-  -  should not be translated. -->
-<!ENTITY btnConsoleWindowlessWorkers "Add-on or Chrome Workers">
-
-<!ENTITY filterOutput.placeholder "Filter output">
-<!ENTITY btnClear.label        "Clear">
-<!ENTITY btnClear.tooltip      "Clear the Web Console output">
-<!ENTITY btnClear.accesskey    "r">
-
-<!ENTITY fullZoomEnlargeCmd.commandkey  "+">
-<!ENTITY fullZoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
-<!ENTITY fullZoomEnlargeCmd.commandkey3 "">
-
-<!ENTITY fullZoomReduceCmd.commandkey   "-">
-<!ENTITY fullZoomReduceCmd.commandkey2  "">
-
-<!ENTITY fullZoomResetCmd.commandkey    "0">
-<!ENTITY fullZoomResetCmd.commandkey2   "">
-
-<!ENTITY copyURLCmd.label     "Copy Link Location">
-<!ENTITY copyURLCmd.accesskey "a">
-
-<!ENTITY closeCmd.key         "W">
-<!ENTITY findCmd.key          "F">
-<!ENTITY clearOutputCtrl.key  "L">
-<!ENTITY openInVarViewCmd.label "Open in Variables View">
-<!ENTITY openInVarViewCmd.accesskey "V">
-<!ENTITY storeAsGlobalVar.label "Store as global variable">
-<!ENTITY storeAsGlobalVar.accesskey "S">
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -9,67 +9,37 @@
 # documentation on web development on the web.
 # LOCALIZATION NOTE (browserConsole.title): shown as the
 # title when opening the browser console popup
 browserConsole.title=Browser Console
 # LOCALIZATION NOTE (timestampFormat): %1$02S = hours (24-hour clock),
 # %2$02S = minutes, %3$02S = seconds, %4$03S = milliseconds.
 timestampFormat=%02S:%02S:%02S.%03S
 helperFuncUnsupportedTypeError=Can’t call pprint on this type of object.
-# LOCALIZATION NOTE (NetworkPanel.durationMS): this string is used to
-# show the duration between two network events (e.g request and response
-# header or response header and response body). Parameters: %S is the duration.
-NetworkPanel.durationMS=%Sms
 
 ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
 
 # LOCALIZATION NOTE (webConsoleXhrIndicator): the indicator displayed before
 # a URL in the Web Console that was requested using an XMLHttpRequest.
-# Should probably be the same as &btnConsoleXhr; in webConsole.dtd
 webConsoleXhrIndicator=XHR
 
-# LOCALIZATION NOTE (webConsoleMixedContentWarning): the message displayed
-# after a URL in the Web Console that has been flagged for Mixed Content (i.e.
-# http content in an https page).
-webConsoleMixedContentWarning=Mixed Content
-
 # LOCALIZATION NOTE (webConsoleMoreInfoLabel): the more info tag displayed
 # after security related web console messages.
 webConsoleMoreInfoLabel=Learn More
 
-# LOCALIZATION NOTE (scratchpad.linkText): the text used in the right hand
-# side of the Web Console command line when JavaScript is being entered, to
-# indicate how to jump into scratchpad mode.
-scratchpad.linkText=Shift+RETURN - Open in Scratchpad
-
-# LOCALIZATION NOTE (reflow.*): the console displays reflow activity.
-# We can get 2 kind of lines: with JS link or without JS link. It looks like
-# that:
-# reflow: 12ms
-# reflow: 12ms function foobar, file.js line 42
-# The 2nd line, from "function" to the end of the line, is a link to the
-# JavaScript debugger.
-reflow.messageWithNoLink=reflow: %Sms
-reflow.messageWithLink=reflow: %Sms\u0020
-reflow.messageLinkText=function %1$S, %2$S line %3$S
-
 # LOCALIZATION NOTE (stacktrace.anonymousFunction): this string is used to
 # display JavaScript functions that have no given name - they are said to be
 # anonymous. Test console.trace() in the webconsole.
 stacktrace.anonymousFunction=<anonymous>
 
 # LOCALIZATION NOTE (stacktrace.asyncStack): this string is used to
 # indicate that a given stack frame has an async parent.
 # %S is the "Async Cause" of the frame.
 stacktrace.asyncStack=(Async: %S)
 
-# LOCALIZATION NOTE (timerStarted): this string is used to display the result
-# of the console.time() call. Parameters: %S is the name of the timer.
-timerStarted=%S: timer started
-
 # LOCALIZATION NOTE (timeEnd): this string is used to display the result of
 # the console.timeEnd() call. Parameters: %1$S is the name of the timer, %2$S
 # is the number of milliseconds.
 timeEnd=%1$S: %2$Sms
 
 # LOCALIZATION NOTE (consoleCleared): this string is displayed when receiving a
 # call to console.clear() to let the user know the previous messages of the
 # console have been removed programmatically.
@@ -87,29 +57,16 @@ noGroupLabel=<no group label>
 # string containing anchor doesn't matches to any property in the content.
 Autocomplete.blank=  <- no result
 
 maxTimersExceeded=The maximum allowed number of timers in this page was exceeded.
 timerAlreadyExists=Timer “%S” already exists.
 timerDoesntExist=Timer “%S” doesn’t exist.
 timerJSError=Failed to process the timer name.
 
-# LOCALIZATION NOTE (maxCountersExceeded): Error message shown when the maximum
-# number of console.count()-counters was exceeded.
-maxCountersExceeded=The maximum allowed number of counters in this page was exceeded.
-
-# LOCALIZATION NOTE (longStringEllipsis): the string displayed after a long
-# string. This string is clickable such that the rest of the string is
-# retrieved from the server.
-longStringEllipsis=[…]
-
-# LOCALIZATION NOTE (longStringTooLong): the string displayed after the user
-# tries to expand a long string.
-longStringTooLong=The string you are trying to view is too long to be displayed by the Web Console.
-
 # LOCALIZATION NOTE (connectionTimeout): message displayed when the Remote Web
 # Console fails to connect to the server due to a timeout.
 connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Web Console to try again.
 
 # LOCALIZATION NOTE (propertiesFilterPlaceholder): this is the text that
 # appears in the filter text box for the properties view container.
 propertiesFilterPlaceholder=Filter properties
 
@@ -149,39 +106,23 @@ selfxss.okstring=allow pasting
 # you hover the arrow for expanding/collapsing the message details. For
 # console.error() and other messages we show the stacktrace.
 messageToggleDetails=Show/hide message details.
 
 # LOCALIZATION NOTE (groupToggle): the text that is displayed when
 # you hover the arrow for expanding/collapsing the messages of a group.
 groupToggle=Show/hide group.
 
-# LOCALIZATION NOTE (emptySlotLabel): the text is displayed when an Array
-# with empty slots is printed to the console.
-# This is a semi-colon list of plural forms.
-# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-# #1 number of empty slots
-# example: 1 empty slot
-# example: 5 empty slots
-emptySlotLabel=#1 empty slot;#1 empty slots
-
 # LOCALIZATION NOTE (table.index, table.iterationIndex, table.key, table.value):
 # the column header displayed in the console table widget.
 table.index=(index)
 table.iterationIndex=(iteration index)
 table.key=Key
 table.value=Values
 
-# LOCALIZATION NOTE (severity.error, severity.warn, severity.info, severity.log):
-# tooltip for icons next to console output
-severity.error=Error
-severity.warn=Warning
-severity.info=Info
-severity.log=Log
-
 # LOCALIZATION NOTE (level.error, level.warn, level.info, level.log, level.debug):
 # tooltip for icons next to console output
 level.error=Error
 level.warn=Warning
 level.info=Info
 level.log=Log
 level.debug=Debug
 
@@ -211,22 +152,16 @@ webconsole.menu.openURL.label=Open URL i
 webconsole.menu.openURL.accesskey=T
 
 # LOCALIZATION NOTE (webconsole.menu.openInNetworkPanel.label)
 # Label used for a context-menu item displayed for network message logs. Clicking on it
 # opens the network message in the Network panel
 webconsole.menu.openInNetworkPanel.label=Open in Network Panel
 webconsole.menu.openInNetworkPanel.accesskey=N
 
-# LOCALIZATION NOTE (webconsole.menu.openInVarView.label)
-# Label used for a context-menu item displayed for object/variable logs. Clicking on it
-# opens the webconsole variable view for the logged variable.
-webconsole.menu.openInVarView.label=Open in Variables View
-webconsole.menu.openInVarView.accesskey=V
-
 # LOCALIZATION NOTE (webconsole.menu.storeAsGlobalVar.label)
 # Label used for a context-menu item displayed for object/variable logs. Clicking on it
 # creates a new global variable pointing to the logged variable.
 webconsole.menu.storeAsGlobalVar.label=Store as global variable
 webconsole.menu.storeAsGlobalVar.accesskey=S
 
 # LOCALIZATION NOTE (webconsole.menu.copyMessage.label)
 # Label used for a context-menu item displayed for any log. Clicking on it will copy the
--- a/devtools/client/performance-new/components/Perf.js
+++ b/devtools/client/performance-new/components/Perf.js
@@ -1,16 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const { PureComponent } = require("devtools/client/shared/vendor/react");
-const { div, button } = require("devtools/client/shared/vendor/react-dom-factories");
+const { PureComponent, createFactory } = require("devtools/client/shared/vendor/react");
+const { div, button, p, span, img } = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const PerfSettings = createFactory(require("devtools/client/performance-new/components/PerfSettings.js"));
+const { openLink } = require("devtools/client/shared/link");
 
 /**
  * The recordingState is one of the following:
  **/
 
 // The initial state before we've queried the PerfActor
 const NOT_YET_KNOWN = "not-yet-known";
 // The profiler is available, we haven't started recording yet.
@@ -27,16 +29,17 @@ const RECORDING = "recording";
 // Some other code with access to the profiler started it.
 const OTHER_IS_RECORDING = "other-is-recording";
 // Profiling is not available when in private browsing mode.
 const LOCKED_BY_PRIVATE_BROWSING = "locked-by-private-browsing";
 
 class Perf extends PureComponent {
   static get propTypes() {
     return {
+      toolbox: PropTypes.object.isRequired,
       perfFront: PropTypes.object.isRequired,
       receiveProfile: PropTypes.func.isRequired
     };
   }
 
   constructor(props) {
     super(props);
     this.state = {
@@ -47,16 +50,18 @@ class Perf extends PureComponent {
     };
     this.startRecording = this.startRecording.bind(this);
     this.getProfileAndStopProfiler = this.getProfileAndStopProfiler.bind(this);
     this.stopProfilerAndDiscardProfile = this.stopProfilerAndDiscardProfile.bind(this);
     this.handleProfilerStarting = this.handleProfilerStarting.bind(this);
     this.handleProfilerStopping = this.handleProfilerStopping.bind(this);
     this.handlePrivateBrowsingStarting = this.handlePrivateBrowsingStarting.bind(this);
     this.handlePrivateBrowsingEnding = this.handlePrivateBrowsingEnding.bind(this);
+    this.settingsComponentCreated = this.settingsComponentCreated.bind(this);
+    this.handleLinkClick = this.handleLinkClick.bind(this);
   }
 
   componentDidMount() {
     const { perfFront } = this.props;
 
     // Ask for the initial state of the profiler.
     Promise.all([
       perfFront.isActive(),
@@ -109,16 +114,25 @@ class Perf extends PureComponent {
         this.props.perfFront.stopProfilerAndDiscardProfile();
         break;
 
       default:
         throw new Error("Unhandled recording state.");
     }
   }
 
+  /**
+   * Store a reference to the settings component. This gives the <Perf> component
+   * access to the `.getRecordingSettings()` method. At this time the recording panel
+   * is not doing much state management, so this avoid the overhead of redux.
+   */
+  settingsComponentCreated(settings) {
+    this.settings = settings;
+  }
+
   getRecordingStateForTesting() {
     return this.state.recordingState;
   }
 
   handleProfilerStarting() {
     switch (this.state.recordingState) {
       case NOT_YET_KNOWN:
         // We couldn't have started it yet, so it must have been someone
@@ -230,66 +244,74 @@ class Perf extends PureComponent {
     // be the only logical state to go into.
     this.setState({
       recordingState: AVAILABLE_TO_RECORD,
       recordingUnexpectedlyStopped: false
     });
   }
 
   startRecording() {
+    const settings = this.settings;
+    if (!settings) {
+      console.error("Expected the PerfSettings panel to be rendered and available.");
+      return;
+    }
     this.setState({
       recordingState: REQUEST_TO_START_RECORDING,
       // Reset this error state since it's no longer valid.
       recordingUnexpectedlyStopped: false,
     });
-    this.props.perfFront.startProfiler();
+    this.props.perfFront.startProfiler(
+      // Pull out the recording settings from the child component. This approach avoids
+      // using Redux as a state manager.
+      settings.getRecordingSettings()
+    );
   }
 
   async getProfileAndStopProfiler() {
     this.setState({ recordingState: REQUEST_TO_GET_PROFILE_AND_STOP_PROFILER });
     const profile = await this.props.perfFront.getProfileAndStopProfiler();
     this.setState({ recordingState: AVAILABLE_TO_RECORD });
     console.log("getProfileAndStopProfiler");
     this.props.receiveProfile(profile);
   }
 
   stopProfilerAndDiscardProfile() {
     this.setState({ recordingState: REQUEST_TO_STOP_PROFILER });
     this.props.perfFront.stopProfilerAndDiscardProfile();
   }
 
-  render() {
+  renderButton() {
     const { recordingState, isSupportedPlatform } = this.state;
 
-    // Handle the cases of platform support.
-    switch (isSupportedPlatform) {
-      case null:
-        // We don't know yet if this is a supported platform, wait for a response.
-        return null;
-      case false:
-        return renderButton({
-          label: "Start recording",
-          disabled: true,
-          additionalMessage: "Your platform is not supported. The Gecko Profiler only " +
-                             "supports Tier-1 platforms."
-        });
-      case true:
-        // Continue on and render the panel.
-        break;
+    if (!isSupportedPlatform) {
+      return renderButton({
+        label: "Start recording",
+        disabled: true,
+        additionalMessage: "Your platform is not supported. The Gecko Profiler only " +
+                           "supports Tier-1 platforms."
+      });
     }
 
     // TODO - L10N all of the messages. Bug 1418056
     switch (recordingState) {
       case NOT_YET_KNOWN:
         return null;
 
       case AVAILABLE_TO_RECORD:
         return renderButton({
           onClick: this.startRecording,
-          label: "Start recording",
+          label: span(
+            null,
+            img({
+              className: "perf-button-image",
+              src: "chrome://devtools/skin/images/tool-profiler.svg"
+            }),
+            "Start recording",
+          ),
           additionalMessage: this.state.recordingUnexpectedlyStopped
             ? div(null, "The recording was stopped by another tool.")
             : null
         });
 
       case REQUEST_TO_STOP_PROFILER:
         return renderButton({
           label: "Stopping the recording",
@@ -324,26 +346,78 @@ class Perf extends PureComponent {
           additionalMessage: `The profiler is disabled when Private Browsing is enabled.
                               Close all Private Windows to re-enable the profiler`
         });
 
       default:
         throw new Error("Unhandled recording state");
     }
   }
+
+  handleLinkClick(event) {
+    openLink(event.target.value, this.props.toolbox);
+  }
+
+  render() {
+    const { isSupportedPlatform } = this.state;
+
+    if (isSupportedPlatform === null) {
+      // We don't know yet if this is a supported platform, wait for a response.
+      return null;
+    }
+
+    return div(
+      { className: "perf" },
+      this.renderButton(),
+      PerfSettings({ ref: this.settingsComponentCreated }),
+      div(
+        { className: "perf-description" },
+        p(null,
+          "This new recording panel is a bit different from the existing " +
+            "performance panel. It records the entire browser, and then opens up " +
+            "and shares the profile with ",
+          button(
+            // Implement links as buttons to avoid any risk of loading the link in the
+            // the panel.
+            {
+              className: "perf-external-link",
+              value: "https://perf-html.io",
+              onClick: this.handleLinkClick
+            },
+            "perf-html.io"
+          ),
+          ", a Mozilla performance analysis tool."
+        ),
+        p(null,
+          "This is still a prototype. Join along or file bugs at: ",
+          button(
+            // Implement links as buttons to avoid any risk of loading the link in the
+            // the panel.
+            {
+              className: "perf-external-link",
+              value: "https://github.com/devtools-html/perf.html",
+              onClick: this.handleLinkClick
+            },
+            "github.com/devtools-html/perf.html"
+          ),
+          "."
+        )
+      ),
+    );
+  }
 }
 
 module.exports = Perf;
 
 function renderButton(props) {
   const { disabled, label, onClick, additionalMessage } = props;
   const nbsp = "\u00A0";
 
   return div(
-    { className: "perf" },
+    { className: "perf-button-container" },
     div({ className: "perf-additional-message" }, additionalMessage || nbsp),
     div(
       null,
       button(
         {
           className: "devtools-button perf-button",
           "data-standalone": true,
           disabled,
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance-new/components/PerfSettings.js
@@ -0,0 +1,409 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+const { PureComponent, createFactory } = require("devtools/client/shared/vendor/react");
+const { div, details, summary, label, input, span, h2, section } = require("devtools/client/shared/vendor/react-dom-factories");
+const Range = createFactory(require("devtools/client/performance-new/components/Range"));
+const { makeExponentialScale, formatFileSize, calculateOverhead } = require("devtools/client/performance-new/utils");
+
+// sizeof(double) + sizeof(char)
+// http://searchfox.org/mozilla-central/rev/e8835f52eff29772a57dca7bcc86a9a312a23729/tools/profiler/core/ProfileEntry.h#73
+const PROFILE_ENTRY_SIZE = 9;
+
+const NOTCHES = Array(22).fill("discrete-level-notch");
+
+const threadColumns = [
+  [
+    {
+      name: "GeckoMain",
+      title: "The main processes for both the parent process, and content processes"
+    },
+    {
+      name: "Compositor",
+      title: "Composites together different painted elements on the page."
+    },
+    {
+      name: "DOM Worker",
+      title: "This handle both web workers and service workers"
+    },
+    {
+      name: "Renderer",
+      title: "When WebRender is enabled, the thread that executes OpenGL calls"
+    },
+  ],
+  [
+    {
+      name: "RenderBackend",
+      title: "The WebRender RenderBackend thread"
+    },
+    {
+      name: "PaintWorker",
+      title: "When off-main-thread painting is enabled, the thread on which " +
+        "painting happens"
+    },
+    {
+      name: "StyleThread",
+      title: "Style computation is split into multiple threads"
+    },
+    {
+      name: "Socket Thread",
+      title: "The thread where networking code runs any blocking socket calls"
+    },
+  ],
+  [
+    {
+      name: "StreamTrans",
+      title: "TODO"
+    },
+    {
+      name: "ImgDecoder",
+      title: "Image decoding threads"
+    },
+    {
+      name: "DNS Resolver",
+      title: "DNS resolution happens on this thread"
+    },
+  ]
+];
+
+const featureCheckboxes = [
+  {
+    name: "Native Stacks",
+    value: "stackwalk",
+    title: "Record native stacks (C++ and Rust). This is not available on all platforms.",
+    recommended: true
+  },
+  {
+    name: "JavaScript",
+    value: "js",
+    title: "Record JavaScript stack information, and interleave it with native stacks.",
+    recommended: true
+  },
+  {
+    name: "Java",
+    value: "java",
+    title: "Profile Java code (Android only)."
+  },
+  {
+    name: "Native Leaf Stack",
+    value: "leaf",
+    title: "Record the native memory address of the leaf-most stack. This could be " +
+      "useful on platforms that do not support stack walking."
+  },
+  {
+    name: "Main Thread IO",
+    value: "mainthreadio",
+    title: "Record main thread I/O markers."
+  },
+  {
+    name: "Memory",
+    value: "memory",
+    title: "Add memory measurements to the samples, this includes resident set size " +
+      "(RSS) and unique set size (USS)."
+  },
+  {
+    name: "Privacy",
+    value: "privacy",
+    title: "Remove some potentially user-identifiable information."
+  },
+  {
+    name: "JIT Optimizations",
+    value: "trackopts",
+    title: "Track JIT optimizations in the JS engine."
+  },
+  {
+    name: "TaskTracer",
+    value: "tasktracer",
+    title: "Enable TaskTracer (Experimental, requires custom build.)"
+  },
+];
+
+/**
+ * This component manages the settings for recording a performance profile. In addition
+ * to rendering the UI, it also manages the state of the settings. In order to not
+ * introduce the complexity of adding Redux to a relatively simple UI, this
+ * component expects to be accessed via the `ref`, and then calling
+ * `settings.getRecordingSettings()` to get out the settings. If the recording panel
+ * takes on new responsibilities, then this decision should be revisited.
+ */
+class PerfSettings extends PureComponent {
+  static get propTypes() {
+    return {};
+  }
+
+  constructor(props) {
+    super(props);
+    // Right now the defaults are reset every time the panel is opened. These should
+    // be persisted between sessions. See Bug 1453014.
+    this.state = {
+      interval: 1,
+      entries: 10000000, // 90MB
+      features: {
+        js: true,
+        stackwalk: true,
+      },
+      threads: "GeckoMain,Compositor",
+      threadListFocused: false,
+    };
+    this._handleThreadCheckboxChange = this._handleThreadCheckboxChange.bind(this);
+    this._handleFeaturesCheckboxChange = this._handleFeaturesCheckboxChange.bind(this);
+    this._handleThreadTextChange = this._handleThreadTextChange.bind(this);
+    this._handleThreadTextCleanup = this._handleThreadTextCleanup.bind(this);
+    this._renderThreadsColumns = this._renderThreadsColumns.bind(this);
+    this._onChangeInterval = this._onChangeInterval.bind(this);
+    this._onChangeEntries = this._onChangeEntries.bind(this);
+    this._intervalExponentialScale = makeExponentialScale(0.01, 100);
+    this._entriesExponentialScale = makeExponentialScale(100000, 100000000);
+  }
+
+  getRecordingSettings() {
+    const features = [];
+    for (const [name, isSet] of Object.entries(this.state.features)) {
+      if (isSet) {
+        features.push(name);
+      }
+    }
+    return {
+      entries: this.state.entries,
+      interval: this.state.interval,
+      features,
+      threads: _threadStringToList(this.state.threads)
+    };
+  }
+
+  _renderNotches() {
+    const { interval, entries, features } = this.state;
+    const overhead = calculateOverhead(interval, entries, features);
+    const notchCount = 22;
+    const notches = [];
+    for (let i = 0; i < notchCount; i++) {
+      const active = i <= Math.round(overhead * (NOTCHES.length - 1))
+        ? "active" : "inactive";
+
+      let level = "normal";
+      if (i > 16) {
+        level = "critical";
+      } else if (i > 10) {
+        level = "warning";
+      }
+      notches.push(
+        div({
+          key: i,
+          className:
+          `perf-settings-notch perf-settings-notch-${level} ` +
+            `perf-settings-notch-${active}`
+        })
+      );
+    }
+    return notches;
+  }
+
+  _handleThreadCheckboxChange(event) {
+    const { checked, value }  = event.target;
+
+    this.setState(state => {
+      let threadsList = _threadStringToList(state.threads);
+      if (checked) {
+        if (!threadsList.includes(value)) {
+          threadsList.push(value);
+        }
+      } else {
+        threadsList = threadsList.filter(thread => thread !== value);
+      }
+      return { threads: threadsList.join(",") };
+    });
+  }
+
+  _handleFeaturesCheckboxChange(event) {
+    const { checked, value }  = event.target;
+
+    this.setState(state => ({
+      features: {...state.features, [value]: checked}
+    }));
+  }
+
+  _handleThreadTextChange(event) {
+    this.setState({ threads: event.target.value });
+  }
+
+  _handleThreadTextCleanup() {
+    this.setState(state => {
+      const threadsList = _threadStringToList(state.threads);
+      return { threads: threadsList.join(",") };
+    });
+  }
+
+  _renderThreadsColumns(threads, index) {
+    return div(
+      { className: "perf-settings-thread-column", key: index },
+      threads.map(({name, title}) => label(
+        {
+          className: "perf-settings-checkbox-label",
+          key: name,
+          title
+        },
+        input({
+          className: "perf-settings-checkbox",
+          type: "checkbox",
+          value: name,
+          checked: this.state.threads.includes(name),
+          onChange: this._handleThreadCheckboxChange
+        }),
+        name
+      ))
+    );
+  }
+
+  _renderThreads() {
+    return details(
+      { className: "perf-settings-details" },
+      summary({ className: "perf-settings-summary" }, "Threads:"),
+      // Contain the overflow of the slide down animation with the first div.
+      div(
+        { className: "perf-settings-details-contents" },
+        // Provide a second <div> element for the contents of the slide down animation.
+        div(
+          { className: "perf-settings-details-contents-slider" },
+          div(
+            { className: "perf-settings-thread-columns" },
+            threadColumns.map(this._renderThreadsColumns),
+          ),
+          div(
+            { className: "perf-settings-row" },
+            label(
+              {
+                className: "perf-settings-text-label",
+                title: "These thread names are a comma separated list that is used to " +
+                  "enable profiling of the threads in the profiler. The name needs to " +
+                  "be only a partial match of the thread name to be included. It " +
+                  "is whitespace sensitive."
+              },
+              div({}, "Add custom threads by name:"),
+              input({
+                className: "perf-settings-text-input",
+                type: "text",
+                value: this.state.threads,
+                onChange: this._handleThreadTextChange,
+                onBlur: this._handleThreadTextCleanup,
+              })
+            )
+          )
+        )
+      )
+    );
+  }
+
+  _renderFeatures() {
+    return details(
+      { className: "perf-settings-details" },
+      summary({ className: "perf-settings-summary" }, "Features:"),
+      div(
+        { className: "perf-settings-details-contents" },
+        div(
+          { className: "perf-settings-details-contents-slider" },
+          featureCheckboxes.map(({name, value, title, recommended}) => label(
+            {
+              className: "perf-settings-checkbox-label perf-settings-feature-label",
+              key: value,
+            },
+            input({
+              className: "perf-settings-checkbox",
+              type: "checkbox",
+              value,
+              checked: this.state.features[value],
+              onChange: this._handleFeaturesCheckboxChange
+            }),
+            div({ className: "perf-settings-feature-name" }, name),
+            div(
+              { className: "perf-settings-feature-title" },
+              title,
+              recommended
+                ? span(
+                  { className: "perf-settings-subtext" },
+                  " (Recommended on by default.)"
+                )
+                : null
+            )
+          ))
+        )
+      )
+    );
+  }
+
+  _onChangeInterval(interval) {
+    this.setState({ interval });
+  }
+
+  _onChangeEntries(entries) {
+    this.setState({ entries });
+  }
+
+  render() {
+    return section(
+      { className: "perf-settings" },
+      h2({ className: "perf-settings-title" }, "Recording Settings"),
+      div(
+        { className: "perf-settings-row" },
+        label({ className: "perf-settings-label" }, "Overhead:"),
+        div(
+          { className: "perf-settings-value perf-settings-notches" },
+          this._renderNotches()
+        )
+      ),
+      Range({
+        label: "Sampling interval:",
+        value: this.state.interval,
+        id: "perf-range-interval",
+        scale: this._intervalExponentialScale,
+        display: _intervalTextDisplay,
+        onChange: this._onChangeInterval
+      }),
+      Range({
+        label: "Buffer size:",
+        value: this.state.entries,
+        id: "perf-range-entries",
+        scale: this._entriesExponentialScale,
+        display: _entriesTextDisplay,
+        onChange: this._onChangeEntries
+      }),
+      this._renderThreads(),
+      this._renderFeatures()
+    );
+  }
+}
+
+/**
+ * Clean up the thread list string into a list of values.
+ * @param string threads, comma separated values.
+ * @return Array list of thread names
+ */
+function _threadStringToList(threads) {
+  return threads
+    // Split on commas
+    .split(",")
+    // Clean up any extraneous whitespace
+    .map(string => string.trim())
+    // Filter out any blank strings
+    .filter(string => string);
+}
+
+/**
+ * Format the interval number for display.
+ * @param {number} value
+ * @return {string}
+ */
+function _intervalTextDisplay(value) {
+  return `${value} ms`;
+}
+
+/**
+ * Format the entries number for display.
+ * @param {number} value
+ * @return {string}
+ */
+function _entriesTextDisplay(value) {
+  return formatFileSize(value * PROFILE_ENTRY_SIZE);
+}
+
+module.exports = PerfSettings;
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance-new/components/Range.js
@@ -0,0 +1,69 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+const { PureComponent } = require("devtools/client/shared/vendor/react");
+const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { div, input, label } = require("devtools/client/shared/vendor/react-dom-factories");
+
+class Range extends PureComponent {
+  static get propTypes() {
+    return {
+      value: PropTypes.number.isRequired,
+      label: PropTypes.string.isRequired,
+      id: PropTypes.string.isRequired,
+      scale: PropTypes.object.isRequired,
+      onChange: PropTypes.func.isRequired,
+      display: PropTypes.func.isRequired,
+    };
+  }
+
+  constructor(props) {
+    super(props);
+    this.handleInput = this.handleInput.bind(this);
+  }
+
+  handleInput(e) {
+    e.preventDefault();
+    const { scale, onChange } = this.props;
+    const frac = e.target.value / 100;
+    onChange(scale.fromFractionToSingleDigitValue(frac));
+  }
+
+  render() {
+    const { label: labelText, scale, id, value, display } = this.props;
+    return (
+      div(
+        { className: "perf-settings-row" },
+        label(
+          {
+            className: "perf-settings-label",
+            htmlFor: id
+          },
+          labelText
+        ),
+        div(
+          { className: "perf-settings-value" },
+          div(
+            { className: "perf-settings-range-input" },
+            input({
+              type: "range",
+              className: `perf-settings-range-input-el`,
+              min: "0",
+              max: "100",
+              value: scale.fromValueToFraction(value) * 100,
+              onChange: this.handleInput,
+              id,
+            })
+          ),
+          div(
+            { className: `perf-settings-range-value`},
+            display(value)
+          )
+        )
+      )
+    );
+  }
+}
+
+module.exports = Range;
--- a/devtools/client/performance-new/components/moz.build
+++ b/devtools/client/performance-new/components/moz.build
@@ -1,8 +1,10 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
     'Perf.js',
+    'PerfSettings.js',
+    'Range.js',
 )
--- a/devtools/client/performance-new/initializer.js
+++ b/devtools/client/performance-new/initializer.js
@@ -14,20 +14,22 @@ const { require } = BrowserLoaderModule.
 const Perf = require("devtools/client/performance-new/components/Perf");
 const Services = require("Services");
 const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
 const { createElement } = require("devtools/client/shared/vendor/react");
 
 /**
  * Perform a simple initialization on the panel. Hook up event listeners.
  *
+ * @param toolbox - The toolbox
  * @param perfFront - The Perf actor's front. Used to start and stop recordings.
  */
-function gInit(perfFront) {
+function gInit(toolbox, perfFront) {
   const props = {
+    toolbox,
     perfFront,
     receiveProfile: profile => {
       // Open up a new tab and send a message with the profile.
       let browser = top.gBrowser;
       if (!browser) {
         // Current isn't browser window. Looking for the recent browser.
         const win = Services.wm.getMostRecentWindow("navigator:browser");
         if (!win) {
--- a/devtools/client/performance-new/moz.build
+++ b/devtools/client/performance-new/moz.build
@@ -4,14 +4,15 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'components',
 ]
 
 DevToolsModules(
     'panel.js',
+    'utils.js',
 )
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools: Performance Tools (Profiler/Timeline)')
--- a/devtools/client/performance-new/panel.js
+++ b/devtools/client/performance-new/panel.js
@@ -30,17 +30,17 @@ class PerformancePanel {
     this.panelWin.gToolbox = this.toolbox;
     this.panelWin.gTarget = this.target;
 
     const rootForm = await this.target.root;
     const perfFront = new PerfFront(this.target.client, rootForm);
 
     this.isReady = true;
     this.emit("ready");
-    this.panelWin.gInit(perfFront);
+    this.panelWin.gInit(this.toolbox, perfFront);
     return this;
   }
 
   // DevToolPanel API:
 
   get target() {
     return this.toolbox.target;
   }
new file mode 100644
--- /dev/null
+++ b/devtools/client/performance-new/utils.js
@@ -0,0 +1,162 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
+
+/**
+ * Linearly interpolate between values.
+ * https://en.wikipedia.org/wiki/Linear_interpolation
+ *
+ * @param {number} frac - Value ranged 0 - 1 to interpolate between the range
+ *                        start and range end.
+ * @param {number} rangeState - The value to start from.
+ * @param {number} rangeEnd - The value to interpolate to.
+ * @returns {number}
+ */
+function lerp(frac, rangeStart, rangeEnd) {
+  return (1 - frac) * rangeStart + frac * rangeEnd;
+}
+
+/**
+ * Make sure a value is clamped between a min and max value.
+ *
+ * @param {number} val - The value to clamp.
+ * @param {number} min - The minimum value.
+ * @returns {number}
+ */
+function clamp(val, min, max) {
+  return Math.max(min, Math.min(max, val));
+}
+
+/**
+ * Formats a file size.
+ * @param {number} num - The number (in bytes) to format.
+ * @returns {string} e.g. "10 B", "100 MB"
+ */
+function formatFileSize(num) {
+  if (!Number.isFinite(num)) {
+    throw new TypeError(`Expected a finite number, got ${typeof num}: ${num}`);
+  }
+
+  const neg = num < 0;
+
+  if (neg) {
+    num = -num;
+  }
+
+  if (num < 1) {
+    return (neg ? "-" : "") + num + " B";
+  }
+
+  const exponent = Math.min(
+    Math.floor(Math.log(num) / Math.log(1000)),
+    UNITS.length - 1
+  );
+  const numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3));
+  const unit = UNITS[exponent];
+
+  return (neg ? "-" : "") + numStr + " " + unit;
+}
+
+/**
+ * Creates numbers that scale exponentially.
+ *
+ * @param {number} rangeStart
+ * @param {number} rangeEnd
+ */
+function makeExponentialScale(rangeStart, rangeEnd) {
+  const startExp = Math.log(rangeStart);
+  const endExp = Math.log(rangeEnd);
+  const fromFractionToValue = frac =>
+    Math.exp((1 - frac) * startExp + frac * endExp);
+  const fromValueToFraction = value =>
+    (Math.log(value) - startExp) / (endExp - startExp);
+  const fromFractionToSingleDigitValue = frac => {
+    return +fromFractionToValue(frac).toPrecision(1);
+  };
+  return {
+    // Takes a number ranged 0-1 and returns it within the range.
+    fromFractionToValue,
+    // Takes a number in the range, and returns a value between 0-1
+    fromValueToFraction,
+    // Takes a number ranged 0-1 and returns a value in the range, but with
+    // a single digit value.
+    fromFractionToSingleDigitValue,
+  };
+}
+
+/**
+ * Scale a source range to a destination range, but clamp it within the
+ * destination range.
+ * @param {number} val - The source range value to map to the destination range,
+ * @param {number} sourceRangeStart,
+ * @param {number} sourceRangeEnd,
+ * @param {number} destRangeStart,
+ * @param {number} destRangeEnd
+ */
+function scaleRangeWithClamping(
+  val,
+  sourceRangeStart,
+  sourceRangeEnd,
+  destRangeStart,
+  destRangeEnd
+) {
+  const frac = clamp(
+    (val - sourceRangeStart) / (sourceRangeEnd - sourceRangeStart),
+    0,
+    1
+  );
+  return lerp(frac, destRangeStart, destRangeEnd);
+}
+
+/**
+ * Use some heuristics to guess at the overhead of the recording settings.
+ * @param {number} interval
+ * @param {number} bufferSize
+ * @param {object} features - Map of the feature name to a boolean.
+ */
+function calculateOverhead(interval, bufferSize, features) {
+  const overheadFromSampling =
+    scaleRangeWithClamping(
+      Math.log(interval),
+      Math.log(0.05),
+      Math.log(1),
+      1,
+      0
+    ) +
+    scaleRangeWithClamping(
+      Math.log(interval),
+      Math.log(1),
+      Math.log(100),
+      0.1,
+      0
+    );
+  const overheadFromBuffersize = scaleRangeWithClamping(
+    Math.log(bufferSize),
+    Math.log(10),
+    Math.log(1000000),
+    0,
+    0.1
+  );
+  const overheadFromStackwalk = features.stackwalk ? 0.05 : 0;
+  const overheadFromJavaScrpt = features.js ? 0.05 : 0;
+  const overheadFromTaskTracer = features.tasktracer ? 0.05 : 0;
+  return clamp(
+    overheadFromSampling +
+      overheadFromBuffersize +
+      overheadFromStackwalk +
+      overheadFromJavaScrpt +
+      overheadFromTaskTracer,
+    0,
+    1
+  );
+}
+
+module.exports = {
+  formatFileSize,
+  makeExponentialScale,
+  scaleRangeWithClamping,
+  calculateOverhead
+};
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -480,19 +480,25 @@ class Telemetry {
    *        The pending property value
    */
   addEventProperty(category, method, object, value, pendingPropName, pendingPropValue) {
     const sig = `${category},${method},${object},${value}`;
 
     // If the pending event has not been created add the property to the pending
     // list.
     if (!PENDING_EVENTS.has(sig)) {
-      PENDING_EVENT_PROPERTIES.set(sig, {
-        [pendingPropName]: pendingPropValue
-      });
+      let props = PENDING_EVENT_PROPERTIES.get(sig);
+
+      if (props) {
+        props[pendingPropName] = pendingPropValue;
+      } else {
+        PENDING_EVENT_PROPERTIES.set(sig, {
+          [pendingPropName]: pendingPropValue
+        });
+      }
       return;
     }
 
     const { expected, extra } = PENDING_EVENTS.get(sig);
 
     if (expected.has(pendingPropName)) {
       extra[pendingPropName] = pendingPropValue;
 
--- a/devtools/client/themes/perf.css
+++ b/devtools/client/themes/perf.css
@@ -12,12 +12,203 @@
 }
 
 .devtools-button.perf-button {
   padding: 5px;
   margin: auto;
   font-size: 120%;
 }
 
+.perf-button-image {
+  vertical-align: text-top;
+  padding-inline-end: 4px;
+}
+
 .perf-additional-message {
   margin: 10px;
   margin-top: 65px;
 }
+
+.perf > * {
+  max-width: 440px;
+}
+
+.perf-description {
+  line-height: 1.4;
+}
+
+.perf-external-link {
+  margin: 0;
+  padding: 0;
+  background: none;
+  border: none;
+  color: var(--blue-60);
+  text-decoration: underline;
+  white-space: nowrap;
+  cursor: pointer;
+}
+
+/* Settings */
+
+.perf-settings {
+  width: 100%;
+  margin: 50px 0 25px;
+}
+
+.perf-settings-title {
+  padding: 5px 10px;
+  margin-bottom: 15px;
+  background-color: var(--grey-10);
+  border: var(--grey-30) 1px solid;
+  font-size: 11px;
+  font-weight: normal;
+}
+
+.perf-settings-row {
+  display: flex;
+  overflow: hidden;
+  line-height: 1.8;
+}
+
+.perf-settings-controls > .tree {
+  height: 100%;
+}
+
+.perf-settings-row.focused {
+  background-color: var(--theme-selection-background);
+  color: var(--theme-selection-color);
+}
+
+.perf-settings-label {
+  height: 30px;
+  min-width: 110px;
+}
+
+.perf-settings-value {
+  display: flex;
+  flex: 1;
+}
+
+.perf-settings-range-input {
+  flex: 1;
+}
+
+.perf-settings-range-input-el {
+  width: 100%;
+}
+
+.perf-settings-range-value {
+  min-width: 70px;
+  text-align: end;
+}
+
+.perf-settings-notches {
+  height: 14px;
+  margin: 5px 0 10px;
+  margin-inline-start: 0.7em;
+  display: flex;
+}
+
+.perf-settings-notch {
+  margin-right: 1px;
+  flex: 1;
+  border: 1px solid rgba(0,0,0,0.2);
+  border-radius: 2px;
+}
+
+.perf-settings-notch-normal.perf-settings-notch-active {
+  border-color: hsl(90, 90%, 40%);
+  background-color: hsla(90, 90%, 40%, 0.5);
+}
+
+.perf-settings-notch-warning.perf-settings-notch-active {
+  border-color: hsl(45, 100%, 49%);
+  background-color: hsla(45, 100%, 49%, 0.5);
+}
+
+.perf-settings-notch-critical.perf-settings-notch-active {
+  border-color: hsl(0, 90%, 40%);
+  background-color: hsla(0, 90%, 40%, 0.5);
+}
+
+.perf-settings-text-input {
+  width: 100%;
+  padding: 4px;
+  box-sizing: border-box;
+}
+
+.perf-settings-text-label {
+  flex: 1;
+}
+
+.perf-settings-details-contents {
+  overflow: hidden;
+}
+
+.perf-settings-details-contents-slider {
+  padding: 10px;
+  margin: 0 0 18px;
+  border: var(--grey-20) 1px solid;
+  background-color: var(--grey-10);
+  opacity: 0;
+  transform: translateY(-100px);
+  transition-duration: 250ms;
+  transition-timing-function: cubic-bezier(.07,.95,0,1);
+  transition-property: transform, opacity;
+}
+
+.perf-settings-details[open] .perf-settings-details-contents-slider {
+  opacity: 1;
+  transform: translateY(0);
+}
+
+.perf-settings-summary {
+  height: 30px;
+  cursor: default;
+  -moz-user-select: none;
+}
+
+.perf-settings-thread-columns {
+  margin-bottom: 20px;
+  display: flex;
+  line-height: 2;
+}
+
+.perf-settings-thread-column {
+  flex: 1;
+}
+
+.perf-settings-checkbox-label {
+  display: block;
+}
+
+.perf-settings-feature-label {
+  margin: 16px 0;
+  display: flex;
+}
+
+.perf-settings-feature-label {
+  margin: 16px 0;
+  display: flex;
+}
+
+.perf-settings-checkbox {
+  align-self: flex-start;
+}
+
+.perf-settings-feature-name {
+  width: 150px;
+  color: var(--blue-60);
+}
+
+.perf-settings-feature-title {
+  flex: 1;
+}
+
+.perf-settings-feature-name {
+  width: 130px;
+  color: var(--blue-60);
+  line-height: 1.6;
+}
+
+.perf-settings-subtext {
+  font-weight: bold;
+}
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -6,17 +6,16 @@
 
 var Services = require("Services");
 loader.lazyRequireGetter(this, "Utils", "devtools/client/webconsole/utils", true);
 loader.lazyRequireGetter(this, "extend", "devtools/shared/extend", true);
 loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/target", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 loader.lazyRequireGetter(this, "Tools", "devtools/client/definitions", true);
 loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
-loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/old/webconsole", true);
 loader.lazyRequireGetter(this, "NewWebConsoleFrame", "devtools/client/webconsole/new-webconsole", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
 loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
 loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
 loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/webconsole-l10n");
 const BC_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
@@ -188,20 +187,16 @@ HUD_SERVICE.prototype =
       let win = Services.ww.openWindow(null, browserConsoleURL, "_blank",
                                        BC_WINDOW_FEATURES, null);
       await new Promise(resolve => {
         win.addEventListener("DOMContentLoaded", resolve, {once: true});
       });
 
       win.document.title = l10n.getStr("browserConsole.title");
 
-      if (browserConsoleURL === Tools.webConsole.oldWebConsoleURL) {
-        return {iframeWindow: win, chromeWindow: win};
-      }
-
       let iframe = win.document.querySelector("iframe");
       await new Promise(resolve => {
         iframe.addEventListener("DOMContentLoaded", resolve, {once: true});
       });
 
       return {iframeWindow: iframe.contentWindow, chromeWindow: win};
     }
 
@@ -268,21 +263,17 @@ function WebConsole(target, iframeWindow
   this.chromeWindow = chromeWindow;
   this.hudId = "hud_" + ++gHudId;
   this.target = target;
   this.browserWindow = this.chromeWindow.top;
   let element = this.browserWindow.document.documentElement;
   if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
     this.browserWindow = HUDService.currentContext();
   }
-  if (iframeWindow.location.href === Tools.webConsole.newWebConsoleURL) {
-    this.ui = new NewWebConsoleFrame(this);
-  } else {
-    this.ui = new WebConsoleFrame(this);
-  }
+  this.ui = new NewWebConsoleFrame(this);
 }
 WebConsole.prototype = {
   iframeWindow: null,
   chromeWindow: null,
   browserWindow: null,
   hudId: null,
   target: null,
   ui: null,
--- a/devtools/client/webconsole/moz.build
+++ b/devtools/client/webconsole/moz.build
@@ -2,17 +2,16 @@
 # vim: set filetype=python:
 # 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/.
 
 DIRS += [
     'actions',
     'components',
-    'old',
     'reducers',
     'selectors',
     'test',
     'utils',
 ]
 DevToolsModules(
     'console-commands.js',
     'constants.js',
deleted file mode 100644
--- a/devtools/client/webconsole/old/console-output.js
+++ /dev/null
@@ -1,3544 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {Ci} = require("chrome");
-
-loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
-loader.lazyImporter(this, "escapeHTML", "resource://devtools/client/shared/widgets/VariablesView.jsm");
-
-loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
-loader.lazyRequireGetter(this, "TableWidget", "devtools/client/shared/widgets/TableWidget", true);
-loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
-
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
-
-const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
-const { getSourceNames } = require("devtools/client/shared/source-utils");
-const l10n = require("devtools/client/webconsole/webconsole-l10n");
-const nodeConstants = require("devtools/shared/dom-node-constants");
-const {PluralForm} = require("devtools/shared/plural-form");
-const {extend} = require("devtools/shared/extend");
-const defer = require("devtools/shared/defer");
-
-const MAX_STRING_GRIP_LENGTH = 36;
-const {ELLIPSIS} = require("devtools/shared/l10n");
-
-const validProtocols = /^(http|https|ftp|data|javascript|resource|chrome):/i;
-
-// Constants for compatibility with the Web Console output implementation before
-// bug 778766.
-// TODO: remove these once bug 778766 is fixed.
-const COMPAT = {
-  // The various categories of messages.
-  CATEGORIES: {
-    NETWORK: 0,
-    CSS: 1,
-    JS: 2,
-    WEBDEV: 3,
-    INPUT: 4,
-    OUTPUT: 5,
-    SECURITY: 6,
-    SERVER: 7,
-  },
-
-  // The possible message severities.
-  SEVERITIES: {
-    ERROR: 0,
-    WARNING: 1,
-    INFO: 2,
-    LOG: 3,
-  },
-
-  // The preference keys to use for each category/severity combination, indexed
-  // first by category (rows) and then by severity (columns).
-  //
-  // Most of these rather idiosyncratic names are historical and predate the
-  // division of message type into "category" and "severity".
-  /* eslint-disable no-multi-spaces */
-  /* eslint-disable max-len */
-  PREFERENCE_KEYS: [
-    // Error         Warning       Info          Log
-    [ "network",     "netwarn",    null,         "networkinfo", ],  // Network
-    [ "csserror",    "cssparser",  null,         null,          ],  // CSS
-    [ "exception",   "jswarn",     null,         "jslog",       ],  // JS
-    [ "error",       "warn",       "info",       "log",         ],  // Web Developer
-    [ null,          null,         null,         null,          ],  // Input
-    [ null,          null,         null,         null,          ],  // Output
-    [ "secerror",    "secwarn",    null,         null,          ],  // Security
-    [ "servererror", "serverwarn", "serverinfo", "serverlog",   ],  // Server Logging
-  ],
-  /* eslint-enable max-len */
-  /* eslint-enable no-multi-spaces */
-
-  // The fragment of a CSS class name that identifies each category.
-  CATEGORY_CLASS_FRAGMENTS: [ "network", "cssparser", "exception", "console",
-                              "input", "output", "security", "server" ],
-
-  // The fragment of a CSS class name that identifies each severity.
-  SEVERITY_CLASS_FRAGMENTS: [ "error", "warn", "info", "log" ],
-
-  // The indent of a console group in pixels.
-  GROUP_INDENT: 12,
-};
-
-// A map from the console API call levels to the Web Console severities.
-const CONSOLE_API_LEVELS_TO_SEVERITIES = {
-  error: "error",
-  exception: "error",
-  assert: "error",
-  warn: "warning",
-  info: "info",
-  log: "log",
-  clear: "log",
-  trace: "log",
-  table: "log",
-  debug: "log",
-  dir: "log",
-  dirxml: "log",
-  group: "log",
-  groupCollapsed: "log",
-  groupEnd: "log",
-  time: "log",
-  timeEnd: "log",
-  count: "log"
-};
-
-// Array of known message source URLs we need to hide from output.
-const IGNORED_SOURCE_URLS = ["debugger eval code"];
-
-// The maximum length of strings to be displayed by the Web Console.
-const MAX_LONG_STRING_LENGTH = 200000;
-
-// Regular expression that matches the allowed CSS property names when using
-// the `window.console` API.
-const RE_ALLOWED_STYLES = new RegExp(["^(?:-moz-)?(?:background|border|box|clear|" +
-                                      "color|cursor|display|float|font|line|margin|" +
-                                      "padding|text|transition|outline|white-space|" +
-                                      "word|writing|(?:min-|max-)?width|" +
-                                      "(?:min-|max-)?height)"]);
-
-// Regular expressions to search and replace with 'notallowed' in the styles
-// given to the `window.console` API methods.
-const RE_CLEANUP_STYLES = [
-  // url(), -moz-element()
-  /\b(?:url|(?:-moz-)?element)[\s('"]+/gi,
-
-  // various URL protocols
-  /['"(]*(?:chrome|resource|about|app|data|https?|ftp|file):+\/*/gi,
-];
-
-// Maximum number of rows to display in console.table().
-const TABLE_ROW_MAX_ITEMS = 1000;
-
-// Maximum number of columns to display in console.table().
-const TABLE_COLUMN_MAX_ITEMS = 10;
-
-/**
- * The ConsoleOutput object is used to manage output of messages in the Web
- * Console.
- *
- * @constructor
- * @param object owner
- *        The console output owner. This usually the WebConsoleFrame instance.
- *        Any other object can be used, as long as it has the following
- *        properties and methods:
- *          - window
- *          - document
- *          - outputMessage(category, methodOrNode[, methodArguments])
- *            TODO: this is needed temporarily, until bug 778766 is fixed.
- */
-function ConsoleOutput(owner) {
-  this.owner = owner;
-  this._onFlushOutputMessage = this._onFlushOutputMessage.bind(this);
-}
-
-ConsoleOutput.prototype = {
-  _dummyElement: null,
-
-  /**
-   * The output container.
-   * @type DOMElement
-   */
-  get element() {
-    return this.owner.outputNode;
-  },
-
-  /**
-   * The document that holds the output.
-   * @type DOMDocument
-   */
-  get document() {
-    return this.owner ? this.owner.document : null;
-  },
-
-  /**
-   * The DOM window that holds the output.
-   * @type Window
-   */
-  get window() {
-    return this.owner.window;
-  },
-
-  /**
-   * Getter for the debugger WebConsoleClient.
-   * @type object
-   */
-  get webConsoleClient() {
-    return this.owner.webConsoleClient;
-  },
-
-  /**
-   * Getter for the current toolbox debuggee target.
-   * @type Target
-   */
-  get toolboxTarget() {
-    return this.owner.owner.target;
-  },
-
-  /**
-   * Release an actor.
-   *
-   * @private
-   * @param string actorId
-   *        The actor ID you want to release.
-   */
-  _releaseObject: function(actorId) {
-    this.owner._releaseObject(actorId);
-  },
-
-  /**
-   * Add a message to output.
-   *
-   * @param object ...args
-   *        Any number of Message objects.
-   * @return this
-   */
-  addMessage: function(...args) {
-    for (let msg of args) {
-      msg.init(this);
-      this.owner.outputMessage(msg._categoryCompat, this._onFlushOutputMessage,
-                               [msg]);
-    }
-    return this;
-  },
-
-  /**
-   * Message renderer used for compatibility with the current Web Console output
-   * implementation. This method is invoked for every message object that is
-   * flushed to output. The message object is initialized and rendered, then it
-   * is displayed.
-   *
-   * TODO: remove this method once bug 778766 is fixed.
-   *
-   * @private
-   * @param object message
-   *        The message object to render.
-   * @return DOMElement
-   *         The message DOM element that can be added to the console output.
-   */
-  _onFlushOutputMessage: function(message) {
-    return message.render().element;
-  },
-
-  /**
-   * Get an array of selected messages. This list is based on the text selection
-   * start and end points.
-   *
-   * @param number [limit]
-   *        Optional limit of selected messages you want. If no value is given,
-   *        all of the selected messages are returned.
-   * @return array
-   *         Array of DOM elements for each message that is currently selected.
-   */
-  getSelectedMessages: function(limit) {
-    let selection = this.window.getSelection();
-    if (selection.isCollapsed) {
-      return [];
-    }
-
-    if (selection.containsNode(this.element, true)) {
-      return Array.slice(this.element.children);
-    }
-
-    let anchor = this.getMessageForElement(selection.anchorNode);
-    let focus = this.getMessageForElement(selection.focusNode);
-    if (!anchor || !focus) {
-      return [];
-    }
-
-    let start, end;
-    if (anchor.timestamp > focus.timestamp) {
-      start = focus;
-      end = anchor;
-    } else {
-      start = anchor;
-      end = focus;
-    }
-
-    let result = [];
-    let current = start;
-    while (current) {
-      result.push(current);
-      if (current == end || (limit && result.length == limit)) {
-        break;
-      }
-      current = current.nextSibling;
-    }
-    return result;
-  },
-
-  /**
-   * Find the DOM element of a message for any given descendant.
-   *
-   * @param DOMElement elem
-   *        The element to start the search from.
-   * @return DOMElement|null
-   *         The DOM element of the message, if any.
-   */
-  getMessageForElement: function(elem) {
-    while (elem && elem.parentNode) {
-      if (elem.classList && elem.classList.contains("message")) {
-        return elem;
-      }
-      elem = elem.parentNode;
-    }
-    return null;
-  },
-
-  /**
-   * Select all messages.
-   */
-  selectAllMessages: function() {
-    let selection = this.window.getSelection();
-    selection.removeAllRanges();
-    let range = this.document.createRange();
-    range.selectNodeContents(this.element);
-    selection.addRange(range);
-  },
-
-  /**
-   * Add a message to the selection.
-   *
-   * @param DOMElement elem
-   *        The message element to select.
-   */
-  selectMessage: function(elem) {
-    let selection = this.window.getSelection();
-    selection.removeAllRanges();
-    let range = this.document.createRange();
-    range.selectNodeContents(elem);
-    selection.addRange(range);
-  },
-
-  /**
-   * Open an URL in a new tab.
-   * @see WebConsole.openLink() in hudservice.js
-   */
-  openLink: function() {
-    this.owner.owner.openLink.apply(this.owner.owner, arguments);
-  },
-
-  openLocationInDebugger: function({url, line}) {
-    return this.owner.owner.viewSourceInDebugger(url, line);
-  },
-
-  /**
-   * Open the variables view to inspect an object actor.
-   * @see JSTerm.openVariablesView() in webconsole.js
-   */
-  openVariablesView: function() {
-    this.owner.jsterm.openVariablesView.apply(this.owner.jsterm, arguments);
-  },
-
-  /**
-   * Destroy this ConsoleOutput instance.
-   */
-  destroy: function() {
-    this._dummyElement = null;
-    this.owner = null;
-  },
-}; // ConsoleOutput.prototype
-
-/**
- * Message objects container.
- * @type object
- */
-var Messages = {};
-
-/**
- * The BaseMessage object is used for all types of messages. Every kind of
- * message should use this object as its base.
- *
- * @constructor
- */
-Messages.BaseMessage = function() {
-  this.widgets = new Set();
-  this._onClickAnchor = this._onClickAnchor.bind(this);
-  this._repeatID = { uid: gSequenceId() };
-  this.textContent = "";
-};
-
-Messages.BaseMessage.prototype = {
-  /**
-   * Reference to the ConsoleOutput owner.
-   *
-   * @type object|null
-   *       This is |null| if the message is not yet initialized.
-   */
-  output: null,
-
-  /**
-   * Reference to the parent message object, if this message is in a group or if
-   * it is otherwise owned by another message.
-   *
-   * @type object|null
-   */
-  parent: null,
-
-  /**
-   * Message DOM element.
-   *
-   * @type DOMElement|null
-   *       This is |null| if the message is not yet rendered.
-   */
-  element: null,
-
-  /**
-   * Tells if this message is visible or not.
-   * @type boolean
-   */
-  get visible() {
-    return this.element && this.element.parentNode;
-  },
-
-  /**
-   * The owner DOM document.
-   * @type DOMElement
-   */
-  get document() {
-    return this.output.document;
-  },
-
-  /**
-   * Holds the text-only representation of the message.
-   * @type string
-   */
-  textContent: null,
-
-  /**
-   * Set of widgets included in this message.
-   * @type Set
-   */
-  widgets: null,
-
-  // Properties that allow compatibility with the current Web Console output
-  // implementation.
-  _categoryCompat: null,
-  _severityCompat: null,
-  _categoryNameCompat: null,
-  _severityNameCompat: null,
-  _filterKeyCompat: null,
-
-  /**
-   * Object that is JSON-ified and used as a non-unique ID for tracking
-   * duplicate messages.
-   * @private
-   * @type object
-   */
-  _repeatID: null,
-
-  /**
-   * Initialize the message.
-   *
-   * @param object output
-   *        The ConsoleOutput owner.
-   * @param object [parent=null]
-   *        Optional: a different message object that owns this instance.
-   * @return this
-   */
-  init: function(output, parent = null) {
-    this.output = output;
-    this.parent = parent;
-    return this;
-  },
-
-  /**
-   * Non-unique ID for this message object used for tracking duplicate messages.
-   * Different message kinds can identify themselves based their own criteria.
-   *
-   * @return string
-   */
-  getRepeatID: function() {
-    return JSON.stringify(this._repeatID);
-  },
-
-  /**
-   * Render the message. After this method is invoked the |element| property
-   * will point to the DOM element of this message.
-   * @return this
-   */
-  render: function() {
-    if (!this.element) {
-      this.element = this._renderCompat();
-    }
-    return this;
-  },
-
-  /**
-   * Prepare the message container for the Web Console, such that it is
-   * compatible with the current implementation.
-   * TODO: remove this once bug 778766 is fixed.
-   *
-   * @private
-   * @return Element
-   *         The DOM element that wraps the message.
-   */
-  _renderCompat: function() {
-    let doc = this.output.document;
-    let container = doc.createElementNS(XHTML_NS, "div");
-    container.id = "console-msg-" + gSequenceId();
-    container.className = "message";
-    if (this.category == "input") {
-      // Assistive technology tools shouldn't echo input to the user,
-      // as the user knows what they've just typed.
-      container.setAttribute("aria-live", "off");
-    }
-    container.category = this._categoryCompat;
-    container.severity = this._severityCompat;
-    container.setAttribute("category", this._categoryNameCompat);
-    container.setAttribute("severity", this._severityNameCompat);
-    container.setAttribute("filter", this._filterKeyCompat);
-    container.clipboardText = this.textContent;
-    container.timestamp = this.timestamp;
-    container._messageObject = this;
-
-    return container;
-  },
-
-  /**
-   * Add a click callback to a given DOM element.
-   *
-   * @private
-   * @param Element element
-   *        The DOM element to which you want to add a click event handler.
-   * @param function [callback=this._onClickAnchor]
-   *        Optional click event handler. The default event handler is
-   *        |this._onClickAnchor|.
-   */
-  _addLinkCallback: function(element, callback = this._onClickAnchor) {
-    // This is going into the WebConsoleFrame object instance that owns
-    // the ConsoleOutput object. The WebConsoleFrame owner is the WebConsole
-    // object instance from hudservice.js.
-    // TODO: move _addMessageLinkCallback() into ConsoleOutput once bug 778766
-    // is fixed.
-    this.output.owner._addMessageLinkCallback(element, callback);
-  },
-
-  /**
-   * The default |click| event handler for links in the output. This function
-   * opens the anchor's link in a new tab.
-   *
-   * @private
-   * @param Event event
-   *        The DOM event that invoked this function.
-   */
-  _onClickAnchor: function(event) {
-    this.output.openLink(event.target.href);
-  },
-
-  destroy: function() {
-    // Destroy all widgets that have registered themselves in this.widgets
-    for (let widget of this.widgets) {
-      widget.destroy();
-    }
-    this.widgets.clear();
-  }
-};
-
-/**
- * The NavigationMarker is used to show a page load event.
- *
- * @constructor
- * @extends Messages.BaseMessage
- * @param object response
- *        The response received from the back end.
- * @param number timestamp
- *        The message date and time, milliseconds elapsed since 1 January 1970
- *        00:00:00 UTC.
- */
-Messages.NavigationMarker = function(response, timestamp) {
-  Messages.BaseMessage.call(this);
-
-  // Store the response packet received from the server. It might
-  // be useful for extensions customizing the console output.
-  this.response = response;
-  this._url = response.url;
-  this.textContent = "------ " + this._url;
-  this.timestamp = timestamp;
-};
-
-Messages.NavigationMarker.prototype = extend(Messages.BaseMessage.prototype, {
-  /**
-   * The address of the loading page.
-   * @private
-   * @type string
-   */
-  _url: null,
-
-  /**
-   * Message timestamp.
-   *
-   * @type number
-   *       Milliseconds elapsed since 1 January 1970 00:00:00 UTC.
-   */
-  timestamp: 0,
-
-  _categoryCompat: COMPAT.CATEGORIES.NETWORK,
-  _severityCompat: COMPAT.SEVERITIES.LOG,
-  _categoryNameCompat: "network",
-  _severityNameCompat: "info",
-  _filterKeyCompat: "networkinfo",
-
-  /**
-   * Prepare the DOM element for this message.
-   * @return this
-   */
-  render: function() {
-    if (this.element) {
-      return this;
-    }
-
-    let url = this._url;
-    let pos = url.indexOf("?");
-    if (pos > -1) {
-      url = url.substr(0, pos);
-    }
-
-    let doc = this.output.document;
-    let urlnode = doc.createElementNS(XHTML_NS, "a");
-    urlnode.className = "url";
-    urlnode.textContent = url;
-    urlnode.title = this._url;
-    urlnode.href = this._url;
-    urlnode.draggable = false;
-    this._addLinkCallback(urlnode);
-
-    let render = Messages.BaseMessage.prototype.render.bind(this);
-    render().element.appendChild(urlnode);
-    this.element.classList.add("navigation-marker");
-    this.element.url = this._url;
-    this.element.appendChild(doc.createTextNode("\n"));
-
-    return this;
-  },
-});
-
-/**
- * The Simple message is used to show any basic message in the Web Console.
- *
- * @constructor
- * @extends Messages.BaseMessage
- * @param string|Node|function message
- *        The message to display.
- * @param object [options]
- *        Options for this message:
- *        - category: (string) category that this message belongs to. Defaults
- *        to no category.
- *        - severity: (string) severity of the message. Defaults to no severity.
- *        - timestamp: (number) date and time when the message was recorded.
- *        Defaults to |Date.now()|.
- *        - link: (string) if provided, the message will be wrapped in an anchor
- *        pointing to the given URL here.
- *        - linkCallback: (function) if provided, the message will be wrapped in
- *        an anchor. The |linkCallback| function will be added as click event
- *        handler.
- *        - location: object that tells the message source: url, line, column
- *        and lineText.
- *        - stack: array that tells the message source stack.
- *        - className: (string) additional element class names for styling
- *        purposes.
- *        - private: (boolean) mark this as a private message.
- *        - filterDuplicates: (boolean) true if you do want this message to be
- *        filtered as a potential duplicate message, false otherwise.
- */
-Messages.Simple = function(message, options = {}) {
-  Messages.BaseMessage.call(this);
-
-  this.category = options.category;
-  this.severity = options.severity;
-  this.location = options.location;
-  this.stack = options.stack;
-  this.timestamp = options.timestamp || Date.now();
-  this.prefix = options.prefix;
-  this.private = !!options.private;
-
-  this._message = message;
-  this._className = options.className;
-  this._link = options.link;
-  this._linkCallback = options.linkCallback;
-  this._filterDuplicates = options.filterDuplicates;
-
-  this._onClickCollapsible = this._onClickCollapsible.bind(this);
-};
-
-Messages.Simple.prototype = extend(Messages.BaseMessage.prototype, {
-  /**
-   * Message category.
-   * @type string
-   */
-  category: null,
-
-  /**
-   * Message severity.
-   * @type string
-   */
-  severity: null,
-
-  /**
-   * Message source location. Properties: url, line, column, lineText.
-   * @type object
-   */
-  location: null,
-
-  /**
-   * Holds the stackframes received from the server.
-   *
-   * @private
-   * @type array
-   */
-  stack: null,
-
-  /**
-   * Message prefix
-   * @type string|null
-   */
-  prefix: null,
-
-  /**
-   * Tells if this message comes from a private browsing context.
-   * @type boolean
-   */
-  private: false,
-
-  /**
-   * Custom class name for the DOM element of the message.
-   * @private
-   * @type string
-   */
-  _className: null,
-
-  /**
-   * Message link - if this message is clicked then this URL opens in a new tab.
-   * @private
-   * @type string
-   */
-  _link: null,
-
-  /**
-   * Message click event handler.
-   * @private
-   * @type function
-   */
-  _linkCallback: null,
-
-  /**
-   * Tells if this message should be checked if it is a duplicate of another
-   * message or not.
-   */
-  _filterDuplicates: false,
-
-  /**
-   * The raw message displayed by this Message object. This can be a function,
-   * DOM node or a string.
-   *
-   * @private
-   * @type mixed
-   */
-  _message: null,
-
-  /**
-   * The message's "attachment" element to be displayed under the message.
-   * Used for things like stack traces or tables in console.table().
-   *
-   * @private
-   * @type DOMElement|null
-   */
-  _attachment: null,
-
-  _objectActors: null,
-  _groupDepthCompat: 0,
-
-  /**
-   * Message timestamp.
-   *
-   * @type number
-   *       Milliseconds elapsed since 1 January 1970 00:00:00 UTC.
-   */
-  timestamp: 0,
-
-  get _categoryCompat() {
-    return this.category ?
-           COMPAT.CATEGORIES[this.category.toUpperCase()] : null;
-  },
-  get _severityCompat() {
-    return this.severity ?
-           COMPAT.SEVERITIES[this.severity.toUpperCase()] : null;
-  },
-  get _categoryNameCompat() {
-    return this.category ?
-           COMPAT.CATEGORY_CLASS_FRAGMENTS[this._categoryCompat] : null;
-  },
-  get _severityNameCompat() {
-    return this.severity ?
-           COMPAT.SEVERITY_CLASS_FRAGMENTS[this._severityCompat] : null;
-  },
-
-  get _filterKeyCompat() {
-    return this._categoryCompat !== null && this._severityCompat !== null ?
-           COMPAT.PREFERENCE_KEYS[this._categoryCompat][this._severityCompat] :
-           null;
-  },
-
-  init: function() {
-    Messages.BaseMessage.prototype.init.apply(this, arguments);
-    this._groupDepthCompat = this.output.owner.groupDepth;
-    this._initRepeatID();
-    return this;
-  },
-
-  /**
-   * Tells if the message can be expanded/collapsed.
-   * @type boolean
-   */
-  collapsible: false,
-
-  /**
-   * Getter that tells if this message is collapsed - no details are shown.
-   * @type boolean
-   */
-  get collapsed() {
-    return this.collapsible && this.element && !this.element.hasAttribute("open");
-  },
-
-  _initRepeatID: function() {
-    if (!this._filterDuplicates) {
-      return;
-    }
-
-    // Add the properties we care about for identifying duplicate messages.
-    let rid = this._repeatID;
-    delete rid.uid;
-
-    rid.category = this.category;
-    rid.severity = this.severity;
-    rid.prefix = this.prefix;
-    rid.private = this.private;
-    rid.location = this.location;
-    rid.link = this._link;
-    rid.linkCallback = this._linkCallback + "";
-    rid.className = this._className;
-    rid.groupDepth = this._groupDepthCompat;
-    rid.textContent = "";
-  },
-
-  getRepeatID: function() {
-    // No point in returning a string that includes other properties when there
-    // is a unique ID.
-    if (this._repeatID.uid) {
-      return JSON.stringify({ uid: this._repeatID.uid });
-    }
-
-    return JSON.stringify(this._repeatID);
-  },
-
-  render: function() {
-    if (this.element) {
-      return this;
-    }
-
-    let timestamp = new Widgets.MessageTimestamp(this, this.timestamp).render();
-
-    let icon = this.document.createElementNS(XHTML_NS, "span");
-    icon.className = "icon";
-    icon.title = l10n.getStr("severity." + this._severityNameCompat);
-    if (this.stack) {
-      icon.addEventListener("click", this._onClickCollapsible);
-    }
-
-    let prefixNode;
-    if (this.prefix) {
-      prefixNode = this.document.createElementNS(XHTML_NS, "span");
-      prefixNode.className = "prefix devtools-monospace";
-      prefixNode.textContent = this.prefix + ":";
-    }
-
-    // Apply the current group by indenting appropriately.
-    // TODO: remove this once bug 778766 is fixed.
-    let indent = this._groupDepthCompat * COMPAT.GROUP_INDENT;
-    let indentNode = this.document.createElementNS(XHTML_NS, "span");
-    indentNode.className = "indent";
-    indentNode.style.width = indent + "px";
-
-    let body = this._renderBody();
-
-    Messages.BaseMessage.prototype.render.call(this);
-    if (this._className) {
-      this.element.className += " " + this._className;
-    }
-
-    this.element.appendChild(timestamp.element);
-    this.element.appendChild(indentNode);
-    this.element.appendChild(icon);
-    if (prefixNode) {
-      this.element.appendChild(prefixNode);
-    }
-
-    if (this.stack) {
-      let twisty = this.document.createElementNS(XHTML_NS, "a");
-      twisty.className = "theme-twisty";
-      twisty.href = "#";
-      twisty.title = l10n.getStr("messageToggleDetails");
-      twisty.addEventListener("click", this._onClickCollapsible);
-      this.element.appendChild(twisty);
-      this.collapsible = true;
-      this.element.setAttribute("collapsible", true);
-    }
-
-    this.element.appendChild(body);
-
-    this.element.clipboardText = this.element.textContent;
-
-    if (this.private) {
-      this.element.setAttribute("private", true);
-    }
-
-    // TODO: handle object releasing in a more elegant way once all console
-    // messages use the new API - bug 778766.
-    this.element._objectActors = this._objectActors;
-    this._objectActors = null;
-
-    return this;
-  },
-
-  /**
-   * Render the message body DOM element.
-   * @private
-   * @return Element
-   */
-  _renderBody: function() {
-    let bodyWrapper = this.document.createElementNS(XHTML_NS, "span");
-    bodyWrapper.className = "message-body-wrapper";
-
-    let bodyFlex = this.document.createElementNS(XHTML_NS, "span");
-    bodyFlex.className = "message-flex-body";
-    bodyWrapper.appendChild(bodyFlex);
-
-    let body = this.document.createElementNS(XHTML_NS, "span");
-    body.className = "message-body devtools-monospace";
-    bodyFlex.appendChild(body);
-
-    let anchor, container = body;
-    if (this._link || this._linkCallback) {
-      container = anchor = this.document.createElementNS(XHTML_NS, "a");
-      anchor.href = this._link || "#";
-      anchor.draggable = false;
-      this._addLinkCallback(anchor, this._linkCallback);
-      body.appendChild(anchor);
-    }
-
-    if (typeof this._message == "function") {
-      container.appendChild(this._message(this));
-    } else if (this._message instanceof Ci.nsIDOMNode) {
-      container.appendChild(this._message);
-    } else {
-      container.textContent = this._message;
-    }
-
-    // do this before repeatNode is rendered - it has no effect afterwards
-    this._repeatID.textContent += "|" + container.textContent;
-
-    let repeatNode = this._renderRepeatNode();
-    let location = this._renderLocation();
-
-    if (repeatNode) {
-      bodyFlex.appendChild(this.document.createTextNode(" "));
-      bodyFlex.appendChild(repeatNode);
-    }
-    if (location) {
-      bodyFlex.appendChild(this.document.createTextNode(" "));
-      bodyFlex.appendChild(location);
-    }
-
-    bodyFlex.appendChild(this.document.createTextNode("\n"));
-
-    if (this.stack) {
-      this._attachment = new Widgets.Stacktrace(this, this.stack).render().element;
-    }
-
-    if (this._attachment) {
-      bodyWrapper.appendChild(this._attachment);
-    }
-
-    return bodyWrapper;
-  },
-
-  /**
-   * Render the repeat bubble DOM element part of the message.
-   * @private
-   * @return Element
-   */
-  _renderRepeatNode: function() {
-    if (!this._filterDuplicates) {
-      return null;
-    }
-
-    let repeatNode = this.document.createElementNS(XHTML_NS, "span");
-    repeatNode.setAttribute("value", "1");
-    repeatNode.className = "message-repeats";
-    repeatNode.textContent = 1;
-    repeatNode._uid = this.getRepeatID();
-    return repeatNode;
-  },
-
-  /**
-   * Render the message source location DOM element.
-   * @private
-   * @return Element
-   */
-  _renderLocation: function() {
-    if (!this.location) {
-      return null;
-    }
-
-    let {url, line, column} = this.location;
-    if (IGNORED_SOURCE_URLS.includes(url)) {
-      return null;
-    }
-
-    // The ConsoleOutput owner is a WebConsoleFrame instance from webconsole.js.
-    // TODO: move createLocationNode() into this file when bug 778766 is fixed.
-    return this.output.owner.createLocationNode({url, line, column });
-  },
-
-  /**
-   * The click event handler for the message expander arrow element. This method
-   * toggles the display of message details.
-   *
-   * @private
-   * @param nsIDOMEvent ev
-   *        The DOM event object.
-   * @see this.toggleDetails()
-   */
-  _onClickCollapsible: function(ev) {
-    ev.preventDefault();
-    this.toggleDetails();
-  },
-
-  /**
-   * Expand/collapse message details.
-   */
-  toggleDetails: function() {
-    let twisty = this.element.querySelector(".theme-twisty");
-    if (this.element.hasAttribute("open")) {
-      this.element.removeAttribute("open");
-      twisty.removeAttribute("open");
-    } else {
-      this.element.setAttribute("open", true);
-      twisty.setAttribute("open", true);
-    }
-  },
-}); // Messages.Simple.prototype
-
-/**
- * The Extended message.
- *
- * @constructor
- * @extends Messages.Simple
- * @param array messagePieces
- *        The message to display given as an array of elements. Each array
- *        element can be a DOM node, function, ObjectActor, LongString or
- *        a string.
- * @param object [options]
- *        Options for rendering this message:
- *        - quoteStrings: boolean that tells if you want strings to be wrapped
- *        in quotes or not.
- */
-Messages.Extended = function(messagePieces, options = {}) {
-  Messages.Simple.call(this, null, options);
-
-  this._messagePieces = messagePieces;
-
-  if ("quoteStrings" in options) {
-    this._quoteStrings = options.quoteStrings;
-  }
-
-  this._repeatID.quoteStrings = this._quoteStrings;
-  this._repeatID.messagePieces = JSON.stringify(messagePieces);
-  this._repeatID.actors = new Set(); // using a set to avoid duplicates
-};
-
-Messages.Extended.prototype = extend(Messages.Simple.prototype, {
-  /**
-   * The message pieces displayed by this message instance.
-   * @private
-   * @type array
-   */
-  _messagePieces: null,
-
-  /**
-   * Boolean that tells if the strings displayed in this message are wrapped.
-   * @private
-   * @type boolean
-   */
-  _quoteStrings: true,
-
-  getRepeatID: function() {
-    if (this._repeatID.uid) {
-      return JSON.stringify({ uid: this._repeatID.uid });
-    }
-
-    // Sets are not stringified correctly. Temporarily switching to an array.
-    let actors = this._repeatID.actors;
-    this._repeatID.actors = [...actors];
-    let result = JSON.stringify(this._repeatID);
-    this._repeatID.actors = actors;
-    return result;
-  },
-
-  render: function() {
-    let result = this.document.createDocumentFragment();
-
-    for (let i = 0; i < this._messagePieces.length; i++) {
-      let separator = i > 0 ? this._renderBodyPieceSeparator() : null;
-      if (separator) {
-        result.appendChild(separator);
-      }
-
-      let piece = this._messagePieces[i];
-      result.appendChild(this._renderBodyPiece(piece));
-    }
-
-    this._message = result;
-    this._messagePieces = null;
-    return Messages.Simple.prototype.render.call(this);
-  },
-
-  /**
-   * Render the separator between the pieces of the message.
-   *
-   * @private
-   * @return Element
-   */
-  _renderBodyPieceSeparator: function() {
-    return null;
-  },
-
-  /**
-   * Render one piece/element of the message array.
-   *
-   * @private
-   * @param mixed piece
-   *        Message element to display - this can be a LongString, ObjectActor,
-   *        DOM node or a function to invoke.
-   * @return Element
-   */
-  _renderBodyPiece: function(piece, options = {}) {
-    if (piece instanceof Ci.nsIDOMNode) {
-      return piece;
-    }
-    if (typeof piece == "function") {
-      return piece(this);
-    }
-
-    return this._renderValueGrip(piece, options);
-  },
-
-  /**
-   * Render a grip that represents a value received from the server. This method
-   * picks the appropriate widget to render the value with.
-   *
-   * @private
-   * @param object grip
-   *        The value grip received from the server.
-   * @param object options
-   *        Options for displaying the value. Available options:
-   *        - noStringQuotes - boolean that tells the renderer to not use quotes
-   *        around strings.
-   *        - concise - boolean that tells the renderer to compactly display the
-   *        grip. This is typically set to true when the object needs to be
-   *        displayed in an array preview, or as a property value in object
-   *        previews, etc.
-   *        - shorten - boolean that tells the renderer to display a truncated
-   *        grip.
-   * @return DOMElement
-   *         The DOM element that displays the given grip.
-   */
-  _renderValueGrip: function(grip, options = {}) {
-    let isPrimitive = VariablesView.isPrimitive({ value: grip });
-    let isActorGrip = WebConsoleUtils.isActorGrip(grip);
-    let noStringQuotes = !this._quoteStrings;
-    if ("noStringQuotes" in options) {
-      noStringQuotes = options.noStringQuotes;
-    }
-
-    if (isActorGrip) {
-      this._repeatID.actors.add(grip.actor);
-
-      if (!isPrimitive) {
-        return this._renderObjectActor(grip, options);
-      }
-      if (grip.type == "longString") {
-        let widget = new Widgets.LongString(this, grip, options).render();
-        return widget.element;
-      }
-    }
-
-    let unshortenedGrip = grip;
-    if (options.shorten) {
-      grip = this.shortenValueGrip(grip);
-    }
-
-    let result = this.document.createElementNS(XHTML_NS, "span");
-    if (isPrimitive) {
-      if (Widgets.URLString.prototype.containsURL(grip)) {
-        let widget = new Widgets.URLString(this, grip, unshortenedGrip).render();
-        return widget.element;
-      }
-
-      let className = this.getClassNameForValueGrip(grip);
-      if (className) {
-        result.className = className;
-      }
-
-      result.textContent = VariablesView.getString(grip, {
-        noStringQuotes: noStringQuotes,
-        concise: options.concise,
-      });
-    } else {
-      result.textContent = grip;
-    }
-
-    return result;
-  },
-
-  /**
-   * Shorten grips of the type string, leaves other grips unmodified.
-   *
-   * @param object grip
-   *        Value grip from the server.
-   * @return object
-   *        Possible values of object:
-   *        - A shortened string, if original grip was of string type.
-   *        - The unmodified input grip, if it wasn't of string type.
-   */
-  shortenValueGrip: function(grip) {
-    let shortVal = grip;
-    if (typeof (grip) == "string") {
-      shortVal = grip.replace(/(\r\n|\n|\r)/gm, " ");
-      if (shortVal.length > MAX_STRING_GRIP_LENGTH) {
-        shortVal = shortVal.substring(0, MAX_STRING_GRIP_LENGTH - 1) + ELLIPSIS;
-      }
-    }
-
-    return shortVal;
-  },
-
-  /**
-   * Get a CodeMirror-compatible class name for a given value grip.
-   *
-   * @param object grip
-   *        Value grip from the server.
-   * @return string
-   *         The class name for the grip.
-   */
-  getClassNameForValueGrip: function(grip) {
-    let map = {
-      "number": "cm-number",
-      "longstring": "console-string",
-      "string": "console-string",
-      "regexp": "cm-string-2",
-      "boolean": "cm-atom",
-      "-infinity": "cm-atom",
-      "infinity": "cm-atom",
-      "null": "cm-atom",
-      "undefined": "cm-comment",
-      "symbol": "cm-atom"
-    };
-
-    let className = map[typeof grip];
-    if (!className && grip && grip.type) {
-      className = map[grip.type.toLowerCase()];
-    }
-    if (!className && grip && grip.class) {
-      className = map[grip.class.toLowerCase()];
-    }
-
-    return className;
-  },
-
-  /**
-   * Display an object actor with the appropriate renderer.
-   *
-   * @private
-   * @param object objectActor
-   *        The ObjectActor to display.
-   * @param object options
-   *        Options to use for displaying the ObjectActor.
-   * @see this._renderValueGrip for the available options.
-   * @return DOMElement
-   *         The DOM element that displays the object actor.
-   */
-  _renderObjectActor: function(objectActor, options = {}) {
-    let Widget = Widgets.ObjectRenderers.byClass[objectActor.class];
-
-    let { preview } = objectActor;
-    if ((!Widget || (Widget.canRender && !Widget.canRender(objectActor)))
-        && preview
-        && preview.kind) {
-      Widget = Widgets.ObjectRenderers.byKind[preview.kind];
-    }
-
-    if (!Widget || (Widget.canRender && !Widget.canRender(objectActor))) {
-      Widget = Widgets.JSObject;
-    }
-
-    let instance = new Widget(this, objectActor, options).render();
-    return instance.element;
-  },
-}); // Messages.Extended.prototype
-
-/**
- * The JavaScriptEvalOutput message.
- *
- * @constructor
- * @extends Messages.Extended
- * @param object evalResponse
- *        The evaluation response packet received from the server.
- * @param string [errorMessage]
- *        Optional error message to display.
- * @param string [errorDocLink]
- * Optional error doc URL to link to.
- */
-Messages.JavaScriptEvalOutput = function(evalResponse, errorMessage, errorDocLink) {
-  let severity = "log", msg, quoteStrings = true;
-
-  // Store also the response packet from the back end. It might
-  // be useful to extensions customizing the console output.
-  this.response = evalResponse;
-
-  if (typeof (errorMessage) !== "undefined") {
-    severity = "error";
-    msg = errorMessage;
-    quoteStrings = false;
-  } else {
-    msg = evalResponse.result;
-  }
-
-  let options = {
-    className: "cm-s-mozilla",
-    timestamp: evalResponse.timestamp,
-    category: "output",
-    severity: severity,
-    quoteStrings: quoteStrings,
-  };
-
-  let messages = [msg];
-  if (errorDocLink) {
-    messages.push(errorDocLink);
-  }
-
-  Messages.Extended.call(this, messages, options);
-};
-
-Messages.JavaScriptEvalOutput.prototype = Messages.Extended.prototype;
-
-/**
- * The ConsoleGeneric message is used for console API calls.
- *
- * @constructor
- * @extends Messages.Extended
- * @param object packet
- *        The Console API call packet received from the server.
- */
-Messages.ConsoleGeneric = function(packet) {
-  let options = {
-    className: "cm-s-mozilla",
-    timestamp: packet.timeStamp,
-    category: packet.category || "webdev",
-    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
-    prefix: packet.prefix,
-    private: packet.private,
-    filterDuplicates: true,
-    location: {
-      url: packet.filename,
-      line: packet.lineNumber,
-      column: packet.columnNumber
-    },
-  };
-
-  switch (packet.level) {
-    case "count": {
-      let counter = packet.counter, label = counter.label;
-      if (!label) {
-        label = l10n.getStr("noCounterLabel");
-      }
-      Messages.Extended.call(this, [label + ": " + counter.count], options);
-      break;
-    }
-    default:
-      Messages.Extended.call(this, packet.arguments, options);
-      break;
-  }
-
-  this._repeatID.consoleApiLevel = packet.level;
-  this._repeatID.styles = packet.styles;
-  this.stack = this._repeatID.stacktrace = packet.stacktrace;
-  this._styles = packet.styles || [];
-};
-
-Messages.ConsoleGeneric.prototype = extend(Messages.Extended.prototype, {
-  _styles: null,
-
-  _renderBodyPieceSeparator: function() {
-    return this.document.createTextNode(" ");
-  },
-
-  render: function() {
-    let result = this.document.createDocumentFragment();
-    this._renderBodyPieces(result);
-
-    this._message = result;
-    this._stacktrace = null;
-
-    Messages.Simple.prototype.render.call(this);
-
-    return this;
-  },
-
-  _renderBodyPieces: function(container) {
-    let lastStyle = null;
-    let stylePieces = this._styles.length > 0 ? this._styles.length : 1;
-
-    for (let i = 0; i < this._messagePieces.length; i++) {
-      // Pieces with an associated style definition come from "%c" formatting.
-      // For body pieces beyond that, add a separator before each one.
-      if (i >= stylePieces) {
-        container.appendChild(this._renderBodyPieceSeparator());
-      }
-
-      let piece = this._messagePieces[i];
-      let style = this._styles[i];
-
-      // No long string support.
-      lastStyle = (style && typeof style == "string") ?
-                  this.cleanupStyle(style) : null;
-
-      container.appendChild(this._renderBodyPiece(piece, lastStyle));
-    }
-
-    this._messagePieces = null;
-    this._styles = null;
-  },
-
-  _renderBodyPiece: function(piece, style) {
-    // Skip quotes for top-level strings.
-    let options = { noStringQuotes: true };
-    let elem = Messages.Extended.prototype._renderBodyPiece.call(this, piece, options);
-    let result = elem;
-
-    if (style) {
-      if (elem.nodeType == nodeConstants.ELEMENT_NODE) {
-        elem.style = style;
-      } else {
-        let span = this.document.createElementNS(XHTML_NS, "span");
-        span.style = style;
-        span.appendChild(elem);
-        result = span;
-      }
-    }
-
-    return result;
-  },
-
-  /**
-   * Given a style attribute value, return a cleaned up version of the string
-   * such that:
-   *
-   * - no external URL is allowed to load. See RE_CLEANUP_STYLES.
-   * - only some of the properties are allowed, based on a whitelist. See
-   *   RE_ALLOWED_STYLES.
-   *
-   * @param string style
-   *        The style string to cleanup.
-   * @return string
-   *         The style value after cleanup.
-   */
-  cleanupStyle: function(style) {
-    for (let r of RE_CLEANUP_STYLES) {
-      style = style.replace(r, "notallowed");
-    }
-
-    let dummy = this.output._dummyElement;
-    if (!dummy) {
-      dummy = this.output._dummyElement =
-        this.document.createElementNS(XHTML_NS, "div");
-    }
-    dummy.style = style;
-
-    let toRemove = [];
-    for (let i = 0; i < dummy.style.length; i++) {
-      let prop = dummy.style[i];
-      if (!RE_ALLOWED_STYLES.test(prop)) {
-        toRemove.push(prop);
-      }
-    }
-
-    for (let prop of toRemove) {
-      dummy.style.removeProperty(prop);
-    }
-
-    style = dummy.style.cssText;
-
-    dummy.style = "";
-
-    return style;
-  },
-}); // Messages.ConsoleGeneric.prototype
-
-/**
- * The ConsoleTrace message is used for console.trace() calls.
- *
- * @constructor
- * @extends Messages.Simple
- * @param object packet
- *        The Console API call packet received from the server.
- */
-Messages.ConsoleTrace = function(packet) {
-  let options = {
-    className: "cm-s-mozilla",
-    timestamp: packet.timeStamp,
-    category: packet.category || "webdev",
-    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
-    private: packet.private,
-    filterDuplicates: true,
-    location: {
-      url: packet.filename,
-      line: packet.lineNumber,
-    },
-  };
-
-  Messages.Simple.call(this, null, options);
-
-  this._repeatID.consoleApiLevel = packet.level;
-  this._stacktrace = this._repeatID.stacktrace = packet.stacktrace;
-  this._arguments = packet.arguments;
-};
-
-Messages.ConsoleTrace.prototype = extend(Messages.Simple.prototype, {
-  /**
-   * Holds the stackframes received from the server.
-   *
-   * @private
-   * @type array
-   */
-  _stacktrace: null,
-
-  /**
-   * Holds the arguments the content script passed to the console.trace()
-   * method. This array is cleared when the message is initialized, and
-   * associated actors are released.
-   *
-   * @private
-   * @type array
-   */
-  _arguments: null,
-
-  init: function() {
-    let result = Messages.Simple.prototype.init.apply(this, arguments);
-
-    // We ignore console.trace() arguments. Release object actors.
-    if (Array.isArray(this._arguments)) {
-      for (let arg of this._arguments) {
-        if (WebConsoleUtils.isActorGrip(arg)) {
-          this.output._releaseObject(arg.actor);
-        }
-      }
-    }
-    this._arguments = null;
-
-    return result;
-  },
-
-  render: function() {
-    this._message = this._renderMessage();
-    this._attachment = this._renderStack();
-
-    Messages.Simple.prototype.render.apply(this, arguments);
-    this.element.setAttribute("open", true);
-    return this;
-  },
-
-  /**
-   * Render the console messageNode
-   */
-  _renderMessage: function() {
-    let cmvar = this.document.createElementNS(XHTML_NS, "span");
-    cmvar.className = "cm-variable";
-    cmvar.textContent = "console";
-
-    let cmprop = this.document.createElementNS(XHTML_NS, "span");
-    cmprop.className = "cm-property";
-    cmprop.textContent = "trace";
-
-    let frag = this.document.createDocumentFragment();
-    frag.appendChild(cmvar);
-    frag.appendChild(this.document.createTextNode("."));
-    frag.appendChild(cmprop);
-    frag.appendChild(this.document.createTextNode("():"));
-
-    return frag;
-  },
-
-  /**
-   * Render the stack frames.
-   *
-   * @private
-   * @return DOMElement
-   */
-  _renderStack: function() {
-    return new Widgets.Stacktrace(this, this._stacktrace).render().element;
-  },
-}); // Messages.ConsoleTrace.prototype
-
-/**
- * The ConsoleTable message is used for console.table() calls.
- *
- * @constructor
- * @extends Messages.Extended
- * @param object packet
- *        The Console API call packet received from the server.
- */
-Messages.ConsoleTable = function(packet) {
-  let options = {
-    className: "cm-s-mozilla",
-    timestamp: packet.timeStamp,
-    category: packet.category || "webdev",
-    severity: CONSOLE_API_LEVELS_TO_SEVERITIES[packet.level],
-    private: packet.private,
-    filterDuplicates: false,
-    location: {
-      url: packet.filename,
-      line: packet.lineNumber,
-    },
-  };
-
-  this._populateTableData = this._populateTableData.bind(this);
-  this._renderMessage = this._renderMessage.bind(this);
-  Messages.Extended.call(this, [this._renderMessage], options);
-
-  this._repeatID.consoleApiLevel = packet.level;
-  this._arguments = packet.arguments;
-};
-
-Messages.ConsoleTable.prototype = extend(Messages.Extended.prototype, {
-  /**
-   * Holds the arguments the content script passed to the console.table()
-   * method.
-   *
-   * @private
-   * @type array
-   */
-  _arguments: null,
-
-  /**
-   * Array of objects that holds the data to log in the table.
-   *
-   * @private
-   * @type array
-   */
-  _data: null,
-
-  /**
-   * Key value pair of the id and display name for the columns in the table.
-   * Refer to the TableWidget API.
-   *
-   * @private
-   * @type object
-   */
-  _columns: null,
-
-  /**
-   * A promise that resolves when the table data is ready or null if invalid
-   * arguments are provided.
-   *
-   * @private
-   * @type promise|null
-   */
-  _populatePromise: null,
-
-  init: function() {
-    let result = Messages.Extended.prototype.init.apply(this, arguments);
-    this._data = [];
-    this._columns = {};
-
-    this._populatePromise = this._populateTableData();
-
-    return result;
-  },
-
-  /**
-   * Sets the key value pair of the id and display name for the columns in the
-   * table.
-   *
-   * @private
-   * @param array|string columns
-   *        Either a string or array containing the names for the columns in
-   *        the output table.
-   */
-  _setColumns: function(columns) {
-    if (columns.class == "Array") {
-      let items = columns.preview.items;
-
-      for (let item of items) {
-        if (typeof item == "string") {
-          this._columns[item] = item;
-        }
-      }
-    } else if (typeof columns == "string" && columns) {
-      this._columns[columns] = columns;
-    }
-  },
-
-  /**
-   * Retrieves the table data and columns from the arguments received from the
-   * server.
-   *
-   * @return Promise|null
-   *         Returns a promise that resolves when the table data is ready or
-   *         null if the arguments are invalid.
-   */
-  _populateTableData: function() {
-    let deferred = defer();
-
-    if (this._arguments.length <= 0) {
-      return deferred.reject();
-    }
-
-    let data = this._arguments[0];
-    if (data.class != "Array" && data.class != "Object" &&
-        data.class != "Map" && data.class != "Set" &&
-        data.class != "WeakMap" && data.class != "WeakSet") {
-      return deferred.reject();
-    }
-
-    let hasColumnsArg = false;
-    if (this._arguments.length > 1) {
-      if (data.class == "Object" || data.class == "Array") {
-        this._columns._index = l10n.getStr("table.index");
-      } else {
-        this._columns._index = l10n.getStr("table.iterationIndex");
-      }
-
-      this._setColumns(this._arguments[1]);
-      hasColumnsArg = true;
-    }
-
-    if (data.class == "Object" || data.class == "Array") {
-      // Get the object properties, and parse the key and value properties into
-      // the table data and columns.
-      this.client = new ObjectClient(this.output.owner.jsterm.hud.proxy.client,
-          data);
-      this.client.getPrototypeAndProperties(response => {
-        let {ownProperties} = response;
-        let rowCount = 0;
-        let columnCount = 0;
-
-        for (let index of Object.keys(ownProperties || {})) {
-          // Avoid outputting the length property if the data argument provided
-          // is an array
-          if (data.class == "Array" && index == "length") {
-            continue;
-          }
-
-          if (!hasColumnsArg) {
-            this._columns._index = l10n.getStr("table.index");
-          }
-
-          if (data.class == "Array") {
-            if (index == parseInt(index, 10)) {
-              index = parseInt(index, 10);
-            }
-          }
-
-          let property = ownProperties[index].value;
-          let item = { _index: index };
-
-          if (property.class == "Object" || property.class == "Array") {
-            let {preview} = property;
-            let entries = property.class == "Object" ?
-                preview.ownProperties : preview.items;
-
-            for (let key of Object.keys(entries)) {
-              let value = property.class == "Object" ?
-                  preview.ownProperties[key].value : preview.items[key];
-
-              item[key] = this._renderValueGrip(value, { concise: true });
-
-              if (!hasColumnsArg && !(key in this._columns) &&
-                  (++columnCount <= TABLE_COLUMN_MAX_ITEMS)) {
-                this._columns[key] = key;
-              }
-            }
-          } else {
-            // Display the value for any non-object data input.
-            item._value = this._renderValueGrip(property, { concise: true });
-
-            if (!hasColumnsArg && !("_value" in this._columns)) {
-              this._columns._value = l10n.getStr("table.value");
-            }
-          }
-
-          this._data.push(item);
-
-          if (++rowCount == TABLE_ROW_MAX_ITEMS) {
-            break;
-          }
-        }
-
-        deferred.resolve();
-      });
-    } else if (data.class == "Map" || data.class == "WeakMap") {
-      let entries = data.preview.entries;
-
-      if (!hasColumnsArg) {
-        this._columns._index = l10n.getStr("table.iterationIndex");
-        this._columns._key = l10n.getStr("table.key");
-        this._columns._value = l10n.getStr("table.value");
-      }
-
-      let rowCount = 0;
-      for (let [key, value] of entries) {
-        let item = {
-          _index: rowCount,
-          _key: this._renderValueGrip(key, { concise: true }),
-          _value: this._renderValueGrip(value, { concise: true })
-        };
-
-        this._data.push(item);
-
-        if (++rowCount == TABLE_ROW_MAX_ITEMS) {
-          break;
-        }
-      }
-
-      deferred.resolve();
-    } else if (data.class == "Set" || data.class == "WeakSet") {
-      let entries = data.preview.items;
-
-      if (!hasColumnsArg) {
-        this._columns._index = l10n.getStr("table.iterationIndex");
-        this._columns._value = l10n.getStr("table.value");
-      }
-
-      let rowCount = 0;
-      for (let entry of entries) {
-        let item = {
-          _index: rowCount,
-          _value: this._renderValueGrip(entry, { concise: true })
-        };
-
-        this._data.push(item);
-
-        if (++rowCount == TABLE_ROW_MAX_ITEMS) {
-          break;
-        }
-      }
-
-      deferred.resolve();
-    }
-
-    return deferred.promise;
-  },
-
-  render: function() {
-    this._attachment = this._renderTable();
-    Messages.Extended.prototype.render.apply(this, arguments);
-    this.element.setAttribute("open", true);
-    return this;
-  },
-
-  _renderMessage: function() {
-    let cmvar = this.document.createElementNS(XHTML_NS, "span");
-    cmvar.className = "cm-variable";
-    cmvar.textContent = "console";
-
-    let cmprop = this.document.createElementNS(XHTML_NS, "span");
-    cmprop.className = "cm-property";
-    cmprop.textContent = "table";
-
-    let frag = this.document.createDocumentFragment();
-    frag.appendChild(cmvar);
-    frag.appendChild(this.document.createTextNode("."));
-    frag.appendChild(cmprop);
-    frag.appendChild(this.document.createTextNode("():"));
-
-    return frag;
-  },
-
-  /**
-   * Render the table.
-   *
-   * @private
-   * @return DOMElement
-   */
-  _renderTable: function() {
-    let result = this.document.createElementNS(XHTML_NS, "div");
-
-    if (this._populatePromise) {
-      this._populatePromise.then(() => {
-        if (this._data.length > 0) {
-          let widget = new Widgets.Table(this, this._data, this._columns).render();
-          result.appendChild(widget.element);
-        }
-
-        result.scrollIntoView();
-        this.output.owner.emit("messages-table-rendered");
-
-        // Release object actors
-        if (Array.isArray(this._arguments)) {
-          for (let arg of this._arguments) {
-            if (WebConsoleUtils.isActorGrip(arg)) {
-              this.output._releaseObject(arg.actor);
-            }
-          }
-        }
-        this._arguments = null;
-      });
-    }
-
-    return result;
-  },
-}); // Messages.ConsoleTable.prototype
-
-var Widgets = {};
-
-/**
- * The base widget class.
- *
- * @constructor
- * @param object message
- *        The owning message.
- */
-Widgets.BaseWidget = function(message) {
-  this.message = message;
-};
-
-Widgets.BaseWidget.prototype = {
-  /**
-   * The owning message object.
-   * @type object
-   */
-  message: null,
-
-  /**
-   * The DOM element of the rendered widget.
-   * @type Element
-   */
-  element: null,
-
-  /**
-   * Getter for the DOM document that holds the output.
-   * @type Document
-   */
-  get document() {
-    return this.message.document;
-  },
-
-  /**
-   * The ConsoleOutput instance that owns this widget instance.
-   */
-  get output() {
-    return this.message.output;
-  },
-
-  /**
-   * Render the widget DOM element.
-   * @return this
-   */
-  render: function() { },
-
-  /**
-   * Destroy this widget instance.
-   */
-  destroy: function() { },
-
-  /**
-   * Helper for creating DOM elements for widgets.
-   *
-   * Usage:
-   *   this.el("tag#id.class.names"); // create element "tag" with ID "id" and
-   *   two class names, .class and .names.
-   *
-   *   this.el("span", { attr1: "value1", ... }) // second argument can be an
-   *   object that holds element attributes and values for the new DOM element.
-   *
-   *   this.el("p", { attr1: "value1", ... }, "text content"); // the third
-   *   argument can include the default .textContent of the new DOM element.
-   *
-   *   this.el("p", "text content"); // if the second argument is not an object,
-   *   it will be used as .textContent for the new DOM element.
-   *
-   * @param string tagNameIdAndClasses
-   *        Tag name for the new element, optionally followed by an ID and/or
-   *        class names. Examples: "span", "div#fooId", "div.class.names",
-   *        "p#id.class".
-   * @param string|object [attributesOrTextContent]
-   *        If this argument is an object it will be used to set the attributes
-   *        of the new DOM element. Otherwise, the value becomes the
-   *        .textContent of the new DOM element.
-   * @param string [textContent]
-   *        If this argument is provided the value is used as the textContent of
-   *        the new DOM element.
-   * @return DOMElement
-   *         The new DOM element.
-   */
-  el: function(tagNameIdAndClasses) {
-    let attrs, text;
-    if (typeof arguments[1] == "object") {
-      attrs = arguments[1];
-      text = arguments[2];
-    } else {
-      text = arguments[1];
-    }
-
-    let tagName = tagNameIdAndClasses.split(/#|\./)[0];
-
-    let elem = this.document.createElementNS(XHTML_NS, tagName);
-    for (let name of Object.keys(attrs || {})) {
-      elem.setAttribute(name, attrs[name]);
-    }
-    if (text !== undefined && text !== null) {
-      elem.textContent = text;
-    }
-
-    let idAndClasses = tagNameIdAndClasses.match(/([#.][^#.]+)/g);
-    for (let idOrClass of (idAndClasses || [])) {
-      if (idOrClass.charAt(0) == "#") {
-        elem.id = idOrClass.substr(1);
-      } else {
-        elem.classList.add(idOrClass.substr(1));
-      }
-    }
-
-    return elem;
-  },
-};
-
-/**
- * The timestamp widget.
- *
- * @constructor
- * @param object message
- *        The owning message.
- * @param number timestamp
- *        The UNIX timestamp to display.
- */
-Widgets.MessageTimestamp = function(message, timestamp) {
-  Widgets.BaseWidget.call(this, message);
-  this.timestamp = timestamp;
-};
-
-Widgets.MessageTimestamp.prototype = extend(Widgets.BaseWidget.prototype, {
-  /**
-   * The UNIX timestamp.
-   * @type number
-   */
-  timestamp: 0,
-
-  render: function() {
-    if (this.element) {
-      return this;
-    }
-
-    this.element = this.document.createElementNS(XHTML_NS, "span");
-    this.element.className = "timestamp devtools-monospace";
-    this.element.textContent = l10n.timestampString(this.timestamp) + " ";
-
-    return this;
-  },
-}); // Widgets.MessageTimestamp.prototype
-
-/**
- * The URLString widget, for rendering strings where at least one token is a
- * URL.
- *
- * @constructor
- * @param object message
- *        The owning message.
- * @param string str
- *        The string, which contains at least one valid URL.
- * @param string unshortenedStr
- *        The unshortened form of the string, if it was shortened.
- */
-Widgets.URLString = function(message, str, unshortenedStr) {
-  Widgets.BaseWidget.call(this, message);
-  this.str = str;
-  this.unshortenedStr = unshortenedStr;
-};
-
-Widgets.URLString.prototype = extend(Widgets.BaseWidget.prototype, {
-  /**
-   * The string to format, which contains at least one valid URL.
-   * @type string
-   */
-  str: "",
-
-  render: function() {
-    if (this.element) {
-      return this;
-    }
-
-    // The rendered URLString will be a <span> containing a number of text
-    // <spans> for non-URL tokens and <a>'s for URL tokens.
-    this.element = this.el("span", {
-      class: "console-string"
-    });
-    this.element.appendChild(this._renderText("\""));
-
-    // As we walk through the tokens of the source string, we make sure to preserve
-    // the original whitespace that separated the tokens.
-    let tokens = this.str.split(/\s+/);
-    let textStart = 0;
-    let tokenStart;
-    for (let i = 0; i < tokens.length; i++) {
-      let token = tokens[i];
-      let unshortenedToken;
-      tokenStart = this.str.indexOf(token, textStart);
-      if (this._isURL(token)) {
-        // The last URL in the string might be shortened.  If so, get the
-        // real URL so the rendered link can point to it.
-        if (i === tokens.length - 1 && this.unshortenedStr) {
-          unshortenedToken = this.unshortenedStr.slice(tokenStart).split(/\s+/, 1)[0];
-        }
-        this.element.appendChild(this._renderText(this.str.slice(textStart, tokenStart)));
-        textStart = tokenStart + token.length;
-        this.element.appendChild(this._renderURL(token, unshortenedToken));
-      }
-    }
-
-    // Clean up any non-URL text at the end of the source string.
-    const rendered = this._renderText(this.str.slice(textStart, this.str.length));
-    this.element.appendChild(rendered);
-    this.element.appendChild(this._renderText("\""));
-
-    return this;
-  },
-
-  /**
-   * Determines whether a grip is a string containing a URL.
-   *
-   * @param string grip
-   *        The grip, which may contain a URL.
-   * @return boolean
-   *         Whether the grip is a string containing a URL.
-   */
-  containsURL: function(grip) {
-    if (typeof grip != "string") {
-      return false;
-    }
-
-    let tokens = grip.split(/\s+/);
-    return tokens.some(this._isURL);
-  },
-
-  /**
-   * Determines whether a string token is a valid URL.
-   *
-   * @param string token
-   *        The token.
-   * @return boolean
-   *         Whenther the token is a URL.
-   */
-  _isURL: function(token) {
-    try {
-      if (!validProtocols.test(token)) {
-        return false;
-      }
-      new URL(token);
-      return true;
-    } catch (e) {
-      return false;
-    }
-  },
-
-  /**
-   * Renders a string as a URL.
-   *
-   * @param string url
-   *        The string to be rendered as a url.
-   * @param string fullUrl
-   *        The unshortened form of the URL, if it was shortened.
-   * @return DOMElement
-   *         An element containing the rendered string.
-   */
-  _renderURL: function(url, fullUrl) {
-    let unshortened = fullUrl || url;
-    let result = this.el("a", {
-      class: "url",
-      title: unshortened,
-      href: unshortened,
-      draggable: false
-    }, url);
-    this.message._addLinkCallback(result);
-    return result;
-  },
-
-  _renderText: function(text) {
-    return this.el("span", text);
-  },
-}); // Widgets.URLString.prototype
-
-/**
- * Widget used for displaying ObjectActors that have no specialised renderers.
- *
- * @constructor
- * @param object message
- *        The owning message.
- * @param object objectActor
- *        The ObjectActor to display.
- * @param object [options]
- *        Options for displaying the given ObjectActor. See
- *        Messages.Extended.prototype._renderValueGrip for the available
- *        options.
- */
-Widgets.JSObject = function(message, objectActor, options = {}) {
-  Widgets.BaseWidget.call(this, message);
-  this.objectActor = objectActor;
-  this.options = options;
-  this._onClick = this._onClick.bind(this);
-};
-
-Widgets.JSObject.prototype = extend(Widgets.BaseWidget.prototype, {
-  /**
-   * The ObjectActor displayed by the widget.
-   * @type object
-   */
-  objectActor: null,
-
-  render: function() {
-    if (!this.element) {
-      this._render();
-    }
-
-    return this;
-  },
-
-  _render: function() {
-    let str = VariablesView.getString(this.objectActor, this.options);
-    let className = this.message.getClassNameForValueGrip(this.objectActor);
-    if (!className && this.objectActor.class == "Object") {
-      className = "cm-variable";
-    }
-
-    this.element = this._anchor(str, { className: className });
-  },
-
-  /**
-   * Render a concise representation of an object.
-   */
-  _renderConciseObject: function() {
-    this.element = this._anchor(this.objectActor.class,
-                                { className: "cm-variable" });
-  },
-
-  /**
-   * Render the `<class> { ` prefix of an object.
-   */
-  _renderObjectPrefix: function() {
-    let { kind } = this.objectActor.preview;
-    this.element = this.el("span.kind-" + kind);
-    this._anchor(this.objectActor.class, { className: "cm-variable" });
-    this._text(" { ");
-  },
-
-  /**
-   * Render the ` }` suffix of an object.
-   */
-  _renderObjectSuffix: function() {
-    this._text(" }");
-  },
-
-  /**
-   * Render an object property.
-   *
-   * @param String key
-   *        The property name.
-   * @param Object value
-   *        The property value, as an RDP grip.
-   * @param nsIDOMNode container
-   *        The container node to render to.
-   * @param Boolean needsComma
-   *        True if there was another property before this one and we need to
-   *        separate them with a comma.
-   * @param Boolean valueIsText
-   *        Add the value as is, don't treat it as a grip and pass it to
-   *        `_renderValueGrip`.
-   */
-  _renderObjectProperty: function(
-    key,
-    value,
-    container,
-    needsComma,
-    valueIsText = false
-  ) {
-    if (needsComma) {
-      this._text(", ");
-    }
-
-    container.appendChild(this.el("span.cm-property", key));
-    this._text(": ");
-
-    if (valueIsText) {
-      this._text(value);
-    } else {
-      let valueElem = this.message._renderValueGrip(value, {
-        concise: true,
-        shorten: true
-      });
-      container.appendChild(valueElem);
-    }
-  },
-
-  /**
-   * Render this object's properties.
-   *
-   * @param nsIDOMNode container
-   *        The container node to render to.
-   * @param Boolean needsComma
-   *        True if there was another property before this one and we need to
-   *        separate them with a comma.
-   */
-  _renderObjectProperties: function(container, needsComma) {
-    let { preview } = this.objectActor;
-    let { ownProperties, safeGetterValues } = preview;
-
-    let shown = 0;
-
-    let getValue = desc => {
-      if (desc.get) {
-        return "Getter";
-      } else if (desc.set) {
-        return "Setter";
-      }
-      return desc.value;
-    };
-
-    for (let key of Object.keys(ownProperties || {})) {
-      this._renderObjectProperty(key, getValue(ownProperties[key]), container,
-                                 shown > 0 || needsComma,
-                                 ownProperties[key].get || ownProperties[key].set);
-      shown++;
-    }
-
-    let ownPropertiesShown = shown;
-
-    for (let key of Object.keys(safeGetterValues || {})) {
-      this._renderObjectProperty(key, safeGetterValues[key].getterValue,
-                                 container, shown > 0 || needsComma);
-      shown++;
-    }
-
-    if (typeof preview.ownPropertiesLength == "number" &&
-        ownPropertiesShown < preview.ownPropertiesLength) {
-      this._text(", ");
-
-      let n = preview.ownPropertiesLength - ownPropertiesShown;
-      let str = VariablesView.stringifiers._getNMoreString(n);
-      this._anchor(str);
-    }
-  },
-
-  /**
-   * Render an anchor with a given text content and link.
-   *
-   * @private
-   * @param string text
-   *        Text to show in the anchor.
-   * @param object [options]
-   *        Available options:
-   *        - onClick (function): "click" event handler.By default a click on
-   *        the anchor opens the variables view for the current object actor
-   *        (this.objectActor).
-   *        - href (string): if given the string is used as a link, and clicks
-   *        on the anchor open the link in a new tab.
-   *        - appendTo (DOMElement): append the element to the given DOM
-   *        element. If not provided, the anchor is appended to |this.element|
-   *        if it is available. If |appendTo| is provided and if it is a falsy
-   *        value, the anchor is not appended to any element.
-   * @return DOMElement
-   *         The DOM element of the new anchor.
-   */
-  _anchor: function(text, options = {}) {
-    if (!options.onClick) {
-      // If the anchor has an URL, open it in a new tab. If not, show the
-      // current object actor.
-      options.onClick = options.href ? this._onClickAnchor : this._onClick;
-    }
-
-    options.onContextMenu = options.onContextMenu || this._onContextMenu;
-
-    let anchor = this.el("a", {
-      class: options.className,
-      draggable: false,
-      href: options.href || "#",
-    }, text);
-
-    this.message._addLinkCallback(anchor, options.onClick);
-
-    anchor.addEventListener("contextmenu", options.onContextMenu.bind(this));
-
-    if (options.appendTo) {
-      options.appendTo.appendChild(anchor);
-    } else if (!("appendTo" in options) && this.element) {
-      this.element.appendChild(anchor);
-    }
-
-    return anchor;
-  },
-
-  openObjectInVariablesView: function() {
-    this.output.openVariablesView({
-      label: VariablesView.getString(this.objectActor, { concise: true }),
-      objectActor: this.objectActor,
-      autofocus: true,
-    });
-  },
-
-  storeObjectInWindow: function() {
-    let evalString = `{ let i = 0;
-      while (this.hasOwnProperty("temp" + i) && i < 1000) {
-        i++;
-      }
-      this["temp" + i] = _self;
-      "temp" + i;
-    }`;
-    let options = {
-      selectedObjectActor: this.objectActor.actor,
-    };
-
-    this.output.owner.jsterm.requestEvaluation(evalString, options).then((res) => {
-      this.output.owner.jsterm.focus();
-      this.output.owner.jsterm.setInputValue(res.result);
-    });
-  },
-
-  /**
-   * The click event handler for objects shown inline.
-   * @private
-   */
-  _onClick: function() {
-    this.openObjectInVariablesView();
-  },
-
-  _onContextMenu: function(ev) {
-    // TODO offer a nice API for the context menu.
-    // Probably worth to take a look at Firebug's way
-    // https://github.com/firebug/firebug/blob/master/extension/content/firebug/chrome/menu.js
-    let doc = ev.target.ownerDocument;
-    let cmPopup = doc.getElementById("output-contextmenu");
-
-    let openInVarViewCmd = doc.getElementById("menu_openInVarView");
-    let openVarView = this.openObjectInVariablesView.bind(this);
-    openInVarViewCmd.addEventListener("command", openVarView);
-    openInVarViewCmd.removeAttribute("disabled");
-    cmPopup.addEventListener("popuphiding", function() {
-      openInVarViewCmd.removeEventListener("command", openVarView);
-      openInVarViewCmd.setAttribute("disabled", "true");
-    }, {once: true});
-
-    // 'Store as global variable' command isn't supported on pre-44 servers,
-    // so remove it from the menu in that case.
-    let storeInGlobalCmd = doc.getElementById("menu_storeAsGlobal");
-    if (!this.output.webConsoleClient.traits.selectedObjectActor) {
-      storeInGlobalCmd.remove();
-    } else if (storeInGlobalCmd) {
-      let storeObjectInWindow = this.storeObjectInWindow.bind(this);
-      storeInGlobalCmd.addEventListener("command", storeObjectInWindow);
-      storeInGlobalCmd.removeAttribute("disabled");
-      cmPopup.addEventListener("popuphiding", function() {
-        storeInGlobalCmd.removeEventListener("command", storeObjectInWindow);
-        storeInGlobalCmd.setAttribute("disabled", "true");
-      }, {once: true});
-    }
-  },
-
-  /**
-   * Add a string to the message.
-   *
-   * @private
-   * @param string str
-   *        String to add.
-   * @param DOMElement [target = this.element]
-   *        Optional DOM element to append the string to. The default is
-   *        this.element.
-   */
-  _text: function(str, target = this.element) {
-    target.appendChild(this.document.createTextNode(str));
-  },
-}); // Widgets.JSObject.prototype
-
-Widgets.ObjectRenderers = {};
-Widgets.ObjectRenderers.byKind = {};
-Widgets.ObjectRenderers.byClass = {};
-
-/**
- * Add an object renderer.
- *
- * @param object obj
- *        An object that represents the renderer. Properties:
- *        - byClass (string, optional): this renderer will be used for the given
- *        object class.
- *        - byKind (string, optional): this renderer will be used for the given
- *        object kind.
- *        One of byClass or byKind must be provided.
- *        - extends (object, optional): the renderer object extends the given
- *        object. Default: Widgets.JSObject.
- *        - canRender (function, optional): this method is invoked when
- *        a candidate object needs to be displayed. The method is invoked as
- *        a static method, as such, none of the properties of the renderer
- *        object will be available. You get one argument: the object actor grip
- *        received from the server. If the method returns true, then this
- *        renderer is used for displaying the object, otherwise not.
- *        - initialize (function, optional): the constructor of the renderer
- *        widget. This function is invoked with the following arguments: the
- *        owner message object instance, the object actor grip to display, and
- *        an options object. See Messages.Extended.prototype._renderValueGrip()
- *        for details about the options object.
- *        - render (function, required): the method that displays the given
- *        object actor.
- */
-Widgets.ObjectRenderers.add = function(obj) {
-  let extendObj = obj.extends || Widgets.JSObject;
-
-  let constructor = function() {
-    if (obj.initialize) {
-      obj.initialize.apply(this, arguments);
-    } else {
-      extendObj.apply(this, arguments);
-    }
-  };
-
-  let proto = WebConsoleUtils.cloneObject(obj, false, function(key) {
-    if (key == "initialize" || key == "canRender" ||
-        (key == "render" && extendObj === Widgets.JSObject)) {
-      return false;
-    }
-    return true;
-  });
-
-  if (extendObj === Widgets.JSObject) {
-    proto._render = obj.render;
-  }
-
-  constructor.canRender = obj.canRender;
-  constructor.prototype = extend(extendObj.prototype, proto);
-
-  if (obj.byClass) {
-    Widgets.ObjectRenderers.byClass[obj.byClass] = constructor;
-  } else if (obj.byKind) {
-    Widgets.ObjectRenderers.byKind[obj.byKind] = constructor;
-  } else {
-    throw new Error("You are adding an object renderer without any byClass or " +
-                    "byKind property.");
-  }
-};
-
-/**
- * The widget used for displaying Date objects.
- */
-Widgets.ObjectRenderers.add({
-  byClass: "Date",
-
-  render: function() {
-    let {preview} = this.objectActor;
-    this.element = this.el("span.class-" + this.objectActor.class);
-
-    let anchorText = this.objectActor.class;
-    let anchorClass = "cm-variable";
-    if (preview && "timestamp" in preview && typeof preview.timestamp != "number") {
-      anchorText = new Date(preview.timestamp).toString(); // invalid date
-      anchorClass = "";
-    }
-
-    this._anchor(anchorText, { className: anchorClass });
-
-    if (!preview || !("timestamp" in preview) || typeof preview.timestamp != "number") {
-      return;
-    }
-
-    this._text(" ");
-
-    let elem = this.el("span.cm-string-2", new Date(preview.timestamp).toISOString());
-    this.element.appendChild(elem);
-  },
-});
-
-/**
- * The widget used for displaying Function objects.
- */
-Widgets.ObjectRenderers.add({
-  byClass: "Function",
-
-  render: function() {
-    let grip = this.objectActor;
-    this.element = this.el("span.class-" + this.objectActor.class);
-
-    // TODO: Bug 948484 - support arrow functions and ES6 generators
-    let name = grip.userDisplayName || grip.displayName || grip.name || "";
-    name = VariablesView.getString(name, { noStringQuotes: true });
-
-    if (this.options.concise) {
-      this._anchor(name || "function", {
-        className: name ? "cm-variable" : "cm-keyword",
-      });
-      if (!name) {
-        this._text(" ");
-      }
-    } else if (name) {
-      this.element.appendChild(this.el("span.cm-keyword", "function"));
-      this._text(" ");
-      this._anchor(name, { className: "cm-variable" });
-    } else {
-      this._anchor("function", { className: "cm-keyword" });
-      this._text(" ");
-    }
-
-    this._text("(");
-
-    // TODO: Bug 948489 - Support functions with destructured parameters and
-    // rest parameters
-    let params = grip.parameterNames || [];
-    let shown = 0;
-    for (let param of params) {
-      if (shown > 0) {
-        this._text(", ");
-      }
-      this.element.appendChild(this.el("span.cm-def", param));
-      shown++;
-    }
-
-    this._text(")");
-  },
-
-  _onClick: function() {
-    let location = this.objectActor.location;
-    let url = location && location.url;
-    if (url && !IGNORED_SOURCE_URLS.includes(url)) {
-      this.output.openLocationInDebugger(location);
-    } else {
-      this.openObjectInVariablesView();
-    }
-  }
-}); // Widgets.ObjectRenderers.byClass.Function
-
-/**
- * The widget used for displaying ArrayLike objects.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "ArrayLike",
-
-  render: function() {
-    let {preview} = this.objectActor;
-    let {items} = preview;
-    this.element = this.el("span.kind-" + preview.kind);
-
-    this._anchor(this.objectActor.class, { className: "cm-variable" });
-
-    if (!items || this.options.concise) {
-      this._text("[");
-      this.element.appendChild(this.el("span.cm-number", preview.length));
-      this._text("]");
-      return this;
-    }
-
-    this._text(" [ ");
-
-    let isFirst = true;
-    let emptySlots = 0;
-    // A helper that renders a comma between items if isFirst == false.
-    let renderSeparator = () => !isFirst && this._text(", ");
-
-    for (let item of items) {
-      if (item === null) {
-        emptySlots++;
-      } else {
-        renderSeparator();
-        isFirst = false;
-
-        if (emptySlots) {
-          this._renderEmptySlots(emptySlots);
-          emptySlots = 0;
-        }
-
-        let elem = this.message._renderValueGrip(item, { concise: true, shorten: true });
-        this.element.appendChild(elem);
-      }
-    }
-
-    if (emptySlots) {
-      renderSeparator();
-      this._renderEmptySlots(emptySlots, false);
-    }
-
-    let shown = items.length;
-    if (shown < preview.length) {
-      this._text(", ");
-
-      let n = preview.length - shown;
-      let str = VariablesView.stringifiers._getNMoreString(n);
-      this._anchor(str);
-    }
-
-    this._text(" ]");
-
-    return this;
-  },
-
-  _renderEmptySlots: function(numSlots, appendComma = true) {
-    let slotLabel = l10n.getStr("emptySlotLabel");
-    let slotText = PluralForm.get(numSlots, slotLabel);
-    this._text("<" + slotText.replace("#1", numSlots) + ">");
-    if (appendComma) {
-      this._text(", ");
-    }
-  },
-
-}); // Widgets.ObjectRenderers.byKind.ArrayLike
-
-/**
- * The widget used for displaying MapLike objects.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "MapLike",
-
-  render: function() {
-    let {preview} = this.objectActor;
-    let {entries} = preview;
-
-    let container = this.element = this.el("span.kind-" + preview.kind);
-    this._anchor(this.objectActor.class, { className: "cm-variable" });
-
-    if (!entries || this.options.concise) {
-      if (typeof preview.size == "number") {
-        this._text("[");
-        container.appendChild(this.el("span.cm-number", preview.size));
-        this._text("]");
-      }
-      return;
-    }
-
-    this._text(" { ");
-
-    let shown = 0;
-    for (let [key, value] of entries) {
-      if (shown > 0) {
-        this._text(", ");
-      }
-
-      let keyElem = this.message._renderValueGrip(key, {
-        concise: true,
-        noStringQuotes: true,
-      });
-
-      // Strings are property names.
-      if (keyElem.classList && keyElem.classList.contains("console-string")) {
-        keyElem.classList.remove("console-string");
-        keyElem.classList.add("cm-property");
-      }
-
-      container.appendChild(keyElem);
-
-      this._text(": ");
-
-      let valueElem = this.message._renderValueGrip(value, { concise: true });
-      container.appendChild(valueElem);
-
-      shown++;
-    }
-
-    if (typeof preview.size == "number" && shown < preview.size) {
-      this._text(", ");
-
-      let n = preview.size - shown;
-      let str = VariablesView.stringifiers._getNMoreString(n);
-      this._anchor(str);
-    }
-
-    this._text(" }");
-  },
-}); // Widgets.ObjectRenderers.byKind.MapLike
-
-/**
- * The widget used for displaying objects with a URL.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "ObjectWithURL",
-
-  render: function() {
-    this.element = this._renderElement(this.objectActor,
-                                       this.objectActor.preview.url);
-  },
-
-  _renderElement: function(objectActor, url) {
-    let container = this.el("span.kind-" + objectActor.preview.kind);
-
-    this._anchor(objectActor.class, {
-      className: "cm-variable",
-      appendTo: container,
-    });
-
-    if (!VariablesView.isFalsy({ value: url })) {
-      this._text(" \u2192 ", container);
-      let shortUrl = getSourceNames(url)[this.options.concise ? "short" : "long"];
-      this._anchor(shortUrl, { href: url, appendTo: container });
-    }
-
-    return container;
-  },
-}); // Widgets.ObjectRenderers.byKind.ObjectWithURL
-
-/**
- * The widget used for displaying objects with a string next to them.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "ObjectWithText",
-
-  render: function() {
-    let {preview} = this.objectActor;
-    this.element = this.el("span.kind-" + preview.kind);
-
-    this._anchor(this.objectActor.class, { className: "cm-variable" });
-
-    if (!this.options.concise) {
-      this._text(" ");
-      this.element.appendChild(this.el("span.theme-fg-color6",
-                                       VariablesView.getString(preview.text)));
-    }
-  },
-});
-
-/**
- * The widget used for displaying DOM event previews.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "DOMEvent",
-
-  render: function() {
-    let {preview} = this.objectActor;
-
-    let container = this.element = this.el("span.kind-" + preview.kind);
-
-    this._anchor(preview.type || this.objectActor.class,
-                 { className: "cm-variable" });
-
-    if (this.options.concise) {
-      return;
-    }
-
-    if (preview.eventKind == "key" && preview.modifiers &&
-        preview.modifiers.length) {
-      this._text(" ");
-
-      let mods = 0;
-      for (let mod of preview.modifiers) {
-        if (mods > 0) {
-          this._text("-");
-        }
-        container.appendChild(this.el("span.cm-keyword", mod));
-        mods++;
-      }
-    }
-
-    this._text(" { ");
-
-    let shown = 0;
-    if (preview.target) {
-      container.appendChild(this.el("span.cm-property", "target"));
-      this._text(": ");
-      let target = this.message._renderValueGrip(preview.target, { concise: true });
-      container.appendChild(target);
-      shown++;
-    }
-
-    for (let key of Object.keys(preview.properties || {})) {
-      if (shown > 0) {
-        this._text(", ");
-      }
-
-      container.appendChild(this.el("span.cm-property", key));
-      this._text(": ");
-
-      let value = preview.properties[key];
-      let valueElem = this.message._renderValueGrip(value, { concise: true });
-      container.appendChild(valueElem);
-
-      shown++;
-    }
-
-    this._text(" }");
-  },
-}); // Widgets.ObjectRenderers.byKind.DOMEvent
-
-/**
- * The widget used for displaying DOM node previews.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "DOMNode",
-
-  canRender: function(objectActor) {
-    let {preview} = objectActor;
-    if (!preview) {
-      return false;
-    }
-
-    switch (preview.nodeType) {
-      case nodeConstants.DOCUMENT_NODE:
-      case nodeConstants.ATTRIBUTE_NODE:
-      case nodeConstants.TEXT_NODE:
-      case nodeConstants.COMMENT_NODE:
-      case nodeConstants.DOCUMENT_FRAGMENT_NODE:
-      case nodeConstants.ELEMENT_NODE:
-        return true;
-      default:
-        return false;
-    }
-  },
-
-  render: function() {
-    const {preview} = this.objectActor;
-
-    switch (preview.nodeType) {
-      case nodeConstants.DOCUMENT_NODE:
-        this._renderDocumentNode();
-        break;
-      case nodeConstants.ATTRIBUTE_NODE: {
-        this.element = this.el("span.attributeNode.kind-" + preview.kind);
-        let attr = this._renderAttributeNode(preview.nodeName, preview.value, true);
-        this.element.appendChild(attr);
-        break;
-      }
-      case nodeConstants.TEXT_NODE:
-        this._renderTextNode();
-        break;
-      case nodeConstants.COMMENT_NODE:
-        this._renderCommentNode();
-        break;
-      case nodeConstants.DOCUMENT_FRAGMENT_NODE:
-        this._renderDocumentFragmentNode();
-        break;
-      case nodeConstants.ELEMENT_NODE:
-        this._renderElementNode();
-        break;
-      default:
-        throw new Error("Unsupported nodeType: " + preview.nodeType);
-    }
-  },
-
-  _renderDocumentNode: function() {
-    let fn =
-      Widgets.ObjectRenderers.byKind.ObjectWithURL.prototype._renderElement;
-    this.element = fn.call(this, this.objectActor,
-                           this.objectActor.preview.location);
-    this.element.classList.add("documentNode");
-  },
-
-  _renderAttributeNode: function(nodeName, nodeValue, addLink) {
-    let value = VariablesView.getString(nodeValue, { noStringQuotes: true });
-
-    let fragment = this.document.createDocumentFragment();
-    if (addLink) {
-      this._anchor(nodeName, { className: "cm-attribute", appendTo: fragment });
-    } else {
-      fragment.appendChild(this.el("span.cm-attribute", nodeName));
-    }
-
-    this._text("=\"", fragment);
-    fragment.appendChild(this.el("span.theme-fg-color6", escapeHTML(value)));
-    this._text("\"", fragment);
-
-    return fragment;
-  },
-
-  _renderTextNode: function() {
-    let {preview} = this.objectActor;
-    this.element = this.el("span.textNode.kind-" + preview.kind);
-
-    this._anchor(preview.nodeName, { className: "cm-variable" });
-    this._text(" ");
-
-    let text = VariablesView.getString(preview.textContent);
-    this.element.appendChild(this.el("span.console-string", text));
-  },
-
-  _renderCommentNode: function() {
-    let {preview} = this.objectActor;
-    let comment = "<!-- " + VariablesView.getString(preview.textContent, {
-      noStringQuotes: true,
-    }) + " -->";
-
-    this.element = this._anchor(comment, {
-      className: "kind-" + preview.kind + " commentNode cm-comment",
-    });
-  },
-
-  _renderDocumentFragmentNode: function() {
-    let {preview} = this.objectActor;
-    let {childNodes} = preview;
-    let container = this.element = this.el("span.documentFragmentNode.kind-" +
-                                           preview.kind);
-
-    this._anchor(this.objectActor.class, { className: "cm-variable" });
-
-    if (!childNodes || this.options.concise) {
-      this._text("[");
-      container.appendChild(this.el("span.cm-number", preview.childNodesLength));
-      this._text("]");
-      return;
-    }
-
-    this._text(" [ ");
-
-    let shown = 0;
-    for (let item of childNodes) {
-      if (shown > 0) {
-        this._text(", ");
-      }
-
-      let elem = this.message._renderValueGrip(item, { concise: true });
-      container.appendChild(elem);
-      shown++;
-    }
-
-    if (shown < preview.childNodesLength) {
-      this._text(", ");
-
-      let n = preview.childNodesLength - shown;
-      let str = VariablesView.stringifiers._getNMoreString(n);
-      this._anchor(str);
-    }
-
-    this._text(" ]");
-  },
-
-  _renderElementNode: function() {
-    let {attributes, nodeName} = this.objectActor.preview;
-
-    this.element = this.el("span." + "kind-" + this.objectActor.preview.kind +
-                           ".elementNode");
-
-    this._text("<");
-    let openTag = this.el("span.cm-tag");
-    this.element.appendChild(openTag);
-
-    let tagName = this._anchor(nodeName, {
-      className: "cm-tag",
-      appendTo: openTag
-    });
-
-    if (this.options.concise) {
-      if (attributes.id) {
-        tagName.appendChild(this.el("span.cm-attribute", "#" + attributes.id));
-      }
-      if (attributes.class) {
-        const joinedClasses = "." + attributes.class.split(/\s+/g).join(".");
-        tagName.appendChild(this.el("span.cm-attribute", joinedClasses));
-      }
-    } else {
-      for (let name of Object.keys(attributes)) {
-        let attr = this._renderAttributeNode(" " + name, attributes[name]);
-        this.element.appendChild(attr);
-      }
-    }
-
-    this._text(">");
-
-    // Register this widget in the owner message so that it gets destroyed when
-    // the message is destroyed.
-    this.message.widgets.add(this);
-
-    this.linkToInspector().catch(console.error);
-  },
-
-  /**
-   * If the DOMNode being rendered can be highlit in the page, this function
-   * will attach mouseover/out event listeners to do so, and the inspector icon
-   * to open the node in the inspector.
-   * @return a promise that resolves when the node has been linked to the
-   * inspector, or rejects if it wasn't (either if no toolbox could be found to
-   * access the inspector, or if the node isn't present in the inspector, i.e.
-   * if the node is in a DocumentFragment or not part of the tree, or not of
-   * type nodeConstants.ELEMENT_NODE).
-   */
-  async linkToInspector() {
-    if (this._linkedToInspector) {
-      return;
-    }
-
-    // Checking the node type
-    if (this.objectActor.preview.nodeType !== nodeConstants.ELEMENT_NODE) {
-      throw new Error("The object cannot be linked to the inspector as it " +
-        "isn't an element node");
-    }
-
-    // Checking the presence of a toolbox
-    let target = this.message.output.toolboxTarget;
-    this.toolbox = gDevTools.getToolbox(target);
-    if (!this.toolbox) {
-      // In cases like the browser console, there is no toolbox.
-      return;
-    }
-
-    // Checking that the inspector supports the node
-    await this.toolbox.initInspector();
-    this._nodeFront = await this.toolbox.walker.getNodeActorFromObjectActor(
-      this.objectActor.actor);
-    if (!this._nodeFront) {
-      throw new Error("The object cannot be linked to the inspector, the " +
-        "corresponding nodeFront could not be found");
-    }
-
-    // At this stage, the message may have been cleared already
-    if (!this.document) {
-      throw new Error("The object cannot be linked to the inspector, the " +
-        "message was got cleared away");
-    }
-
-    // Check it again as this method is async!
-    if (this._linkedToInspector) {
-      return;
-    }
-    this._linkedToInspector = true;
-
-    this.highlightDomNode = this.highlightDomNode.bind(this);
-    this.element.addEventListener("mouseover", this.highlightDomNode);
-    this.unhighlightDomNode = this.unhighlightDomNode.bind(this);
-    this.element.addEventListener("mouseout", this.unhighlightDomNode);
-
-    this._openInspectorNode = this._anchor("", {
-      className: "open-inspector",
-      onClick: this.openNodeInInspector.bind(this)
-    });
-    this._openInspectorNode.title = l10n.getStr("openNodeInInspector");
-  },
-
-  /**
-   * Highlight the DOMNode corresponding to the ObjectActor in the page.
-   * @return a promise that resolves when the node has been highlighted, or
-   * rejects if the node cannot be highlighted (detached from the DOM)
-   */
-  async highlightDomNode() {
-    await this.linkToInspector();
-    let isAttached = await this.toolbox.walker.isInDOMTree(this._nodeFront);
-    if (isAttached) {
-      await this.toolbox.highlighterUtils.highlightNodeFront(this._nodeFront);
-    } else {
-      throw new Error("Node is not attached.");
-    }
-  },
-
-  /**
-   * Unhighlight a previously highlit node
-   * @see highlightDomNode
-   * @return a promise that resolves when the highlighter has been hidden
-   */
-  unhighlightDomNode: function() {
-    return this.linkToInspector().then(() => {
-      return this.toolbox.highlighterUtils.unhighlight();
-    }).catch(console.error);
-  },
-
-  /**
-   * Open the DOMNode corresponding to the ObjectActor in the inspector panel
-   * @return a promise that resolves when the inspector has been switched to
-   * and the node has been selected, or rejects if the node cannot be selected
-   * (detached from the DOM). Note that in any case, the inspector panel will
-   * be switched to.
-   */
-  async openNodeInInspector() {
-    await this.linkToInspector();
-    await this.toolbox.selectTool("inspector");
-
-    let isAttached = await this.toolbox.walker.isInDOMTree(this._nodeFront);
-    if (isAttached) {
-      let onReady = defer();
-      this.toolbox.inspector.once("inspector-updated", onReady.resolve);
-      await this.toolbox.selection.setNodeFront(this._nodeFront, { reason: "console" });
-      await onReady.promise;
-    } else {
-      throw new Error("Node is not attached.");
-    }
-  },
-
-  destroy: function() {
-    if (this.toolbox && this._nodeFront) {
-      this.element.removeEventListener("mouseover", this.highlightDomNode);
-      this.element.removeEventListener("mouseout", this.unhighlightDomNode);
-      this._openInspectorNode.removeEventListener("mousedown",
-                                                  this.openNodeInInspector,
-                                                  true);
-
-      if (this._linkedToInspector) {
-        this.unhighlightDomNode().then(() => {
-          this.toolbox = null;
-          this._nodeFront = null;
-        });
-      } else {
-        this.toolbox = null;
-        this._nodeFront = null;
-      }
-    }
-  },
-}); // Widgets.ObjectRenderers.byKind.DOMNode
-
-/**
- * The widget user for displaying Promise objects.
- */
-Widgets.ObjectRenderers.add({
-  byClass: "Promise",
-
-  render: function() {
-    let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
-    if ((!ownProperties && !safeGetterValues) || this.options.concise) {
-      this._renderConciseObject();
-      return;
-    }
-
-    this._renderObjectPrefix();
-    let container = this.element;
-    let addedPromiseInternalProps = false;
-
-    if (this.objectActor.promiseState) {
-      const { state, value, reason } = this.objectActor.promiseState;
-
-      this._renderObjectProperty("<state>", state, container, false);
-      addedPromiseInternalProps = true;
-
-      if (state == "fulfilled") {
-        this._renderObjectProperty("<value>", value, container, true);
-      } else if (state == "rejected") {
-        this._renderObjectProperty("<reason>", reason, container, true);
-      }
-    }
-
-    this._renderObjectProperties(container, addedPromiseInternalProps);
-    this._renderObjectSuffix();
-  }
-}); // Widgets.ObjectRenderers.byClass.Promise
-
-/*
- * A renderer used for wrapped primitive objects.
- */
-
-function WrappedPrimitiveRenderer() {
-  let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
-  if ((!ownProperties && !safeGetterValues) || this.options.concise) {
-    this._renderConciseObject();
-    return;
-  }
-
-  this._renderObjectPrefix();
-
-  let elem =
-      this.message._renderValueGrip(this.objectActor.preview.wrappedValue);
-  this.element.appendChild(elem);
-
-  this._renderObjectProperties(this.element, true);
-  this._renderObjectSuffix();
-}
-
-/**
- * The widget used for displaying Boolean previews.
- */
-Widgets.ObjectRenderers.add({
-  byClass: "Boolean",
-
-  render: WrappedPrimitiveRenderer,
-});
-
-/**
- * The widget used for displaying Number previews.
- */
-Widgets.ObjectRenderers.add({
-  byClass: "Number",
-
-  render: WrappedPrimitiveRenderer,
-});
-
-/**
- * The widget used for displaying String previews.
- */
-Widgets.ObjectRenderers.add({
-  byClass: "String",
-
-  render: WrappedPrimitiveRenderer,
-});
-
-/**
- * The widget used for displaying generic JS object previews.
- */
-Widgets.ObjectRenderers.add({
-  byKind: "Object",
-
-  render: function() {
-    let { ownProperties, safeGetterValues } = this.objectActor.preview || {};
-    if ((!ownProperties && !safeGetterValues) || this.options.concise) {
-      this._renderConciseObject();
-      return;
-    }
-
-    this._renderObjectPrefix();
-    this._renderObjectProperties(this.element, false);
-    this._renderObjectSuffix();
-  },
-}); // Widgets.ObjectRenderers.byKind.Object
-
-/**
- * The long string widget.
- *
- * @constructor
- * @param object message
- *        The owning message.
- * @param object longStringActor
- *        The LongStringActor to display.
- * @param object options
- *        Options, such as noStringQuotes
- */
-Widgets.LongString = function(message, longStringActor, options) {
-  Widgets.BaseWidget.call(this, message);
-  this.longStringActor = longStringActor;
-  this.noStringQuotes = (options && "noStringQuotes" in options) ?
-    options.noStringQuotes : !this.message._quoteStrings;
-
-  this._onClick = this._onClick.bind(this);
-  this._onSubstring = this._onSubstring.bind(this);
-};
-
-Widgets.LongString.prototype = extend(Widgets.BaseWidget.prototype, {
-  /**
-   * The LongStringActor displayed by the widget.
-   * @type object
-   */
-  longStringActor: null,
-
-  render: function() {
-    if (this.element) {
-      return this;
-    }
-
-    let result = this.element = this.document.createElementNS(XHTML_NS, "span");
-    result.className = "longString console-string";
-    this._renderString(this.longStringActor.initial);
-    result.appendChild(this._renderEllipsis());
-
-    return this;
-  },
-
-  /**
-   * Render the long string in the widget element.
-   * @private
-   * @param string str
-   *        The string to display.
-   */
-  _renderString: function(str) {
-    this.element.textContent = VariablesView.getString(str, {
-      noStringQuotes: this.noStringQuotes,
-      noEllipsis: true,
-    });
-  },
-
-  /**
-   * Render the anchor ellipsis that allows the user to expand the long string.
-   *
-   * @private
-   * @return Element
-   */
-  _renderEllipsis: function() {
-    let ellipsis = this.document.createElementNS(XHTML_NS, "a");
-    ellipsis.className = "longStringEllipsis";
-    ellipsis.textContent = l10n.getStr("longStringEllipsis");
-    ellipsis.href = "#";
-    ellipsis.draggable = false;
-    this.message._addLinkCallback(ellipsis, this._onClick);
-
-    return ellipsis;
-  },
-
-  /**
-   * The click event handler for the ellipsis shown after the short string. This
-   * function expands the element to show the full string.
-   * @private
-   */
-  _onClick: function() {
-    let longString = this.output.webConsoleClient.longString(this.longStringActor);
-    let toIndex = Math.min(longString.length, MAX_LONG_STRING_LENGTH);
-
-    longString.substring(longString.initial.length, toIndex, this._onSubstring);
-  },
-
-  /**
-   * The longString substring response callback.
-   *
-   * @private
-   * @param object response
-   *        Response packet.
-   */
-  _onSubstring: function(response) {
-    if (response.error) {
-      console.error("LongString substring failure: " + response.error);
-      return;
-    }
-
-    this.element.lastChild.remove();
-    this.element.classList.remove("longString");
-
-    this._renderString(this.longStringActor.initial + response.substring);
-
-    this.output.owner.emit("new-messages", new Set([{
-      update: true,
-      node: this.message.element,
-      response: response,
-    }]));
-
-    let toIndex = Math.min(this.longStringActor.length, MAX_LONG_STRING_LENGTH);
-    if (toIndex != this.longStringActor.length) {
-      this._logWarningAboutStringTooLong();
-    }
-  },
-
-  /**
-   * Inform user that the string he tries to view is too long.
-   * @private
-   */
-  _logWarningAboutStringTooLong: function() {
-    let msg = new Messages.Simple(l10n.getStr("longStringTooLong"), {
-      category: "output",
-      severity: "warning",
-    });
-    this.output.addMessage(msg);
-  },
-}); // Widgets.LongString.prototype
-
-/**
- * The stacktrace widget.
- *
- * @constructor
- * @extends Widgets.BaseWidget
- * @param object message
- *        The owning message.
- * @param array stacktrace
- *        The stacktrace to display, array of frames as supplied by the server,
- *        over the remote protocol.
- */
-Widgets.Stacktrace = function(message, stacktrace) {
-  Widgets.BaseWidget.call(this, message);
-  this.stacktrace = stacktrace;
-};
-
-Widgets.Stacktrace.prototype = extend(Widgets.BaseWidget.prototype, {
-  /**
-   * The stackframes received from the server.
-   * @type array
-   */
-  stacktrace: null,
-
-  render() {
-    if (this.element) {
-      return this;
-    }
-
-    let result = this.element = this.document.createElementNS(XHTML_NS, "div");
-    result.className = "stacktrace devtools-monospace";
-
-    if (this.stacktrace) {
-      const target = this.message.output.toolboxTarget;
-      const toolbox = gDevTools.getToolbox(target);
-      this.output.owner.ReactDOM.render(this.output.owner.StackTraceView({
-        stacktrace: this.stacktrace,
-        onViewSourceInDebugger: frame => this.output.openLocationInDebugger(frame),
-        sourceMapService: toolbox ? toolbox.sourceMapURLService : null,
-      }), result);
-    }
-
-    return this;
-  }
-});
-
-/**
- * The table widget.
- *
- * @constructor
- * @extends Widgets.BaseWidget
- * @param object message
- *        The owning message.
- * @param array data
- *        Array of objects that holds the data to log in the table.
- * @param object columns
- *        Object containing the key value pair of the id and display name for
- *        the columns in the table.
- */
-Widgets.Table = function(message, data, columns) {
-  Widgets.BaseWidget.call(this, message);
-  this.data = data;
-  this.columns = columns;
-};
-
-Widgets.Table.prototype = extend(Widgets.BaseWidget.prototype, {
-  /**
-   * Array of objects that holds the data to output in the table.
-   * @type array
-   */
-  data: null,
-
-  /**
-   * Object containing the key value pair of the id and display name for
-   * the columns in the table.
-   * @type object
-   */
-  columns: null,
-
-  render: function() {
-    if (this.element) {
-      return this;
-    }
-
-    let result = this.element = this.document.createElementNS(XHTML_NS, "div");
-    result.className = "consoletable devtools-monospace";
-
-    this.table = new TableWidget(result, {
-      wrapTextInElements: true,
-      initialColumns: this.columns,
-      uniqueId: "_index",
-      firstColumn: "_index"
-    });
-
-    for (let row of this.data) {
-      this.table.push(row);
-    }
-
-    return this;
-  }
-}); // Widgets.Table.prototype
-
-function gSequenceId() {
-  return gSequenceId.n++;
-}
-gSequenceId.n = 0;
-
-exports.ConsoleOutput = ConsoleOutput;
-exports.Messages = Messages;
-exports.Widgets = Widgets;
deleted file mode 100644
--- a/devtools/client/webconsole/old/jsterm.js
+++ /dev/null
@@ -1,1867 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const {Utils: WebConsoleUtils} =
-  require("devtools/client/webconsole/utils");
-const defer = require("devtools/shared/defer");
-const Debugger = require("Debugger");
-const Services = require("Services");
-const {KeyCodes} = require("devtools/client/shared/keycodes");
-
-loader.lazyServiceGetter(this, "clipboardHelper",
-                         "@mozilla.org/widget/clipboardhelper;1",
-                         "nsIClipboardHelper");
-loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
-loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup");
-loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
-loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/old/console-output", true);
-loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
-loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
-loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
-loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
-loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
-loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
-loader.lazyRequireGetter(this, "NotificationBox", "devtools/client/shared/components/NotificationBox", true);
-loader.lazyRequireGetter(this, "PriorityLevels", "devtools/client/shared/components/NotificationBox", true);
-
-const l10n = require("devtools/client/webconsole/webconsole-l10n");
-
-// Constants used for defining the direction of JSTerm input history navigation.
-const HISTORY_BACK = -1;
-const HISTORY_FORWARD = 1;
-
-const XHTML_NS = "http://www.w3.org/1999/xhtml";
-
-const HELP_URL = "https://developer.mozilla.org/docs/Tools/Web_Console/Helpers";
-
-const VARIABLES_VIEW_URL = "chrome://devtools/content/shared/widgets/VariablesView.xul";
-
-const PREF_INPUT_HISTORY_COUNT = "devtools.webconsole.inputHistoryCount";
-const PREF_AUTO_MULTILINE = "devtools.webconsole.autoMultiline";
-
-/**
- * Create a JSTerminal (a JavaScript command line). This is attached to an
- * existing HeadsUpDisplay (a Web Console instance). This code is responsible
- * with handling command line input, code evaluation and result output.
- *
- * @constructor
- * @param object webConsoleFrame
- *        The WebConsoleFrame object that owns this JSTerm instance.
- */
-function JSTerm(webConsoleFrame) {
-  this.hud = webConsoleFrame;
-  this.hudId = this.hud.hudId;
-  this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT);
-
-  this.lastCompletion = { value: null };
-  this._loadHistory();
-
-  this._objectActorsInVariablesViews = new Map();
-
-  this._keyPress = this._keyPress.bind(this);
-  this._inputEventHandler = this._inputEventHandler.bind(this);
-  this._focusEventHandler = this._focusEventHandler.bind(this);
-  this._onKeypressInVariablesView = this._onKeypressInVariablesView.bind(this);
-  this._blurEventHandler = this._blurEventHandler.bind(this);
-
-  EventEmitter.decorate(this);
-}
-
-JSTerm.prototype = {
-  SELECTED_FRAME: -1,
-
-  /**
-   * Load the console history from previous sessions.
-   * @private
-   */
-  _loadHistory: function() {
-    this.history = [];
-    this.historyIndex = this.historyPlaceHolder = 0;
-
-    this.historyLoaded = asyncStorage.getItem("webConsoleHistory")
-      .then(value => {
-        if (Array.isArray(value)) {
-          // Since it was gotten asynchronously, there could be items already in
-          // the history.  It's not likely but stick them onto the end anyway.
-          this.history = value.concat(this.history);
-
-          // Holds the number of entries in history. This value is incremented
-          // in this.execute().
-          this.historyIndex = this.history.length;
-
-          // Holds the index of the history entry that the user is currently
-          // viewing. This is reset to this.history.length when this.execute()
-          // is invoked.
-          this.historyPlaceHolder = this.history.length;
-        }
-      }, console.error);
-  },
-
-  /**
-   * Clear the console history altogether.  Note that this will not affect
-   * other consoles that are already opened (since they have their own copy),
-   * but it will reset the array for all newly-opened consoles.
-   * @returns Promise
-   *          Resolves once the changes have been persisted.
-   */
-  clearHistory: function() {
-    this.history = [];
-    this.historyIndex = this.historyPlaceHolder = 0;
-    return this.storeHistory();
-  },
-
-  /**
-   * Stores the console history for future console instances.
-   * @returns Promise
-   *          Resolves once the changes have been persisted.
-   */
-  storeHistory: function() {
-    return asyncStorage.setItem("webConsoleHistory", this.history);
-  },
-
-  /**
-   * Stores the data for the last completion.
-   * @type object
-   */
-  lastCompletion: null,
-
-  /**
-   * Array that caches the user input suggestions received from the server.
-   * @private
-   * @type array
-   */
-  _autocompleteCache: null,
-
-  /**
-   * The input that caused the last request to the server, whose response is
-   * cached in the _autocompleteCache array.
-   * @private
-   * @type string
-   */
-  _autocompleteQuery: null,
-
-  /**
-   * The frameActorId used in the last autocomplete query. Whenever this changes
-   * the autocomplete cache must be invalidated.
-   * @private
-   * @type string
-   */
-  _lastFrameActorId: null,
-
-  /**
-   * The Web Console sidebar.
-   * @see this._createSidebar()
-   * @see Sidebar.jsm
-   */
-  sidebar: null,
-
-  /**
-   * The Variables View instance shown in the sidebar.
-   * @private
-   * @type object
-   */
-  _variablesView: null,
-
-  /**
-   * Tells if you want the variables view UI updates to be lazy or not. Tests
-   * disable lazy updates.
-   *
-   * @private
-   * @type boolean
-   */
-  _lazyVariablesView: true,
-
-  /**
-   * Holds a map between VariablesView instances and sets of ObjectActor IDs
-   * that have been retrieved from the server. This allows us to release the
-   * objects when needed.
-   *
-   * @private
-   * @type Map
-   */
-  _objectActorsInVariablesViews: null,
-
-  /**
-   * Last input value.
-   * @type string
-   */
-  lastInputValue: "",
-
-  /**
-   * Tells if the input node changed since the last focus.
-   *
-   * @private
-   * @type boolean
-   */
-  _inputChanged: false,
-
-  /**
-   * Tells if the autocomplete popup was navigated since the last open.
-   *
-   * @private
-   * @type boolean
-   */
-  _autocompletePopupNavigated: false,
-
-  /**
-   * History of code that was executed.
-   * @type array
-   */
-  history: null,
-  autocompletePopup: null,
-  inputNode: null,
-  completeNode: null,
-
-  /**
-   * Getter for the element that holds the messages we display.
-   * @type nsIDOMElement
-   */
-  get outputNode() {
-    return this.hud.outputNode;
-  },
-
-  /**
-   * Getter for the debugger WebConsoleClient.
-   * @type object
-   */
-  get webConsoleClient() {
-    return this.hud.webConsoleClient;
-  },
-
-  COMPLETE_FORWARD: 0,
-  COMPLETE_BACKWARD: 1,
-  COMPLETE_HINT_ONLY: 2,
-  COMPLETE_PAGEUP: 3,
-  COMPLETE_PAGEDOWN: 4,
-
-  /**
-   * Initialize the JSTerminal UI.
-   */
-  init: function() {
-    let autocompleteOptions = {
-      onSelect: this.onAutocompleteSelect.bind(this),
-      onClick: this.acceptProposedCompletion.bind(this),
-      listId: "webConsole_autocompletePopupListBox",
-      position: "top",
-      theme: "auto",
-      autoSelect: true
-    };
-
-    let doc = this.hud.document;
-    let toolbox = gDevTools.getToolbox(this.hud.owner.target);
-    let tooltipDoc = toolbox ? toolbox.doc : doc;
-    // The popup will be attached to the toolbox document or HUD document in the case
-    // such as the browser console which doesn't have a toolbox.
-    this.autocompletePopup = new AutocompletePopup(tooltipDoc, autocompleteOptions);
-    let inputContainer = doc.querySelector(".jsterm-input-container");
-    this.completeNode = doc.querySelector(".jsterm-complete-node");
-    this.inputNode = doc.querySelector(".jsterm-input-node");
-    this.inputBorderSize = this.inputNode.getBoundingClientRect().height -
-                           this.inputNode.clientHeight;
-    // Update the character width and height needed for the popup offset
-    // calculations.
-    this._updateCharSize();
-
-    if (this.hud.isBrowserConsole &&
-        !Services.prefs.getBoolPref("devtools.chrome.enabled")) {
-      inputContainer.style.display = "none";
-    } else {
-      this.inputNode.addEventListener("keypress", this._keyPress);
-      this.inputNode.addEventListener("input", this._inputEventHandler);
-      this.inputNode.addEventListener("keyup", this._inputEventHandler);
-      this.inputNode.addEventListener("focus", this._focusEventHandler);
-    }
-
-    if (!this.hud.isBrowserConsole) {
-      let okstring = l10n.getStr("selfxss.okstring");
-      let msg = l10n.getFormatStr("selfxss.msg", [okstring]);
-      this._onPaste = WebConsoleUtils.pasteHandlerGen(this.inputNode,
-          this.getNotificationBox(), msg, okstring);
-      this.inputNode.addEventListener("paste", this._onPaste);
-      this.inputNode.addEventListener("drop", this._onPaste);
-    }
-
-    this.hud.window.addEventListener("blur", this._blurEventHandler);
-    this.lastInputValue && this.setInputValue(this.lastInputValue);
-  },
-
-  focus: function() {
-    if (!this.inputNode.getAttribute("focused")) {
-      this.inputNode.focus();
-    }
-  },
-
-  /**
-   * The JavaScript evaluation response handler.
-   *
-   * @private
-   * @param function [callback]
-   *        Optional function to invoke when the evaluation result is added to
-   *        the output.
-   * @param object response
-   *        The message received from the server.
-   */
-  _executeResultCallback: function(callback, response) {
-    if (!this.hud) {
-      return;
-    }
-    if (response.error) {
-      console.error("Evaluation error " + response.error + ": " +
-                    response.message);
-      return;
-    }
-    let errorMessage = response.exceptionMessage;
-    let errorDocURL = response.exceptionDocURL;
-
-    let errorDocLink;
-    if (errorDocURL) {
-      errorMessage += " ";
-      errorDocLink = this.hud.document.createElementNS(XHTML_NS, "a");
-      errorDocLink.className = "learn-more-link webconsole-learn-more-link";
-      errorDocLink.textContent = `[${l10n.getStr("webConsoleMoreInfoLabel")}]`;
-      errorDocLink.title = errorDocURL.split("?")[0];
-      errorDocLink.href = "#";
-      errorDocLink.draggable = false;
-      errorDocLink.addEventListener("click", () => {
-        this.hud.owner.openLink(errorDocURL);
-      });
-    }
-
-    // Wrap thrown strings in Error objects, so `throw "foo"` outputs
-    // "Error: foo"
-    if (typeof response.exception === "string") {
-      errorMessage = new Error(errorMessage).toString();
-    }
-    let result = response.result;
-    let helperResult = response.helperResult;
-    let helperHasRawOutput = !!(helperResult || {}).rawOutput;
-
-    if (helperResult && helperResult.type) {
-      switch (helperResult.type) {
-        case "clearOutput":
-          this.clearOutput();
-          break;
-        case "clearHistory":
-          this.clearHistory();
-          break;
-        case "inspectObject":
-          this.inspectObjectActor(helperResult.object);
-          break;
-        case "error":
-          try {
-            errorMessage = l10n.getStr(helperResult.message);
-          } catch (ex) {
-            errorMessage = helperResult.message;
-          }
-          break;
-        case "help":
-          this.hud.owner.openLink(HELP_URL);
-          break;
-        case "copyValueToClipboard":
-          clipboardHelper.copyString(helperResult.value);
-          break;
-      }
-    }
-
-    // Hide undefined results coming from JSTerm helper functions.
-    if (!errorMessage && result && typeof result == "object" &&
-      result.type == "undefined" &&
-      helperResult && !helperHasRawOutput) {
-      callback && callback();
-      return;
-    }
-
-    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
-      this.hud.newConsoleOutput.dispatchMessageAdd(response, true).then(callback);
-      return;
-    }
-    let msg = new Messages.JavaScriptEvalOutput(response,
-                                                errorMessage, errorDocLink);
-    this.hud.output.addMessage(msg);
-
-    if (callback) {
-      let oldFlushCallback = this.hud._flushCallback;
-      this.hud._flushCallback = () => {
-        callback(msg.element);
-        if (oldFlushCallback) {
-          oldFlushCallback();
-          this.hud._flushCallback = oldFlushCallback;
-          return true;
-        }
-
-        return false;
-      };
-    }
-
-    msg._objectActors = new Set();
-
-    if (WebConsoleUtils.isActorGrip(response.exception)) {
-      msg._objectActors.add(response.exception.actor);
-    }
-
-    if (WebConsoleUtils.isActorGrip(result)) {
-      msg._objectActors.add(result.actor);
-    }
-  },
-
-  inspectObjectActor: function(objectActor) {
-    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
-      this.hud.newConsoleOutput.dispatchMessageAdd({
-        helperResult: {
-          type: "inspectObject",
-          object: objectActor
-        }
-      }, true);
-      return this.hud.newConsoleOutput;
-    }
-
-    return this.openVariablesView({
-      objectActor,
-      label: VariablesView.getString(objectActor, {concise: true}),
-    });
-  },
-
-  /**
-   * Execute a string. Execution happens asynchronously in the content process.
-   *
-   * @param string [executeString]
-   *        The string you want to execute. If this is not provided, the current
-   *        user input is used - taken from |this.getInputValue()|.
-   * @param function [callback]
-   *        Optional function to invoke when the result is displayed.
-   *        This is deprecated - please use the promise return value instead.
-   * @returns Promise
-   *          Resolves with the message once the result is displayed.
-   */
-  execute: async function(executeString, callback) {
-    let deferred = defer();
-    let resultCallback;
-    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
-      resultCallback = (msg) => deferred.resolve(msg);
-    } else {
-      resultCallback = (msg) => {
-        deferred.resolve(msg);
-        if (callback) {
-          callback(msg);
-        }
-      };
-    }
-
-    // attempt to execute the content of the inputNode
-    executeString = executeString || this.getInputValue();
-    if (!executeString) {
-      return null;
-    }
-
-    // Append a new value in the history of executed code, or overwrite the most
-    // recent entry. The most recent entry may contain the last edited input
-    // value that was not evaluated yet.
-    this.history[this.historyIndex++] = executeString;
-    this.historyPlaceHolder = this.history.length;
-
-    if (this.history.length > this.inputHistoryCount) {
-      this.history.splice(0, this.history.length - this.inputHistoryCount);
-      this.historyIndex = this.historyPlaceHolder = this.history.length;
-    }
-    this.storeHistory();
-    WebConsoleUtils.usageCount++;
-    this.setInputValue("");
-    this.clearCompletion();
-
-    let selectedNodeActor = null;
-    let inspectorSelection = this.hud.owner.getInspectorSelection();
-    if (inspectorSelection && inspectorSelection.nodeFront) {
-      selectedNodeActor = inspectorSelection.nodeFront.actorID;
-    }
-
-    if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
-      const { ConsoleCommand } = require("devtools/client/webconsole/types");
-      let message = new ConsoleCommand({
-        messageText: executeString,
-      });
-      this.hud.proxy.dispatchMessageAdd(message);
-    } else {
-      let message = new Messages.Simple(executeString, {
-        category: "input",
-        severity: "log",
-      });
-      this.hud.output.addMessage(message);
-    }
-    let onResult = this._executeResultCallback.bind(this, resultCallback);
-
-    let options = {
-      frame: this.SELECTED_FRAME,
-      selectedNodeActor: selectedNodeActor,
-    };
-
-    const mappedString = await this.hud.owner.getMappedExpression(executeString);
-    this.requestEvaluation(mappedString, options).then(onResult, onResult);
-
-    return deferred.promise;
-  },
-
-  /**
-   * Request a JavaScript string evaluation from the server.
-   *
-   * @param string str
-   *        String to execute.
-   * @param object [options]
-   *        Options for evaluation:
-   *        - bindObjectActor: tells the ObjectActor ID for which you want to do
-   *        the evaluation. The Debugger.Object of the OA will be bound to
-   *        |_self| during evaluation, such that it's usable in the string you
-   *        execute.
-   *        - frame: tells the stackframe depth to evaluate the string in. If
-   *        the jsdebugger is paused, you can pick the stackframe to be used for
-   *        evaluation. Use |this.SELECTED_FRAME| to always pick the
-   *        user-selected stackframe.
-   *        If you do not provide a |frame| the string will be evaluated in the
-   *        global content window.
-   *        - selectedNodeActor: tells the NodeActor ID of the current selection
-   *        in the Inspector, if such a selection exists. This is used by
-   *        helper functions that can evaluate on the current selection.
-   * @return object
-   *         A promise object that is resolved when the server response is
-   *         received.
-   */
-  requestEvaluation: function(str, options = {}) {
-    let deferred = defer();
-
-    function onResult(response) {
-      if (!response.error) {
-        deferred.resolve(response);
-      } else {
-        deferred.reject(response);
-      }
-    }
-
-    let frameActor = null;
-    if ("frame" in options) {
-      frameActor = this.getFrameActor(options.frame);
-    }
-
-    let evalOptions = {
-      bindObjectActor: options.bindObjectActor,
-      frameActor: frameActor,
-      selectedNodeActor: options.selectedNodeActor,
-      selectedObjectActor: options.selectedObjectActor,
-    };
-
-    this.webConsoleClient.evaluateJSAsync(str, onResult, evalOptions);
-    return deferred.promise;
-  },
-
-  /**
-   * Copy the object/variable by invoking the server
-   * which invokes the `copy(variable)` command and makes it
-   * available in the clipboard
-   * @param evalString - string which has the evaluation string to be copied
-   * @param options - object - Options for evaluation
-   * @return object
-   *         A promise object that is resolved when the server response is
-   *         received.
-   */
-  copyObject: function(evalString, evalOptions) {
-    return this.webConsoleClient.evaluateJSAsync(`copy(${evalString})`,
-      null, evalOptions);
-  },
-
-  /**
-   * Retrieve the FrameActor ID given a frame depth.
-   *
-   * @param number frame
-   *        Frame depth.
-   * @return string|null
-   *         The FrameActor ID for the given frame depth.
-   */
-  getFrameActor: function(frame) {
-    let state = this.hud.owner.getDebuggerFrames();
-    if (!state) {
-      return null;
-    }
-
-    let grip;
-    if (frame == this.SELECTED_FRAME) {
-      grip = state.frames[state.selected];
-    } else {
-      grip = state.frames[frame];
-    }
-
-    return grip ? grip.actor : null;
-  },
-
-  /**
-   * Opens a new variables view that allows the inspection of the given object.
-   *
-   * @param object options
-   *        Options for the variables view:
-   *        - objectActor: grip of the ObjectActor you want to show in the
-   *        variables view.
-   *        - rawObject: the raw object you want to show in the variables view.
-   *        - label: label to display in the variables view for inspected
-   *        object.
-   *        - hideFilterInput: optional boolean, |true| if you want to hide the
-   *        variables view filter input.
-   *        - targetElement: optional nsIDOMElement to append the variables view
-   *        to. An iframe element is used as a container for the view. If this
-   *        option is not used, then the variables view opens in the sidebar.
-   *        - autofocus: optional boolean, |true| if you want to give focus to
-   *        the variables view window after open, |false| otherwise.
-   * @return object
-   *         A promise object that is resolved when the variables view has
-   *         opened. The new variables view instance is given to the callbacks.
-   */
-  openVariablesView: function(options) {
-    // Bail out if the side bar doesn't exist.
-    if (!this.hud.document.querySelector("#webconsole-sidebar")) {
-      return Promise.resolve(null);
-    }
-
-    let onContainerReady = (window) => {
-      let container = window.document.querySelector("#variables");
-      let view = this._variablesView;
-      if (!view || options.targetElement) {
-        let viewOptions = {
-          container: container,
-          hideFilterInput: options.hideFilterInput,
-        };
-        view = this._createVariablesView(viewOptions);
-        if (!options.targetElement) {
-          this._variablesView = view;
-          window.addEventListener("keypress", this._onKeypressInVariablesView);
-        }
-      }
-      options.view = view;
-      this._updateVariablesView(options);
-
-      if (!options.targetElement && options.autofocus) {
-        window.focus();
-      }
-
-      this.emit("variablesview-open", {view, options});
-      return view;
-    };
-
-    let openPromise;
-    if (options.targetElement) {
-      let deferred = defer();
-      openPromise = deferred.promise;
-      let document = options.targetElement.ownerDocument;
-      let iframe = document.createElementNS(XHTML_NS, "iframe");
-
-      iframe.addEventListener("load", function() {
-        iframe.style.visibility = "visible";
-        deferred.resolve(iframe.contentWindow);
-      }, {capture: true, once: true});
-
-      iframe.flex = 1;
-      iframe.style.visibility = "hidden";
-      iframe.setAttribute("src", VARIABLES_VIEW_URL);
-      options.targetElement.appendChild(iframe);
-    } else {
-      if (!this.sidebar) {
-        this._createSidebar();
-      }
-      openPromise = this._addVariablesViewSidebarTab();
-    }
-
-    return openPromise.then(onContainerReady);
-  },
-
-  /**
-   * Create the Web Console sidebar.
-   *
-   * @see devtools/framework/sidebar.js
-   * @private
-   */
-  _createSidebar: function() {
-    let tabbox = this.hud.document.querySelector("#webconsole-sidebar");
-    this.sidebar = new ToolSidebar(tabbox, this, "webconsole");
-    this.sidebar.show();
-    this.emit("sidebar-opened");
-  },
-
-  /**
-   * Add the variables view tab to the sidebar.
-   *
-   * @private
-   * @return object
-   *         A promise object for the adding of the new tab.
-   */
-  _addVariablesViewSidebarTab: function() {
-    let deferred = defer();
-
-    let onTabReady = () => {
-      let window = this.sidebar.getWindowForTab("variablesview");
-      deferred.resolve(window);
-    };
-
-    let tabPanel = this.sidebar.getTabPanel("variablesview");
-    if (tabPanel) {
-      if (this.sidebar.getCurrentTabID() == "variablesview") {
-        onTabReady();
-      } else {
-        this.sidebar.once("variablesview-selected", onTabReady);
-        this.sidebar.select("variablesview");
-      }
-    } else {
-      this.sidebar.once("variablesview-ready", onTabReady);
-      this.sidebar.addTab("variablesview", VARIABLES_VIEW_URL, {selected: true});
-    }
-
-    return deferred.promise;
-  },
-
-  /**
-   * The keypress event handler for the Variables View sidebar. Currently this
-   * is used for removing the sidebar when Escape is pressed.
-   *
-   * @private
-   * @param nsIDOMEvent event
-   *        The keypress DOM event object.
-   */
-  _onKeypressInVariablesView: function(event) {
-    let tag = event.target.nodeName;
-    if (event.keyCode != KeyCodes.DOM_VK_ESCAPE || event.shiftKey ||
-        event.altKey || event.ctrlKey || event.metaKey ||
-        ["input", "textarea", "select", "textbox"].indexOf(tag) > -1) {
-      return;
-    }
-
-    this._sidebarDestroy();
-    this.focus();
-    event.stopPropagation();
-  },
-
-  /**
-   * Create a variables view instance.
-   *
-   * @private
-   * @param object options
-   *        Options for the new Variables View instance:
-   *        - container: the DOM element where the variables view is inserted.
-   *        - hideFilterInput: boolean, if true the variables filter input is
-   *        hidden.
-   * @return object
-   *         The new Variables View instance.
-   */
-  _createVariablesView: function(options) {
-    let view = new VariablesView(options.container);
-    view.toolbox = gDevTools.getToolbox(this.hud.owner.target);
-    view.searchPlaceholder = l10n.getStr("propertiesFilterPlaceholder");
-    view.emptyText = l10n.getStr("emptyPropertiesList");
-    view.searchEnabled = !options.hideFilterInput;
-    view.lazyEmpty = this._lazyVariablesView;
-
-    VariablesViewController.attach(view, {
-      getEnvironmentClient: grip => {
-        return new EnvironmentClient(this.hud.proxy.client, grip);
-      },
-      getObjectClient: grip => {
-        return new ObjectClient(this.hud.proxy.client, grip);
-      },
-      getLongStringClient: grip => {
-        return this.webConsoleClient.longString(grip);
-      },
-      releaseActor: actor => {
-        this.hud._releaseObject(actor);
-      },
-      simpleValueEvalMacro: simpleValueEvalMacro,
-      overrideValueEvalMacro: overrideValueEvalMacro,
-      getterOrSetterEvalMacro: getterOrSetterEvalMacro,
-    });
-
-    // Relay events from the VariablesView.
-    view.on("fetched", (type, variableObject) => {
-      this.emit("variablesview-fetched", variableObject);
-    });
-
-    return view;
-  },
-
-  /**
-   * Update the variables view.
-   *
-   * @private
-   * @param object options
-   *        Options for updating the variables view:
-   *        - view: the view you want to update.
-   *        - objectActor: the grip of the new ObjectActor you want to show in
-   *        the view.
-   *        - rawObject: the new raw object you want to show.
-   *        - label: the new label for the inspected object.
-   */
-  _updateVariablesView: function(options) {
-    let view = options.view;
-    view.empty();
-
-    // We need to avoid pruning the object inspection starting point.
-    // That one is pruned when the console message is removed.
-    view.controller.releaseActors(actor => {
-      return view._consoleLastObjectActor != actor;
-    });
-
-    if (options.objectActor &&
-        (!this.hud.isBrowserConsole ||
-         Services.prefs.getBoolPref("devtools.chrome.enabled"))) {
-      // Make sure eval works in the correct context.
-      view.eval = this._variablesViewEvaluate.bind(this, options);
-      view.switch = this._variablesViewSwitch.bind(this, options);
-      view.delete = this._variablesViewDelete.bind(this, options);
-    } else {
-      view.eval = null;
-      view.switch = null;
-      view.delete = null;
-    }
-
-    let { variable, expanded } = view.controller.setSingleVariable(options);
-    variable.evaluationMacro = simpleValueEvalMacro;
-
-    if (options.objectActor) {
-      view._consoleLastObjectActor = options.objectActor.actor;
-    } else if (options.rawObject) {
-      view._consoleLastObjectActor = null;
-    } else {
-      throw new Error(
-        "Variables View cannot open without giving it an object display.");
-    }
-
-    expanded.then(() => {
-      this.emit("variablesview-updated", view, options);
-    });
-  },
-
-  /**
-   * The evaluation function used by the variables view when editing a property
-   * value.
-   *
-   * @private
-   * @param object options
-   *        The options used for |this._updateVariablesView()|.
-   * @param object variableObject
-   *        The Variable object instance for the edited property.
-   * @param string value
-   *        The value the edited property was changed to.
-   */
-  _variablesViewEvaluate: function(options, variableObject, value) {
-    let updater = this._updateVariablesView.bind(this, options);
-    let onEval = this._silentEvalCallback.bind(this, updater);
-    let string = variableObject.evaluationMacro(variableObject, value);
-
-    let evalOptions = {
-      frame: this.SELECTED_FRAME,
-      bindObjectActor: options.objectActor.actor,
-    };
-
-    this.requestEvaluation(string, evalOptions).then(onEval, onEval);
-  },
-
-  /**
-   * The property deletion function used by the variables view when a property
-   * is deleted.
-   *
-   * @private
-   * @param object options
-   *        The options used for |this._updateVariablesView()|.
-   * @param object variableObject
-   *        The Variable object instance for the deleted property.
-   */
-  _variablesViewDelete: function(options, variableObject) {
-    let onEval = this._silentEvalCallback.bind(this, null);
-
-    let evalOptions = {
-      frame: this.SELECTED_FRAME,
-      bindObjectActor: options.objectActor.actor,
-    };
-
-    this.requestEvaluation("delete _self" +
-      variableObject.symbolicName, evalOptions).then(onEval, onEval);
-  },
-
-  /**
-   * The property rename function used by the variables view when a property
-   * is renamed.
-   *
-   * @private
-   * @param object options
-   *        The options used for |this._updateVariablesView()|.
-   * @param object variableObject
-   *        The Variable object instance for the renamed property.
-   * @param string newName
-   *        The new name for the property.
-   */
-  _variablesViewSwitch: function(options, variableObject, newName) {
-    let updater = this._updateVariablesView.bind(this, options);
-    let onEval = this._silentEvalCallback.bind(this, updater);
-
-    let evalOptions = {
-      frame: this.SELECTED_FRAME,
-      bindObjectActor: options.objectActor.actor,
-    };
-
-    let newSymbolicName =
-      variableObject.ownerView.symbolicName + '["' + newName + '"]';
-    if (newSymbolicName == variableObject.symbolicName) {
-      return;
-    }
-
-    let code = "_self" + newSymbolicName + " = _self" +
-      variableObject.symbolicName + ";" + "delete _self" +
-      variableObject.symbolicName;
-
-    this.requestEvaluation(code, evalOptions).then(onEval, onEval);
-  },
-
-  /**
-   * A noop callback for JavaScript evaluation. This method releases any
-   * result ObjectActors that come from the server for evaluation requests. This
-   * is used for editing, renaming and deleting properties in the variables
-   * view.
-   *
-   * Exceptions are displayed in the output.
-   *
-   * @private
-   * @param function callback
-   *        Function to invoke once the response is received.
-   * @param object response
-   *        The response packet received from the server.
-   */
-  _silentEvalCallback: function(callback, response) {
-    if (response.error) {
-      console.error("Web Console evaluation failed. " + response.error + ":" +
-                    response.message);
-
-      callback && callback(response);
-      return;
-