summaryrefslogtreecommitdiffstats
path: root/dom/html/test
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/test')
-rw-r--r--dom/html/test/347174transform.xsl41
-rw-r--r--dom/html/test/347174transformable.xml3
-rw-r--r--dom/html/test/allowMedia.sjs12
-rw-r--r--dom/html/test/browser.toml46
-rw-r--r--dom/html/test/browser_DOMDocElementInserted.js23
-rw-r--r--dom/html/test/browser_ImageDocument_svg_zoom.js39
-rw-r--r--dom/html/test/browser_bug1081537.js11
-rw-r--r--dom/html/test/browser_bug1108547.js149
-rw-r--r--dom/html/test/browser_bug436200.js60
-rw-r--r--dom/html/test/browser_bug592641.js61
-rw-r--r--dom/html/test/browser_containerLoadingContent.js108
-rw-r--r--dom/html/test/browser_form_post_from_file_to_http.js181
-rw-r--r--dom/html/test/browser_refresh_after_document_write.js52
-rw-r--r--dom/html/test/browser_submission_flush.js97
-rw-r--r--dom/html/test/browser_targetBlankNoOpener.js121
-rw-r--r--dom/html/test/bug100533_iframe.html8
-rw-r--r--dom/html/test/bug100533_load.html14
-rw-r--r--dom/html/test/bug1260704_iframe.html38
-rw-r--r--dom/html/test/bug1260704_iframe_empty.html15
-rw-r--r--dom/html/test/bug1292522_iframe.html10
-rw-r--r--dom/html/test/bug1292522_page.html14
-rw-r--r--dom/html/test/bug1315146-iframe.html4
-rw-r--r--dom/html/test/bug1315146-main.html15
-rw-r--r--dom/html/test/bug196523-subframe.html37
-rw-r--r--dom/html/test/bug199692-nested-d2.html14
-rw-r--r--dom/html/test/bug199692-nested.html15
-rw-r--r--dom/html/test/bug199692-popup.html190
-rw-r--r--dom/html/test/bug199692-scrolled.html34
-rw-r--r--dom/html/test/bug242709_iframe.html20
-rw-r--r--dom/html/test/bug242709_load.html11
-rw-r--r--dom/html/test/bug277724_iframe1.html28
-rw-r--r--dom/html/test/bug277724_iframe2.xhtml27
-rw-r--r--dom/html/test/bug277890_iframe.html20
-rw-r--r--dom/html/test/bug277890_load.html11
-rw-r--r--dom/html/test/bug340800_iframe.txt4
-rw-r--r--dom/html/test/bug369370-popup.pngbin0 -> 4073 bytes
-rw-r--r--dom/html/test/bug372098-link-target.html7
-rw-r--r--dom/html/test/bug436200.html12
-rw-r--r--dom/html/test/bug441930_iframe.html27
-rw-r--r--dom/html/test/bug445004-inner.html14
-rw-r--r--dom/html/test/bug445004-inner.js27
-rw-r--r--dom/html/test/bug445004-outer-abs.html11
-rw-r--r--dom/html/test/bug445004-outer-rel.html11
-rw-r--r--dom/html/test/bug445004-outer-write.html11
-rw-r--r--dom/html/test/bug446483-iframe.html10
-rw-r--r--dom/html/test/bug448564-echo.sjs6
-rw-r--r--dom/html/test/bug448564-iframe-1.html16
-rw-r--r--dom/html/test/bug448564-iframe-2.html16
-rw-r--r--dom/html/test/bug448564-iframe-3.html16
-rw-r--r--dom/html/test/bug448564-submit.js6
-rw-r--r--dom/html/test/bug499092.html6
-rw-r--r--dom/html/test/bug499092.xml4
-rw-r--r--dom/html/test/bug514856_iframe.html21
-rw-r--r--dom/html/test/bug592641_img.jpgbin0 -> 42018 bytes
-rw-r--r--dom/html/test/bug649134/file_bug649134-1.sjs12
-rw-r--r--dom/html/test/bug649134/file_bug649134-2.sjs12
-rw-r--r--dom/html/test/bug649134/index.html3
-rw-r--r--dom/html/test/chrome.toml12
-rw-r--r--dom/html/test/dialog/mochitest.toml4
-rw-r--r--dom/html/test/dialog/test_bug1648877_dialog_fullscreen_denied.html52
-rw-r--r--dom/html/test/dummy_page.html10
-rw-r--r--dom/html/test/empty.html1
-rw-r--r--dom/html/test/file.webmbin0 -> 512 bytes
-rw-r--r--dom/html/test/file_anchor_ping.html13
-rw-r--r--dom/html/test/file_broadcast_load.html16
-rw-r--r--dom/html/test/file_bug1108547-1.html4
-rw-r--r--dom/html/test/file_bug1108547-2.html6
-rw-r--r--dom/html/test/file_bug1108547-3.html5
-rw-r--r--dom/html/test/file_bug1166138_1x.pngbin0 -> 91 bytes
-rw-r--r--dom/html/test/file_bug1166138_2x.pngbin0 -> 100 bytes
-rw-r--r--dom/html/test/file_bug1166138_def.pngbin0 -> 85 bytes
-rw-r--r--dom/html/test/file_bug1260704.pngbin0 -> 91 bytes
-rw-r--r--dom/html/test/file_bug209275_1.html28
-rw-r--r--dom/html/test/file_bug209275_2.html23
-rw-r--r--dom/html/test/file_bug209275_3.html23
-rw-r--r--dom/html/test/file_bug297761.html13
-rw-r--r--dom/html/test/file_bug417760.pngbin0 -> 1991 bytes
-rw-r--r--dom/html/test/file_bug871161-1.html16
-rw-r--r--dom/html/test/file_bug871161-2.html14
-rw-r--r--dom/html/test/file_bug893537.html9
-rw-r--r--dom/html/test/file_cookiemanager.js20
-rw-r--r--dom/html/test/file_formSubmission_img.jpgbin0 -> 2711 bytes
-rw-r--r--dom/html/test/file_formSubmission_text.txt1
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if1.html13
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if10.html12
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if11.html23
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if12.html23
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if13.html13
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if14.html34
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if15.html33
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if16.html25
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if17.html27
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if18.html26
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if19.html21
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if2.html21
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if3.html24
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if4.html30
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if5.html22
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if6.html21
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if7.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if8.html26
-rw-r--r--dom/html/test/file_iframe_sandbox_a_if9.html18
-rw-r--r--dom/html/test/file_iframe_sandbox_b_if1.html11
-rw-r--r--dom/html/test/file_iframe_sandbox_b_if2.html49
-rw-r--r--dom/html/test/file_iframe_sandbox_b_if3.html92
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if1.html35
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if2.html23
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if3.html26
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if4.html36
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if5.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if6.html24
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if7.html27
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if8.html27
-rw-r--r--dom/html/test/file_iframe_sandbox_c_if9.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_close.html3
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if1.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if10.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if11.html30
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if12.html16
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if13.html34
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if14.html35
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if15.html14
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if16.html22
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if17.html24
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if18.html33
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if19.html13
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if2.html28
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if20.html25
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if21.html14
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if22.html25
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if23.html61
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if3.html13
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if4.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if5.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if6.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if7.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if8.html18
-rw-r--r--dom/html/test/file_iframe_sandbox_d_if9.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if1.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if10.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if11.html22
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if12.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if13.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if14.html24
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if15.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if16.html27
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if2.html12
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if3.html11
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if4.html11
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if5.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if6.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if7.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if8.html23
-rw-r--r--dom/html/test/file_iframe_sandbox_e_if9.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_fail.js4
-rw-r--r--dom/html/test/file_iframe_sandbox_form_fail.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_form_pass.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_g_if1.html60
-rw-r--r--dom/html/test/file_iframe_sandbox_h_if1.html34
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if1.html47
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if2.html50
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if3.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if4.html34
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if5.html33
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if6.html21
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if7.html26
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if8.html36
-rw-r--r--dom/html/test/file_iframe_sandbox_k_if9.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_navigation_fail.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_navigation_pass.html17
-rw-r--r--dom/html/test/file_iframe_sandbox_navigation_start.html11
-rw-r--r--dom/html/test/file_iframe_sandbox_open_window_fail.html19
-rw-r--r--dom/html/test/file_iframe_sandbox_open_window_pass.html25
-rw-r--r--dom/html/test/file_iframe_sandbox_pass.js4
-rw-r--r--dom/html/test/file_iframe_sandbox_redirect.html2
-rw-r--r--dom/html/test/file_iframe_sandbox_redirect.html^headers^2
-rw-r--r--dom/html/test/file_iframe_sandbox_redirect_target.html9
-rw-r--r--dom/html/test/file_iframe_sandbox_refresh.html2
-rw-r--r--dom/html/test/file_iframe_sandbox_refresh.html^headers^1
-rw-r--r--dom/html/test/file_iframe_sandbox_srcdoc_allow_scripts.html1
-rw-r--r--dom/html/test/file_iframe_sandbox_srcdoc_no_allow_scripts.html1
-rw-r--r--dom/html/test/file_iframe_sandbox_top_navigation_fail.html18
-rw-r--r--dom/html/test/file_iframe_sandbox_top_navigation_pass.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_window_form_fail.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_window_form_pass.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_window_navigation_fail.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_window_navigation_pass.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_window_top_navigation_fail.html24
-rw-r--r--dom/html/test/file_iframe_sandbox_window_top_navigation_pass.html20
-rw-r--r--dom/html/test/file_iframe_sandbox_worker.js3
-rw-r--r--dom/html/test/file_refresh_after_document_write.html15
-rw-r--r--dom/html/test/file_script_module.html42
-rw-r--r--dom/html/test/file_srcdoc-2.html10
-rw-r--r--dom/html/test/file_srcdoc.html16
-rw-r--r--dom/html/test/file_srcdoc_iframe3.html1
-rw-r--r--dom/html/test/file_window_close_and_open.html20
-rw-r--r--dom/html/test/file_window_open_close_inner.html7
-rw-r--r--dom/html/test/file_window_open_close_outer.html5
-rw-r--r--dom/html/test/formData_test.js289
-rw-r--r--dom/html/test/formData_worker.js23
-rw-r--r--dom/html/test/formSubmission_chrome.js20
-rw-r--r--dom/html/test/form_data_file.bin1
-rw-r--r--dom/html/test/form_data_file.txt1
-rw-r--r--dom/html/test/form_submit_server.sjs86
-rw-r--r--dom/html/test/forms/FAIL.html1
-rw-r--r--dom/html/test/forms/PASS.html1
-rw-r--r--dom/html/test/forms/chrome.toml6
-rw-r--r--dom/html/test/forms/file_double_submit.html11
-rw-r--r--dom/html/test/forms/file_login_fields.html16
-rw-r--r--dom/html/test/forms/mochitest.toml229
-rw-r--r--dom/html/test/forms/save_restore_custom_elements_sample.html43
-rw-r--r--dom/html/test/forms/save_restore_radio_groups.sjs48
-rw-r--r--dom/html/test/forms/submit_invalid_file.sjs13
-rw-r--r--dom/html/test/forms/test_MozEditableElement_setUserInput.html581
-rw-r--r--dom/html/test/forms/test_autocomplete.html164
-rw-r--r--dom/html/test/forms/test_autocompleteinfo.html206
-rw-r--r--dom/html/test/forms/test_bug1039548.html55
-rw-r--r--dom/html/test/forms/test_bug1283915.html67
-rw-r--r--dom/html/test/forms/test_bug1286509.html49
-rw-r--r--dom/html/test/forms/test_button_attributes_reflection.html144
-rw-r--r--dom/html/test/forms/test_change_event.html286
-rw-r--r--dom/html/test/forms/test_datalist_element.html118
-rw-r--r--dom/html/test/forms/test_double_submit.html33
-rw-r--r--dom/html/test/forms/test_form_attribute-1.html473
-rw-r--r--dom/html/test/forms/test_form_attribute-2.html53
-rw-r--r--dom/html/test/forms/test_form_attribute-3.html68
-rw-r--r--dom/html/test/forms/test_form_attribute-4.html48
-rw-r--r--dom/html/test/forms/test_form_attributes_reflection.html90
-rw-r--r--dom/html/test/forms/test_form_named_getter_dynamic.html54
-rw-r--r--dom/html/test/forms/test_formaction_attribute.html169
-rw-r--r--dom/html/test/forms/test_formnovalidate_attribute.html125
-rw-r--r--dom/html/test/forms/test_input_attributes_reflection.html271
-rw-r--r--dom/html/test/forms/test_input_color_input_change_events.html119
-rw-r--r--dom/html/test/forms/test_input_color_picker_datalist.html42
-rw-r--r--dom/html/test/forms/test_input_color_picker_initial.html78
-rw-r--r--dom/html/test/forms/test_input_color_picker_popup.html144
-rw-r--r--dom/html/test/forms/test_input_color_picker_update.html86
-rw-r--r--dom/html/test/forms/test_input_date_bad_input.html113
-rw-r--r--dom/html/test/forms/test_input_date_key_events.html270
-rw-r--r--dom/html/test/forms/test_input_datetime_calendar_button.html179
-rw-r--r--dom/html/test/forms/test_input_datetime_disabled_focus.html82
-rw-r--r--dom/html/test/forms/test_input_datetime_focus_blur.html64
-rw-r--r--dom/html/test/forms/test_input_datetime_focus_blur_events.html93
-rw-r--r--dom/html/test/forms/test_input_datetime_focus_state.html79
-rw-r--r--dom/html/test/forms/test_input_datetime_hidden.html32
-rw-r--r--dom/html/test/forms/test_input_datetime_input_change_events.html143
-rw-r--r--dom/html/test/forms/test_input_datetime_readonly.html20
-rw-r--r--dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html122
-rw-r--r--dom/html/test/forms/test_input_datetime_tabindex.html113
-rw-r--r--dom/html/test/forms/test_input_defaultValue.html81
-rw-r--r--dom/html/test/forms/test_input_email.html237
-rw-r--r--dom/html/test/forms/test_input_event.html409
-rw-r--r--dom/html/test/forms/test_input_file_picker.html280
-rw-r--r--dom/html/test/forms/test_input_hasBeenTypePassword.html67
-rw-r--r--dom/html/test/forms/test_input_hasBeenTypePassword_navigation.html68
-rw-r--r--dom/html/test/forms/test_input_list_attribute.html253
-rw-r--r--dom/html/test/forms/test_input_number_data.js54
-rw-r--r--dom/html/test/forms/test_input_number_focus.html109
-rw-r--r--dom/html/test/forms/test_input_number_key_events.html238
-rw-r--r--dom/html/test/forms/test_input_number_l10n.html77
-rw-r--r--dom/html/test/forms/test_input_number_mouse_events.html272
-rw-r--r--dom/html/test/forms/test_input_number_placeholder_shown.html30
-rw-r--r--dom/html/test/forms/test_input_number_rounding.html120
-rw-r--r--dom/html/test/forms/test_input_number_validation.html139
-rw-r--r--dom/html/test/forms/test_input_password_click_show_password_button.html97
-rw-r--r--dom/html/test/forms/test_input_password_show_password_button.html81
-rw-r--r--dom/html/test/forms/test_input_radio_indeterminate.html109
-rw-r--r--dom/html/test/forms/test_input_radio_radiogroup.html75
-rw-r--r--dom/html/test/forms/test_input_radio_required.html31
-rw-r--r--dom/html/test/forms/test_input_range_attr_order.html48
-rw-r--r--dom/html/test/forms/test_input_range_key_events.html207
-rw-r--r--dom/html/test/forms/test_input_range_mouse_and_touch_events.html240
-rw-r--r--dom/html/test/forms/test_input_range_rounding.html103
-rw-r--r--dom/html/test/forms/test_input_sanitization.html585
-rw-r--r--dom/html/test/forms/test_input_setting_value.html619
-rw-r--r--dom/html/test/forms/test_input_textarea_set_value_no_scroll.html125
-rw-r--r--dom/html/test/forms/test_input_time_key_events.html221
-rw-r--r--dom/html/test/forms/test_input_time_sec_millisec_field.html134
-rw-r--r--dom/html/test/forms/test_input_types_pref.html77
-rw-r--r--dom/html/test/forms/test_input_typing_sanitization.html217
-rw-r--r--dom/html/test/forms/test_input_untrusted_key_events.html90
-rw-r--r--dom/html/test/forms/test_input_url.html91
-rw-r--r--dom/html/test/forms/test_interactive_content_in_label.html101
-rw-r--r--dom/html/test/forms/test_interactive_content_in_summary.html97
-rw-r--r--dom/html/test/forms/test_label_control_attribute.html100
-rw-r--r--dom/html/test/forms/test_label_input_controls.html84
-rw-r--r--dom/html/test/forms/test_max_attribute.html473
-rw-r--r--dom/html/test/forms/test_maxlength_attribute.html129
-rw-r--r--dom/html/test/forms/test_meter_element.html376
-rw-r--r--dom/html/test/forms/test_meter_pseudo-classes.html169
-rw-r--r--dom/html/test/forms/test_min_attribute.html473
-rw-r--r--dom/html/test/forms/test_minlength_attribute.html130
-rw-r--r--dom/html/test/forms/test_mozistextfield.html111
-rw-r--r--dom/html/test/forms/test_novalidate_attribute.html85
-rw-r--r--dom/html/test/forms/test_option_disabled.html123
-rw-r--r--dom/html/test/forms/test_option_index_attribute.html76
-rw-r--r--dom/html/test/forms/test_option_text.html57
-rw-r--r--dom/html/test/forms/test_output_element.html182
-rw-r--r--dom/html/test/forms/test_pattern_attribute.html324
-rw-r--r--dom/html/test/forms/test_preserving_metadata_between_reloads.html84
-rw-r--r--dom/html/test/forms/test_progress_element.html307
-rw-r--r--dom/html/test/forms/test_radio_in_label.html54
-rw-r--r--dom/html/test/forms/test_radio_radionodelist.html57
-rw-r--r--dom/html/test/forms/test_reportValidation_preventDefault.html89
-rw-r--r--dom/html/test/forms/test_required_attribute.html416
-rw-r--r--dom/html/test/forms/test_restore_form_elements.html174
-rw-r--r--dom/html/test/forms/test_save_restore_custom_elements.html90
-rw-r--r--dom/html/test/forms/test_save_restore_radio_groups.html70
-rw-r--r--dom/html/test/forms/test_select_change_event.html54
-rw-r--r--dom/html/test/forms/test_select_input_change_event.html122
-rw-r--r--dom/html/test/forms/test_select_selectedOptions.html119
-rw-r--r--dom/html/test/forms/test_select_validation.html39
-rw-r--r--dom/html/test/forms/test_set_range_text.html242
-rw-r--r--dom/html/test/forms/test_step_attribute.html1060
-rw-r--r--dom/html/test/forms/test_stepup_stepdown.html1137
-rw-r--r--dom/html/test/forms/test_submit_invalid_file.html55
-rw-r--r--dom/html/test/forms/test_textarea_attributes_reflection.html107
-rw-r--r--dom/html/test/forms/test_validation.html343
-rw-r--r--dom/html/test/forms/test_validation_not_in_doc.html19
-rw-r--r--dom/html/test/forms/test_valueasdate_attribute.html751
-rw-r--r--dom/html/test/forms/test_valueasnumber_attribute.html858
-rw-r--r--dom/html/test/forms/without_selectionchange/mochitest.toml5
-rw-r--r--dom/html/test/forms/without_selectionchange/test_select.html21
-rw-r--r--dom/html/test/head.js65
-rw-r--r--dom/html/test/image-allow-credentials.pngbin0 -> 844 bytes
-rw-r--r--dom/html/test/image-allow-credentials.png^headers^2
-rw-r--r--dom/html/test/image.pngbin0 -> 268 bytes
-rw-r--r--dom/html/test/image_yellow.pngbin0 -> 95 bytes
-rw-r--r--dom/html/test/mochitest.toml990
-rw-r--r--dom/html/test/nnc_lockup.gifbin0 -> 732 bytes
-rw-r--r--dom/html/test/object_bug287465_o1.html1
-rw-r--r--dom/html/test/object_bug287465_o2.html1
-rw-r--r--dom/html/test/object_bug556645.html1
-rw-r--r--dom/html/test/post_action_page.html10
-rw-r--r--dom/html/test/reflect.js1078
-rw-r--r--dom/html/test/script_fakepath.js16
-rw-r--r--dom/html/test/simpleFileOpener.js38
-rw-r--r--dom/html/test/submission_flush.html13
-rw-r--r--dom/html/test/sw_formSubmission.js36
-rw-r--r--dom/html/test/test_a_text.html44
-rw-r--r--dom/html/test/test_allowMedia.html97
-rw-r--r--dom/html/test/test_anchor_href_cache_invalidation.html30
-rw-r--r--dom/html/test/test_anchor_ping.html304
-rw-r--r--dom/html/test/test_base_attributes_reflection.html34
-rw-r--r--dom/html/test/test_bug1003539.html37
-rw-r--r--dom/html/test/test_bug100533.html47
-rw-r--r--dom/html/test/test_bug1013316.html46
-rw-r--r--dom/html/test/test_bug1045270.html46
-rw-r--r--dom/html/test/test_bug1089326.html108
-rw-r--r--dom/html/test/test_bug109445.html55
-rw-r--r--dom/html/test/test_bug109445.xhtml55
-rw-r--r--dom/html/test/test_bug1146116.html59
-rw-r--r--dom/html/test/test_bug1166138.html130
-rw-r--r--dom/html/test/test_bug1203668.html62
-rw-r--r--dom/html/test/test_bug1230665.html46
-rw-r--r--dom/html/test/test_bug1250401.html97
-rw-r--r--dom/html/test/test_bug1260664.html51
-rw-r--r--dom/html/test/test_bug1260704.html90
-rw-r--r--dom/html/test/test_bug1261673.html72
-rw-r--r--dom/html/test/test_bug1261674-1.html77
-rw-r--r--dom/html/test/test_bug1261674-2.html70
-rw-r--r--dom/html/test/test_bug1264157.html90
-rw-r--r--dom/html/test/test_bug1279218.html23
-rw-r--r--dom/html/test/test_bug1287321.html57
-rw-r--r--dom/html/test/test_bug1292522_same_domain_with_different_port_number.html43
-rw-r--r--dom/html/test/test_bug1295719_event_sequence_for_arrow_keys.html66
-rw-r--r--dom/html/test/test_bug1295719_event_sequence_for_number_keys.html65
-rw-r--r--dom/html/test/test_bug1297.html46
-rw-r--r--dom/html/test/test_bug1310865.html18
-rw-r--r--dom/html/test/test_bug1315146.html33
-rw-r--r--dom/html/test/test_bug1322678.html113
-rw-r--r--dom/html/test/test_bug1323815.html50
-rw-r--r--dom/html/test/test_bug1366.html35
-rw-r--r--dom/html/test/test_bug1400.html42
-rw-r--r--dom/html/test/test_bug1414077.html50
-rw-r--r--dom/html/test/test_bug143220.html72
-rw-r--r--dom/html/test/test_bug1472426.html120
-rw-r--r--dom/html/test/test_bug1682.html37
-rw-r--r--dom/html/test/test_bug1785739.html48
-rw-r--r--dom/html/test/test_bug182279.html35
-rw-r--r--dom/html/test/test_bug1823.html30
-rw-r--r--dom/html/test/test_bug196523.html41
-rw-r--r--dom/html/test/test_bug199692.html21
-rw-r--r--dom/html/test/test_bug2082.html30
-rw-r--r--dom/html/test/test_bug209275.xhtml258
-rw-r--r--dom/html/test/test_bug237071.html28
-rw-r--r--dom/html/test/test_bug242709.html33
-rw-r--r--dom/html/test/test_bug24958.html31
-rw-r--r--dom/html/test/test_bug255820.html99
-rw-r--r--dom/html/test/test_bug259332.html64
-rw-r--r--dom/html/test/test_bug274626.html97
-rw-r--r--dom/html/test/test_bug277724.html141
-rw-r--r--dom/html/test/test_bug277890.html33
-rw-r--r--dom/html/test/test_bug287465.html45
-rw-r--r--dom/html/test/test_bug295561.html86
-rw-r--r--dom/html/test/test_bug297761.html77
-rw-r--r--dom/html/test/test_bug300691-1.html126
-rw-r--r--dom/html/test/test_bug300691-2.html142
-rw-r--r--dom/html/test/test_bug300691-3.xhtml48
-rw-r--r--dom/html/test/test_bug311681.html99
-rw-r--r--dom/html/test/test_bug311681.xhtml102
-rw-r--r--dom/html/test/test_bug324378.html76
-rw-r--r--dom/html/test/test_bug330705-1.html41
-rw-r--r--dom/html/test/test_bug332246.html75
-rw-r--r--dom/html/test/test_bug332848.xhtml86
-rw-r--r--dom/html/test/test_bug332893-1.html38
-rw-r--r--dom/html/test/test_bug332893-2.html53
-rw-r--r--dom/html/test/test_bug332893-3.html58
-rw-r--r--dom/html/test/test_bug332893-4.html29
-rw-r--r--dom/html/test/test_bug332893-5.html29
-rw-r--r--dom/html/test/test_bug332893-6.html27
-rw-r--r--dom/html/test/test_bug332893-7.html69
-rw-r--r--dom/html/test/test_bug3348.html33
-rw-r--r--dom/html/test/test_bug340017.xhtml27
-rw-r--r--dom/html/test/test_bug340800.html55
-rw-r--r--dom/html/test/test_bug347174.html64
-rw-r--r--dom/html/test/test_bug347174_write.html71
-rw-r--r--dom/html/test/test_bug347174_xsl.html55
-rw-r--r--dom/html/test/test_bug347174_xslp.html61
-rw-r--r--dom/html/test/test_bug353415-1.html42
-rw-r--r--dom/html/test/test_bug353415-2.html67
-rw-r--r--dom/html/test/test_bug359657.html40
-rw-r--r--dom/html/test/test_bug369370.html153
-rw-r--r--dom/html/test/test_bug371375.html58
-rw-r--r--dom/html/test/test_bug372098.html68
-rw-r--r--dom/html/test/test_bug373589.html29
-rw-r--r--dom/html/test/test_bug375003-1.html157
-rw-r--r--dom/html/test/test_bug375003-2.html110
-rw-r--r--dom/html/test/test_bug377624.html25
-rw-r--r--dom/html/test/test_bug380383.html39
-rw-r--r--dom/html/test/test_bug383383.html41
-rw-r--r--dom/html/test/test_bug383383_2.xhtml20
-rw-r--r--dom/html/test/test_bug384419.html56
-rw-r--r--dom/html/test/test_bug386496.html53
-rw-r--r--dom/html/test/test_bug386728.html45
-rw-r--r--dom/html/test/test_bug386996.html43
-rw-r--r--dom/html/test/test_bug388558.html76
-rw-r--r--dom/html/test/test_bug388746.html62
-rw-r--r--dom/html/test/test_bug388794.html107
-rw-r--r--dom/html/test/test_bug389797.html243
-rw-r--r--dom/html/test/test_bug390975.html61
-rw-r--r--dom/html/test/test_bug391994.html184
-rw-r--r--dom/html/test/test_bug394700.html49
-rw-r--r--dom/html/test/test_bug395107.html108
-rw-r--r--dom/html/test/test_bug401160.xhtml27
-rw-r--r--dom/html/test/test_bug402680.html50
-rw-r--r--dom/html/test/test_bug403868.html87
-rw-r--r--dom/html/test/test_bug403868.xhtml86
-rw-r--r--dom/html/test/test_bug405242.html35
-rw-r--r--dom/html/test/test_bug406596.html83
-rw-r--r--dom/html/test/test_bug417760.html71
-rw-r--r--dom/html/test/test_bug421640.html56
-rw-r--r--dom/html/test/test_bug424698.html94
-rw-r--r--dom/html/test/test_bug428135.xhtml156
-rw-r--r--dom/html/test/test_bug430351.html523
-rw-r--r--dom/html/test/test_bug435128.html42
-rw-r--r--dom/html/test/test_bug441930.html29
-rw-r--r--dom/html/test/test_bug442801.html63
-rw-r--r--dom/html/test/test_bug445004.html138
-rw-r--r--dom/html/test/test_bug446483.html47
-rw-r--r--dom/html/test/test_bug448166.html39
-rw-r--r--dom/html/test/test_bug448564.html53
-rw-r--r--dom/html/test/test_bug456229.html30
-rw-r--r--dom/html/test/test_bug458037.xhtml112
-rw-r--r--dom/html/test/test_bug460568.html144
-rw-r--r--dom/html/test/test_bug463104.html25
-rw-r--r--dom/html/test/test_bug478251.html74
-rw-r--r--dom/html/test/test_bug481335.xhtml122
-rw-r--r--dom/html/test/test_bug481440.html30
-rw-r--r--dom/html/test/test_bug481647.html42
-rw-r--r--dom/html/test/test_bug482659.html64
-rw-r--r--dom/html/test/test_bug486741.html43
-rw-r--r--dom/html/test/test_bug489532.html33
-rw-r--r--dom/html/test/test_bug497242.xhtml41
-rw-r--r--dom/html/test/test_bug499092.html43
-rw-r--r--dom/html/test/test_bug500885.html67
-rw-r--r--dom/html/test/test_bug512367.html40
-rw-r--r--dom/html/test/test_bug514856.html61
-rw-r--r--dom/html/test/test_bug518122.html126
-rw-r--r--dom/html/test/test_bug519987.html33
-rw-r--r--dom/html/test/test_bug523771.html106
-rw-r--r--dom/html/test/test_bug529819.html32
-rw-r--r--dom/html/test/test_bug529859.html42
-rw-r--r--dom/html/test/test_bug535043.html90
-rw-r--r--dom/html/test/test_bug536891.html67
-rw-r--r--dom/html/test/test_bug536895.html54
-rw-r--r--dom/html/test/test_bug546995.html40
-rw-r--r--dom/html/test/test_bug547850.html45
-rw-r--r--dom/html/test/test_bug551846.html164
-rw-r--r--dom/html/test/test_bug555567.html42
-rw-r--r--dom/html/test/test_bug556645.html73
-rw-r--r--dom/html/test/test_bug557087-1.html129
-rw-r--r--dom/html/test/test_bug557087-2.html363
-rw-r--r--dom/html/test/test_bug557087-3.html215
-rw-r--r--dom/html/test/test_bug557087-4.html90
-rw-r--r--dom/html/test/test_bug557087-5.html94
-rw-r--r--dom/html/test/test_bug557087-6.html44
-rw-r--r--dom/html/test/test_bug557620.html30
-rw-r--r--dom/html/test/test_bug558788-1.html212
-rw-r--r--dom/html/test/test_bug558788-2.html174
-rw-r--r--dom/html/test/test_bug560112.html211
-rw-r--r--dom/html/test/test_bug561634.html126
-rw-r--r--dom/html/test/test_bug561636.html99
-rw-r--r--dom/html/test/test_bug561640.html72
-rw-r--r--dom/html/test/test_bug564001.html48
-rw-r--r--dom/html/test/test_bug566046.html200
-rw-r--r--dom/html/test/test_bug567938-1.html69
-rw-r--r--dom/html/test/test_bug567938-2.html70
-rw-r--r--dom/html/test/test_bug567938-3.html71
-rw-r--r--dom/html/test/test_bug567938-4.html43
-rw-r--r--dom/html/test/test_bug569955.html37
-rw-r--r--dom/html/test/test_bug573969.html37
-rw-r--r--dom/html/test/test_bug57600.html42
-rw-r--r--dom/html/test/test_bug579079.html40
-rw-r--r--dom/html/test/test_bug582412-1.html200
-rw-r--r--dom/html/test/test_bug582412-2.html199
-rw-r--r--dom/html/test/test_bug583514.html71
-rw-r--r--dom/html/test/test_bug583533.html81
-rw-r--r--dom/html/test/test_bug586763.html43
-rw-r--r--dom/html/test/test_bug586786.html57
-rw-r--r--dom/html/test/test_bug587469.html41
-rw-r--r--dom/html/test/test_bug589.html42
-rw-r--r--dom/html/test/test_bug590353-1.html36
-rw-r--r--dom/html/test/test_bug590353-2.html79
-rw-r--r--dom/html/test/test_bug590363.html133
-rw-r--r--dom/html/test/test_bug592802.html96
-rw-r--r--dom/html/test/test_bug593689.html50
-rw-r--r--dom/html/test/test_bug595429.html56
-rw-r--r--dom/html/test/test_bug595447.html29
-rw-r--r--dom/html/test/test_bug595449.html95
-rw-r--r--dom/html/test/test_bug596350.html65
-rw-r--r--dom/html/test/test_bug596511.html237
-rw-r--r--dom/html/test/test_bug598643.html80
-rw-r--r--dom/html/test/test_bug598833-1.html45
-rw-r--r--dom/html/test/test_bug600155.html44
-rw-r--r--dom/html/test/test_bug601030.html52
-rw-r--r--dom/html/test/test_bug605124-1.html98
-rw-r--r--dom/html/test/test_bug605124-2.html117
-rw-r--r--dom/html/test/test_bug605125-1.html105
-rw-r--r--dom/html/test/test_bug605125-2.html149
-rw-r--r--dom/html/test/test_bug606817.html59
-rw-r--r--dom/html/test/test_bug607145.html86
-rw-r--r--dom/html/test/test_bug610212.html42
-rw-r--r--dom/html/test/test_bug610687.html195
-rw-r--r--dom/html/test/test_bug611189.html45
-rw-r--r--dom/html/test/test_bug612730.html51
-rw-r--r--dom/html/test/test_bug613019.html84
-rw-r--r--dom/html/test/test_bug613113.html52
-rw-r--r--dom/html/test/test_bug613722.html32
-rw-r--r--dom/html/test/test_bug613979.html50
-rw-r--r--dom/html/test/test_bug615595.htmlbin0 -> 2706 bytes
-rw-r--r--dom/html/test/test_bug615833.html141
-rw-r--r--dom/html/test/test_bug618948.html88
-rw-r--r--dom/html/test/test_bug619278.html56
-rw-r--r--dom/html/test/test_bug622597.html105
-rw-r--r--dom/html/test/test_bug623291.html46
-rw-r--r--dom/html/test/test_bug6296.html31
-rw-r--r--dom/html/test/test_bug629801.html50
-rw-r--r--dom/html/test/test_bug633058.html66
-rw-r--r--dom/html/test/test_bug636336.html41
-rw-r--r--dom/html/test/test_bug641219.html34
-rw-r--r--dom/html/test/test_bug643051.html55
-rw-r--r--dom/html/test/test_bug646157.html95
-rw-r--r--dom/html/test/test_bug649134.html54
-rw-r--r--dom/html/test/test_bug651956.html48
-rw-r--r--dom/html/test/test_bug658746.html97
-rw-r--r--dom/html/test/test_bug659596.html96
-rw-r--r--dom/html/test/test_bug659743.xml55
-rw-r--r--dom/html/test/test_bug660663.html30
-rw-r--r--dom/html/test/test_bug660959-1.html25
-rw-r--r--dom/html/test/test_bug660959-2.html30
-rw-r--r--dom/html/test/test_bug660959-3.html28
-rw-r--r--dom/html/test/test_bug666200.html43
-rw-r--r--dom/html/test/test_bug666666.html32
-rw-r--r--dom/html/test/test_bug669012.html44
-rw-r--r--dom/html/test/test_bug674558.html287
-rw-r--r--dom/html/test/test_bug674927.html55
-rw-r--r--dom/html/test/test_bug677495-1.html34
-rw-r--r--dom/html/test/test_bug677495.html34
-rw-r--r--dom/html/test/test_bug677658.html41
-rw-r--r--dom/html/test/test_bug682886.html33
-rw-r--r--dom/html/test/test_bug691.html62
-rw-r--r--dom/html/test/test_bug694.html30
-rw-r--r--dom/html/test/test_bug694503.html75
-rw-r--r--dom/html/test/test_bug696.html28
-rw-r--r--dom/html/test/test_bug717819.html36
-rw-r--r--dom/html/test/test_bug741266.html44
-rw-r--r--dom/html/test/test_bug742030.html31
-rw-r--r--dom/html/test/test_bug742549.html47
-rw-r--r--dom/html/test/test_bug745685.html105
-rw-r--r--dom/html/test/test_bug763626.html29
-rw-r--r--dom/html/test/test_bug765780.html46
-rw-r--r--dom/html/test/test_bug780993.html39
-rw-r--r--dom/html/test/test_bug787134.html28
-rw-r--r--dom/html/test/test_bug797113.html39
-rw-r--r--dom/html/test/test_bug803677.html49
-rw-r--r--dom/html/test/test_bug821307.html41
-rw-r--r--dom/html/test/test_bug827126.html28
-rw-r--r--dom/html/test/test_bug838582.html35
-rw-r--r--dom/html/test/test_bug839371.html44
-rw-r--r--dom/html/test/test_bug839913.html14
-rw-r--r--dom/html/test/test_bug841466.html33
-rw-r--r--dom/html/test/test_bug845057.html59
-rw-r--r--dom/html/test/test_bug869040.html36
-rw-r--r--dom/html/test/test_bug870787.html84
-rw-r--r--dom/html/test/test_bug871161.html37
-rw-r--r--dom/html/test/test_bug874758.html31
-rw-r--r--dom/html/test/test_bug879319.html92
-rw-r--r--dom/html/test/test_bug885024.html46
-rw-r--r--dom/html/test/test_bug893537.html45
-rw-r--r--dom/html/test/test_bug95530.html38
-rw-r--r--dom/html/test/test_bug969346.html33
-rw-r--r--dom/html/test/test_bug982039.html46
-rw-r--r--dom/html/test/test_change_crossorigin.html89
-rw-r--r--dom/html/test/test_checked.html347
-rw-r--r--dom/html/test/test_dir_attributes_reflection.html27
-rw-r--r--dom/html/test/test_dl_attributes_reflection.html27
-rw-r--r--dom/html/test/test_document-element-inserted.html54
-rw-r--r--dom/html/test/test_documentAll.html167
-rw-r--r--dom/html/test/test_element_prototype.html32
-rw-r--r--dom/html/test/test_embed_attributes_reflection.html57
-rw-r--r--dom/html/test/test_external_protocol_iframe.html80
-rw-r--r--dom/html/test/test_fakepath.html40
-rw-r--r--dom/html/test/test_filepicker_default_directory.html81
-rw-r--r--dom/html/test/test_focusshift_button.html34
-rw-r--r--dom/html/test/test_form-parsing.html35
-rw-r--r--dom/html/test/test_formData.html50
-rw-r--r--dom/html/test/test_formSubmission.html910
-rw-r--r--dom/html/test/test_formSubmission2.html220
-rw-r--r--dom/html/test/test_formelements.html68
-rw-r--r--dom/html/test/test_fragment_form_pointer.html27
-rw-r--r--dom/html/test/test_frame_count_with_synthetic_doc.html36
-rw-r--r--dom/html/test/test_getElementsByName_after_mutation.html51
-rw-r--r--dom/html/test/test_hidden.html52
-rw-r--r--dom/html/test/test_html_attributes_reflection.html27
-rw-r--r--dom/html/test/test_htmlcollection.html55
-rw-r--r--dom/html/test/test_iframe_sandbox_general.html283
-rw-r--r--dom/html/test/test_iframe_sandbox_inheritance.html202
-rw-r--r--dom/html/test/test_iframe_sandbox_navigation.html285
-rw-r--r--dom/html/test/test_iframe_sandbox_navigation2.html216
-rw-r--r--dom/html/test/test_iframe_sandbox_popups.html78
-rw-r--r--dom/html/test/test_iframe_sandbox_popups_inheritance.html157
-rw-r--r--dom/html/test/test_iframe_sandbox_redirect.html45
-rw-r--r--dom/html/test/test_iframe_sandbox_refresh.html101
-rw-r--r--dom/html/test/test_iframe_sandbox_same_origin.html108
-rw-r--r--dom/html/test/test_iframe_sandbox_workers.html74
-rw-r--r--dom/html/test/test_imageSrcSet.html38
-rw-r--r--dom/html/test/test_image_clone_load.html21
-rw-r--r--dom/html/test/test_img_attributes_reflection.html103
-rw-r--r--dom/html/test/test_input_file_cancel_event.html43
-rw-r--r--dom/html/test/test_input_files_not_nsIFile.html48
-rw-r--r--dom/html/test/test_input_lastInteractiveValue.html134
-rw-r--r--dom/html/test/test_inputmode.html132
-rw-r--r--dom/html/test/test_li_attributes_reflection.html34
-rw-r--r--dom/html/test/test_link_attributes_reflection.html96
-rw-r--r--dom/html/test/test_link_sizes.html35
-rw-r--r--dom/html/test/test_map_attributes_reflection.html27
-rw-r--r--dom/html/test/test_meta_attributes_reflection.html45
-rw-r--r--dom/html/test/test_mod_attributes_reflection.html41
-rw-r--r--dom/html/test/test_multipleFilePicker.html79
-rw-r--r--dom/html/test/test_named_options.html61
-rw-r--r--dom/html/test/test_nested_invalid_fieldsets.html47
-rw-r--r--dom/html/test/test_nestediframe.html55
-rw-r--r--dom/html/test/test_non-ascii-cookie.html69
-rw-r--r--dom/html/test/test_non-ascii-cookie.html^headers^1
-rw-r--r--dom/html/test/test_object_attributes_reflection.html117
-rw-r--r--dom/html/test/test_ol_attributes_reflection.html65
-rw-r--r--dom/html/test/test_option_defaultSelected.html47
-rw-r--r--dom/html/test/test_option_selected_state.html61
-rw-r--r--dom/html/test/test_param_attributes_reflection.html45
-rw-r--r--dom/html/test/test_plugin.tst1
-rw-r--r--dom/html/test/test_q_attributes_reflection.html32
-rw-r--r--dom/html/test/test_restore_from_parser_fragment.html59
-rw-r--r--dom/html/test/test_rowscollection.html69
-rw-r--r--dom/html/test/test_script_module.html31
-rw-r--r--dom/html/test/test_set_input_files.html55
-rw-r--r--dom/html/test/test_srcdoc-2.html57
-rw-r--r--dom/html/test/test_srcdoc.html118
-rw-r--r--dom/html/test/test_style_attributes_reflection.html35
-rw-r--r--dom/html/test/test_track.html62
-rw-r--r--dom/html/test/test_ul_attributes_reflection.html33
-rw-r--r--dom/html/test/test_viewport_resize.html44
-rw-r--r--dom/html/test/test_window_open_close.html53
-rw-r--r--dom/html/test/test_window_open_from_closing.html43
684 files changed, 53186 insertions, 0 deletions
diff --git a/dom/html/test/347174transform.xsl b/dom/html/test/347174transform.xsl
new file mode 100644
index 0000000000..1b201de3f3
--- /dev/null
+++ b/dom/html/test/347174transform.xsl
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:template match="/">
+<html>
+<head>
+<script>
+window.parent.frameScriptTag(document.readyState);
+
+function attachCustomEventListener(element, eventName, command) {
+ if (window.addEventListener &amp;&amp; !window.opera)
+ element.addEventListener(eventName, command, true);
+ else if (window.attachEvent)
+ element.attachEvent("on" + eventName, command);
+}
+
+function load() {
+ window.parent.frameLoad(document.readyState);
+}
+
+function readyStateChange() {
+ window.parent.frameReadyStateChange(document.readyState);
+}
+
+function DOMContentLoaded() {
+ window.parent.frameDOMContentLoaded(document.readyState);
+}
+
+window.onload=load;
+
+attachCustomEventListener(document, "readystatechange", readyStateChange);
+attachCustomEventListener(document, "DOMContentLoaded", DOMContentLoaded);
+
+</script>
+</head>
+<body>
+</body>
+</html>
+</xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/dom/html/test/347174transformable.xml b/dom/html/test/347174transformable.xml
new file mode 100644
index 0000000000..68f7bc6dca
--- /dev/null
+++ b/dom/html/test/347174transformable.xml
@@ -0,0 +1,3 @@
+<?xml version='1.0'?>
+<?xml-stylesheet type="text/xsl" href="347174transform.xsl"?>
+<doc>This is a sample document.</doc>
diff --git a/dom/html/test/allowMedia.sjs b/dom/html/test/allowMedia.sjs
new file mode 100644
index 0000000000..f29619cd89
--- /dev/null
+++ b/dom/html/test/allowMedia.sjs
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(req, resp) {
+ resp.setHeader("Cache-Control", "no-cache", false);
+ resp.setHeader("Content-Type", "text/plain", false);
+
+ let stateKey = "allowMediaState";
+ let state = getState(stateKey);
+ setState(stateKey, req.queryString ? "FAIL" : "");
+ resp.write(state || "PASS");
+}
diff --git a/dom/html/test/browser.toml b/dom/html/test/browser.toml
new file mode 100644
index 0000000000..13775f607b
--- /dev/null
+++ b/dom/html/test/browser.toml
@@ -0,0 +1,46 @@
+[DEFAULT]
+support-files = [
+ "bug592641_img.jpg",
+ "dummy_page.html",
+ "image.png",
+ "submission_flush.html",
+ "post_action_page.html",
+ "form_data_file.bin",
+ "form_data_file.txt",
+ "form_submit_server.sjs",
+ "head.js",
+]
+
+["browser_DOMDocElementInserted.js"]
+skip-if = ["bits == 64 && (os == 'mac' || os == 'linux')"] #Bug 1646862
+
+["browser_ImageDocument_svg_zoom.js"]
+
+["browser_bug436200.js"]
+support-files = ["bug436200.html"]
+
+["browser_bug592641.js"]
+
+["browser_bug1081537.js"]
+
+["browser_bug1108547.js"]
+support-files = [
+ "file_bug1108547-1.html",
+ "file_bug1108547-2.html",
+ "file_bug1108547-3.html",
+]
+
+["browser_containerLoadingContent.js"]
+
+["browser_form_post_from_file_to_http.js"]
+
+["browser_refresh_after_document_write.js"]
+support-files = ["file_refresh_after_document_write.html"]
+
+["browser_submission_flush.js"]
+
+["browser_targetBlankNoOpener.js"]
+support-files = [
+ "empty.html",
+ "image_yellow.png",
+]
diff --git a/dom/html/test/browser_DOMDocElementInserted.js b/dom/html/test/browser_DOMDocElementInserted.js
new file mode 100644
index 0000000000..fb2b2ae63b
--- /dev/null
+++ b/dom/html/test/browser_DOMDocElementInserted.js
@@ -0,0 +1,23 @@
+// Tests that the DOMDocElementInserted event is visible on the frame
+add_task(async function () {
+ let tab = BrowserTestUtils.addTab(gBrowser);
+ let uri = "data:text/html;charset=utf-8,<html/>";
+
+ let eventPromise = ContentTask.spawn(tab.linkedBrowser, null, function () {
+ return new Promise(resolve => {
+ addEventListener(
+ "DOMDocElementInserted",
+ event => resolve(event.target.documentURIObject.spec),
+ {
+ once: true,
+ }
+ );
+ });
+ });
+
+ BrowserTestUtils.startLoadingURIString(tab.linkedBrowser, uri);
+ let loadedURI = await eventPromise;
+ is(loadedURI, uri, "Should have seen the event for the right URI");
+
+ gBrowser.removeTab(tab);
+});
diff --git a/dom/html/test/browser_ImageDocument_svg_zoom.js b/dom/html/test/browser_ImageDocument_svg_zoom.js
new file mode 100644
index 0000000000..f0df2282a3
--- /dev/null
+++ b/dom/html/test/browser_ImageDocument_svg_zoom.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URL = `data:image/svg+xml,<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><rect width="100" height="100" fill="green"/></svg>`;
+
+function test_once() {
+ return BrowserTestUtils.withNewTab(URL, async browser => {
+ return await SpecialPowers.spawn(browser, [], async function () {
+ const rect = content.document.documentElement.getBoundingClientRect();
+ info(
+ `${rect.width}x${rect.height}, ${content.innerWidth}x${content.innerHeight}`
+ );
+ is(
+ Math.round(rect.height),
+ content.innerHeight,
+ "Should fill the viewport and not overflow"
+ );
+ });
+ });
+}
+
+add_task(async function test_with_no_text_zoom() {
+ await test_once();
+});
+
+add_task(async function test_with_text_zoom() {
+ let dpi = window.devicePixelRatio;
+
+ await SpecialPowers.pushPrefEnv({ set: [["ui.textScaleFactor", 200]] });
+ Assert.greater(
+ window.devicePixelRatio,
+ dpi,
+ "DPI should change as a result of the pref flip"
+ );
+
+ return test_once();
+});
diff --git a/dom/html/test/browser_bug1081537.js b/dom/html/test/browser_bug1081537.js
new file mode 100644
index 0000000000..2a079be2f7
--- /dev/null
+++ b/dom/html/test/browser_bug1081537.js
@@ -0,0 +1,11 @@
+// This test is useful because mochitest-browser runs as an addon, so we test
+// addon-scope paths here.
+var ifr;
+function test() {
+ ifr = document.createXULElement("iframe");
+ document.getElementById("main-window").appendChild(ifr);
+ is(ifr.contentDocument.nodePrincipal.origin, "[System Principal]");
+ ifr.contentDocument.open();
+ ok(true, "Didn't throw");
+}
+registerCleanupFunction(() => ifr.remove());
diff --git a/dom/html/test/browser_bug1108547.js b/dom/html/test/browser_bug1108547.js
new file mode 100644
index 0000000000..4949827086
--- /dev/null
+++ b/dom/html/test/browser_bug1108547.js
@@ -0,0 +1,149 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+requestLongerTimeout(2);
+
+function test() {
+ waitForExplicitFinish();
+
+ runPass("file_bug1108547-2.html", function () {
+ runPass("file_bug1108547-3.html", function () {
+ finish();
+ });
+ });
+}
+
+function runPass(getterFile, finishedCallback) {
+ var rootDir = "http://mochi.test:8888/browser/dom/html/test/";
+ var testBrowser;
+ var privateWin;
+
+ function whenDelayedStartupFinished(win, callback) {
+ let topic = "browser-delayed-startup-finished";
+ Services.obs.addObserver(function onStartup(aSubject) {
+ if (win != aSubject) {
+ return;
+ }
+
+ Services.obs.removeObserver(onStartup, topic);
+ executeSoon(callback);
+ }, topic);
+ }
+
+ // First, set the cookie in a normal window.
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ rootDir + "file_bug1108547-1.html"
+ );
+ BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(
+ afterOpenCookieSetter
+ );
+
+ function afterOpenCookieSetter() {
+ gBrowser.removeCurrentTab();
+
+ // Now, open a private window.
+ privateWin = OpenBrowserWindow({ private: true });
+ whenDelayedStartupFinished(privateWin, afterPrivateWindowOpened);
+ }
+
+ function afterPrivateWindowOpened() {
+ // In the private window, open the getter file, and wait for a new tab to be opened.
+ privateWin.gBrowser.selectedTab = BrowserTestUtils.addTab(
+ privateWin.gBrowser,
+ rootDir + getterFile
+ );
+ testBrowser = privateWin.gBrowser.selectedBrowser;
+ privateWin.gBrowser.tabContainer.addEventListener(
+ "TabOpen",
+ onNewTabOpened,
+ true
+ );
+ }
+
+ function fetchResult() {
+ return SpecialPowers.spawn(testBrowser, [], function () {
+ return content.document.getElementById("result").textContent;
+ });
+ }
+
+ function onNewTabOpened() {
+ // When the new tab is opened, wait for it to load.
+ privateWin.gBrowser.tabContainer.removeEventListener(
+ "TabOpen",
+ onNewTabOpened,
+ true
+ );
+ BrowserTestUtils.browserLoaded(
+ privateWin.gBrowser.tabs[privateWin.gBrowser.tabs.length - 1]
+ .linkedBrowser
+ )
+ .then(fetchResult)
+ .then(onNewTabLoaded);
+ }
+
+ function onNewTabLoaded(result) {
+ // Now, ensure that the private tab doesn't have access to the cookie set in normal mode.
+ is(result, "", "Shouldn't have access to the cookies");
+
+ // We're done with the private window, close it.
+ privateWin.close();
+
+ // Clear all cookies.
+ Cc["@mozilla.org/cookiemanager;1"]
+ .getService(Ci.nsICookieManager)
+ .removeAll();
+
+ // Open a new private window, this time to set a cookie inside it.
+ privateWin = OpenBrowserWindow({ private: true });
+ whenDelayedStartupFinished(privateWin, afterPrivateWindowOpened2);
+ }
+
+ function afterPrivateWindowOpened2() {
+ // In the private window, open the setter file, and wait for it to load.
+ privateWin.gBrowser.selectedTab = BrowserTestUtils.addTab(
+ privateWin.gBrowser,
+ rootDir + "file_bug1108547-1.html"
+ );
+ BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser).then(
+ afterOpenCookieSetter2
+ );
+ }
+
+ function afterOpenCookieSetter2() {
+ // We're done with the private window now, close it.
+ privateWin.close();
+
+ // Now try to read the cookie in a normal window, and wait for a new tab to be opened.
+ gBrowser.selectedTab = BrowserTestUtils.addTab(
+ gBrowser,
+ rootDir + getterFile
+ );
+ testBrowser = gBrowser.selectedBrowser;
+ gBrowser.tabContainer.addEventListener("TabOpen", onNewTabOpened2, true);
+ }
+
+ function onNewTabOpened2() {
+ // When the new tab is opened, wait for it to load.
+ gBrowser.tabContainer.removeEventListener("TabOpen", onNewTabOpened2, true);
+ BrowserTestUtils.browserLoaded(
+ gBrowser.tabs[gBrowser.tabs.length - 1].linkedBrowser
+ )
+ .then(fetchResult)
+ .then(onNewTabLoaded2);
+ }
+
+ function onNewTabLoaded2(result) {
+ // Now, ensure that the normal tab doesn't have access to the cookie set in private mode.
+ is(result, "", "Shouldn't have access to the cookies");
+
+ // Remove both of the tabs opened here.
+ gBrowser.removeCurrentTab();
+ gBrowser.removeCurrentTab();
+
+ privateWin = null;
+ testBrowser = null;
+
+ finishedCallback();
+ }
+}
diff --git a/dom/html/test/browser_bug436200.js b/dom/html/test/browser_bug436200.js
new file mode 100644
index 0000000000..7e739c02ad
--- /dev/null
+++ b/dom/html/test/browser_bug436200.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const kTestPage = "https://example.org/browser/dom/html/test/bug436200.html";
+
+async function run_test(shouldShowPrompt, msg) {
+ let promptShown = false;
+
+ function tabModalObserver(subject) {
+ promptShown = true;
+ subject.querySelector(".tabmodalprompt-button0").click();
+ }
+ Services.obs.addObserver(tabModalObserver, "tabmodal-dialog-loaded");
+
+ function commonDialogObserver(subject) {
+ let dialog = subject.Dialog;
+ promptShown = true;
+ dialog.ui.button0.click();
+ }
+ Services.obs.addObserver(commonDialogObserver, "common-dialog-loaded");
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, kTestPage);
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ let form = content.document.getElementById("test_form");
+ form.submit();
+ });
+ Services.obs.removeObserver(tabModalObserver, "tabmodal-dialog-loaded");
+ Services.obs.removeObserver(commonDialogObserver, "common-dialog-loaded");
+
+ is(promptShown, shouldShowPrompt, msg);
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async function test_prompt() {
+ await run_test(true, "Should show prompt");
+});
+
+add_task(async function test_noprompt() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.warn_submit_secure_to_insecure", false]],
+ });
+ await run_test(false, "Should not show prompt");
+ await SpecialPowers.popPrefEnv();
+});
+
+add_task(async function test_prompt_modal() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [
+ "prompts.modalType.insecureFormSubmit",
+ Services.prompt.MODAL_TYPE_WINDOW,
+ ],
+ ],
+ });
+ await run_test(true, "Should show prompt");
+ await SpecialPowers.popPrefEnv();
+});
diff --git a/dom/html/test/browser_bug592641.js b/dom/html/test/browser_bug592641.js
new file mode 100644
index 0000000000..761af6a568
--- /dev/null
+++ b/dom/html/test/browser_bug592641.js
@@ -0,0 +1,61 @@
+// Test for bug 592641 - Image document doesn't show dimensions of cached images
+
+// Globals
+var testPath = "http://mochi.test:8888/browser/dom/html/test/";
+var ctx = { loadsDone: 0 };
+
+// Entry point from Mochikit
+function test() {
+ waitForExplicitFinish();
+
+ ctx.tab1 = BrowserTestUtils.addTab(gBrowser, testPath + "bug592641_img.jpg");
+ ctx.tab1Browser = gBrowser.getBrowserForTab(ctx.tab1);
+ BrowserTestUtils.browserLoaded(ctx.tab1Browser).then(load1Soon);
+}
+
+function checkTitle(title) {
+ ctx.loadsDone++;
+ ok(
+ /^bug592641_img\.jpg \(JPEG Image, 1500\u00A0\u00D7\u00A01500 pixels\)/.test(
+ title
+ ),
+ "Title should be correct on load #" + ctx.loadsDone + ", was: " + title
+ );
+}
+
+function load1Soon() {
+ // onload is fired in OnStopDecode, so let's use executeSoon() to make sure
+ // that any other OnStopDecode event handlers get the chance to fire first.
+ executeSoon(load1Done);
+}
+
+function load1Done() {
+ // Check the title
+ var title = ctx.tab1Browser.contentTitle;
+ checkTitle(title);
+
+ // Try loading the same image in a new tab to make sure things work in
+ // the cached case.
+ ctx.tab2 = BrowserTestUtils.addTab(gBrowser, testPath + "bug592641_img.jpg");
+ ctx.tab2Browser = gBrowser.getBrowserForTab(ctx.tab2);
+ BrowserTestUtils.browserLoaded(ctx.tab2Browser).then(load2Soon);
+}
+
+function load2Soon() {
+ // onload is fired in OnStopDecode, so let's use executeSoon() to make sure
+ // that any other OnStopDecode event handlers get the chance to fire first.
+ executeSoon(load2Done);
+}
+
+function load2Done() {
+ // Check the title
+ var title = ctx.tab2Browser.contentTitle;
+ checkTitle(title);
+
+ // Clean up
+ gBrowser.removeTab(ctx.tab1);
+ gBrowser.removeTab(ctx.tab2);
+
+ // Test done
+ finish();
+}
diff --git a/dom/html/test/browser_containerLoadingContent.js b/dom/html/test/browser_containerLoadingContent.js
new file mode 100644
index 0000000000..4fb10db614
--- /dev/null
+++ b/dom/html/test/browser_containerLoadingContent.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const DIRPATH = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ ""
+);
+
+const ORIGIN = "https://example.com";
+const CROSSORIGIN = "https://example.org";
+
+const TABURL = `${ORIGIN}/${DIRPATH}dummy_page.html`;
+
+const IMAGEURL = `${ORIGIN}/${DIRPATH}image.png`;
+const CROSSIMAGEURL = `${CROSSORIGIN}/${DIRPATH}image.png`;
+
+const DOCUMENTURL = `${ORIGIN}/${DIRPATH}dummy_page.html`;
+const CROSSDOCUMENTURL = `${CROSSORIGIN}/${DIRPATH}dummy_page.html`;
+
+function getPids(browser) {
+ return browser.browsingContext.children.map(
+ child => child.currentWindowContext.osPid
+ );
+}
+
+async function runTest(spec, tabUrl, imageurl, crossimageurl, check) {
+ await BrowserTestUtils.withNewTab(tabUrl, async browser => {
+ await SpecialPowers.spawn(
+ browser,
+ [spec, imageurl, crossimageurl],
+ async ({ element, attribute }, url1, url2) => {
+ for (let url of [url1, url2]) {
+ const object = content.document.createElement(element);
+ object[attribute] = url;
+ const onloadPromise = new Promise(res => {
+ object.onload = res;
+ });
+ content.document.body.appendChild(object);
+ await onloadPromise;
+ }
+ }
+ );
+
+ await check(browser);
+ });
+}
+
+let iframe = { element: "iframe", attribute: "src" };
+let embed = { element: "embed", attribute: "src" };
+let object = { element: "object", attribute: "data" };
+
+async function checkImage(browser) {
+ let pids = getPids(browser);
+ is(pids.length, 2, "There should be two browsing contexts");
+ ok(pids[0], "The first pid should have a sane value");
+ ok(pids[1], "The second pid should have a sane value");
+ isnot(pids[0], pids[1], "The two pids should be different");
+
+ let images = [];
+ for (let context of browser.browsingContext.children) {
+ images.push(
+ await SpecialPowers.spawn(context, [], async () => {
+ let img = new URL(content.document.querySelector("img").src);
+ is(
+ `${img.protocol}//${img.host}`,
+ `${content.location.protocol}//${content.location.host}`,
+ "Images should be loaded in the same domain as the document"
+ );
+ return img.href;
+ })
+ );
+ }
+ isnot(images[0], images[1], "The images should have different sources");
+}
+
+function checkDocument(browser) {
+ let pids = getPids(browser);
+ is(pids.length, 2, "There should be two browsing contexts");
+ ok(pids[0], "The first pid should have a sane value");
+ ok(pids[1], "The second pid should have a sane value");
+ isnot(pids[0], pids[1], "The two pids should be different");
+}
+
+add_task(async function test_iframeImageDocument() {
+ await runTest(iframe, TABURL, IMAGEURL, CROSSIMAGEURL, checkImage);
+});
+
+add_task(async function test_embedImageDocument() {
+ await runTest(embed, TABURL, IMAGEURL, CROSSIMAGEURL, checkImage);
+});
+
+add_task(async function test_objectImageDocument() {
+ await runTest(object, TABURL, IMAGEURL, CROSSIMAGEURL, checkImage);
+});
+
+add_task(async function test_iframeDocument() {
+ await runTest(iframe, TABURL, DOCUMENTURL, CROSSDOCUMENTURL, checkDocument);
+});
+
+add_task(async function test_embedDocument() {
+ await runTest(embed, TABURL, DOCUMENTURL, CROSSDOCUMENTURL, checkDocument);
+});
+
+add_task(async function test_objectDocument() {
+ await runTest(object, TABURL, DOCUMENTURL, CROSSDOCUMENTURL, checkDocument);
+});
diff --git a/dom/html/test/browser_form_post_from_file_to_http.js b/dom/html/test/browser_form_post_from_file_to_http.js
new file mode 100644
index 0000000000..e62912bdcd
--- /dev/null
+++ b/dom/html/test/browser_form_post_from_file_to_http.js
@@ -0,0 +1,181 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+
+const TEST_HTTP_POST =
+ "http://example.org/browser/dom/html/test/form_submit_server.sjs";
+
+// Test for bug 1351358.
+async function runTest(doNewTab) {
+ // Create file URI and test data file paths.
+ let testFile = getChromeDir(getResolvedURI(gTestPath));
+ testFile.append("dummy_page.html");
+ const fileUriString = Services.io.newFileURI(testFile).spec;
+ let filePaths = [];
+ testFile.leafName = "form_data_file.txt";
+ filePaths.push(testFile.path);
+ testFile.leafName = "form_data_file.bin";
+ filePaths.push(testFile.path);
+
+ // Open file:// page tab in which to run the test.
+ await BrowserTestUtils.withNewTab(
+ fileUriString,
+ async function (fileBrowser) {
+ // Create a form to post to server that writes posted data into body as JSON.
+
+ var promiseLoad;
+ if (doNewTab) {
+ promiseLoad = BrowserTestUtils.waitForNewTab(
+ gBrowser,
+ TEST_HTTP_POST,
+ true,
+ false
+ );
+ } else {
+ promiseLoad = BrowserTestUtils.browserLoaded(
+ fileBrowser,
+ false,
+ TEST_HTTP_POST
+ );
+ }
+
+ /* eslint-disable no-shadow */
+ await SpecialPowers.spawn(
+ fileBrowser,
+ [TEST_HTTP_POST, filePaths, doNewTab],
+ (actionUri, filePaths, doNewTab) => {
+ // eslint-disable-next-line mozilla/reject-importGlobalProperties
+ Cu.importGlobalProperties(["File"]);
+
+ let doc = content.document;
+ let form = doc.body.appendChild(doc.createElement("form"));
+ form.action = actionUri;
+ form.method = "POST";
+ form.enctype = "multipart/form-data";
+ if (doNewTab) {
+ form.target = "_blank";
+ }
+
+ let inputText = form.appendChild(doc.createElement("input"));
+ inputText.type = "text";
+ inputText.name = "text";
+ inputText.value = "posted";
+
+ let inputCheckboxOn = form.appendChild(doc.createElement("input"));
+ inputCheckboxOn.type = "checkbox";
+ inputCheckboxOn.name = "checked";
+ inputCheckboxOn.checked = true;
+
+ let inputCheckboxOff = form.appendChild(doc.createElement("input"));
+ inputCheckboxOff.type = "checkbox";
+ inputCheckboxOff.name = "unchecked";
+ inputCheckboxOff.checked = false;
+
+ let inputFile = form.appendChild(doc.createElement("input"));
+ inputFile.type = "file";
+ inputFile.name = "file";
+ let fileList = [];
+ let promises = [];
+ for (let path of filePaths) {
+ promises.push(
+ File.createFromFileName(path).then(file => {
+ fileList.push(file);
+ })
+ );
+ }
+
+ Promise.all(promises).then(() => {
+ inputFile.mozSetFileArray(fileList);
+ form.submit();
+ });
+ }
+ );
+ /* eslint-enable no-shadow */
+
+ var href;
+ var testBrowser;
+ var newTab;
+ if (doNewTab) {
+ newTab = await promiseLoad;
+ testBrowser = newTab.linkedBrowser;
+ href = testBrowser.currentURI.spec;
+ } else {
+ testBrowser = fileBrowser;
+ href = await promiseLoad;
+ }
+ is(
+ href,
+ TEST_HTTP_POST,
+ "Check that the loaded page is the one to which we posted."
+ );
+
+ let binContentType;
+ if (AppConstants.platform == "macosx") {
+ binContentType = "application/macbinary";
+ } else {
+ binContentType = "application/octet-stream";
+ }
+
+ /* eslint-disable no-shadow */
+ await SpecialPowers.spawn(
+ testBrowser,
+ [binContentType],
+ binContentType => {
+ let data = JSON.parse(content.document.body.textContent);
+ is(
+ data[0].headers["Content-Disposition"],
+ 'form-data; name="text"',
+ "Check text input Content-Disposition"
+ );
+ is(data[0].body, "posted", "Check text input body");
+
+ is(
+ data[1].headers["Content-Disposition"],
+ 'form-data; name="checked"',
+ "Check checkbox input Content-Disposition"
+ );
+ is(data[1].body, "on", "Check checkbox input body");
+
+ // Note that unchecked checkbox details are not sent.
+
+ is(
+ data[2].headers["Content-Disposition"],
+ 'form-data; name="file"; filename="form_data_file.txt"',
+ "Check text file input Content-Disposition"
+ );
+ is(
+ data[2].headers["Content-Type"],
+ "text/plain",
+ "Check text file input Content-Type"
+ );
+ is(data[2].body, "1234\n", "Check text file input body");
+
+ is(
+ data[3].headers["Content-Disposition"],
+ 'form-data; name="file"; filename="form_data_file.bin"',
+ "Check binary file input Content-Disposition"
+ );
+ is(
+ data[3].headers["Content-Type"],
+ binContentType,
+ "Check binary file input Content-Type"
+ );
+ is(
+ data[3].body,
+ "\u0001\u0002\u0003\u0004\n",
+ "Check binary file input body"
+ );
+ }
+ );
+ /* eslint-enable no-shadow */
+
+ if (newTab) {
+ BrowserTestUtils.removeTab(newTab);
+ }
+ }
+ );
+}
+
+add_task(async function runWithDocumentChannel() {
+ await runTest(false);
+ await runTest(true);
+});
diff --git a/dom/html/test/browser_refresh_after_document_write.js b/dom/html/test/browser_refresh_after_document_write.js
new file mode 100644
index 0000000000..88e0dbe489
--- /dev/null
+++ b/dom/html/test/browser_refresh_after_document_write.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+Test that after using document.write(...), refreshing the document and calling write again,
+resulting document.URL is identical to the original URL.
+
+This testcase is aimed at preventing bug 619092
+*/
+var testURL =
+ "http://mochi.test:8888/browser/dom/html/test/file_refresh_after_document_write.html";
+let aTab, aBrowser;
+
+function test() {
+ waitForExplicitFinish();
+
+ aTab = BrowserTestUtils.addTab(gBrowser, testURL);
+ aBrowser = gBrowser.getBrowserForTab(aTab);
+ BrowserTestUtils.browserLoaded(aBrowser)
+ .then(() => {
+ is(
+ aBrowser.currentURI.spec,
+ testURL,
+ "Make sure we start at the correct URL"
+ );
+
+ SpecialPowers.spawn(aBrowser, [], () => {
+ // test_btn calls document.write() then reloads the document
+ let test_btn = content.document.getElementById("test_btn");
+
+ docShell.chromeEventHandler.addEventListener(
+ "load",
+ () => {
+ test_btn.click();
+ },
+ { once: true, capture: true }
+ );
+
+ test_btn.click();
+ });
+
+ return BrowserTestUtils.browserLoaded(aBrowser);
+ })
+ .then(() => {
+ return SpecialPowers.spawn(aBrowser, [], () => content.document.URL);
+ })
+ .then(url => {
+ is(url, testURL, "Document URL should be identical after reload");
+ gBrowser.removeTab(aTab);
+ finish();
+ });
+}
diff --git a/dom/html/test/browser_submission_flush.js b/dom/html/test/browser_submission_flush.js
new file mode 100644
index 0000000000..add886c6a3
--- /dev/null
+++ b/dom/html/test/browser_submission_flush.js
@@ -0,0 +1,97 @@
+"use strict";
+// Form submissions triggered by the Javascript 'submit' event listener are
+// deferred until the event listener finishes. However, changes to specific
+// attributes ("action" and "target" attributes) need to cause an immediate
+// flush of any pending submission to prevent the form submission from using the
+// wrong action or target. This test ensures that such flushes happen properly.
+
+const kTestPage =
+ "https://example.org/browser/dom/html/test/submission_flush.html";
+// This is the page pointed to by the form action in the test HTML page.
+const kPostActionPage =
+ "https://example.org/browser/dom/html/test/post_action_page.html";
+
+const kFormId = "test_form";
+const kFrameId = "test_frame";
+const kSubmitButtonId = "submit_button";
+
+// Take in a variety of actions (in the form of setting and unsetting form
+// attributes). Then submit the form in the submit event listener to cause a
+// deferred form submission. Then perform the test actions and ensure that the
+// form used the correct attribute values rather than the changed ones.
+async function runTest(aTestActions) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, kTestPage);
+ registerCleanupFunction(() => BrowserTestUtils.removeTab(tab));
+
+ /* eslint-disable no-shadow */
+ let frame_url = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ kFormId, kFrameId, kSubmitButtonId, aTestActions }],
+ async function ({ kFormId, kFrameId, kSubmitButtonId, aTestActions }) {
+ let form = content.document.getElementById(kFormId);
+
+ form.addEventListener(
+ "submit",
+ event => {
+ // Need to trigger the deferred submission by submitting in the submit
+ // event handler. To prevent the form from being submitted twice, the
+ // <form> tag contains the attribute |onsubmit="return false;"| to cancel
+ // the original submission.
+ form.submit();
+
+ if (aTestActions.setattr) {
+ for (let { attr, value } of aTestActions.setattr) {
+ form.setAttribute(attr, value);
+ }
+ }
+ if (aTestActions.unsetattr) {
+ for (let attr of aTestActions.unsetattr) {
+ form.removeAttribute(attr);
+ }
+ }
+ },
+ { capture: true, once: true }
+ );
+
+ // Trigger the above event listener
+ content.document.getElementById(kSubmitButtonId).click();
+
+ // Test that the form was submitted to the correct target (the frame) with
+ // the correct action (kPostActionPage).
+ let frame = content.document.getElementById(kFrameId);
+ await new Promise(resolve => {
+ frame.addEventListener("load", resolve, { once: true });
+ });
+ return frame.contentWindow.location.href;
+ }
+ );
+ /* eslint-enable no-shadow */
+ is(
+ frame_url,
+ kPostActionPage,
+ "Form should have submitted with correct target and action"
+ );
+}
+
+add_task(async function () {
+ info("Changing action should flush pending submissions");
+ await runTest({ setattr: [{ attr: "action", value: "about:blank" }] });
+});
+
+add_task(async function () {
+ info("Changing target should flush pending submissions");
+ await runTest({ setattr: [{ attr: "target", value: "_blank" }] });
+});
+
+add_task(async function () {
+ info("Unsetting action should flush pending submissions");
+ await runTest({ unsetattr: ["action"] });
+});
+
+// On failure, this test will time out rather than failing an assert. When the
+// target attribute is not set, the form will submit the active page, navigating
+// it away and preventing the wait for iframe load from ever finishing.
+add_task(async function () {
+ info("Unsetting target should flush pending submissions");
+ await runTest({ unsetattr: ["target"] });
+});
diff --git a/dom/html/test/browser_targetBlankNoOpener.js b/dom/html/test/browser_targetBlankNoOpener.js
new file mode 100644
index 0000000000..1647df0be2
--- /dev/null
+++ b/dom/html/test/browser_targetBlankNoOpener.js
@@ -0,0 +1,121 @@
+const TEST_URL = "http://mochi.test:8888/browser/dom/html/test/empty.html";
+
+async function checkOpener(browser, elm, name, rel) {
+ let p = BrowserTestUtils.waitForNewTab(gBrowser, null, true, true);
+
+ await SpecialPowers.spawn(
+ browser,
+ [{ url: TEST_URL, name, rel, elm }],
+ async obj => {
+ let element;
+
+ if (obj.elm == "anchor") {
+ element = content.document.createElement("a");
+ content.document.body.appendChild(element);
+ element.appendChild(content.document.createTextNode(obj.name));
+ } else {
+ let img = content.document.createElement("img");
+ img.src = "image_yellow.png";
+ content.document.body.appendChild(img);
+
+ element = content.document.createElement("area");
+ img.appendChild(element);
+
+ element.setAttribute("shape", "rect");
+ element.setAttribute("coords", "0,0,100,100");
+ }
+
+ element.setAttribute("target", "_blank");
+ element.setAttribute("href", obj.url);
+
+ if (obj.rel) {
+ element.setAttribute("rel", obj.rel);
+ }
+
+ element.click();
+ }
+ );
+
+ let newTab = await p;
+ let newBrowser = gBrowser.getBrowserForTab(newTab);
+
+ let hasOpener = await SpecialPowers.spawn(
+ newTab.linkedBrowser,
+ [],
+ _ => !!content.window.opener
+ );
+
+ BrowserTestUtils.removeTab(newTab);
+ return hasOpener;
+}
+
+async function runTests(browser, elm) {
+ info("Creating an " + elm + " with target=_blank rel=opener");
+ ok(
+ !!(await checkOpener(browser, elm, "rel=opener", "opener")),
+ "We want the opener with rel=opener"
+ );
+
+ info("Creating an " + elm + " with target=_blank rel=noopener");
+ ok(
+ !(await checkOpener(browser, elm, "rel=noopener", "noopener")),
+ "We don't want the opener with rel=noopener"
+ );
+
+ info("Creating an " + elm + " with target=_blank");
+ ok(
+ !(await checkOpener(browser, elm, "no rel", null)),
+ "We don't want the opener with no rel is passed"
+ );
+
+ info("Creating an " + elm + " with target=_blank rel='noopener opener'");
+ ok(
+ !(await checkOpener(
+ browser,
+ elm,
+ "rel=noopener+opener",
+ "noopener opener"
+ )),
+ "noopener wins with rel=noopener+opener"
+ );
+
+ info("Creating an " + elm + " with target=_blank rel='noreferrer opener'");
+ ok(
+ !(await checkOpener(browser, elm, "noreferrer wins", "noreferrer opener")),
+ "We don't want the opener with rel=noreferrer+opener"
+ );
+
+ info("Creating an " + elm + " with target=_blank rel='opener noreferrer'");
+ ok(
+ !(await checkOpener(
+ browser,
+ elm,
+ "noreferrer wins again",
+ "noreferrer opener"
+ )),
+ "We don't want the opener with rel=opener+noreferrer"
+ );
+}
+
+add_task(async _ => {
+ await SpecialPowers.flushPrefEnv();
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.block_multiple_popups", false],
+ ["dom.disable_open_during_load", true],
+ ["dom.targetBlankNoOpener.enabled", true],
+ ],
+ });
+
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ await runTests(browser, "anchor");
+ await runTests(browser, "area");
+
+ info("Removing the tab");
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/html/test/bug100533_iframe.html b/dom/html/test/bug100533_iframe.html
new file mode 100644
index 0000000000..ddf58a15c6
--- /dev/null
+++ b/dom/html/test/bug100533_iframe.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title></title>
+</head>
+<body>
+<form method='get' action='bug100533_load.html' id='b'><input type="submit"/></form>
+</body>
+</html>
diff --git a/dom/html/test/bug100533_load.html b/dom/html/test/bug100533_load.html
new file mode 100644
index 0000000000..99cf26640c
--- /dev/null
+++ b/dom/html/test/bug100533_load.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<title></title>
+</head>
+
+
+<body onload="parent.submitted();">
+
+<span id="foo"></span>
+
+
+
+</body>
+</html>
diff --git a/dom/html/test/bug1260704_iframe.html b/dom/html/test/bug1260704_iframe.html
new file mode 100644
index 0000000000..695dc7c1ac
--- /dev/null
+++ b/dom/html/test/bug1260704_iframe.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript">
+ var noDefault = (location.search.includes("noDefault=true"));
+ var isMap = (location.search.includes("isMap=true"));
+
+ window.addEventListener("load", () => {
+ let image = document.getElementById("testImage");
+ isMap ? image.setAttribute("ismap", "") : image.removeAttribute("ismap");
+ image.addEventListener("click", event => {
+ if (noDefault) {
+ ok(true, "image element prevents default");
+ event.preventDefault();
+ }
+ });
+
+ window.addEventListener("click", event => {
+ ok(true, "expected prevent default = " + noDefault);
+ ok(true, "actual prevent default = " + event.defaultPrevented);
+ ok(event.defaultPrevented == noDefault, "PreventDefault should work fine");
+ if (noDefault) {
+ window.parent.postMessage("finished", "http://mochi.test:8888");
+ }
+ });
+ window.parent.postMessage("started", "http://mochi.test:8888");
+ });
+ </script>
+</head>
+<body>
+<a href="bug1260704_iframe_empty.html">
+ <img id="testImage" src="file_bug1260704.png" width="100" height="100"/>
+</a>
+</body>
+</html>
diff --git a/dom/html/test/bug1260704_iframe_empty.html b/dom/html/test/bug1260704_iframe_empty.html
new file mode 100644
index 0000000000..e826b1e5e6
--- /dev/null
+++ b/dom/html/test/bug1260704_iframe_empty.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript">
+ window.addEventListener("load", () => {
+ window.parent.postMessage("empty_frame_loaded", "http://mochi.test:8888");
+ });
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/dom/html/test/bug1292522_iframe.html b/dom/html/test/bug1292522_iframe.html
new file mode 100644
index 0000000000..99a3369d00
--- /dev/null
+++ b/dom/html/test/bug1292522_iframe.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html><head><title>iframe</title></head>
+ <body>
+ <p>var testvar = "testiframe"</p>
+ <script>
+ document.domain='example.org';
+ var testvar = "testiframe";
+ </script>
+ </body>
+</html>
diff --git a/dom/html/test/bug1292522_page.html b/dom/html/test/bug1292522_page.html
new file mode 100644
index 0000000000..9570f12d2d
--- /dev/null
+++ b/dom/html/test/bug1292522_page.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Test for Bug 1292522</title>
+ <script>
+ var check_var = function() {
+ opener.postMessage(document.getElementsByTagName('iframe')[0].contentWindow.testvar, "http://mochi.test:8888");
+ }
+ </script>
+ </head>
+ <body>
+ <iframe src="http://test2.example.org:80/tests/dom/html/test/bug1292522_iframe.html" onload="document.domain='example.org';check_var();"></iframe>
+ </body>
+</html>
diff --git a/dom/html/test/bug1315146-iframe.html b/dom/html/test/bug1315146-iframe.html
new file mode 100644
index 0000000000..280db53052
--- /dev/null
+++ b/dom/html/test/bug1315146-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+document.domain = "example.org";
+</script>
diff --git a/dom/html/test/bug1315146-main.html b/dom/html/test/bug1315146-main.html
new file mode 100644
index 0000000000..e9f356dda6
--- /dev/null
+++ b/dom/html/test/bug1315146-main.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<iframe src="http://example.org/tests/dom/html/test/bug1315146-iframe.html"></iframe>
+<input value="test">
+<script>
+document.domain = "example.org";
+onload = function() {
+ let iframe = document.querySelector("iframe");
+ let input = document.querySelector("input");
+ input.selectionStart = input.selectionEnd = 2;
+ document.body.style.overflow = "scroll";
+ iframe.contentDocument.body.offsetWidth;
+ opener.postMessage({start: input.selectionStart,
+ end: input.selectionEnd}, "*");
+}
+</script>
diff --git a/dom/html/test/bug196523-subframe.html b/dom/html/test/bug196523-subframe.html
new file mode 100644
index 0000000000..ac53572a7a
--- /dev/null
+++ b/dom/html/test/bug196523-subframe.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script>
+ function checkDomain(str, msg) {
+ window.parent.postMessage((str == document.domain) + ";" +msg,
+ "http://mochi.test:8888");
+ }
+
+ function reportException(msg) {
+ window.parent.postMessage(false + ";" + msg, "http://mochi.test:8888");
+ }
+
+ var win1;
+ try {
+ win1 = window.open("", "", "width=100,height=100");
+ var otherDomain1 = win1.document.domain;
+ win1.close();
+ checkDomain(otherDomain1, "Opened document should have our domain");
+ } catch(e) {
+ reportException("Exception getting document.domain: " + e);
+ } finally {
+ win1.close();
+ }
+
+ document.domain = "example.org";
+
+ var win2;
+ try {
+ win2 = window.open("", "", "width=100,height=100");
+ var otherDomain2 = win2.document.domain;
+ checkDomain(otherDomain2, "Opened document should have our domain");
+ win2.close();
+ } catch(e) {
+ reportException("Exception getting document.domain after domain set: " + e);
+ } finally {
+ win2.close();
+ }
+</script>
diff --git a/dom/html/test/bug199692-nested-d2.html b/dom/html/test/bug199692-nested-d2.html
new file mode 100644
index 0000000000..70064efe74
--- /dev/null
+++ b/dom/html/test/bug199692-nested-d2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=199692
+-->
+<head>
+ <title>Nested, nested iframe for bug 199692 tests</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+ <div id="nest2div" style="border: 2px dotted blue;">nested, depth 2</div>
+</body>
+</html>
+
diff --git a/dom/html/test/bug199692-nested.html b/dom/html/test/bug199692-nested.html
new file mode 100644
index 0000000000..27201a953d
--- /dev/null
+++ b/dom/html/test/bug199692-nested.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=199692
+-->
+<head>
+ <title>Nested iframe for bug 199692 tests</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<body>
+ <div id="nest1div" style="border: 2px dotted green;">nested, depth 1</div>
+ <iframe src="bug199692-nested-d2.html"></iframe>
+</body>
+</html>
+
diff --git a/dom/html/test/bug199692-popup.html b/dom/html/test/bug199692-popup.html
new file mode 100644
index 0000000000..de93ca8599
--- /dev/null
+++ b/dom/html/test/bug199692-popup.html
@@ -0,0 +1,190 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=199692
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Popup in test for Bug 199692</title>
+ <style type="text/css">
+#content * {
+ border: 2px solid black;
+ margin: 2px;
+ clear: both;
+ height: 20px;
+ overflow: hidden;
+}
+
+#txt, #static, #fixed, #absolute, #relative, #hidden, #float, #empty, #static, #relative {
+ width: 200px !important;
+}
+ </style>
+
+</head>
+<!--
+Elements are styled in such a way that they don't overlap visually
+unless they also overlap structurally.
+
+This file is designed to be opened from test_bug199692.html in a popup
+window, to guarantee that the window in which document.elementFromPoint runs
+is large enough to display all the elements being tested.
+-->
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=199692">Mozilla Bug 199692</a>
+
+<div id="content" style="width: 500px; background-color: #ccc;">
+
+<!-- element containing text -->
+<div id="txt" style="height: 30px;">txt</div>
+
+<!-- element not containing text -->
+<div id="empty" style="border: 2px solid blue;"></div>
+
+<!-- element with only whitespace -->
+<p id="whitespace" style="border: 2px solid magenta;"> </p>
+
+<!-- position: static -->
+<span id="static" style="position: static; border-color: green;">static</span>
+
+<!-- floated element -->
+<div id="float" style="border-color: red; float: right;">float</div>
+
+<!-- position: fixed -->
+<span id="fixed" style="position: fixed; top: 500px; left: 100px; border: 3px solid yellow;">fixed</span>
+
+<!-- position: absolute -->
+<span id="absolute" style="position: absolute; top: 550px; left: 150px; border-color: orange;">abs</span>
+
+<!-- position: relative -->
+<div id="relative" style="position: relative; top: 200px; border-color: teal;">rel</div>
+
+<!-- visibility: hidden -->
+<div id="hidden-wrapper" style="border: 1px dashed teal;">
+ <div id="hidden" style="opacity: 0.5; background-color: blue; visibility:hidden;">hidden</div>
+</div>
+
+<!-- iframe (within iframe) -->
+<iframe id="our-iframe" src="bug199692-nested.html" style="height: 100px; overflow: scroll;"></iframe>
+
+<input type="textbox" id="textbox" value="textbox"></input>
+</div>
+
+<!-- interaction with scrolling -->
+<iframe id="scrolled-iframe"
+ src="bug199692-scrolled.html#down"
+ style="position: absolute; top: 345px; left: 325px; height: 200px; width: 200px"></iframe>
+
+<script type="application/javascript">
+
+var SimpleTest = window.opener.SimpleTest;
+function ok() { window.opener.ok.apply(window.opener, arguments); }
+function is() { window.opener.is.apply(window.opener, arguments); }
+function todo() { window.opener.todo.apply(window.opener, arguments); }
+function todo_is() { window.opener.todo_is.apply(window.opener, arguments); }
+function $(id) { return document.getElementById(id); }
+
+/**
+ * Like is, but for tests which don't always succeed or always fail on all
+ * platforms.
+ */
+function random_fail(a, b, m)
+{
+ if (a != b)
+ todo_is(a, b, m);
+ else
+ is(a, b, m);
+}
+
+/* Test for Bug 199692 */
+
+function getCoords(elt)
+{
+ var x = 0, y = 0;
+
+ do
+ {
+ x += elt.offsetLeft;
+ y += elt.offsetTop;
+ } while ((elt = elt.offsetParent));
+
+ return { x, y };
+}
+
+var elts = ["txt", "empty", "whitespace", "static", "fixed", "absolute",
+ "relative", "float", "textbox"];
+
+function testPoints()
+{
+ ok('elementFromPoint' in document, "document.elementFromPoint must exist");
+ ok(typeof document.elementFromPoint === "function", "must be a function");
+
+ var doc = document;
+ doc.pt = doc.elementFromPoint; // for shorter lines
+ is(doc.pt(-1, 0), null, "Negative coordinates (-1, 0) should return null");
+ is(doc.pt(0, -1), null, "Negative coordinates (0, -1) should return null");
+ is(doc.pt(-1, -1), null, "Negative coordinates (-1, -1) should return null");
+
+ var pos;
+ for (var i = 0; i < elts.length; i++)
+ {
+ var id = elts[i];
+ var elt = $(id);
+
+ // The upper left corner of an element (with a moderate offset) will
+ // usually contain text, and the lower right corner usually won't.
+ var pos = getCoords(elt);
+ var x = pos.x, y = pos.y;
+ var w = elt.offsetWidth, h = elt.offsetHeight;
+
+ var d = 5;
+ is(doc.pt(x + d, y + d), elt,
+ "(" + (x + d) + "," + (y + d) + ") IDs should match (upper left " +
+ "corner of " + id + ")");
+ is(doc.pt(x + w - d, y + h - d), elt,
+ "(" + (x + w - d) + "," + (y + h - d) + ") IDs should match (lower " +
+ "right corner of " + id + ")");
+ }
+
+ // content
+ var c = $("content");
+ pos = getCoords(c);
+ x = pos.x + c.offsetWidth / 2;
+ y = pos.y;
+
+ // This fails on some platforms but not others for unknown reasons
+ random_fail(doc.pt(x, y), c, "Point to right of #txt should be #content");
+ is(doc.pt(x, y + 1), c, "Point to right of #txt should be #content");
+ random_fail(doc.pt(x + 1, y), c, "Point to right of #txt should be #content");
+ is(doc.pt(x + 1, y + 1), c, "Point to right of #txt should be #content");
+
+ // hidden
+ c = $("hidden");
+ pos = getCoords(c);
+ x = pos.x;
+ y = pos.y;
+ is(doc.pt(x, y), $("hidden-wrapper"),
+ "Hit testing should bypass hidden elements.");
+
+ // iframe nested
+ var iframe = $("our-iframe");
+ pos = getCoords(iframe);
+ x = pos.x;
+ y = pos.y;
+ is(doc.pt(x + 20, y + 20), $("our-iframe"),
+ "Element from nested iframe returned is from calling document");
+ // iframe, doubly nested
+ is(doc.pt(x + 60, y + 60), $("our-iframe"),
+ "Element from doubly nested iframe returned is from calling document");
+
+ // scrolled iframe tests
+ $("scrolled-iframe").contentWindow.runTests();
+
+ SimpleTest.finish();
+ window.close();
+}
+
+window.onload = testPoints;
+</script>
+</body>
+</html>
+
diff --git a/dom/html/test/bug199692-scrolled.html b/dom/html/test/bug199692-scrolled.html
new file mode 100644
index 0000000000..f13bf7ab12
--- /dev/null
+++ b/dom/html/test/bug199692-scrolled.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=199692
+-->
+<head>
+ <title>Scrolled page for bug 199692 tests</title>
+ <style type="text/css">
+/* Disable default margins/padding/borders so (0, 0) gets a div. */
+* { margin: 0; padding: 0; border: 0; }
+ </style>
+ <script type="application/javascript">
+function $(id) { return document.getElementById(id); }
+
+function runTests()
+{
+ var is = window.parent.is;
+
+ is(document.elementFromPoint(0, 0), $("down"),
+ "document.elementFromPoint not respecting scrolling?");
+ is(document.elementFromPoint(200, 200), null,
+ "should have returned null for a not-visible point");
+ is(document.elementFromPoint(3, -5), null,
+ "should have returned null for a not-visible point");
+}
+ </script>
+</head>
+<!-- This page is loaded in a 200px-square iframe scrolled to #down. -->
+<body>
+<div style="height: 150px; background: lightblue;">first</div>
+<div id="down" style="height: 250px; background: lightgreen;">second</div>
+</body>
+</html>
+
diff --git a/dom/html/test/bug242709_iframe.html b/dom/html/test/bug242709_iframe.html
new file mode 100644
index 0000000000..1155299692
--- /dev/null
+++ b/dom/html/test/bug242709_iframe.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script type="text/javascript">
+function submitIframeForm () {
+ document.getElementById('b').submit();
+ document.getElementById('thebutton').disabled = true;
+}
+</script>
+
+</head>
+<body onload="sendMouseEvent({type:'click'}, 'thebutton')">
+
+<form method="get" action="bug242709_load.html" id="b">
+<input type="submit" onclick="submitIframeForm()" id="thebutton">
+</form>
+
+</body>
+</html>
diff --git a/dom/html/test/bug242709_load.html b/dom/html/test/bug242709_load.html
new file mode 100644
index 0000000000..c9be79b241
--- /dev/null
+++ b/dom/html/test/bug242709_load.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title></title>
+</head>
+
+<body onload="parent.submitted();">
+
+<span id="foo"></span>
+
+</body>
+</html>
diff --git a/dom/html/test/bug277724_iframe1.html b/dom/html/test/bug277724_iframe1.html
new file mode 100644
index 0000000000..d0d881b766
--- /dev/null
+++ b/dom/html/test/bug277724_iframe1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!-- Use an unload handler to prevent bfcache from messing with us -->
+<body onunload="parent.childUnloaded = true;">
+ <select id="select">
+ <option>aaa</option>
+ <option>bbbb</option>
+ </select>
+
+ <textarea id="textarea">
+ </textarea>
+
+ <input type="text" id="text">
+ <input type="password" id="password">
+ <input type="checkbox" id="checkbox">
+ <input type="radio" id="radio">
+ <input type="image" id="image">
+ <input type="submit" id="submit">
+ <input type="reset" id="reset">
+ <input type="button" id="button input">
+ <input type="hidden" id="hidden">
+ <input type="file" id="file">
+
+ <button type="submit" id="submit button"></button>
+ <button type="reset" id="reset button"></button>
+ <button type="button" id="button"></button>
+</body>
+</html>
diff --git a/dom/html/test/bug277724_iframe2.xhtml b/dom/html/test/bug277724_iframe2.xhtml
new file mode 100644
index 0000000000..14423aa06c
--- /dev/null
+++ b/dom/html/test/bug277724_iframe2.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!-- Use an unload handler to prevent bfcache from messing with us -->
+<body onunload="parent.childUnloaded = true;">
+ <select id="select">
+ <option>aaa</option>
+ <option>bbbb</option>
+ </select>
+
+ <textarea id="textarea">
+ </textarea>
+
+ <input type="text" id="text" />
+ <input type="password" id="password" />
+ <input type="checkbox" id="checkbox" />
+ <input type="radio" id="radio" />
+ <input type="image" id="image" />
+ <input type="submit" id="submit" />
+ <input type="reset" id="reset" />
+ <input type="button" id="button input" />
+ <input type="hidden" id="hidden" />
+ <input type="file" id="file" />
+
+ <button type="submit" id="submit button"></button>
+ <button type="reset" id="reset button"></button>
+ <button type="button" id="button"></button>
+</body>
+</html>
diff --git a/dom/html/test/bug277890_iframe.html b/dom/html/test/bug277890_iframe.html
new file mode 100644
index 0000000000..c1cb4ff2e1
--- /dev/null
+++ b/dom/html/test/bug277890_iframe.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<title></title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script type="text/javascript">
+function submitIframeForm () {
+ document.getElementById('b').submit();
+ document.getElementById('thebutton').disabled = true;
+}
+</script>
+
+</head>
+<body onload="sendMouseEvent({type:'click'}, 'thebutton')">
+
+<form method="get" action="bug277890_load.html" id="b">
+<button onclick="submitIframeForm()" id="thebutton">Submit</button>
+</form>
+
+</body>
+</html>
diff --git a/dom/html/test/bug277890_load.html b/dom/html/test/bug277890_load.html
new file mode 100644
index 0000000000..c9be79b241
--- /dev/null
+++ b/dom/html/test/bug277890_load.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title></title>
+</head>
+
+<body onload="parent.submitted();">
+
+<span id="foo"></span>
+
+</body>
+</html>
diff --git a/dom/html/test/bug340800_iframe.txt b/dom/html/test/bug340800_iframe.txt
new file mode 100644
index 0000000000..369dfe7441
--- /dev/null
+++ b/dom/html/test/bug340800_iframe.txt
@@ -0,0 +1,4 @@
+Line 1.
+Line 2.
+Line 3.
+Line 4.
diff --git a/dom/html/test/bug369370-popup.png b/dom/html/test/bug369370-popup.png
new file mode 100644
index 0000000000..9063d12648
--- /dev/null
+++ b/dom/html/test/bug369370-popup.png
Binary files differ
diff --git a/dom/html/test/bug372098-link-target.html b/dom/html/test/bug372098-link-target.html
new file mode 100644
index 0000000000..b22b8e020e
--- /dev/null
+++ b/dom/html/test/bug372098-link-target.html
@@ -0,0 +1,7 @@
+<html>
+<script type="text/javascript">
+
+parent.callback(location.search.substr(1));
+
+</script>
+</html>
diff --git a/dom/html/test/bug436200.html b/dom/html/test/bug436200.html
new file mode 100644
index 0000000000..1ef7e73b5e
--- /dev/null
+++ b/dom/html/test/bug436200.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Secure to Insecure Test</title>
+ </head>
+ <body>
+ <form id="test_form" action="http://example.org/browser/dom/html/test/bug436200.html">
+ <button type="submit" id="submit_button">Submit</button>
+ </form>
+ </body>
+</html>
diff --git a/dom/html/test/bug441930_iframe.html b/dom/html/test/bug441930_iframe.html
new file mode 100644
index 0000000000..532cd5c36a
--- /dev/null
+++ b/dom/html/test/bug441930_iframe.html
@@ -0,0 +1,27 @@
+<html>
+<body>
+ The content of this <code>textarea</code> should not disappear on page reload:<br />
+ <textarea>This text should not disappear on page reload!</textarea>
+ <script>
+ var ta = document.getElementsByTagName("textarea").item(0);
+ if (!parent.reloaded) {
+ parent.reloaded = true;
+ ta.disabled = true;
+ location.reload();
+ } else {
+ // Primary regression test:
+ parent.isnot(ta.value, "",
+ "Content of dynamically disabled textarea disappeared on page reload.");
+
+ // Bonus regression test: changing the textarea's defaultValue after
+ // reloading should also update the textarea's value.
+ var newDefaultValue = "new default value";
+ ta.defaultValue = newDefaultValue;
+ parent.is(ta.value, newDefaultValue,
+ "Changing the defaultValue attribute of a textarea fails to update its value attribute.");
+
+ parent.SimpleTest.finish();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/bug445004-inner.html b/dom/html/test/bug445004-inner.html
new file mode 100644
index 0000000000..b946520ea6
--- /dev/null
+++ b/dom/html/test/bug445004-inner.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <base href="http://test1.example.org/tests/dom/html/test/bug445004-inner.html">
+ <script src="bug445004-inner.js"></script>
+ </head>
+ <body>
+ <iframe name="w" id="w" width="100" height="100"></iframe>
+ <iframe name="x" id="x" width="100" height="100"></iframe>
+ <iframe name="y" id="y" width="100" height="100"></iframe>
+ <iframe name="z" id="z" width="100" height="100"></iframe>
+ <img src="test1.example.org.png">
+ </body>
+</html>
diff --git a/dom/html/test/bug445004-inner.js b/dom/html/test/bug445004-inner.js
new file mode 100644
index 0000000000..2d751da454
--- /dev/null
+++ b/dom/html/test/bug445004-inner.js
@@ -0,0 +1,27 @@
+document.domain = "example.org";
+function $(str) {
+ return document.getElementById(str);
+}
+function hookLoad(str) {
+ $(str).onload = function () {
+ window.parent.parent.postMessage("end", "*");
+ };
+ window.parent.parent.postMessage("start", "*");
+}
+window.onload = function () {
+ hookLoad("w");
+ $("w").contentWindow.location.href = "test1.example.org.png";
+ hookLoad("x");
+ var doc = $("x").contentDocument;
+ doc.write('<img src="test1.example.org.png">');
+ doc.close();
+};
+function doIt() {
+ hookLoad("y");
+ $("y").contentWindow.location.href = "example.org.png";
+ hookLoad("z");
+ var doc = $("z").contentDocument;
+ doc.write('<img src="example.org.png">');
+ doc.close();
+}
+window.addEventListener("message", doIt);
diff --git a/dom/html/test/bug445004-outer-abs.html b/dom/html/test/bug445004-outer-abs.html
new file mode 100644
index 0000000000..8a93ef2b73
--- /dev/null
+++ b/dom/html/test/bug445004-outer-abs.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <base href="http://example.org/tests/dom/html/test/bug445004-outer.html">
+ <script>document.domain = "example.org"</script>
+ </head>
+ <body>
+ <iframe width="500" height="200" src="http://test1.example.org/tests/dom/html/test/bug445004-inner.html"
+ onload="window.frames[0].doIt()"></iframe>
+ </body>
+</html>
diff --git a/dom/html/test/bug445004-outer-rel.html b/dom/html/test/bug445004-outer-rel.html
new file mode 100644
index 0000000000..0967338899
--- /dev/null
+++ b/dom/html/test/bug445004-outer-rel.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <base href="http://example.org/tests/dom/html/test/bug445004-outer.html">
+ <script>document.domain = "example.org"</script>
+ </head>
+ <body>
+ <iframe width="500" height="200" src="bug445004-inner.html"
+ onload="window.frames[0].doIt()"></iframe>
+ </body>
+</html>
diff --git a/dom/html/test/bug445004-outer-write.html b/dom/html/test/bug445004-outer-write.html
new file mode 100644
index 0000000000..be6e37b6d7
--- /dev/null
+++ b/dom/html/test/bug445004-outer-write.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <base href="http://example.org/tests/dom/html/test/bug445004-outer.html">
+ <script>document.domain = "example.org"</script>
+ </head>
+ <body>
+ <iframe width="500" height="200" src="javascript:&quot;<!DOCTYPE html> <html> <script> function $(str) { return document.getElementById(str); } function hookLoad(str) { $(str).onload = function() { window.parent.parent.postMessage('end', '*'); }; window.parent.parent.postMessage('start', '*'); } window.onload = function() { hookLoad(\&quot;w\&quot;); $(\&quot;w\&quot;).contentWindow.location.href = \&quot;example.org.png\&quot;; hookLoad(\&quot;x\&quot;); var doc = $(\&quot;x\&quot;).contentDocument; doc.write('<img src=\&quot;example.org.png\&quot;>'); doc.close(); }; function doIt() { hookLoad(\&quot;y\&quot;); $(\&quot;y\&quot;).contentWindow.location.href = \&quot;example.org.png\&quot;; hookLoad(\&quot;z\&quot;); var doc = $(\&quot;z\&quot;).contentDocument; doc.write('<img src=\&quot;example.org.png\&quot;>'); doc.close(); } </script> <body> <iframe name=\&quot;w\&quot; id=\&quot;w\&quot; width=\&quot;100\&quot; height=\&quot;100\&quot;></iframe> <iframe name=\&quot;x\&quot; id=\&quot;x\&quot; width=\&quot;100\&quot; height=\&quot;100\&quot;></iframe> <iframe name=\&quot;y\&quot; id=\&quot;y\&quot; width=\&quot;100\&quot; height=\&quot;100\&quot;></iframe> <iframe name=\&quot;z\&quot; id=\&quot;z\&quot; width=\&quot;100\&quot; height=\&quot;100\&quot;></iframe><img src=\&quot;example.org.png\&quot;> </body> </html>&quot; "
+ onload="window.frames[0].doIt();"></iframe>
+ </body>
+</html>
diff --git a/dom/html/test/bug446483-iframe.html b/dom/html/test/bug446483-iframe.html
new file mode 100644
index 0000000000..fe5a6cf9f7
--- /dev/null
+++ b/dom/html/test/bug446483-iframe.html
@@ -0,0 +1,10 @@
+<script>
+function doe(){
+window.focus();
+window.getSelection().collapse(document.body, 0);
+}
+setTimeout(doe,50);
+
+setTimeout(function() {window.location.reload()}, 200);
+</script>
+<span contenteditable="true"></span>
diff --git a/dom/html/test/bug448564-echo.sjs b/dom/html/test/bug448564-echo.sjs
new file mode 100644
index 0000000000..1eee116fd7
--- /dev/null
+++ b/dom/html/test/bug448564-echo.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setStatusLine(request.httpVersion, 200, "OK");
+
+ response.write(request.queryString);
+}
diff --git a/dom/html/test/bug448564-iframe-1.html b/dom/html/test/bug448564-iframe-1.html
new file mode 100644
index 0000000000..4f3e79e5d2
--- /dev/null
+++ b/dom/html/test/bug448564-iframe-1.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+
+ <table>
+ <form action="bug448564-echo.sjs" method="GET">
+ <tr><td><input name="a" value="aval"></td></tr>
+ <input type="hidden" name="b" value="bval">
+ <input name="c" value="cval">
+ <tr><td><input name="d" value="dval" type="submit"></td></tr>
+ </form>
+ </table>
+
+ <script src="bug448564-submit.js"></script>
+
+</body>
+</html>
diff --git a/dom/html/test/bug448564-iframe-2.html b/dom/html/test/bug448564-iframe-2.html
new file mode 100644
index 0000000000..dba19b37e2
--- /dev/null
+++ b/dom/html/test/bug448564-iframe-2.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+
+ <form action="bug448564-echo.sjs" method="GET">
+ <table>
+ <tr><td><input name="a" value="aval"></td></tr>
+ <input type="hidden" name="b" value="bval">
+ <input name="c" value="cval">
+ <tr><td><input name="d" value="dval" type="submit"></td></tr>
+ </table>
+ </form>
+
+ <script src="bug448564-submit.js"></script>
+
+</body>
+</html>
diff --git a/dom/html/test/bug448564-iframe-3.html b/dom/html/test/bug448564-iframe-3.html
new file mode 100644
index 0000000000..64288ebb15
--- /dev/null
+++ b/dom/html/test/bug448564-iframe-3.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+
+ <table>
+ <span><form action="bug448564-echo.sjs" method="GET">
+ <tr><td><input name="a" value="aval"></td></tr>
+ <input type="hidden" name="b" value="bval">
+ <input name="c" value="cval">
+ <tr><td><input name="d" value="dval" type="submit"></td></tr>
+ </form></span>
+ </table>
+
+ <script src="bug448564-submit.js"></script>
+
+</body>
+</html>
diff --git a/dom/html/test/bug448564-submit.js b/dom/html/test/bug448564-submit.js
new file mode 100644
index 0000000000..a650487d65
--- /dev/null
+++ b/dom/html/test/bug448564-submit.js
@@ -0,0 +1,6 @@
+var inputs = document.getElementsByTagName("input");
+for (var input, i = 0; (input = inputs[i]); ++i) {
+ if ("submit" == input.type) {
+ input.click();
+ }
+}
diff --git a/dom/html/test/bug499092.html b/dom/html/test/bug499092.html
new file mode 100644
index 0000000000..0476fa4e76
--- /dev/null
+++ b/dom/html/test/bug499092.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+var title = document.createElementNS("http://www.w3.org/1999/xhtml", "aa:title");
+title.textContent = "HTML OK";
+document.documentElement.firstChild.appendChild(title);
+</script>
diff --git a/dom/html/test/bug499092.xml b/dom/html/test/bug499092.xml
new file mode 100644
index 0000000000..eedd2c77b3
--- /dev/null
+++ b/dom/html/test/bug499092.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<doc xmlns:aa="http://www.w3.org/1999/xhtml">
+<aa:title>XML OK</aa:title>
+</doc>
diff --git a/dom/html/test/bug514856_iframe.html b/dom/html/test/bug514856_iframe.html
new file mode 100644
index 0000000000..2abf9e91e2
--- /dev/null
+++ b/dom/html/test/bug514856_iframe.html
@@ -0,0 +1,21 @@
+<html>
+ <head>
+ <style>
+ html, body, a, img {
+ padding: 0px;
+ margin: 0px;
+ border: 0px;
+ }
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ </style>
+ </head>
+ <body>
+ <a href="bug514856_iframe.html">
+ <img ismap="ismap"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0vr4MkhoXe0rZigAAAABJRU5ErkJggg==">
+ </a>
+ </body>
+</html>
diff --git a/dom/html/test/bug592641_img.jpg b/dom/html/test/bug592641_img.jpg
new file mode 100644
index 0000000000..c9103b8b0e
--- /dev/null
+++ b/dom/html/test/bug592641_img.jpg
Binary files differ
diff --git a/dom/html/test/bug649134/file_bug649134-1.sjs b/dom/html/test/bug649134/file_bug649134-1.sjs
new file mode 100644
index 0000000000..fed0a9d693
--- /dev/null
+++ b/dom/html/test/bug649134/file_bug649134-1.sjs
@@ -0,0 +1,12 @@
+function handleRequest(request, response) {
+ response.seizePower();
+ var r =
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Type: text/html\r\n" +
+ 'Link: < \014>; rel="stylesheet"\r\n' +
+ "\r\n" +
+ "<!-- selector {} body {display:none;} --><body>PASS</body>\r\n";
+ response.bodyOutputStream.write(r, r.length);
+ response.bodyOutputStream.flush();
+ response.finish();
+}
diff --git a/dom/html/test/bug649134/file_bug649134-2.sjs b/dom/html/test/bug649134/file_bug649134-2.sjs
new file mode 100644
index 0000000000..3cbacf7184
--- /dev/null
+++ b/dom/html/test/bug649134/file_bug649134-2.sjs
@@ -0,0 +1,12 @@
+function handleRequest(request, response) {
+ response.seizePower();
+ var r =
+ "HTTP/1.1 200 OK\r\n" +
+ "Content-Type: text/html\r\n" +
+ 'Link: < \014>; rel="stylesheet",\r\n' +
+ "\r\n" +
+ "<!-- selector {} body {display:none;} --><body>PASS</body>\r\n";
+ response.bodyOutputStream.write(r, r.length);
+ response.bodyOutputStream.flush();
+ response.finish();
+}
diff --git a/dom/html/test/bug649134/index.html b/dom/html/test/bug649134/index.html
new file mode 100644
index 0000000000..2f3973704e
--- /dev/null
+++ b/dom/html/test/bug649134/index.html
@@ -0,0 +1,3 @@
+body {
+ display:none;
+}
diff --git a/dom/html/test/chrome.toml b/dom/html/test/chrome.toml
new file mode 100644
index 0000000000..ac226b51c2
--- /dev/null
+++ b/dom/html/test/chrome.toml
@@ -0,0 +1,12 @@
+[DEFAULT]
+support-files = [
+ "file_anchor_ping.html",
+ "image.png",
+]
+
+["test_anchor_ping.html"]
+skip-if = ["os == 'android'"]
+
+["test_bug1414077.html"]
+
+["test_external_protocol_iframe.html"]
diff --git a/dom/html/test/dialog/mochitest.toml b/dom/html/test/dialog/mochitest.toml
new file mode 100644
index 0000000000..18f1f551a7
--- /dev/null
+++ b/dom/html/test/dialog/mochitest.toml
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+["test_bug1648877_dialog_fullscreen_denied.html"]
+
diff --git a/dom/html/test/dialog/test_bug1648877_dialog_fullscreen_denied.html b/dom/html/test/dialog/test_bug1648877_dialog_fullscreen_denied.html
new file mode 100644
index 0000000000..906c7dd53e
--- /dev/null
+++ b/dom/html/test/dialog/test_bug1648877_dialog_fullscreen_denied.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1648877
+-->
+<head>
+ <title>Test for Bug 1648877</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=1648877">Requesting
+ fullscreen a dialog element should be denied</a>
+<p id="display"></p>
+<dialog>
+</dialog>
+<div style="width: 30px; height:30px" </div>
+
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ document.addEventListener("fullscreenchange", () => {
+ ok(false, "Should never receive " +
+ "a fullscreenchange event in the main window.");
+ });
+
+ document.addEventListener('fullscreenerror', (event) => {
+ ok(!document.fullscreenElement,
+ "Should not grant request if the element is dialog");
+ SimpleTest.finish();
+ });
+
+ const div = document.querySelector("div");
+
+ div.addEventListener("click", function() {
+ const dialog = document.querySelector("dialog");
+ dialog.requestFullscreen();
+ });
+
+ synthesizeMouseAtCenter(div, {});
+}
+
+SimpleTest.waitForFocus(runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/dummy_page.html b/dom/html/test/dummy_page.html
new file mode 100644
index 0000000000..fd238954c6
--- /dev/null
+++ b/dom/html/test/dummy_page.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>Dummy test page</title>
+<meta charset="utf-8"/>
+</head>
+<body>
+<p>Dummy test page</p>
+</body>
+</html>
diff --git a/dom/html/test/empty.html b/dom/html/test/empty.html
new file mode 100644
index 0000000000..0dc101b533
--- /dev/null
+++ b/dom/html/test/empty.html
@@ -0,0 +1 @@
+<html><body></body></html>
diff --git a/dom/html/test/file.webm b/dom/html/test/file.webm
new file mode 100644
index 0000000000..7bc738b8b4
--- /dev/null
+++ b/dom/html/test/file.webm
Binary files differ
diff --git a/dom/html/test/file_anchor_ping.html b/dom/html/test/file_anchor_ping.html
new file mode 100644
index 0000000000..3b9717263f
--- /dev/null
+++ b/dom/html/test/file_anchor_ping.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>file_anchor_ping.html</title>
+ </head>
+ <body onload="document.body.firstElementChild.click()">
+ <a href="/">click me</a>
+ <script>
+ document.body.firstElementChild.ping = window.location.search.slice(1);
+ </script>
+ </body>
+</html>
diff --git a/dom/html/test/file_broadcast_load.html b/dom/html/test/file_broadcast_load.html
new file mode 100644
index 0000000000..ffae9c6536
--- /dev/null
+++ b/dom/html/test/file_broadcast_load.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<h1>file_broadcast_load.html</h1>
+<script>
+let channel = new BroadcastChannel("test");
+channel.onmessage = function(e) {
+ console.log("file_broadcast_load.html got message:", e.data);
+ if (e.data == "close") {
+ window.close();
+ }
+};
+
+addEventListener("load", function() {
+ console.log("file_broadcast_load.html loaded");
+ channel.postMessage("load");
+});
+</script>
diff --git a/dom/html/test/file_bug1108547-1.html b/dom/html/test/file_bug1108547-1.html
new file mode 100644
index 0000000000..efc0eae494
--- /dev/null
+++ b/dom/html/test/file_bug1108547-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+document.cookie = "foo=bar";
+</script>
diff --git a/dom/html/test/file_bug1108547-2.html b/dom/html/test/file_bug1108547-2.html
new file mode 100644
index 0000000000..f5d8c5f964
--- /dev/null
+++ b/dom/html/test/file_bug1108547-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body onload="document.querySelector('form').submit();">
+<form action="javascript:opener.document.getElementById('result').textContent = document.cookie;" target="_blank" rel="opener">
+</form>
+<div id="result">not tested yet</div>
+</body>
diff --git a/dom/html/test/file_bug1108547-3.html b/dom/html/test/file_bug1108547-3.html
new file mode 100644
index 0000000000..e6a8ba3fa2
--- /dev/null
+++ b/dom/html/test/file_bug1108547-3.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<body onload="document.querySelector('a').click();">
+<a href="javascript:opener.document.getElementById('result').textContent = document.cookie;" target="_blank" rel="opener">test</a>
+<div id="result">not tested yet</div>
+</body>
diff --git a/dom/html/test/file_bug1166138_1x.png b/dom/html/test/file_bug1166138_1x.png
new file mode 100644
index 0000000000..df421453c2
--- /dev/null
+++ b/dom/html/test/file_bug1166138_1x.png
Binary files differ
diff --git a/dom/html/test/file_bug1166138_2x.png b/dom/html/test/file_bug1166138_2x.png
new file mode 100644
index 0000000000..6f76d44387
--- /dev/null
+++ b/dom/html/test/file_bug1166138_2x.png
Binary files differ
diff --git a/dom/html/test/file_bug1166138_def.png b/dom/html/test/file_bug1166138_def.png
new file mode 100644
index 0000000000..144a2f0b93
--- /dev/null
+++ b/dom/html/test/file_bug1166138_def.png
Binary files differ
diff --git a/dom/html/test/file_bug1260704.png b/dom/html/test/file_bug1260704.png
new file mode 100644
index 0000000000..df421453c2
--- /dev/null
+++ b/dom/html/test/file_bug1260704.png
Binary files differ
diff --git a/dom/html/test/file_bug209275_1.html b/dom/html/test/file_bug209275_1.html
new file mode 100644
index 0000000000..3f7233876b
--- /dev/null
+++ b/dom/html/test/file_bug209275_1.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <base href="http://example.org" />
+</head>
+<body onload="load();">
+Initial state
+
+<script>
+function load() {
+ // Nuke and rebuild the page.
+ document.removeChild(document.documentElement);
+ var html = document.createElement("html");
+ var body = document.createElement("body");
+ html.appendChild(body);
+ var link = document.createElement("a");
+ link.href = "#";
+ link.id = "link";
+ body.appendChild(link);
+ document.appendChild(html);
+
+ // Tell our parent to have a look at us.
+ parent.gGen.next();
+}
+</script>
+
+</body>
+</html>
diff --git a/dom/html/test/file_bug209275_2.html b/dom/html/test/file_bug209275_2.html
new file mode 100644
index 0000000000..36e9ff4672
--- /dev/null
+++ b/dom/html/test/file_bug209275_2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <base href="http://example.com" />
+</head>
+<body onload="load();">
+Page 2 initial state
+
+<script>
+function load() {
+ // Nuke and rebuild the page.
+ document.removeChild(document.documentElement);
+ html = document.createElement("html");
+ html.innerHTML = "<body><a href='/' id='link'>B</a></body>"
+ document.appendChild(html);
+
+ // Tell our parent to have a look at us
+ parent.gGen.next();
+}
+</script>
+
+</body>
+</html>
diff --git a/dom/html/test/file_bug209275_3.html b/dom/html/test/file_bug209275_3.html
new file mode 100644
index 0000000000..2544115901
--- /dev/null
+++ b/dom/html/test/file_bug209275_3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <base href="http://example.org" />
+</head>
+<body onload="load();">
+Initial state
+
+<script>
+function load() {
+ // Nuke and rebuild the page. If document.open() clears the <base> properly,
+ // our new <base> will take precedence and the test will pass.
+ document.open();
+ document.write("<html><base href='http://mochi.test:8888' /><body>" +
+ "<a id='link' href='/'>A</a></body></html>");
+
+ // Tell our parent to have a look at us.
+ parent.gGen.next();
+}
+</script>
+
+</body>
+</html>
diff --git a/dom/html/test/file_bug297761.html b/dom/html/test/file_bug297761.html
new file mode 100644
index 0000000000..5e861a00fd
--- /dev/null
+++ b/dom/html/test/file_bug297761.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <base href="http://www.mozilla.org/">
+ </head>
+ <body>
+ <form action="">
+ <input type='submit' formaction="">
+ <button type='submit' formaction=""></button>
+ <input id='i' type='image' formaction="">
+ </form>
+ </body>
+</html>
diff --git a/dom/html/test/file_bug417760.png b/dom/html/test/file_bug417760.png
new file mode 100644
index 0000000000..743292dc6f
--- /dev/null
+++ b/dom/html/test/file_bug417760.png
Binary files differ
diff --git a/dom/html/test/file_bug871161-1.html b/dom/html/test/file_bug871161-1.html
new file mode 100644
index 0000000000..16015f0c4e
--- /dev/null
+++ b/dom/html/test/file_bug871161-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset=windows-1251>
+<title>Page with non-default charset</title>
+<script>
+function run() {
+ document.forms[0].submit();
+}
+</script>
+</head>
+<body onload="run();">
+<form method=post action="http://example.org/tests/dom/html/test/file_bug871161-2.html"></form>
+</body>
+</html>
+
diff --git a/dom/html/test/file_bug871161-2.html b/dom/html/test/file_bug871161-2.html
new file mode 100644
index 0000000000..18cf825b2d
--- /dev/null
+++ b/dom/html/test/file_bug871161-2.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Page without declared charset</title>
+<script>
+function done() {
+ window.opener.postMessage(document.characterSet, "*");
+}
+</script>
+</head>
+<body onload="done();">
+</body>
+</html>
+
diff --git a/dom/html/test/file_bug893537.html b/dom/html/test/file_bug893537.html
new file mode 100644
index 0000000000..1dcb454ff1
--- /dev/null
+++ b/dom/html/test/file_bug893537.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=893537
+-->
+<body>
+<iframe id="iframe" src="data:text/html;charset=US-ASCII,Goodbye World" srcdoc="Hello World"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_cookiemanager.js b/dom/html/test/file_cookiemanager.js
new file mode 100644
index 0000000000..08c9d72898
--- /dev/null
+++ b/dom/html/test/file_cookiemanager.js
@@ -0,0 +1,20 @@
+/* eslint-env mozilla/chrome-script */
+
+addMessageListener("getCookieFromManager", ({ host, path }) => {
+ let cm = Cc["@mozilla.org/cookiemanager;1"].getService(Ci.nsICookieManager);
+ let values = [];
+ path = path.substring(0, path.lastIndexOf("/"));
+ for (let cookie of cm.cookies) {
+ if (!cookie) {
+ break;
+ }
+ if (host != cookie.host || path != cookie.path) {
+ continue;
+ }
+ values.push(cookie.name + "=" + cookie.value);
+ }
+
+ sendAsyncMessage("getCookieFromManager:return", {
+ cookie: values.join("; "),
+ });
+});
diff --git a/dom/html/test/file_formSubmission_img.jpg b/dom/html/test/file_formSubmission_img.jpg
new file mode 100644
index 0000000000..dcd99b9670
--- /dev/null
+++ b/dom/html/test/file_formSubmission_img.jpg
Binary files differ
diff --git a/dom/html/test/file_formSubmission_text.txt b/dom/html/test/file_formSubmission_text.txt
new file mode 100644
index 0000000000..a496efee84
--- /dev/null
+++ b/dom/html/test/file_formSubmission_text.txt
@@ -0,0 +1 @@
+This is a text file
diff --git a/dom/html/test/file_iframe_sandbox_a_if1.html b/dom/html/test/file_iframe_sandbox_a_if1.html
new file mode 100644
index 0000000000..b60d52ca00
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ I am sandboxed without any permissions
+ <iframe id="if_2a" src="file_iframe_sandbox_a_if2.html" height="10" width="10"></iframe>
+ <iframe id="if_2b" sandbox="allow-scripts" src="file_iframe_sandbox_a_if2.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_a_if10.html b/dom/html/test/file_iframe_sandbox_a_if10.html
new file mode 100644
index 0000000000..14306eb613
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if10.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<frameset>
+ <frame src="file_iframe_sandbox_a_if11.html">
+ <frame src="file_iframe_sandbox_a_if16.html">
+</frameset>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_a_if11.html b/dom/html/test/file_iframe_sandbox_a_if11.html
new file mode 100644
index 0000000000..8eee71df1d
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if11.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script>
+ function doStuff() {
+ try {
+ window.parent.parent.ok_wrapper(false, "a frame inside a sandboxed iframe should NOT be same origin with the iframe's parent");
+ }
+ catch (e) {
+ window.parent.parent.postMessage({ok: true, desc: "a frame inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
+ }
+ }
+ </script>
+</head>
+<frameset>
+ <frame onload='doStuff()' src="file_iframe_sandbox_a_if12.html">
+</frameset>
+I'm a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if12.html b/dom/html/test/file_iframe_sandbox_a_if12.html
new file mode 100644
index 0000000000..d49d4e5625
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if12.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+function doStuff() {
+ try {
+ window.parent.parent.parent.ok_wrapper(false, "a frame inside a frame inside a sandboxed iframe should NOT be same origin with the iframe's parent");
+ }
+ catch (e) {
+ dump("caught some e if12\n");
+ window.parent.parent.parent.postMessage({ok: true, desc: "a frame inside a frame inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
+ }
+}
+</script>
+<body onload='doStuff()'>
+ I'm a &lt;frame&gt; inside a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if13.html b/dom/html/test/file_iframe_sandbox_a_if13.html
new file mode 100644
index 0000000000..8737a7682e
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if13.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+ <object data="file_iframe_sandbox_a_if14.html"></object>
+</body>
+
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_a_if14.html b/dom/html/test/file_iframe_sandbox_a_if14.html
new file mode 100644
index 0000000000..b588f7ec50
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if14.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ window.addEventListener("message", receiveMessage);
+
+ function receiveMessage(event)
+ {
+ window.parent.parent.postMessage({ok: event.data.ok, desc: "objects containing " + event.data.desc}, "*");
+ }
+
+ function doStuff() {
+ try {
+ window.parent.parent.ok_wrapper(false, "an object inside a sandboxed iframe should NOT be same origin with the iframe's parent");
+ }
+ catch (e) {
+ window.parent.parent.postMessage({ok: true, desc: "an object inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
+ }
+ }
+</script>
+
+<body onload='doStuff()'>
+I'm a &lt;object&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+
+ <object data="file_iframe_sandbox_a_if15.html"></object>
+</body>
+
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if15.html b/dom/html/test/file_iframe_sandbox_a_if15.html
new file mode 100644
index 0000000000..9c5a003d7c
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if15.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+function doStuff() {
+ try {
+ window.parent.parent.parent.ok_wrapper(false, "an object inside a frame or object inside a sandboxed iframe should NOT be same origin with the iframe's parent");
+ }
+ catch (e) {
+ window.parent.parent.parent.postMessage({ok: true, desc: "an object inside a frame or object inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
+ }
+
+ // Check that sandboxed forms browsing context flag NOT set by attempting to submit a form.
+ document.getElementById('a_form').submit();
+}
+</script>
+
+<body onload='doStuff()'>
+ I'm a &lt;object&gt; inside a &lt;frame&gt; or &lt;object&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+
+ <form method="get" action="file_iframe_sandbox_form_pass.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" id="a_button">
+ </form>
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if16.html b/dom/html/test/file_iframe_sandbox_a_if16.html
new file mode 100644
index 0000000000..141d3c2b06
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if16.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ window.addEventListener("message", receiveMessage);
+
+ function receiveMessage(event)
+ {
+ window.parent.parent.postMessage({ok: event.data.ok, desc: "objects containing " + event.data.desc}, "*");
+ }
+</script>
+
+<body>
+I'm a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+
+ <object data="file_iframe_sandbox_a_if15.html"></object>
+</body>
+
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if17.html b/dom/html/test/file_iframe_sandbox_a_if17.html
new file mode 100644
index 0000000000..a736924bf5
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if17.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doTest() {
+ var if_18_19 = document.getElementById('if_18_19');
+ if_18_19.sandbox = "allow-scripts allow-same-origin";
+ if_18_19.contentWindow.postMessage("go", "*");
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed but with "allow-scripts". I change the sandbox flags on if_18_19 to
+ "allow-scripts allow-same-origin" then get it to re-navigate itself to
+ file_iframe_sandbox_a_if18.html, which attemps to call a function in my parent.
+ This should fail since my sandbox flags should be copied to it when the sandbox
+ flags are changed.
+
+ <iframe sandbox="allow-scripts" id="if_18_19" src="file_iframe_sandbox_a_if19.html" height="10" width="10"></iframe>
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if18.html b/dom/html/test/file_iframe_sandbox_a_if18.html
new file mode 100644
index 0000000000..bbe90970d4
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if18.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doTest() {
+ try {
+ window.parent.parent.ok_wrapper(false, "an iframe in an iframe SHOULD copy its parent's sandbox flags when its sandbox flags are changed");
+ }
+ catch (e) {
+ window.parent.parent.postMessage({ok: true, desc: "an iframe in an iframe copies its parent's sandbox flags when its sandbox flags are changed"}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I'm an iframe whose sandbox flags have been changed to include allow-same-origin.
+ I should not be able to call a function in my parent's parent because my parent's
+ iframe does not have allow-same-origin set.
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if19.html b/dom/html/test/file_iframe_sandbox_a_if19.html
new file mode 100644
index 0000000000..e4d3d68887
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if19.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 886262</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script>
+ window.addEventListener("message", function(e){
+ window.open("file_iframe_sandbox_a_if18.html", "_self");
+ });
+</script>
+
+<body>
+ I'm just here to navigate to file_iframe_sandbox_a_if18.html after my owning
+ iframe has had allow-same-origin added.
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if2.html b/dom/html/test/file_iframe_sandbox_a_if2.html
new file mode 100644
index 0000000000..72bde69e41
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if2.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+function doStuff() {
+ // should NOT be able to execute scripts
+ window.parent.parent.postMessage({ok: false, desc: "a document within an iframe sandboxed with sandbox='' should NOT be able to execute scripts"}, "*");
+}
+</script>
+
+<body onLoad="doStuff()">
+ I am NOT sandboxed or am sandboxed with "allow-scripts" but am contained within an iframe sandboxed with sandbox = ""
+ or am sandboxed with sandbox='' inside an iframe sandboxed with "allow-scripts"
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if3.html b/dom/html/test/file_iframe_sandbox_a_if3.html
new file mode 100644
index 0000000000..899c2f1093
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if3.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="text/javascript">
+ function ok_wrapper(condition, msg) {
+ window.parent.ok_wrapper(condition, msg);
+ }
+</script>
+
+<body>
+ I am sandboxed but with "allow-scripts"
+
+ <iframe id='if_4' src='file_iframe_sandbox_a_if4.html' height="10" width="10"></iframe>
+ <iframe id='if_7' src='file_iframe_sandbox_a_if7.html' height="10" width="10"></iframe>
+ <iframe id='if_2' sandbox='' src='file_iframe_sandbox_a_if2.html' height="10" width="10"></iframe>
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if4.html b/dom/html/test/file_iframe_sandbox_a_if4.html
new file mode 100644
index 0000000000..a216fb572a
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if4.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="text/javascript">
+function doStuff() {
+ try {
+ window.parent.ok_wrapper(false, "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with its parent");
+ } catch(e) {
+ window.parent.parent.postMessage({type: "ok", ok: true, desc: "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with its parent"}, "*");
+ }
+
+ try {
+ window.parent.parent.ok_wrapper(false, "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with the top level");
+ } catch(e) {
+ window.parent.parent.postMessage({type: "ok", ok: true, desc: "a document contained within a sandboxed document without 'allow-same-origin' should NOT be same domain with the top level"}, "*");
+ }
+}
+</script>
+
+<body onLoad="doStuff()">
+ I am not sandboxed but contained within a sandboxed document with 'allow-scripts'
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if5.html b/dom/html/test/file_iframe_sandbox_a_if5.html
new file mode 100644
index 0000000000..c1081c5039
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if5.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="text/javascript">
+ function ok_wrapper(result, desc) {
+ window.parent.ok_wrapper(result, desc);
+ }
+</script>
+
+<body>
+ I am sandboxed but with "allow-scripts allow-same-origin"
+
+ <iframe sandbox='allow-scripts allow-same-origin' id='if_6' src='file_iframe_sandbox_a_if6.html' height="10" width="10"></iframe>
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if6.html b/dom/html/test/file_iframe_sandbox_a_if6.html
new file mode 100644
index 0000000000..62a7114316
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if6.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="text/javascript">
+function doStuff() {
+ window.parent.ok_wrapper(true, "a document sandboxed with 'allow-same-origin' and contained within a sandboxed document with 'allow-same-origin' should be same domain with its parent");
+ window.parent.parent.ok_wrapper(true, "a document sandboxed with 'allow-same-origin' contained within a sandboxed document with 'allow-same-origin' should be same domain with the top level");
+}
+</script>
+
+<body onLoad="doStuff()">
+ I am sandboxed with 'allow-scripts allow-same-origin' and contained within a sandboxed document with 'allow-scripts allow-same-origin'
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if7.html b/dom/html/test/file_iframe_sandbox_a_if7.html
new file mode 100644
index 0000000000..6480eebdba
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if7.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+function doStuff() {
+ // should be able to execute scripts
+ window.parent.parent.postMessage({ok: true, desc: "a document contained within an iframe contained within an iframe sandboxed with 'allow-scripts' should be able to execute scripts"}, "*");
+}
+</script>
+
+<body onLoad="doStuff()">
+ I am NOT sandboxed but am contained within an iframe contained within an iframe sandboxed with sandbox = "allow-scripts"
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if8.html b/dom/html/test/file_iframe_sandbox_a_if8.html
new file mode 100644
index 0000000000..87748f542a
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if8.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script>
+function doSubload() {
+ var if_9 = document.getElementById('if_9');
+ if_9.src = 'file_iframe_sandbox_a_if9.html';
+}
+
+window.doSubload = doSubload;
+
+</script>
+<body>
+ I am sandboxed but with "allow-scripts allow-same-origin". After my initial load, "allow-same-origin" is removed
+ and then I load file_iframe_sandbox_a_if9.html, which attemps to call a function in window.top. This should
+ succeed since the new sandbox flags shouldn't have taken affect on me until I'm reloaded.
+
+ <iframe id='if_9' src='about:blank' height="10" width="10"></iframe>
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_a_if9.html b/dom/html/test/file_iframe_sandbox_a_if9.html
new file mode 100644
index 0000000000..da2bcf1fa3
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_a_if9.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+function doStuff() {
+ window.parent.parent.ok_wrapper(true, "a subloaded document should inherit the flags of the document, not of the docshell/sandbox attribute");
+}
+</script>
+<body onload='doStuff()'>
+ I'm a subloaded document of file_iframe_sandbox_a_if8.html. I should be able to call a function in window.top
+ because I should be same-origin with it.
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_b_if1.html b/dom/html/test/file_iframe_sandbox_b_if1.html
new file mode 100644
index 0000000000..a65cbec6b9
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_b_if1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ I am sandboxed without any permissions
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_b_if2.html b/dom/html/test/file_iframe_sandbox_b_if2.html
new file mode 100644
index 0000000000..08e7453574
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_b_if2.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+ function ok(condition, msg) {
+ window.parent.ok_wrapper(condition, msg);
+ }
+
+ function testXHR() {
+ var xhr = new XMLHttpRequest();
+
+ xhr.open("GET", "file_iframe_sandbox_b_if1.html");
+
+ xhr.onreadystatechange = function (oEvent) {
+ var result = false;
+ if (xhr.readyState == 4) {
+ if (xhr.status == 200) {
+ result = true;
+ }
+ ok(result, "XHR should work normally in an iframe sandboxed with 'allow-same-origin'");
+ }
+ }
+
+ xhr.send(null);
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with 'allow-same-origin' should be able to access their parent");
+
+ // should be able to access document.cookie since we have 'allow-same-origin'
+ ok(document.cookie == "", "a document sandboxed with allow-same-origin should be able to access document.cookie");
+
+ // should be able to access localStorage since we have 'allow-same-origin'
+ ok(window.localStorage, "a document sandboxed with allow-same-origin should be able to access localStorage");
+
+ // should be able to access sessionStorage since we have 'allow-same-origin'
+ ok(window.sessionStorage, "a document sandboxed with allow-same-origin should be able to access sessionStorage");
+
+ testXHR();
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-same-origin" and "allow-scripts"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_b_if3.html b/dom/html/test/file_iframe_sandbox_b_if3.html
new file mode 100644
index 0000000000..350e2ac472
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_b_if3.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+ function ok(result, message) {
+ window.parent.postMessage({ok: result, desc: message}, "*");
+ }
+
+ function testXHR() {
+ // Standard URL should be blocked as we have a unique origin.
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "file_iframe_sandbox_b_if1.html");
+ xhr.onreadystatechange = function (oEvent) {
+ var result = false;
+ if (xhr.readyState == 4) {
+ if (xhr.status == 0) {
+ result = true;
+ }
+ ok(result, "XHR should be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
+ }
+ }
+ xhr.send(null);
+
+ // Blob URL should work as it will have our unique origin.
+ var blobXhr = new XMLHttpRequest();
+ var blobUrl = URL.createObjectURL(new Blob(["wibble"], {type: "text/plain"}));
+ blobXhr.open("GET", blobUrl);
+ blobXhr.onreadystatechange = function () {
+ if (this.readyState == 4) {
+ ok(this.status == 200 && this.response == "wibble", "XHR for a blob URL created in this document should NOT be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
+ }
+ }
+ try {
+ blobXhr.send();
+ } catch(e) {
+ ok(false, "failed to send XHR for blob URL: error: " + e);
+ }
+
+ // Data URL should work as it inherits the loader's origin.
+ var dataXhr = new XMLHttpRequest();
+ dataXhr.open("GET", "data:text/html,wibble");
+ dataXhr.onreadystatechange = function () {
+ if (this.readyState == 4) {
+ ok(this.status == 200 && this.response == "wibble", "XHR for a data URL should NOT be blocked in an iframe sandboxed WITHOUT 'allow-same-origin'");
+ }
+ }
+ try {
+ dataXhr.send();
+ } catch(e) {
+ ok(false, "failed to send XHR for data URL: error: " + e);
+ }
+ }
+
+ function doStuff() {
+ try {
+ window.parent.ok(false, "documents sandboxed without 'allow-same-origin' should NOT be able to access their parent");
+ } catch (error) {
+ ok(true, "documents sandboxed without 'allow-same-origin' should NOT be able to access their parent");
+ }
+
+ // should NOT be able to access document.cookie
+ try {
+ var foo = document.cookie;
+ } catch(error) {
+ ok(true, "a document sandboxed without allow-same-origin should NOT be able to access document.cookie");
+ }
+
+ // should NOT be able to access localStorage
+ try {
+ var foo = window.localStorage;
+ } catch(error) {
+ ok(true, "a document sandboxed without allow-same-origin should NOT be able to access localStorage");
+ }
+
+ // should NOT be able to access sessionStorage
+ try {
+ var foo = window.sessionStorage;
+ } catch(error) {
+ ok(true, "a document sandboxed without allow-same-origin should NOT be able to access sessionStorage");
+ }
+
+ testXHR();
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-scripts"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if1.html b/dom/html/test/file_iframe_sandbox_c_if1.html
new file mode 100644
index 0000000000..c2fbf136ae
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "documents sandboxed with allow-scripts should be able to run inline scripts");
+
+ document.getElementById('a_form').submit();
+
+ // trigger the javascript: url test
+ sendMouseEvent({type:'click'}, 'a_link');
+ }
+</script>
+<script src='file_iframe_sandbox_pass.js'></script>
+<body onLoad='ok(true, "documents sandboxed with allow-scripts should be able to run script from event listeners");doStuff();'>
+ I am sandboxed but with "allow-scripts"
+
+ <form method="get" action="file_iframe_sandbox_form_fail.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" onclick="doSubmit()" id="a_button">
+ </form>
+
+ <a href = 'javascript:ok(true, "documents sandboxed with allow-scripts should be able to run script from javascript: URLs");' id='a_link'>click me</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if2.html b/dom/html/test/file_iframe_sandbox_c_if2.html
new file mode 100644
index 0000000000..1ea8a90ca3
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc}, "*");
+ }
+
+ function doStuff() {
+ ok(false, "documents sandboxed without allow-scripts should NOT be able to run inline scripts");
+ }
+</script>
+<script src='file_iframe_sandbox_fail.js'></script>
+<body onLoad='window.parent.postmessage({ok: false, desc: "documents sandboxed without allow-scripts should NOT be able to run script from event handlers"}, "*");doStuff();'>
+ I am sandboxed with no permissions
+ <img src="about:blank" onerror='ok(false, "documents sandboxed without allow-scripts should NOT be able to run script from event handlers");')>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if3.html b/dom/html/test/file_iframe_sandbox_c_if3.html
new file mode 100644
index 0000000000..fdf98d93d4
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if3.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+ function doStuff() {
+ dump("*** c_if3 has loaded\n");
+ // try and submit the form - this should succeed
+ document.getElementById('a_form').submit();
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-scripts allow-forms"
+
+ <form method="get" action="file_iframe_sandbox_form_pass.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" onclick="doSubmit()" id="a_button">
+ </form>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if4.html b/dom/html/test/file_iframe_sandbox_c_if4.html
new file mode 100644
index 0000000000..ee2438f28a
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if4.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.ok_wrapper(result, desc);
+ }
+
+ function doStuff() {
+ // try to open a new window via target="_blank", target="BC341604", and window.open()
+ // the window we try to open closes itself once it opens
+ sendMouseEvent({type:'click'}, 'target_blank');
+ sendMouseEvent({type:'click'}, 'target_BC341604');
+
+ var threw = false;
+ try {
+ window.open("about:blank");
+ } catch (error) {
+ threw = true;
+ }
+
+ ok(threw, "window.open threw a JS exception and was not allowed");
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-scripts allow-same-origin"
+
+ <a href="file_iframe_sandbox_open_window_fail.html" target="_blank" id="target_blank" rel="opener">open window</a>
+ <a href="file_iframe_sandbox_open_window_fail.html" target="BC341604" id="target_BC341604">open window</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if5.html b/dom/html/test/file_iframe_sandbox_c_if5.html
new file mode 100644
index 0000000000..bd368de425
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if5.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.ok_wrapper(result, desc);
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-same-origin"
+
+ <a href = 'javascript:ok(false, "documents sandboxed without allow-scripts should not be able to run script with javascript: URLs");' id='a_link'>click me</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if6.html b/dom/html/test/file_iframe_sandbox_c_if6.html
new file mode 100644
index 0000000000..e5ecf3051e
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if6.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.ok_wrapper(result, desc);
+ window.parent.postMessage({ok: result, desc}, "*");
+ }
+
+ function doStuff() {
+ ok(true, "a document sandboxed with allow-same-origin and allow-scripts should be same origin with its parent and able to run scripts " +
+ "regardless of what kind of whitespace was used in its sandbox attribute");
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-same-origin" and "allow-scripts"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if7.html b/dom/html/test/file_iframe_sandbox_c_if7.html
new file mode 100644
index 0000000000..b9a55def6f
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if7.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc}, "*");
+ }
+
+ function doStuff() {
+ try {
+ var thing = indexedDB.open("sandbox");
+ ok(false, "documents sandboxed without allow-same-origin should NOT be able to access indexedDB");
+ }
+
+ catch(e) {
+ ok(true, "documents sandboxed without allow-same-origin should NOT be able to access indexedDB");
+ }
+ }
+</script>
+<body onLoad='doStuff();'>
+ I am sandboxed but with "allow-scripts"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if8.html b/dom/html/test/file_iframe_sandbox_c_if8.html
new file mode 100644
index 0000000000..d8b8948466
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if8.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc}, "*");
+ }
+
+ function doStuff() {
+ var thing = indexedDB.open("sandbox");
+
+ thing.onerror = function(event) {
+ ok(false, "documents sandboxed with allow-same-origin SHOULD be able to access indexedDB");
+ };
+ thing.onsuccess = function(event) {
+ ok(true, "documents sandboxed with allow-same-origin SHOULD be able to access indexedDB");
+ };
+ }
+</script>
+<body onLoad='doStuff();'>
+ I am sandboxed but with "allow-scripts allow-same-origin"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_c_if9.html b/dom/html/test/file_iframe_sandbox_c_if9.html
new file mode 100644
index 0000000000..0c88a677cb
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_c_if9.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 671389</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ I am
+ <ul>
+ <li>sandboxed but with "allow-forms", "allow-pointer-lock", "allow-popups", "allow-same-origin", "allow-scripts", and "allow-top-navigation", </li>
+ <li>sandboxed but with "allow-same-origin", "allow-scripts", </li>
+ <li>sandboxed, or </li>
+ <li>not sandboxed.</li>
+ </ul>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_close.html b/dom/html/test/file_iframe_sandbox_close.html
new file mode 100644
index 0000000000..3b87534978
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_close.html
@@ -0,0 +1,3 @@
+<script>
+ self.close();
+</script>
diff --git a/dom/html/test/file_iframe_sandbox_d_if1.html b/dom/html/test/file_iframe_sandbox_d_if1.html
new file mode 100644
index 0000000000..744594e813
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function doTest() {
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+
+ <a href="file_iframe_sandbox_navigation_pass.html?Test 1:%20" target="_self" id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if10.html b/dom/html/test/file_iframe_sandbox_d_if10.html
new file mode 100644
index 0000000000..41fb46b586
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if10.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function doTest() {
+ window.parent.postMessage({type: "if_10"}, "*");
+}
+</script>
+<body onload='doTest()'>
+ I am sandboxed with 'allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if11.html b/dom/html/test/file_iframe_sandbox_d_if11.html
new file mode 100644
index 0000000000..63880587f5
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if11.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+
+function navigateAway() {
+ document.getElementById("anchor").click();
+}
+
+function doTest() {
+ try {
+ // this should fail the first time, but work the second
+ window.parent.ok_wrapper(true, "a document that was loaded, navigated to another document, had 'allow-same-origin' added and then was" +
+ " navigated back should be same-origin with its parent");
+ } catch (e) {
+ navigateAway();
+ }
+}
+
+</script>
+<body onload='doTest()'>
+ I am sandboxed with 'allow-scripts'
+ <a href='file_iframe_sandbox_d_if12.html' id='anchor'>CLICK ME</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if12.html b/dom/html/test/file_iframe_sandbox_d_if12.html
new file mode 100644
index 0000000000..0d7936512e
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if12.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+function doTest() {
+ window.parent.postMessage({test:'if_11'}, "*");
+}
+</script>
+<body onload='doTest()'>
+ I am sandboxed with 'allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if13.html b/dom/html/test/file_iframe_sandbox_d_if13.html
new file mode 100644
index 0000000000..aad330c33c
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if13.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event) {
+ // this message is part of if_11's test
+ if (event.data.test == 'if_11') {
+ doIf11TestPart2();
+ }
+}
+
+function ok_wrapper(result, msg) {
+ window.opener.postMessage({ok: result, desc: msg}, "*");
+ window.close();
+}
+
+function doIf11TestPart2() {
+ var if_11 = document.getElementById('if_11');
+ if_11.sandbox = 'allow-scripts allow-same-origin';
+ // window.history is no longer cross-origin accessible in gecko.
+ SpecialPowers.wrap(if_11).contentWindow.history.back();
+}
+</script>
+<body>
+ <iframe sandbox='allow-scripts' id="if_11" src="file_iframe_sandbox_d_if11.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if14.html b/dom/html/test/file_iframe_sandbox_d_if14.html
new file mode 100644
index 0000000000..237a9d704f
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if14.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 838692</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+ var test20Context = "Test 20: Navigate another window (not opened by us): ";
+
+ function doTest() {
+ // Try to navigate auxiliary browsing context (window) not opened by us.
+ // We should not be able to do this as we are sandboxed.
+ sendMouseEvent({type:'click'}, 'navigate_window');
+ window.parent.postMessage({type: "attempted"}, "*");
+
+ // Try to navigate auxiliary browsing context (window) not opened by us, using window.open().
+ // We should not be able to do this as we are sandboxed.
+ try {
+ window.open("file_iframe_sandbox_window_navigation_fail.html?" + escape(test20Context), "window_to_navigate2");
+ window.parent.postMessage({type: "attempted"}, "*");
+ } catch(error) {
+ window.parent.postMessage({ok: true, desc: test20Context + "as expected, error thrown during window.open(..., \"window_to_navigate2\")"}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed but with "allow-scripts allow-same-origin allow-top-navigation".
+
+ <a href="file_iframe_sandbox_window_navigation_fail.html?Test 14: Navigate another window (not opened by us):%20" target="window_to_navigate" id="navigate_window">navigate window</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if15.html b/dom/html/test/file_iframe_sandbox_d_if15.html
new file mode 100644
index 0000000000..6c969c8fe1
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if15.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+ I am an unsandboxed iframe.
+
+ <iframe sandbox="allow-same-origin allow-scripts" id="if_16" src="file_iframe_sandbox_d_if16.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if16.html b/dom/html/test/file_iframe_sandbox_d_if16.html
new file mode 100644
index 0000000000..e50dd97ea0
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if16.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="application/javascript">
+function doTest() {
+ window.parent.parent.postMessage({type: "attempted"}, "*");
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-same-origin allow-scripts'
+
+ <a href="file_iframe_sandbox_navigation_fail.html?Test 16: Navigate parent/ancestor by name:%20" target='if_parent' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if17.html b/dom/html/test/file_iframe_sandbox_d_if17.html
new file mode 100644
index 0000000000..047a08137d
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if17.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+ var testContext = "Test 17: navigate _self with window.open(): ";
+
+ function doTest() {
+ try {
+ window.open("file_iframe_sandbox_navigation_pass.html?" + escape(testContext), "_self");
+ } catch(error) {
+ window.parent.postMessage({ok: false, desc: testContext + "error thrown during window.open(..., \"_self\")"}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if18.html b/dom/html/test/file_iframe_sandbox_d_if18.html
new file mode 100644
index 0000000000..fdcb4198f4
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if18.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="application/javascript">
+ window.addEventListener("message", receiveMessage);
+
+ function receiveMessage(event) {
+ window.parent.postMessage(event.data, "*");
+ }
+
+ var testContext = "Test 18: navigate child with window.open(): ";
+
+ function doTest() {
+ try {
+ window.open("file_iframe_sandbox_navigation_pass.html?" + escape(testContext), "foo");
+ } catch(error) {
+ window.parent.postMessage({ok: false, desc: testContext + " error thrown during window.open(..., \"foo\")"}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+
+ <iframe name="foo" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if19.html b/dom/html/test/file_iframe_sandbox_d_if19.html
new file mode 100644
index 0000000000..d766d26492
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if19.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ I am sandboxed with 'allow-scripts'
+
+ <iframe sandbox="allow-scripts" id="if_20" src="file_iframe_sandbox_d_if20.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if2.html b/dom/html/test/file_iframe_sandbox_d_if2.html
new file mode 100644
index 0000000000..b45cb975ca
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+// needed to forward the message to the main test page
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event) {
+ window.parent.postMessage(event.data, "*");
+}
+
+function doTest() {
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+
+ <iframe name="foo" src="file_iframe_sandbox_navigation_start.html" height="10" width="10"></iframe>
+
+ <a href="file_iframe_sandbox_navigation_pass.html?Test 2:%20" target='foo' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if20.html b/dom/html/test/file_iframe_sandbox_d_if20.html
new file mode 100644
index 0000000000..005c4bc823
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if20.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+ var testContext = "Test 19: navigate _parent with window.open(): ";
+
+ function doTest() {
+ try {
+ window.open("file_iframe_sandbox_navigation_fail.html?" + escape(testContext), "_parent");
+ window.parent.parent.postMessage({type: "attempted"}, "*");
+ } catch(error) {
+ window.parent.parent.postMessage({ok: true, desc: testContext + "as expected, error thrown during window.open(..., \"_parent\")"}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if21.html b/dom/html/test/file_iframe_sandbox_d_if21.html
new file mode 100644
index 0000000000..6d0ab232e0
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if21.html
@@ -0,0 +1,14 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+ I am an unsandboxed iframe.
+
+ <iframe sandbox="allow-same-origin allow-scripts" id="if_22" src="file_iframe_sandbox_d_if22.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if22.html b/dom/html/test/file_iframe_sandbox_d_if22.html
new file mode 100644
index 0000000000..bd27157926
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if22.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+ var testContext = "Test 21: navigate parent by name with window.open(): ";
+
+ function doTest() {
+ try {
+ window.open("file_iframe_sandbox_navigation_fail.html?" + escape(testContext), "if_parent2");
+ window.parent.parent.postMessage({type: "attempted"}, "*");
+ } catch(error) {
+ window.parent.parent.postMessage({ok: true, desc: testContext + "as expected, error thrown during window.open(..., \"if_parent2\")"}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-same-origin allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if23.html b/dom/html/test/file_iframe_sandbox_d_if23.html
new file mode 100644
index 0000000000..e755511e37
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if23.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+ var test27Context = "Test 27: navigate opened window by name with anchor: ";
+ var test28Context = "Test 28: navigate opened window by name with window.open(): ";
+
+ var windowsToClose = new Array();
+
+ function closeWindows() {
+ for (var i = 0; i < windowsToClose.length; i++) {
+ windowsToClose[i].close();
+ }
+ }
+
+ // Add message listener to forward messages on to parent
+ window.addEventListener("message", receiveMessage);
+
+ function receiveMessage(event) {
+ switch (event.data.type) {
+ case "closeWindows":
+ closeWindows();
+ break;
+ default:
+ window.parent.postMessage(event.data, "*");
+ }
+ }
+
+ function doTest() {
+ try {
+ windowsToClose.push(window.open("about:blank", "test27window"));
+ var test27Anchor = document.getElementById("test27Anchor");
+ test27Anchor.href = "file_iframe_sandbox_window_navigation_pass.html?" + escape(test27Context);
+ sendMouseEvent({type:"click"}, "test27Anchor");
+ window.parent.postMessage({type: "attempted"}, "*");
+ } catch(error) {
+ window.parent.postMessage({ok: false, desc: test27Context + "error thrown during window.open(): " + error}, "*");
+ }
+
+ try {
+ windowsToClose.push(window.open("about:blank", "test28window"));
+ window.open("file_iframe_sandbox_window_navigation_pass.html?" + escape(test28Context), "test28window");
+ window.parent.postMessage({type: "attempted"}, "*");
+ } catch(error) {
+ window.parent.postMessage({ok: false, desc: test28Context + "error thrown during window.open(): " + error}, "*");
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts allow-popups'
+
+ <a id="test27Anchor" target="test27window">Test 27 anchor</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if3.html b/dom/html/test/file_iframe_sandbox_d_if3.html
new file mode 100644
index 0000000000..cd2d53bce9
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if3.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ I am sandboxed with 'allow-scripts'
+
+ <iframe sandbox="allow-scripts" id="if_4" src="file_iframe_sandbox_d_if4.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if4.html b/dom/html/test/file_iframe_sandbox_d_if4.html
new file mode 100644
index 0000000000..c11a414551
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if4.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function doTest() {
+ window.parent.parent.postMessage({type: "attempted"}, "*");
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+
+ <a href="file_iframe_sandbox_navigation_fail.html" target='_parent' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if5.html b/dom/html/test/file_iframe_sandbox_d_if5.html
new file mode 100644
index 0000000000..d8fe4289af
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if5.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function doTest() {
+ window.parent.postMessage({type: "attempted"}, "*");
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts allow-same-origin'
+
+ <a href="file_iframe_sandbox_navigation_fail.html?Test 4: Navigate sibling iframe by name:%20" target='if_sibling' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if6.html b/dom/html/test/file_iframe_sandbox_d_if6.html
new file mode 100644
index 0000000000..9bb48cbb20
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if6.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function doTest() {
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+
+ <a href="file_iframe_sandbox_d_if7.html" target='_self' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if7.html b/dom/html/test/file_iframe_sandbox_d_if7.html
new file mode 100644
index 0000000000..5023ee0294
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if7.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+function doTest() {
+ try {
+ window.parent.ok_wrapper(false, "a sandboxed document when navigated should still NOT be same-origin with its parent");
+ } catch(error) {
+ window.parent.postMessage({ok: true, desc: "sandboxed document's attempt to access parent after navigation blocked, as not same-origin."}, "*");
+ }
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if8.html b/dom/html/test/file_iframe_sandbox_d_if8.html
new file mode 100644
index 0000000000..2b4398ef00
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if8.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+ function doTest() {
+ window.parent.modify_if_8();
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts' and 'allow-same-origin' the first time I am loaded, and with 'allow-scripts' the second time
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_d_if9.html b/dom/html/test/file_iframe_sandbox_d_if9.html
new file mode 100644
index 0000000000..ee641904fc
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_d_if9.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+function doTest() {
+ window.parent.modify_if_9();
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts' and 'allow-same-origin' the first time I am loaded, and with 'allow-same-origin' the second time
+</body>
+</html>
+
diff --git a/dom/html/test/file_iframe_sandbox_e_if1.html b/dom/html/test/file_iframe_sandbox_e_if1.html
new file mode 100644
index 0000000000..e3882dfb28
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+
+<script>
+ function doTest() {
+ var testContext = location.search == "" ? "?Test 10: Navigate _top:%20" : location.search;
+ document.getElementById("if_6").src = "file_iframe_sandbox_e_if6.html" + testContext;
+ }
+</script>
+
+<body onload="doTest()">
+ <iframe sandbox='allow-scripts' id='if_6' height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if10.html b/dom/html/test/file_iframe_sandbox_e_if10.html
new file mode 100644
index 0000000000..2484b8f342
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if10.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doTest() {
+ var testContext = "?Test 23: Nested navigate _top with window.open():%20";
+ document.getElementById("if_9").src = "file_iframe_sandbox_e_if9.html" + testContext;
+ }
+</script>
+
+<body onload="doTest()">
+ <iframe sandbox='allow-scripts allow-top-navigation' id='if_9' height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if11.html b/dom/html/test/file_iframe_sandbox_e_if11.html
new file mode 100644
index 0000000000..106c4c629b
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if11.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+ function doTest() {
+ var testContext = location.search.substring(1);
+ try {
+ window.open("file_iframe_sandbox_top_navigation_pass.html?" + testContext, "_top");
+ } catch(error) {
+ window.top.opener.postMessage({ok: false, desc: unescape(testContext) + "error thrown during window.open(..., \"_top\")"}, "*");
+ window.top.close();
+ }
+ }
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts and allow-top-navigation'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if12.html b/dom/html/test/file_iframe_sandbox_e_if12.html
new file mode 100644
index 0000000000..0b1b87e09b
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if12.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doTest() {
+ var testContext = location.search == "" ? "?Test 24: Navigate _top with window.open():%20" : location.search;
+ document.getElementById("if_14").src = "file_iframe_sandbox_e_if14.html" + testContext;
+ }
+</script>
+
+<body onload="doTest()">
+ <iframe sandbox='allow-scripts' id='if_14' height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if13.html b/dom/html/test/file_iframe_sandbox_e_if13.html
new file mode 100644
index 0000000000..f5cf912f67
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if13.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doTest() {
+ var testContext = "?Test 25: Nested navigate _top with window.open():%20";
+ document.getElementById("if_12").src = "file_iframe_sandbox_e_if12.html" + testContext;
+ }
+</script>
+
+<body onload="doTest()">
+ <iframe sandbox='allow-scripts allow-top-navigation' id='if_12' height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if14.html b/dom/html/test/file_iframe_sandbox_e_if14.html
new file mode 100644
index 0000000000..76d9787020
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if14.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+ function doTest() {
+ var testContext = location.search.substring(1);
+ try {
+ var topsOpener = window.top.opener;
+ window.open("file_iframe_sandbox_top_navigation_fail.html?" + testContext, "_top");
+ topsOpener.postMessage({ok: false, desc: unescape(testContext) + "top navigation should NOT be allowed by a document sandboxed without 'allow-top-navigation.'"}, "*");
+ } catch(error) {
+ window.top.opener.postMessage({ok: true, desc: unescape(testContext) + "as expected error thrown during window.open(..., \"_top\")"}, "*");
+ window.top.close();
+ }
+ }
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if15.html b/dom/html/test/file_iframe_sandbox_e_if15.html
new file mode 100644
index 0000000000..bf4138e1d6
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if15.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ // Set our name, to allow an attempt to navigate us by name.
+ window.name = "e_if15";
+</script>
+
+<body>
+ <iframe sandbox='allow-scripts' id='if_16' src="file_iframe_sandbox_e_if16.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if16.html b/dom/html/test/file_iframe_sandbox_e_if16.html
new file mode 100644
index 0000000000..06c8bf8714
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if16.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ var testContext = "Test 26: navigate top by name with window.open(): ";
+
+ function doTest() {
+ try {
+ var topsOpener = window.top.opener;
+ window.open("file_iframe_sandbox_top_navigation_fail.html?" + escape(testContext), "e_if15");
+ topsOpener.postMessage({ok: false, desc: unescape(testContext) + "top navigation should NOT be allowed by a document sandboxed without 'allow-top-navigation.'"}, "*");
+ } catch(error) {
+ window.top.opener.postMessage({ok: true, desc: testContext + "as expected, error thrown during window.open(..., \"e_if15\")"}, "*");
+ window.top.close();
+ }
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed but with "allow-scripts"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if2.html b/dom/html/test/file_iframe_sandbox_e_if2.html
new file mode 100644
index 0000000000..739dbacbd5
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+ <iframe sandbox='allow-scripts allow-top-navigation allow-same-origin' id='if_1' src="file_iframe_sandbox_e_if1.html?Test 11: Nested navigate _top:%20" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if3.html b/dom/html/test/file_iframe_sandbox_e_if3.html
new file mode 100644
index 0000000000..ce010e6893
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if3.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <iframe sandbox='allow-scripts allow-top-navigation' id='if_5' src="file_iframe_sandbox_e_if5.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if4.html b/dom/html/test/file_iframe_sandbox_e_if4.html
new file mode 100644
index 0000000000..740a33a94d
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if4.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <iframe sandbox='allow-scripts allow-top-navigation' id='if_3' src="file_iframe_sandbox_e_if3.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if5.html b/dom/html/test/file_iframe_sandbox_e_if5.html
new file mode 100644
index 0000000000..e550df45e5
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if5.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function navigateAway() {
+ document.getElementById("anchor").click();
+}
+</script>
+<body onload="navigateAway()">
+ I am sandboxed with 'allow-scripts and allow-top-navigation'
+
+ <a href="file_iframe_sandbox_top_navigation_pass.html" target='_top' id='anchor'>Click me</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if6.html b/dom/html/test/file_iframe_sandbox_e_if6.html
new file mode 100644
index 0000000000..399c3c202b
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if6.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+function doTest() {
+ document.getElementById('anchor').href = "file_iframe_sandbox_top_navigation_fail.html" + location.search;
+ window.top.opener.postMessage({type: "attempted"}, "*");
+ sendMouseEvent({type:'click'}, 'anchor');
+}
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts'
+
+ <a target='_top' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if7.html b/dom/html/test/file_iframe_sandbox_e_if7.html
new file mode 100644
index 0000000000..9d60ed2dbc
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if7.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ // Set our name, to allow an attempt to navigate us by name.
+ window.name = "e_if7";
+</script>
+
+<body>
+ <iframe sandbox='allow-scripts' id='if_8' src="file_iframe_sandbox_e_if8.html" height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if8.html b/dom/html/test/file_iframe_sandbox_e_if8.html
new file mode 100644
index 0000000000..97699abba9
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if8.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script>
+ function doTest() {
+ // Try to navigate top using its name (e_if7). We should not be able to do this as allow-top-navigation is not specified.
+ window.top.opener.postMessage({type: "attempted"}, "*");
+ sendMouseEvent({type:'click'}, 'navigate_top');
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed but with "allow-scripts"
+
+ <a href="file_iframe_sandbox_top_navigation_fail.html?Test 15: Navigate top by name:%20" target="e_if7" id="navigate_top">navigate top</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_e_if9.html b/dom/html/test/file_iframe_sandbox_e_if9.html
new file mode 100644
index 0000000000..f18a16dba6
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_e_if9.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doTest() {
+ var testContext = location.search == "" ? "?Test 22: Navigate _top with window.open():%20" : location.search;
+ document.getElementById("if_11").src = "file_iframe_sandbox_e_if11.html" + testContext;
+ }
+</script>
+
+<body onload="doTest()">
+ <iframe sandbox='allow-scripts allow-top-navigation' id='if_11' height="10" width="10"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_fail.js b/dom/html/test/file_iframe_sandbox_fail.js
new file mode 100644
index 0000000000..1f1290d046
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_fail.js
@@ -0,0 +1,4 @@
+ok(
+ false,
+ "documents sandboxed with allow-scripts should NOT be able to run <script src=...>"
+);
diff --git a/dom/html/test/file_iframe_sandbox_form_fail.html b/dom/html/test/file_iframe_sandbox_form_fail.html
new file mode 100644
index 0000000000..6976ced8ad
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_form_fail.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onLoad="doStuff()">
+ I should NOT be loaded by a form submit from a sandbox without 'allow-forms'
+</body>
+</html>
+
+<script>
+ function doStuff() {
+ window.parent.postMessage({ok: false, desc: "documents sandboxed without allow-forms should NOT be able to submit forms"}, "*");
+ }
+</script> \ No newline at end of file
diff --git a/dom/html/test/file_iframe_sandbox_form_pass.html b/dom/html/test/file_iframe_sandbox_form_pass.html
new file mode 100644
index 0000000000..1ba8853fa5
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_form_pass.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+</head>
+
+<body onLoad="doStuff()">
+ I should be loaded by a form submit from a sandbox with 'allow-forms'
+</body>
+</html>
+
+<script>
+ function doStuff() {
+ window.parent.postMessage({ok: true, desc: "documents sandboxed with allow-forms should be able to submit forms"}, "*");
+ }
+</script> \ No newline at end of file
diff --git a/dom/html/test/file_iframe_sandbox_g_if1.html b/dom/html/test/file_iframe_sandbox_g_if1.html
new file mode 100644
index 0000000000..67604f1f64
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_g_if1.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.postMessage({ok: result, desc}, "*");
+ }
+
+ function doStuff() {
+ // test data URI
+
+ // self.onmessage = function(event) {
+ // self.postMessage('make it so');
+ // };
+ var data_url = "data:text/plain;charset=utf-8;base64,c2VsZi5vbm1lc3NhZ2UgPSBmdW5jdGlvbihldmVudCkgeyAgDQogICAgc2VsZi5wb3N0TWVzc2FnZSgnbWFrZSBpdCBzbycpOyAgDQp9Ow==";
+ var worker_data = new Worker(data_url);
+ worker_data.addEventListener('message', function(event) {
+ ok(true, "a worker in a sandboxed document should be able to be loaded from a data: URI");
+ });
+
+ worker_data.postMessage("engage!");
+
+ // test a blob URI we created (will have the same null principal
+ // as us
+ var b = new Blob(["onmessage = function(event) { self.postMessage('make it so');};"]);
+
+ var blobURL = URL.createObjectURL(b);
+
+ var worker_blob = new Worker(blobURL);
+
+ worker_blob.addEventListener('message', function(event) {
+ ok(true, "a worker in a sandboxed document should be able to be loaded from a blob URI " +
+ "created by that sandboxed document");
+ });
+
+ worker_blob.postMessage("engage!");
+
+ // test loading with relative url - this should fail since we are
+ // sandboxed and have a null principal
+ var worker_js = new Worker('file_iframe_sandbox_worker.js');
+ worker_js.onerror = function(error) {
+ ok(true, "a worker in a sandboxed document should tell the load error via error event");
+ }
+
+ worker_js.addEventListener('message', function(event) {
+ ok(false, "a worker in a sandboxed document should not be able to load from a relative URI");
+ });
+
+ worker_js.postMessage('engage');
+ }
+</script>
+<body onload='doStuff();'>
+ I am sandboxed but with "allow-scripts"
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_h_if1.html b/dom/html/test/file_iframe_sandbox_h_if1.html
new file mode 100644
index 0000000000..7c5cada2dc
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_h_if1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 766282</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+</head>
+<script type="text/javascript">
+ function ok(result, desc) {
+ window.parent.ok_wrapper(result, desc);
+ }
+
+ function doStuff() {
+ // Try to open a new window via target="_blank", target="BC766282" and window.open().
+ // The window we try to open closes itself once it opens.
+ sendMouseEvent({type:'click'}, 'target_blank');
+ sendMouseEvent({type:'click'}, 'target_BC766282');
+
+ try {
+ window.open("file_iframe_sandbox_open_window_pass.html");
+ } catch(e) {
+ ok(false, "Test 3: iframes sandboxed with allow-popups, should be able to open windows");
+ }
+ }
+</script>
+<body onLoad="doStuff()">
+ I am sandboxed but with "allow-popups allow-scripts allow-same-origin"
+
+ <a href="file_iframe_sandbox_open_window_pass.html" target="_blank" rel="opener" id="target_blank">open window</a>
+ <a href="file_iframe_sandbox_open_window_pass.html?BC766282" target="BC766282" id="target_BC766282">open window</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if1.html b/dom/html/test/file_iframe_sandbox_k_if1.html
new file mode 100644
index 0000000000..f6f1238085
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if1.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="text/javascript">
+ var windowsToClose = new Array();
+
+ function closeWindows() {
+ for (var i = 0; i < windowsToClose.length; i++) {
+ windowsToClose[i].close();
+ }
+ window.open("file_iframe_sandbox_close.html", "blank_if2");
+ window.open("file_iframe_sandbox_close.html", "BC766282_if2");
+ }
+
+ // Add message listener to forward messages on to parent
+ window.addEventListener("message", receiveMessage);
+
+ function receiveMessage(event) {
+ switch (event.data.type) {
+ case "closeWindows":
+ closeWindows();
+ break;
+ }
+ }
+
+ function doStuff() {
+ // Open a new window via target="_blank", target="BC766282_if2" and window.open().
+ sendMouseEvent({type:'click'}, 'target_blank_if2');
+ sendMouseEvent({type:'click'}, 'target_BC766282_if2');
+
+ windowsToClose.push(window.open("file_iframe_sandbox_k_if2.html"));
+ }
+</script>
+<body onLoad="doStuff()">
+ I am navigated to from file_iframe_sandbox_k_if8.html.
+ This was opened in an iframe with "allow-scripts allow-popups allow-same-origin".
+ However allow-same-origin was removed from the iframe before navigating to me,
+ so I should only have "allow-scripts allow-popups" in force.
+ <a href="file_iframe_sandbox_k_if2.html" target="_blank" id="target_blank_if2" rel="opener">open window</a>
+ <a href="file_iframe_sandbox_k_if2.html" target="BC766282_if2" id="target_BC766282_if2">open window</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if2.html b/dom/html/test/file_iframe_sandbox_k_if2.html
new file mode 100644
index 0000000000..dce42aef54
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if2.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+ if (window.name == "") {
+ window.name = "blank_if2";
+ }
+
+ function ok(result, message) {
+ window.opener.parent.postMessage({type: "ok", ok: result, desc: message}, "*");
+ }
+
+ function doStuff() {
+ // Check that sandboxed forms browsing context flag copied by attempting to submit a form.
+ document.getElementById('a_form').submit();
+ window.opener.parent.postMessage({type: "attempted"}, "*");
+
+ // Check that sandboxed origin browsing context flag copied by attempting to access cookies.
+ try {
+ var foo = document.cookie;
+ ok(false, "Sandboxed origin browsing context flag NOT copied to new auxiliary browsing context.");
+ } catch(error) {
+ ok(true, "Sandboxed origin browsing context flag copied to new auxiliary browsing context.");
+ }
+
+ // Check that sandboxed top-level navigation browsing context flag copied.
+ // if_3 tries to navigate this document.
+ var if_3 = document.getElementById('if_3');
+ if_3.src = "file_iframe_sandbox_k_if3.html";
+ }
+</script>
+
+<body onLoad="doStuff()">
+ I am not sandboxed directly, but opened from a sandboxed document with 'allow-scripts allow-popups'
+
+ <form method="get" action="file_iframe_sandbox_window_form_fail.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" id="a_button">
+ </form>
+
+ <iframe id="if_3" src="about:blank" height="10" width="10"></iframe>
+
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if3.html b/dom/html/test/file_iframe_sandbox_k_if3.html
new file mode 100644
index 0000000000..a2619dd006
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if3.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<script type="application/javascript">
+ function doTest() {
+ sendMouseEvent({type:'click'}, 'anchor');
+ window.parent.opener.parent.postMessage({type: "attempted"}, "*");
+ }
+</script>
+<body onload="doTest()">
+ I am sandboxed with 'allow-scripts allow-popups'
+
+ <a href="file_iframe_sandbox_window_top_navigation_fail.html" target='_top' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if4.html b/dom/html/test/file_iframe_sandbox_k_if4.html
new file mode 100644
index 0000000000..3d030158dc
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+ function doStuff() {
+ // Open a new window via target="_blank", target="BC766282_if5" and window.open().
+ sendMouseEvent({type:'click'}, 'target_blank_if5');
+ sendMouseEvent({type:'click'}, 'target_BC766282_if5');
+
+ window.open("file_iframe_sandbox_k_if5.html");
+
+ // Open a new window via target="_blank", target="BC766282_if7" and window.open().
+ sendMouseEvent({type:'click'}, 'target_blank_if7');
+ sendMouseEvent({type:'click'}, 'target_BC766282_if7');
+
+ window.open("file_iframe_sandbox_k_if7.html");
+ }
+</script>
+
+<body onLoad="doStuff()">
+ I am sandboxed with "allow-scripts allow-popups allow-same-origin allow-forms allow-top-navigation".
+ <a href="file_iframe_sandbox_k_if5.html" target="_blank" id="target_blank_if5" rel="opener">open window</a>
+ <a href="file_iframe_sandbox_k_if5.html" target="BC766282_if5" id="target_BC766282_if5">open window</a>
+
+ <a href="file_iframe_sandbox_k_if7.html" target="_blank" id="target_blank_if7" rel="opener">open window</a>
+ <a href="file_iframe_sandbox_k_if7.html" target="BC766282_if7" id="target_BC766282_if7">open window</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if5.html b/dom/html/test/file_iframe_sandbox_k_if5.html
new file mode 100644
index 0000000000..8deb65852f
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if5.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+ function doStuff() {
+ // Check that sandboxed origin browsing context flag NOT set by attempting to access cookies.
+ try {
+ var foo = document.cookie;
+ window.opener.parent.ok_wrapper(true, "Sandboxed origin browsing context flag NOT set on new auxiliary browsing context.");
+ } catch(error) {
+ window.opener.parent.ok_wrapper(false, "Sandboxed origin browsing context flag set on new auxiliary browsing context.");
+ }
+
+ // Check that sandboxed top-level navigation browsing context flag NOT set.
+ // if_6 tries to navigate this document.
+ var if_6 = document.getElementById('if_6');
+ if_6.src = "file_iframe_sandbox_k_if6.html";
+ }
+</script>
+
+<body onLoad="doStuff()">
+ I am not sandboxed directly, but opened from a sandboxed document with at least
+ 'allow-scripts allow-popups allow-same-origin allow-top-navigation'
+
+ <iframe id="if_6" src="about:blank" height="10" width="10"></iframe>
+
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if6.html b/dom/html/test/file_iframe_sandbox_k_if6.html
new file mode 100644
index 0000000000..53ed080e3e
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if6.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script type="application/javascript">
+ function doTest() {
+ sendMouseEvent({type:'click'}, 'anchor');
+ }
+</script>
+
+<body onload="doTest()">
+ I am sandboxed with at least 'allow-scripts allow-popups allow-top-navigation'
+
+ <a href="file_iframe_sandbox_window_top_navigation_pass.html" target='_top' id='anchor'>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if7.html b/dom/html/test/file_iframe_sandbox_k_if7.html
new file mode 100644
index 0000000000..269e31eb5b
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if7.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+ function doStuff() {
+ // Check that sandboxed forms browsing context flag NOT set by attempting to submit a form.
+ document.getElementById('a_form').submit();
+ }
+</script>
+
+<body onLoad="doStuff()">
+ I am not sandboxed directly, but opened from a sandboxed document with at least
+ 'allow-scripts allow-popups allow-forms allow-same-origin'
+
+ <form method="get" action="file_iframe_sandbox_window_form_pass.html" id="a_form">
+ First name: <input type="text" name="firstname">
+ Last name: <input type="text" name="lastname">
+ <input type="submit" id="a_button">
+ </form>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if8.html b/dom/html/test/file_iframe_sandbox_k_if8.html
new file mode 100644
index 0000000000..e4aad97f3b
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if8.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="text/javascript">
+ function doSubOpens() {
+ // Open a new window via target="_blank", target="BC766282_if9" and window.open().
+ sendMouseEvent({type:'click'}, 'target_blank_if9');
+ sendMouseEvent({type:'click'}, 'target_BC766282_if9');
+
+ window.open("file_iframe_sandbox_k_if9.html");
+
+ sendMouseEvent({type:'click'}, 'target_if1');
+ }
+
+ window.doSubOpens = doSubOpens;
+</script>
+
+<body>
+ I am sandboxed but with "allow-scripts allow-popups allow-same-origin".
+ After my initial load, "allow-same-origin" is removed and then I open file_iframe_sandbox_k_if9.html
+ in 3 different ways, which attemps to call a function in my parent.
+ This should succeed since the new sandbox flags shouldn't have taken affect on me until I'm reloaded.
+ <a href="file_iframe_sandbox_k_if9.html" target="_blank" id="target_blank_if9" rel="opener">open window</a>
+ <a href="file_iframe_sandbox_k_if9.html" target="BC766282_if9" id="target_BC766282_if9">open window</a>
+
+ Now navigate to file_iframe_sandbox_k_if1.html to do tests for a sandbox opening a window
+ when only "allow-scripts allow-popups" are specified.
+ <a href="file_iframe_sandbox_k_if1.html" id="target_if1">navigate to if1</a>
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_k_if9.html b/dom/html/test/file_iframe_sandbox_k_if9.html
new file mode 100644
index 0000000000..56e8db3f9a
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_k_if9.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doStuff() {
+ window.opener.parent.ok_wrapper(true, "A window opened from within a sandboxed document should inherit the flags of the document, not of the docshell/sandbox attribute.");
+ self.close();
+ }
+</script>
+
+<body onload='doStuff()'>
+ I'm a window opened from the sandboxed document of file_iframe_sandbox_k_if8.html.
+ I should be able to call ok_wrapper in main test page directly because I should be same-origin with it.
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_navigation_fail.html b/dom/html/test/file_iframe_sandbox_navigation_fail.html
new file mode 100644
index 0000000000..bae5276bd1
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_navigation_fail.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onLoad="doStuff()">
+FAIL
+</body>
+<script>
+ function doStuff() {
+ var testContext = unescape(location.search.substring(1));
+ window.parent.postMessage({ok: false, desc: testContext + "this navigation should NOT be allowed by a sandboxed document", addToAttempted: false}, "*");
+ }
+</script>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_navigation_pass.html b/dom/html/test/file_iframe_sandbox_navigation_pass.html
new file mode 100644
index 0000000000..e07248247b
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_navigation_pass.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+function doStuff() {
+ var testContext = unescape(location.search.substring(1));
+ window.parent.postMessage({ok: true, desc: testContext + "this navigation should be allowed by a sandboxed document"}, "*");
+}
+</script>
+<body onLoad="doStuff()">
+PASS
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_navigation_start.html b/dom/html/test/file_iframe_sandbox_navigation_start.html
new file mode 100644
index 0000000000..fa56425177
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_navigation_start.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+I am just a normal HTML document, probably contained in a sandboxed iframe
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_open_window_fail.html b/dom/html/test/file_iframe_sandbox_open_window_fail.html
new file mode 100644
index 0000000000..64e0d36180
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_open_window_fail.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onLoad="doStuff()">
+ I should NOT be opened by a sandboxed iframe via any method
+</body>
+</html>
+
+<script>
+ function doStuff() {
+ window.opener.ok(false, "sandboxed documents should NOT be able to open windows");
+ self.close();
+ }
+</script>
diff --git a/dom/html/test/file_iframe_sandbox_open_window_pass.html b/dom/html/test/file_iframe_sandbox_open_window_pass.html
new file mode 100644
index 0000000000..ac45c7fd32
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_open_window_pass.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onLoad="doStuff()">
+ I should be opened by a sandboxed iframe via any method when "allow-popups" is specified.
+</body>
+</html>
+
+<script>
+ function doStuff() {
+ // Check that the browsing context's (window's) name is as expected.
+ var expectedName = location.search.substring(1);
+ if (expectedName == window.name) {
+ window.opener.ok(true, "sandboxed documents should be able to open windows when \"allow-popups\" is specified");
+ } else {
+ window.opener.ok(false, "window opened with \"allow-popups\", but expected name was " + expectedName + " and actual was " + window.name);
+ }
+ self.close();
+ }
+</script>
diff --git a/dom/html/test/file_iframe_sandbox_pass.js b/dom/html/test/file_iframe_sandbox_pass.js
new file mode 100644
index 0000000000..15b3e7d3ff
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_pass.js
@@ -0,0 +1,4 @@
+ok(
+ true,
+ "documents sandboxed with allow-scripts should be able to run <script src=...>"
+);
diff --git a/dom/html/test/file_iframe_sandbox_redirect.html b/dom/html/test/file_iframe_sandbox_redirect.html
new file mode 100644
index 0000000000..62419d7f46
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_redirect.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<body>redirect</body>
diff --git a/dom/html/test/file_iframe_sandbox_redirect.html^headers^ b/dom/html/test/file_iframe_sandbox_redirect.html^headers^
new file mode 100644
index 0000000000..71b739c42a
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_redirect.html^headers^
@@ -0,0 +1,2 @@
+HTTP 301 Moved Permanently
+Location: file_iframe_sandbox_redirect_target.html
diff --git a/dom/html/test/file_iframe_sandbox_redirect_target.html b/dom/html/test/file_iframe_sandbox_redirect_target.html
new file mode 100644
index 0000000000..c134ac0ffd
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_redirect_target.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<head>
+ <script>
+ onmessage = function(event) {
+ parent.postMessage(event.data + " redirect target", "*");
+ }
+ </script>
+</head>
+<body>I have been redirected</body>
diff --git a/dom/html/test/file_iframe_sandbox_refresh.html b/dom/html/test/file_iframe_sandbox_refresh.html
new file mode 100644
index 0000000000..1fad80c428
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_refresh.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<body>refresh</body>
diff --git a/dom/html/test/file_iframe_sandbox_refresh.html^headers^ b/dom/html/test/file_iframe_sandbox_refresh.html^headers^
new file mode 100644
index 0000000000..a7cc383b4f
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_refresh.html^headers^
@@ -0,0 +1 @@
+Refresh: 0 url=data:text/html,Refreshed
diff --git a/dom/html/test/file_iframe_sandbox_srcdoc_allow_scripts.html b/dom/html/test/file_iframe_sandbox_srcdoc_allow_scripts.html
new file mode 100644
index 0000000000..7d585be04f
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_srcdoc_allow_scripts.html
@@ -0,0 +1 @@
+<script>parent.parent.ok_wrapper(true, "an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe")</script>
diff --git a/dom/html/test/file_iframe_sandbox_srcdoc_no_allow_scripts.html b/dom/html/test/file_iframe_sandbox_srcdoc_no_allow_scripts.html
new file mode 100644
index 0000000000..b6faf83cc9
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_srcdoc_no_allow_scripts.html
@@ -0,0 +1 @@
+<script>parent.parent.ok_wrapper(false, 'an object inside an iframe sandboxed with only allow-same-origin should not be able to run scripts')</script>
diff --git a/dom/html/test/file_iframe_sandbox_top_navigation_fail.html b/dom/html/test/file_iframe_sandbox_top_navigation_fail.html
new file mode 100644
index 0000000000..dad6b2c006
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_top_navigation_fail.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+function doStuff() {
+ var testContext = unescape(location.search.substring(1));
+ window.opener.postMessage({ok: false, desc: testContext + "top navigation should NOT be allowed by a document sandboxed without 'allow-top-navigation'", addToAttempted: false}, "*");
+ window.close();
+}
+</script>
+<body onLoad="doStuff()">
+FAIL\
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_top_navigation_pass.html b/dom/html/test/file_iframe_sandbox_top_navigation_pass.html
new file mode 100644
index 0000000000..712240ecb2
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_top_navigation_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+function doStuff() {
+ var testContext = unescape(location.search.substring(1));
+ var bc = new BroadcastChannel("test_iframe_sandbox_navigation");
+ bc.postMessage({ok: true, desc: testContext + "top navigation should be allowed by a document sandboxed with 'allow-top-navigation'"});
+ bc.close();
+ window.close();
+}
+</script>
+<body onLoad="doStuff()">
+PASS
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_window_form_fail.html b/dom/html/test/file_iframe_sandbox_window_form_fail.html
new file mode 100644
index 0000000000..2d678b3ac9
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_window_form_fail.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body onLoad="doStuff()">
+ I should NOT be loaded by a form submit from a window opened from a sandbox without 'allow-forms'.
+</body>
+</html>
+
+<script>
+ function doStuff() {
+ window.opener.parent.postMessage({ok: false, desc: "documents sandboxed without allow-forms should NOT be able to submit forms"}, "*");
+
+ self.close();
+ }
+</script>
diff --git a/dom/html/test/file_iframe_sandbox_window_form_pass.html b/dom/html/test/file_iframe_sandbox_window_form_pass.html
new file mode 100644
index 0000000000..dd2656c1ec
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_window_form_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doStuff() {
+ window.opener.parent.ok_wrapper(true, "Sandboxed forms browsing context flag NOT set on new auxiliary browsing context.");
+
+ self.close();
+ }
+</script>
+
+<body onLoad="doStuff()">
+ I should be loaded by a form submit from a window opened from a sandbox with 'allow-forms allow-same-origin'.
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_window_navigation_fail.html b/dom/html/test/file_iframe_sandbox_window_navigation_fail.html
new file mode 100644
index 0000000000..f8e3c83ce8
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_window_navigation_fail.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 838692</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+function doStuff() {
+ var testContext = unescape(location.search.substring(1));
+ window.opener.postMessage({ok: false, desc: testContext + "a sandboxed document should not be able to navigate a window it hasn't opened.", addToAttempted: false}, "*");
+ window.close();
+}
+</script>
+
+<body onLoad="doStuff()">
+FAIL
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_window_navigation_pass.html b/dom/html/test/file_iframe_sandbox_window_navigation_pass.html
new file mode 100644
index 0000000000..a1bff9eb83
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_window_navigation_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+function doStuff() {
+ var testContext = unescape(location.search.substring(1));
+ window.opener.postMessage({type: "ok", ok: true, desc: testContext + "a permitted sandboxed document should be able to navigate a window it has opened.", addToAttempted: false}, "*");
+ window.close();
+}
+</script>
+
+<body onLoad="doStuff()">
+PASS
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_window_top_navigation_fail.html b/dom/html/test/file_iframe_sandbox_window_top_navigation_fail.html
new file mode 100644
index 0000000000..af50476045
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_window_top_navigation_fail.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script>
+ function doStuff() {
+ window.opener.parent.postMessage({ok: false, desc: "Sandboxed top-level navigation browsing context flag NOT copied to new auxiliary browsing context."}, "*");
+
+ // Check that when no browsing context returned by "target='_top'", a new browsing context isn't opened by mistake.
+ try {
+ window.opener.parent.opener.parent.postMessage({ok: false, desc: "An attempt at top navigation without 'allow-top-navigation' should not have opened a new browsing context."}, "*");
+ } catch (error) {
+ }
+
+ self.close();
+ }
+</script>
+<body onLoad="doStuff()">
+FAIL
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_window_top_navigation_pass.html b/dom/html/test/file_iframe_sandbox_window_top_navigation_pass.html
new file mode 100644
index 0000000000..d3637fb04e
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_window_top_navigation_pass.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 766282</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+ function doStuff() {
+ window.opener.parent.ok_wrapper(true, "Sandboxed top-level navigation browsing context flag NOT copied to new auxiliary browsing context.");
+
+ self.close();
+ }
+</script>
+
+<body onLoad="doStuff()">
+ I am navigated to from a window opened from a sandbox with allow-top-navigation.
+</body>
+</html>
diff --git a/dom/html/test/file_iframe_sandbox_worker.js b/dom/html/test/file_iframe_sandbox_worker.js
new file mode 100644
index 0000000000..3cb9f650dc
--- /dev/null
+++ b/dom/html/test/file_iframe_sandbox_worker.js
@@ -0,0 +1,3 @@
+self.onmessage = function (event) {
+ self.postMessage("make it so");
+};
diff --git a/dom/html/test/file_refresh_after_document_write.html b/dom/html/test/file_refresh_after_document_write.html
new file mode 100644
index 0000000000..ebf3272e08
--- /dev/null
+++ b/dom/html/test/file_refresh_after_document_write.html
@@ -0,0 +1,15 @@
+<html>
+<head>
+<title></title>
+</head>
+<script>
+function write_and_refresh(){
+ document.write("This could be anything");
+ location.reload();
+}
+</script>
+<body>
+<button id='test_btn' onclick='write_and_refresh()'>
+</body>
+
+</html>
diff --git a/dom/html/test/file_script_module.html b/dom/html/test/file_script_module.html
new file mode 100644
index 0000000000..78c4992654
--- /dev/null
+++ b/dom/html/test/file_script_module.html
@@ -0,0 +1,42 @@
+<html>
+<body>
+ <script>
+// Helper methods.
+function ok(a, msg) {
+ parent.postMessage({ check: !!a, msg }, "*")
+}
+
+function is(a, b, msg) {
+ ok(a === b, msg);
+}
+
+function finish() {
+ parent.postMessage({ done: true }, "*");
+}
+ </script>
+
+ <script id="a" nomodule>42</script>
+ <script id="b">42</script>
+ <script>
+// Let's test the behavior of nomodule attribute and noModule getter/setter.
+var a = document.getElementById("a");
+is(a.noModule, true, "HTMLScriptElement with nomodule attribute has noModule set to true");
+a.removeAttribute("nomodule");
+is(a.noModule, false, "HTMLScriptElement without nomodule attribute has noModule set to false");
+a.noModule = true;
+ok(a.hasAttribute('nomodule'), "HTMLScriptElement.noModule = true add the nomodule attribute");
+
+var b = document.getElementById("b");
+is(b.noModule, false, "HTMLScriptElement without nomodule attribute has noModule set to false");
+b.noModule = true;
+ok(b.hasAttribute('nomodule'), "HTMLScriptElement.noModule = true add the nomodule attribute");
+ </script>
+
+ <script>var foo = 42;</script>
+ <script nomodule>foo = 43;</script>
+ <script>
+is(foo, 42, "nomodule HTMLScriptElements should not be executed in modern browsers");
+finish();
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/file_srcdoc-2.html b/dom/html/test/file_srcdoc-2.html
new file mode 100644
index 0000000000..bd75f5e059
--- /dev/null
+++ b/dom/html/test/file_srcdoc-2.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=802895
+-->
+<body>
+<iframe id="iframe" srcdoc="Hello World"></iframe>
+</body>
+
+</html>
diff --git a/dom/html/test/file_srcdoc.html b/dom/html/test/file_srcdoc.html
new file mode 100644
index 0000000000..7f084bc74b
--- /dev/null
+++ b/dom/html/test/file_srcdoc.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=802895
+-->
+<body>
+<iframe id="iframe" srcdoc="Hello World"></iframe>
+
+<iframe id="iframe1" src="about:mozilla"
+ srcdoc="Goodbye World"></iframe>
+<iframe id="iframe2" srcdoc="Peeking test" sandbox=""></iframe>
+<iframe id="iframe3" src="file_srcdoc_iframe3.html"
+ srcdoc="Going"></iframe>
+</body>
+
+</html>
diff --git a/dom/html/test/file_srcdoc_iframe3.html b/dom/html/test/file_srcdoc_iframe3.html
new file mode 100644
index 0000000000..233692734f
--- /dev/null
+++ b/dom/html/test/file_srcdoc_iframe3.html
@@ -0,0 +1 @@
+Gone
diff --git a/dom/html/test/file_window_close_and_open.html b/dom/html/test/file_window_close_and_open.html
new file mode 100644
index 0000000000..ad96e50aac
--- /dev/null
+++ b/dom/html/test/file_window_close_and_open.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<script>
+ console.log("loading file_window_close_and_open.html");
+ addEventListener("load", function() {
+ console.log("got load event!");
+ let link = document.querySelector("a");
+ if (window.location.hash === "#noopener") {
+ link.setAttribute("rel", "noopener");
+ } else if (window.location.hash === "#opener") {
+ link.setAttribute("rel", "opener");
+ }
+ link.click();
+ });
+</script>
+<body>
+ <h1>close and re-open popup</h1>
+ <a href="file_broadcast_load.html" target="_blank" onclick="window.close()">close and open</a>
+</body>
+</html>
diff --git a/dom/html/test/file_window_open_close_inner.html b/dom/html/test/file_window_open_close_inner.html
new file mode 100644
index 0000000000..dbc7e3aba8
--- /dev/null
+++ b/dom/html/test/file_window_open_close_inner.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<script>
+window.close();
+</script>
+</html>
+</body>
diff --git a/dom/html/test/file_window_open_close_outer.html b/dom/html/test/file_window_open_close_outer.html
new file mode 100644
index 0000000000..682b399e75
--- /dev/null
+++ b/dom/html/test/file_window_open_close_outer.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<a id="link" href="file_window_open_close_inner.html" target="_blank" rel="opener" onclick="setTimeout(function () { window.close() }, 0)">link</a>
+</html>
+</body>
diff --git a/dom/html/test/formData_test.js b/dom/html/test/formData_test.js
new file mode 100644
index 0000000000..3997aff4d1
--- /dev/null
+++ b/dom/html/test/formData_test.js
@@ -0,0 +1,289 @@
+function testHas() {
+ var f = new FormData();
+ f.append("foo", "bar");
+ f.append("another", "value");
+ ok(f.has("foo"), "has() on existing name should be true.");
+ ok(f.has("another"), "has() on existing name should be true.");
+ ok(!f.has("nonexistent"), "has() on non-existent name should be false.");
+}
+
+function testGet() {
+ var f = new FormData();
+ f.append("foo", "bar");
+ f.append("foo", "bar2");
+ f.append("blob", new Blob(["hey"], { type: "text/plain" }));
+ f.append("file", new File(["hey"], "testname", { type: "text/plain" }));
+
+ is(f.get("foo"), "bar", "get() on existing name should return first value");
+ ok(
+ f.get("blob") instanceof Blob,
+ "get() on existing name should return first value"
+ );
+ is(
+ f.get("blob").type,
+ "text/plain",
+ "get() on existing name should return first value"
+ );
+ ok(
+ f.get("file") instanceof File,
+ "get() on existing name should return first value"
+ );
+ is(
+ f.get("file").name,
+ "testname",
+ "get() on existing name should return first value"
+ );
+
+ is(
+ f.get("nonexistent"),
+ null,
+ "get() on non-existent name should return null."
+ );
+}
+
+function testGetAll() {
+ var f = new FormData();
+ f.append("other", "value");
+ f.append("foo", "bar");
+ f.append("foo", "bar2");
+ f.append("foo", new Blob(["hey"], { type: "text/plain" }));
+
+ var arr = f.getAll("foo");
+ is(arr.length, 3, "getAll() should retrieve all matching entries.");
+ is(arr[0], "bar", "values should match and be in order");
+ is(arr[1], "bar2", "values should match and be in order");
+ ok(arr[2] instanceof Blob, "values should match and be in order");
+
+ is(
+ f.get("nonexistent"),
+ null,
+ "get() on non-existent name should return null."
+ );
+}
+
+function testDelete() {
+ var f = new FormData();
+ f.append("other", "value");
+ f.append("foo", "bar");
+ f.append("foo", "bar2");
+ f.append("foo", new Blob(["hey"], { type: "text/plain" }));
+
+ ok(f.has("foo"), "has() on existing name should be true.");
+ f.delete("foo");
+ ok(!f.has("foo"), "has() on deleted name should be false.");
+ is(f.getAll("foo").length, 0, "all entries should be deleted.");
+
+ is(f.getAll("other").length, 1, "other names should still be there.");
+ f.delete("other");
+ is(f.getAll("other").length, 0, "all entries should be deleted.");
+}
+
+function testSet() {
+ var f = new FormData();
+
+ f.set("other", "value");
+ ok(f.has("other"), "set() on new name should be similar to append()");
+ is(
+ f.getAll("other").length,
+ 1,
+ "set() on new name should be similar to append()"
+ );
+
+ f.append("other", "value2");
+ is(
+ f.getAll("other").length,
+ 2,
+ "append() should not replace existing entries."
+ );
+
+ f.append("foo", "bar");
+ f.append("other", "value3");
+ f.append("other", "value3");
+ f.append("other", "value3");
+ is(
+ f.getAll("other").length,
+ 5,
+ "append() should not replace existing entries."
+ );
+
+ f.set("other", "value4");
+ is(f.getAll("other").length, 1, "set() should replace existing entries.");
+ is(f.getAll("other")[0], "value4", "set() should replace existing entries.");
+}
+
+function testFilename() {
+ var f = new FormData();
+ f.append("blob", new Blob(["hi"]));
+ ok(f.get("blob") instanceof Blob, "We should have a blob back.");
+
+ // If a filename is passed, that should replace the original.
+ f.append("blob2", new Blob(["hi"]), "blob2.txt");
+ is(
+ f.get("blob2").name,
+ "blob2.txt",
+ 'Explicit filename should override "blob".'
+ );
+
+ var file = new File(["hi"], "file1.txt");
+ f.append("file1", file);
+ // If a file is passed, the "create entry" algorithm should not create a new File, but reuse the existing one.
+ is(
+ f.get("file1"),
+ file,
+ "Retrieved File object should be original File object and not a copy."
+ );
+ is(
+ f.get("file1").name,
+ "file1.txt",
+ "File's filename should be original's name if no filename is explicitly passed."
+ );
+
+ file = new File(["hi"], "file2.txt");
+ f.append("file2", file, "fakename.txt");
+ ok(
+ f.get("file2") !== file,
+ "Retrieved File object should be new File object if explicit filename is passed."
+ );
+ is(
+ f.get("file2").name,
+ "fakename.txt",
+ "File's filename should be explicitly passed name."
+ );
+ f.append("file3", new File(["hi"], ""));
+ is(f.get("file3").name, "", "File's filename is returned even if empty.");
+}
+
+function testIterable() {
+ var fd = new FormData();
+ fd.set("1", "2");
+ fd.set("2", "4");
+ fd.set("3", "6");
+ fd.set("4", "8");
+ fd.set("5", "10");
+
+ var key_iter = fd.keys();
+ var value_iter = fd.values();
+ var entries_iter = fd.entries();
+ for (var i = 0; i < 5; ++i) {
+ var v = i + 1;
+ var key = key_iter.next();
+ var value = value_iter.next();
+ var entry = entries_iter.next();
+ is(key.value, v.toString(), "Correct Key iterator: " + v.toString());
+ ok(!key.done, "key.done is false");
+ is(
+ value.value,
+ (v * 2).toString(),
+ "Correct Value iterator: " + (v * 2).toString()
+ );
+ ok(!value.done, "value.done is false");
+ is(
+ entry.value[0],
+ v.toString(),
+ "Correct Entry 0 iterator: " + v.toString()
+ );
+ is(
+ entry.value[1],
+ (v * 2).toString(),
+ "Correct Entry 1 iterator: " + (v * 2).toString()
+ );
+ ok(!entry.done, "entry.done is false");
+ }
+
+ var last = key_iter.next();
+ ok(last.done, "Nothing more to read.");
+ is(last.value, undefined, "Undefined is the last key");
+
+ last = value_iter.next();
+ ok(last.done, "Nothing more to read.");
+ is(last.value, undefined, "Undefined is the last value");
+
+ last = entries_iter.next();
+ ok(last.done, "Nothing more to read.");
+
+ key_iter = fd.keys();
+ key_iter.next();
+ key_iter.next();
+ fd.delete("1");
+ fd.delete("2");
+ fd.delete("3");
+ fd.delete("4");
+ fd.delete("5");
+
+ last = key_iter.next();
+ ok(last.done, "Nothing more to read.");
+ is(last.value, undefined, "Undefined is the last key");
+}
+
+function testSend(doneCb) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", "form_submit_server.sjs");
+ xhr.onload = function () {
+ var response = xhr.response;
+
+ for (var entry of response) {
+ is(entry.body, "hey");
+ is(entry.headers["Content-Type"], "text/plain");
+ }
+
+ is(
+ response[0].headers["Content-Disposition"],
+ 'form-data; name="empty"; filename="blob"'
+ );
+
+ is(
+ response[1].headers["Content-Disposition"],
+ 'form-data; name="explicit"; filename="explicit-file-name"'
+ );
+
+ is(
+ response[2].headers["Content-Disposition"],
+ 'form-data; name="explicit-empty"; filename=""'
+ );
+
+ is(
+ response[3].headers["Content-Disposition"],
+ 'form-data; name="file-name"; filename="testname"'
+ );
+
+ is(
+ response[4].headers["Content-Disposition"],
+ 'form-data; name="empty-file-name"; filename=""'
+ );
+
+ is(
+ response[5].headers["Content-Disposition"],
+ 'form-data; name="file-name-overwrite"; filename="overwrite"'
+ );
+
+ doneCb();
+ };
+
+ var file,
+ blob = new Blob(["hey"], { type: "text/plain" });
+
+ var fd = new FormData();
+ fd.append("empty", blob);
+ fd.append("explicit", blob, "explicit-file-name");
+ fd.append("explicit-empty", blob, "");
+ file = new File([blob], "testname", { type: "text/plain" });
+ fd.append("file-name", file);
+ file = new File([blob], "", { type: "text/plain" });
+ fd.append("empty-file-name", file);
+ file = new File([blob], "testname", { type: "text/plain" });
+ fd.append("file-name-overwrite", file, "overwrite");
+ xhr.responseType = "json";
+ xhr.send(fd);
+}
+
+function runTest(doneCb) {
+ testHas();
+ testGet();
+ testGetAll();
+ testDelete();
+ testSet();
+ testFilename();
+ testIterable();
+ // Finally, send an XHR and verify the response matches.
+ testSend(doneCb);
+}
diff --git a/dom/html/test/formData_worker.js b/dom/html/test/formData_worker.js
new file mode 100644
index 0000000000..750522fbfa
--- /dev/null
+++ b/dom/html/test/formData_worker.js
@@ -0,0 +1,23 @@
+function ok(a, msg) {
+ postMessage({ type: "status", status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+ postMessage({
+ type: "status",
+ status: a === b,
+ msg: a + " === " + b + ": " + msg,
+ });
+}
+
+function todo(a, msg) {
+ postMessage({ type: "todo", status: !!a, msg: a + ": " + msg });
+}
+
+importScripts("formData_test.js");
+
+onmessage = function () {
+ runTest(function () {
+ postMessage({ type: "finish" });
+ });
+};
diff --git a/dom/html/test/formSubmission_chrome.js b/dom/html/test/formSubmission_chrome.js
new file mode 100644
index 0000000000..da1224d107
--- /dev/null
+++ b/dom/html/test/formSubmission_chrome.js
@@ -0,0 +1,20 @@
+/* eslint-env mozilla/chrome-script */
+
+// eslint-disable-next-line mozilla/reject-importGlobalProperties
+Cu.importGlobalProperties(["File"]);
+
+addMessageListener("files.open", function (message) {
+ let list = [];
+ let promises = [];
+ for (let path of message) {
+ promises.push(
+ File.createFromFileName(path).then(file => {
+ list.push(file);
+ })
+ );
+ }
+
+ Promise.all(promises).then(() => {
+ sendAsyncMessage("files.opened", list);
+ });
+});
diff --git a/dom/html/test/form_data_file.bin b/dom/html/test/form_data_file.bin
new file mode 100644
index 0000000000..744bde3558
--- /dev/null
+++ b/dom/html/test/form_data_file.bin
@@ -0,0 +1 @@
+
diff --git a/dom/html/test/form_data_file.txt b/dom/html/test/form_data_file.txt
new file mode 100644
index 0000000000..81c545efeb
--- /dev/null
+++ b/dom/html/test/form_data_file.txt
@@ -0,0 +1 @@
+1234
diff --git a/dom/html/test/form_submit_server.sjs b/dom/html/test/form_submit_server.sjs
new file mode 100644
index 0000000000..553809c01f
--- /dev/null
+++ b/dom/html/test/form_submit_server.sjs
@@ -0,0 +1,86 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+
+function utf8decode(s) {
+ return decodeURIComponent(escape(s));
+}
+
+function utf8encode(s) {
+ return unescape(encodeURIComponent(s));
+}
+
+function handleRequest(request, response) {
+ var bodyStream = new BinaryInputStream(request.bodyInputStream);
+ var result = [];
+ var requestBody = "";
+ while ((bodyAvail = bodyStream.available()) > 0) {
+ requestBody += bodyStream.readBytes(bodyAvail);
+ }
+
+ if (request.method == "POST") {
+ var contentTypeParams = {};
+ request
+ .getHeader("Content-Type")
+ .split(/\s*\;\s*/)
+ .forEach(function (s) {
+ if (s.indexOf("=") >= 0) {
+ let [name, value] = s.split("=");
+ contentTypeParams[name] = value;
+ } else {
+ contentTypeParams[""] = s;
+ }
+ });
+
+ if (
+ contentTypeParams[""] == "multipart/form-data" &&
+ request.queryString == ""
+ ) {
+ requestBody
+ .split("--" + contentTypeParams.boundary)
+ .slice(1, -1)
+ .forEach(function (s) {
+ let headers = {};
+ let headerEnd = s.indexOf("\r\n\r\n");
+ s.substr(2, headerEnd - 2)
+ .split("\r\n")
+ .forEach(function (str) {
+ // We're assuming UTF8 for now
+ let [name, value] = str.split(": ");
+ headers[name] = utf8decode(value);
+ });
+
+ let body = s.substring(headerEnd + 4, s.length - 2);
+ if (
+ !headers["Content-Type"] ||
+ headers["Content-Type"] == "text/plain"
+ ) {
+ // We're assuming UTF8 for now
+ body = utf8decode(body);
+ }
+ result.push({ headers, body });
+ });
+ }
+ if (
+ contentTypeParams[""] == "text/plain" &&
+ request.queryString == "plain"
+ ) {
+ result = utf8decode(requestBody);
+ }
+ if (
+ contentTypeParams[""] == "application/x-www-form-urlencoded" &&
+ request.queryString == "url"
+ ) {
+ result = requestBody;
+ }
+ } else if (request.method == "GET") {
+ result = request.queryString;
+ }
+
+ // Send response body
+ response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
+ response.write(utf8encode(JSON.stringify(result)));
+}
diff --git a/dom/html/test/forms/FAIL.html b/dom/html/test/forms/FAIL.html
new file mode 100644
index 0000000000..94e1707e85
--- /dev/null
+++ b/dom/html/test/forms/FAIL.html
@@ -0,0 +1 @@
+FAIL
diff --git a/dom/html/test/forms/PASS.html b/dom/html/test/forms/PASS.html
new file mode 100644
index 0000000000..7ef22e9a43
--- /dev/null
+++ b/dom/html/test/forms/PASS.html
@@ -0,0 +1 @@
+PASS
diff --git a/dom/html/test/forms/chrome.toml b/dom/html/test/forms/chrome.toml
new file mode 100644
index 0000000000..0f49518b9b
--- /dev/null
+++ b/dom/html/test/forms/chrome.toml
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files = ["submit_invalid_file.sjs"]
+
+["test_autocompleteinfo.html"]
+
+["test_submit_invalid_file.html"]
diff --git a/dom/html/test/forms/file_double_submit.html b/dom/html/test/forms/file_double_submit.html
new file mode 100644
index 0000000000..44889f86bc
--- /dev/null
+++ b/dom/html/test/forms/file_double_submit.html
@@ -0,0 +1,11 @@
+<form action="PASS.html" method="POST"><input name="foo"></form>
+<button>clicky</button>
+
+<script>
+document.querySelector("button")
+ .addEventListener("click", () => {
+ let f = document.querySelector("form");
+ f.dispatchEvent(new Event("submit"));
+ f.submit();
+ });
+</script>
diff --git a/dom/html/test/forms/file_login_fields.html b/dom/html/test/forms/file_login_fields.html
new file mode 100644
index 0000000000..f23ee0ad6a
--- /dev/null
+++ b/dom/html/test/forms/file_login_fields.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ // Add an unload listener to bypass bfcache.
+ window.addEventListner("unload", _ => _);
+ </script>
+ </head>
+ <body>
+ <input id="un" />
+ <input id="pw1" type="password" />
+ <input id="pw2" />
+ <a id="navigate" href="?navigated">Navigate</a>
+ <a id="back" href="javascript:history.back()">Back</a>
+ </body>
+</html>
diff --git a/dom/html/test/forms/mochitest.toml b/dom/html/test/forms/mochitest.toml
new file mode 100644
index 0000000000..80d6d3530f
--- /dev/null
+++ b/dom/html/test/forms/mochitest.toml
@@ -0,0 +1,229 @@
+[DEFAULT]
+support-files = [
+ "save_restore_radio_groups.sjs",
+ "test_input_number_data.js",
+ "!/dom/html/test/reflect.js",
+ "FAIL.html",
+ "PASS.html",
+]
+prefs = ["formhelper.autozoom.force-disable.test-only=true"]
+
+["test_MozEditableElement_setUserInput.html"]
+
+["test_autocomplete.html"]
+
+["test_bug1039548.html"]
+
+["test_bug1283915.html"]
+
+["test_bug1286509.html"]
+
+["test_button_attributes_reflection.html"]
+
+["test_change_event.html"]
+
+["test_datalist_element.html"]
+
+["test_double_submit.html"]
+support-files = ["file_double_submit.html"]
+
+["test_form_attribute-1.html"]
+
+["test_form_attribute-2.html"]
+
+["test_form_attribute-3.html"]
+
+["test_form_attribute-4.html"]
+
+["test_form_attributes_reflection.html"]
+
+["test_form_named_getter_dynamic.html"]
+
+["test_formaction_attribute.html"]
+
+["test_formnovalidate_attribute.html"]
+
+["test_input_attributes_reflection.html"]
+
+["test_input_color_input_change_events.html"]
+
+["test_input_color_picker_datalist.html"]
+
+["test_input_color_picker_initial.html"]
+
+["test_input_color_picker_popup.html"]
+
+["test_input_color_picker_update.html"]
+
+["test_input_date_bad_input.html"]
+
+["test_input_date_key_events.html"]
+
+["test_input_datetime_calendar_button.html"]
+
+["test_input_datetime_disabled_focus.html"]
+
+["test_input_datetime_focus_blur.html"]
+
+["test_input_datetime_focus_blur_events.html"]
+
+["test_input_datetime_focus_state.html"]
+
+["test_input_datetime_hidden.html"]
+
+["test_input_datetime_input_change_events.html"]
+
+["test_input_datetime_readonly.html"]
+
+["test_input_datetime_reset_default_value_input_change_event.html"]
+
+["test_input_datetime_tabindex.html"]
+
+["test_input_defaultValue.html"]
+
+["test_input_email.html"]
+
+["test_input_event.html"]
+
+["test_input_file_picker.html"]
+
+["test_input_hasBeenTypePassword.html"]
+
+["test_input_hasBeenTypePassword_navigation.html"]
+support-files = ["file_login_fields.html"]
+
+["test_input_list_attribute.html"]
+
+["test_input_number_focus.html"]
+
+["test_input_number_key_events.html"]
+
+["test_input_number_l10n.html"]
+
+["test_input_number_mouse_events.html"]
+# Not run on Firefox for Android where the spin buttons are hidden:
+skip-if = [
+ "os == 'android'",
+ "os == 'mac' && debug", # Bug 1484442
+]
+
+["test_input_number_placeholder_shown.html"]
+
+["test_input_number_rounding.html"]
+
+["test_input_number_validation.html"]
+
+["test_input_password_click_show_password_button.html"]
+
+["test_input_password_show_password_button.html"]
+
+["test_input_radio_indeterminate.html"]
+
+["test_input_radio_radiogroup.html"]
+
+["test_input_radio_required.html"]
+
+["test_input_range_attr_order.html"]
+
+["test_input_range_key_events.html"]
+
+["test_input_range_mouse_and_touch_events.html"]
+
+["test_input_range_rounding.html"]
+
+["test_input_sanitization.html"]
+
+["test_input_setting_value.html"]
+
+["test_input_textarea_set_value_no_scroll.html"]
+
+["test_input_time_key_events.html"]
+
+["test_input_time_sec_millisec_field.html"]
+
+["test_input_types_pref.html"]
+
+["test_input_typing_sanitization.html"]
+
+["test_input_untrusted_key_events.html"]
+
+["test_input_url.html"]
+
+["test_interactive_content_in_label.html"]
+
+["test_interactive_content_in_summary.html"]
+
+["test_label_control_attribute.html"]
+
+["test_label_input_controls.html"]
+
+["test_max_attribute.html"]
+
+["test_maxlength_attribute.html"]
+
+["test_meter_element.html"]
+
+["test_meter_pseudo-classes.html"]
+
+["test_min_attribute.html"]
+
+["test_minlength_attribute.html"]
+
+["test_mozistextfield.html"]
+
+["test_novalidate_attribute.html"]
+
+["test_option_disabled.html"]
+
+["test_option_index_attribute.html"]
+
+["test_option_text.html"]
+
+["test_output_element.html"]
+
+["test_pattern_attribute.html"]
+
+["test_preserving_metadata_between_reloads.html"]
+
+["test_progress_element.html"]
+
+["test_radio_in_label.html"]
+
+["test_radio_radionodelist.html"]
+
+["test_reportValidation_preventDefault.html"]
+
+["test_required_attribute.html"]
+
+["test_restore_form_elements.html"]
+
+["test_save_restore_custom_elements.html"]
+support-files = ["save_restore_custom_elements_sample.html"]
+
+["test_save_restore_radio_groups.html"]
+
+["test_select_change_event.html"]
+skip-if = ["os == 'mac'"]
+
+["test_select_input_change_event.html"]
+skip-if = ["os == 'mac'"]
+
+["test_select_selectedOptions.html"]
+
+["test_select_validation.html"]
+
+["test_set_range_text.html"]
+
+["test_step_attribute.html"]
+
+["test_stepup_stepdown.html"]
+
+["test_textarea_attributes_reflection.html"]
+
+["test_validation.html"]
+
+["test_validation_not_in_doc.html"]
+
+["test_valueasdate_attribute.html"]
+
+["test_valueasnumber_attribute.html"]
diff --git a/dom/html/test/forms/save_restore_custom_elements_sample.html b/dom/html/test/forms/save_restore_custom_elements_sample.html
new file mode 100644
index 0000000000..75dc4c388d
--- /dev/null
+++ b/dom/html/test/forms/save_restore_custom_elements_sample.html
@@ -0,0 +1,43 @@
+<script>
+ class CEBase extends HTMLElement {
+ static formAssociated = true;
+ constructor() {
+ super();
+ this.internals = this.attachInternals();
+ this.state_ = undefined;
+ }
+ formStateRestoreCallback(state, reason) {
+ if (reason == "restore") {
+ this.state_ = state;
+ }
+ }
+ set(state, value) {
+ this.state_ = state;
+ this.value_ = value;
+ this.internals.setFormValue(value, state);
+ }
+ get state() {
+ return this.state_;
+ }
+ get value() {
+ return this.value_;
+ }
+ }
+
+ customElements.define("c-e", class extends CEBase {});
+</script>
+<form>
+ <c-e id="custom0"></c-e>
+ <c-e id="custom1"></c-e>
+ <c-e id="custom2"></c-e>
+ <c-e id="custom3"></c-e>
+ <c-e id="custom4"></c-e>
+ <upgraded-ce id="upgraded0"></upgraded-ce>
+ <upgraded-ce id="upgraded1"></upgraded-ce>
+ <upgraded-ce id="upgraded2"></upgraded-ce>
+ <upgraded-ce id="upgraded3"></upgraded-ce>
+ <upgraded-ce id="upgraded4"></upgraded-ce>
+</form>
+<script>
+ customElements.define("upgraded-ce", class extends CEBase {});
+</script>
diff --git a/dom/html/test/forms/save_restore_radio_groups.sjs b/dom/html/test/forms/save_restore_radio_groups.sjs
new file mode 100644
index 0000000000..b4c9c4401a
--- /dev/null
+++ b/dom/html/test/forms/save_restore_radio_groups.sjs
@@ -0,0 +1,48 @@
+var pages = [
+ "<!DOCTYPE html>" +
+ "<html><body>" +
+ "<form>" +
+ "<input name='a' type='radio' checked><input name='a' type='radio'><input name='a' type='radio'>" +
+ "</form>" +
+ "</body></html>",
+ "<!DOCTYPE html>" +
+ "<html><body>" +
+ "<form>" +
+ "<input name='a' type='radio'><input name='a' type='radio' checked><input name='a' type='radio'>" +
+ "</form>" +
+ "</body></html>",
+];
+
+/**
+ * This SJS is going to send the same page the two first times it will be called
+ * and another page the two following times. After that, the response will have
+ * no content.
+ * The use case is to have two iframes using this SJS and both being reloaded
+ * once.
+ */
+
+function handleRequest(request, response) {
+ var counter = +getState("counter"); // convert to number; +"" === 0
+
+ response.setStatusLine(request.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html");
+ response.setHeader("Cache-Control", "no-cache");
+
+ switch (counter) {
+ case 0:
+ case 1:
+ response.write(pages[0]);
+ break;
+ case 2:
+ case 3:
+ response.write(pages[1]);
+ break;
+ }
+
+ // When we finish the test case we need to reset the counter
+ if (counter == 3) {
+ setState("counter", "0");
+ } else {
+ setState("counter", "" + ++counter);
+ }
+}
diff --git a/dom/html/test/forms/submit_invalid_file.sjs b/dom/html/test/forms/submit_invalid_file.sjs
new file mode 100644
index 0000000000..3b4b576ec6
--- /dev/null
+++ b/dom/html/test/forms/submit_invalid_file.sjs
@@ -0,0 +1,13 @@
+function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html");
+ response.setHeader("Cache-Control", "no-cache");
+
+ var result = {};
+ request.bodyInputStream.search("testfile", true, result, {});
+ if (result.value) {
+ response.write("SUCCESS");
+ } else {
+ response.write("FAIL");
+ }
+}
diff --git a/dom/html/test/forms/test_MozEditableElement_setUserInput.html b/dom/html/test/forms/test_MozEditableElement_setUserInput.html
new file mode 100644
index 0000000000..06380776f6
--- /dev/null
+++ b/dom/html/test/forms/test_MozEditableElement_setUserInput.html
@@ -0,0 +1,581 @@
+<!DOCTYPE>
+<html>
+<head>
+ <title>Test for MozEditableElement.setUserInput()</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<div id="display">
+</div>
+<div id="content"></div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+// eslint-disable-next-line complexity
+SimpleTest.waitForFocus(async () => {
+ const kSetUserInputCancelable = SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input");
+
+ let content = document.getElementById("content");
+ /**
+ * Test structure:
+ * element: the tag name to create.
+ * type: the type attribute value for the element. If unnecessary omit it.
+ * input: the values calling setUserInput() with.
+ * before: used when calling setUserInput() before the element gets focus.
+ * after: used when calling setUserInput() after the element gets focus.
+ * result: the results of calling setUserInput().
+ * before: the element's expected value of calling setUserInput() before the element gets focus.
+ * after: the element's expected value of calling setUserInput() after the element gets focus.
+ * fireBeforeInputEvent: true if "beforeinput" event should be fired. Otherwise, false.
+ * fireInputEvent: true if "input" event should be fired. Otherwise, false.
+ */
+ for (let test of [{element: "input", type: "hidden",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
+ {element: "input", type: "text",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ {element: "input", type: "search",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ {element: "input", type: "tel",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ {element: "input", type: "url",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ {element: "input", type: "email",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ {element: "input", type: "password",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ // "date" does not support setUserInput, but dispatches "input" event...
+ {element: "input", type: "date",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ // "month" does not support setUserInput, but dispatches "input" event...
+ {element: "input", type: "month",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ // "week" does not support setUserInput, but dispatches "input" event...
+ {element: "input", type: "week",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ // "time" does not support setUserInput, but dispatches "input" event...
+ {element: "input", type: "time",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ // "datetime-local" does not support setUserInput, but dispatches "input" event...
+ {element: "input", type: "datetime-local",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ {element: "input", type: "number",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}},
+ {element: "input", type: "range",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ // "color" does not support setUserInput, but dispatches "input" event...
+ {element: "input", type: "color",
+ input: {before: "#5C5C5C", after: "#FFFFFF"},
+ result: {before: "#5c5c5c", after:"#ffffff", fireBeforeInputEvent: false, fireInputEvent: true}},
+ {element: "input", type: "checkbox",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ {element: "input", type: "radio",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: true}},
+ // "file" is not supported by setUserInput? But there is a path...
+ {element: "input", type: "file",
+ input: {before: "3", after: "6"},
+ result: {before: "", after:"", fireBeforeInputEvent: false, fireInputEvent: true}},
+ {element: "input", type: "submit",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
+ {element: "input", type: "image",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
+ {element: "input", type: "reset",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
+ {element: "input", type: "button",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: false, fireInputEvent: false}},
+ {element: "textarea",
+ input: {before: "3", after: "6"},
+ result: {before: "3", after:"6", fireBeforeInputEvent: true, fireInputEvent: true}}]) {
+ let tag =
+ test.type !== undefined ? `<${test.element} type="${test.type}">` :
+ `<${test.element}>`;
+ content.innerHTML =
+ test.element !== "input" ? tag : `${tag}</${test.element}>`;
+ content.scrollTop; // Flush pending layout.
+ let target = content.firstChild;
+
+ let inputEvents = [], beforeInputEvents = [];
+ function onBeforeInput(aEvent) {
+ beforeInputEvents.push(aEvent);
+ }
+ function onInput(aEvent) {
+ inputEvents.push(aEvent);
+ }
+ target.addEventListener("beforeinput", onBeforeInput);
+ target.addEventListener("input", onInput);
+
+ // Before setting focus, editor of the element may have not been created yet.
+ let previousValue = target.value;
+ SpecialPowers.wrap(target).setUserInput(test.input.before);
+ if (target.value == previousValue && test.result.before != previousValue) {
+ todo_is(target.value, test.result.before, `setUserInput("${test.input.before}") before ${tag} gets focus should set its value to "${test.result.before}"`);
+ } else {
+ is(target.value, test.result.before, `setUserInput("${test.input.before}") before ${tag} gets focus should set its value to "${test.result.before}"`);
+ }
+ if (target.value == previousValue) {
+ if (test.type === "date" || test.type === "time" || test.type === "datetime-local") {
+ todo_is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ } else {
+ is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ } else {
+ if (!test.result.fireBeforeInputEvent) {
+ is(beforeInputEvents.length, 0,
+ `No "beforeinput" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ } else {
+ is(beforeInputEvents.length, 1,
+ `Only one "beforeinput" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ if (!test.result.fireInputEvent) {
+ // HTML spec defines that "input" elements whose type are "hidden",
+ // "submit", "image", "reset" and "button" shouldn't fire input event
+ // when its value is changed.
+ // XXX Perhaps, we shouldn't support setUserInput() with such types.
+ if (test.type === "hidden" ||
+ test.type === "submit" ||
+ test.type === "image" ||
+ test.type === "reset" ||
+ test.type === "button") {
+ todo_is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ } else {
+ is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ } else {
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ }
+ if (inputEvents.length) {
+ if (SpecialPowers.wrap(target).isInputEventTarget) {
+ if (test.type === "time") {
+ todo(inputEvents[0] instanceof InputEvent,
+ `"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ } else {
+ if (beforeInputEvents.length && test.result.fireBeforeInputEvent) {
+ is(beforeInputEvents[0].cancelable, kSetUserInputCancelable,
+ `"beforeinput" event for "insertReplacementText" should be cancelable when setUserInput("${test.input.before}") is called before ${tag} gets focus unless it's suppressed by the pref`);
+ is(beforeInputEvents[0].inputType, "insertReplacementText",
+ `inputType of "beforeinput"event should be "insertReplacementText" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(beforeInputEvents[0].data, test.input.before,
+ `data of "beforeinput" event should be "${test.input.before}" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(beforeInputEvents[0].dataTransfer, null,
+ `dataTransfer of "beforeinput" event should be null when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(beforeInputEvents[0].getTargetRanges().length, 0,
+ `getTargetRanges() of "beforeinput" event should return empty array when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ ok(inputEvents[0] instanceof InputEvent,
+ `"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(inputEvents[0].inputType, "insertReplacementText",
+ `inputType of "input" event should be "insertReplacementText" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(inputEvents[0].data, test.input.before,
+ `data of "input" event should be "${test.input.before}" when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(inputEvents[0].dataTransfer, null,
+ `dataTransfer of "input" event should be null when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ is(inputEvents[0].getTargetRanges().length, 0,
+ `getTargetRanges() of "input" event should return empty array when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ } else {
+ ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent),
+ `"input" event should be dispatched with Event interface when setUserInput("${test.input.before}") is called before ${tag} gets focus`);
+ }
+ is(inputEvents[0].cancelable, false,
+ `"input" event should be never cancelable (${tag}, before getting focus)`);
+ is(inputEvents[0].bubbles, true,
+ `"input" event should always bubble (${tag}, before getting focus)`);
+ }
+
+ beforeInputEvents = [];
+ inputEvents = [];
+ target.focus();
+ previousValue = target.value;
+ SpecialPowers.wrap(target).setUserInput(test.input.after);
+ if (target.value == previousValue && test.result.after != previousValue) {
+ todo_is(target.value, test.result.after, `setUserInput("${test.input.after}") after ${tag} gets focus should set its value to "${test.result.after}"`);
+ } else {
+ is(target.value, test.result.after, `setUserInput("${test.input.after}") after ${tag} gets focus should set its value to "${test.result.after}"`);
+ }
+ if (target.value == previousValue) {
+ if (test.type === "date" || test.type === "time" || test.type === "datetime-local") {
+ todo_is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ } else {
+ is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ } else {
+ if (!test.result.fireBeforeInputEvent) {
+ is(beforeInputEvents.length, 0,
+ `No "beforeinput" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ } else {
+ is(beforeInputEvents.length, 1,
+ `Only one "beforeinput" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ if (!test.result.fireInputEvent) {
+ // HTML spec defines that "input" elements whose type are "hidden",
+ // "submit", "image", "reset" and "button" shouldn't fire input event
+ // when its value is changed.
+ // XXX Perhaps, we shouldn't support setUserInput() with such types.
+ if (test.type === "hidden" ||
+ test.type === "submit" ||
+ test.type === "image" ||
+ test.type === "reset" ||
+ test.type === "button") {
+ todo_is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ } else {
+ is(inputEvents.length, 0,
+ `No "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ } else {
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ }
+ if (inputEvents.length) {
+ if (SpecialPowers.wrap(target).isInputEventTarget) {
+ if (test.type === "time") {
+ todo(inputEvents[0] instanceof InputEvent,
+ `"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ } else {
+ if (beforeInputEvents.length && test.result.fireBeforeInputEvent) {
+ is(beforeInputEvents[0].cancelable, kSetUserInputCancelable,
+ `"beforeinput" event should be cancelable when setUserInput("${test.input.after}") is called after ${tag} gets focus unless it's suppressed by the pref`);
+ is(beforeInputEvents[0].inputType, "insertReplacementText",
+ `inputType of "beforeinput" event should be "insertReplacementText" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(beforeInputEvents[0].data, test.input.after,
+ `data of "beforeinput" should be "${test.input.after}" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(beforeInputEvents[0].dataTransfer, null,
+ `dataTransfer of "beforeinput" should be null when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(beforeInputEvents[0].getTargetRanges().length, 0,
+ `getTargetRanges() of "beforeinput" should return empty array when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ ok(inputEvents[0] instanceof InputEvent,
+ `"input" event should be dispatched with InputEvent interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(inputEvents[0].inputType, "insertReplacementText",
+ `inputType of "input" event should be "insertReplacementText" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(inputEvents[0].data, test.input.after,
+ `data of "input" event should be "${test.input.after}" when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(inputEvents[0].dataTransfer, null,
+ `dataTransfer of "input" event should be null when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ is(inputEvents[0].getTargetRanges().length, 0,
+ `getTargetRanges() of "input" event should return empty array when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ } else {
+ ok(inputEvents[0] instanceof Event && !(inputEvents[0] instanceof UIEvent),
+ `"input" event should be dispatched with Event interface when setUserInput("${test.input.after}") is called after ${tag} gets focus`);
+ }
+ is(inputEvents[0].cancelable, false,
+ `"input" event should be never cancelable (${tag}, after getting focus)`);
+ is(inputEvents[0].bubbles, true,
+ `"input" event should always bubble (${tag}, after getting focus)`);
+ }
+
+ target.removeEventListener("input", onInput);
+ }
+
+ function testValidationMessage(aType, aInvalidValue, aValidValue) {
+ let tag = `<input type="${aType}">`
+ content.innerHTML = tag;
+ content.scrollTop; // Flush pending layout.
+ let target = content.firstChild;
+
+ let inputEvents = [];
+ let validationMessage = "";
+
+ function reset() {
+ inputEvents = [];
+ validationMessage = "";
+ }
+
+ function onInput(aEvent) {
+ inputEvents.push(aEvent);
+ validationMessage = aEvent.target.validationMessage;
+ }
+ target.addEventListener("input", onInput);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aInvalidValue);
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
+ isnot(validationMessage, "",
+ `${tag}.validationMessage should not be empty when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
+ ok(target.matches(":invalid"),
+ `The target should have invalid pseudo class when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aValidValue);
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched when setUserInput("${aValidValue}") is called before ${tag} gets focus`);
+ is(validationMessage, "",
+ `${tag}.validationMessage should be empty when setUserInput("${aValidValue}") is called before ${tag} gets focus`);
+ ok(!target.matches(":invalid"),
+ `The target shouldn't have invalid pseudo class when setUserInput("${aValidValue}") is called before ${tag} gets focus`);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aInvalidValue);
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched again when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
+ isnot(validationMessage, "",
+ `${tag}.validationMessage should not be empty again when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
+ ok(target.matches(":invalid"),
+ `The target should have invalid pseudo class again when setUserInput("${aInvalidValue}") is called before ${tag} gets focus`);
+
+ target.value = "";
+ target.focus();
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aInvalidValue);
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
+ isnot(validationMessage, "",
+ `${tag}.validationMessage should not be empty when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
+ ok(target.matches(":invalid"),
+ `The target should have invalid pseudo class when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aValidValue);
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched when setUserInput("${aValidValue}") is called after ${tag} gets focus`);
+ is(validationMessage, "",
+ `${tag}.validationMessage should be empty when setUserInput("${aValidValue}") is called after ${tag} gets focus`);
+ ok(!target.matches(":invalid"),
+ `The target shouldn't have invalid pseudo class when setUserInput("${aValidValue}") is called after ${tag} gets focus`);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aInvalidValue);
+ is(inputEvents.length, 1,
+ `Only one "input" event should be dispatched again when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
+ isnot(validationMessage, "",
+ `${tag}.validationMessage should not be empty again when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
+ ok(target.matches(":invalid"),
+ `The target should have invalid pseudo class again when setUserInput("${aInvalidValue}") is called after ${tag} gets focus`);
+
+ target.removeEventListener("input", onInput);
+ }
+ testValidationMessage("email", "f", "foo@example.com");
+
+ function testValueMissing(aType, aValidValue) {
+ let tag = aType === "textarea" ? "<textarea required>" : `<input type="${aType}" required>`;
+ content.innerHTML = `${tag}${aType === "textarea" ? "</textarea>" : ""}`;
+ content.scrollTop; // Flush pending layout.
+ let target = content.firstChild;
+
+ let inputEvents = [], beforeInputEvents = [];
+ function reset() {
+ beforeInputEvents = [];
+ inputEvents = [];
+ }
+
+ function onBeforeInput(aEvent) {
+ aEvent.validity = aEvent.target.checkValidity();
+ beforeInputEvents.push(aEvent);
+ }
+ function onInput(aEvent) {
+ aEvent.validity = aEvent.target.checkValidity();
+ inputEvents.push(aEvent);
+ }
+ target.addEventListener("beforeinput", onBeforeInput);
+ target.addEventListener("input", onInput);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aValidValue);
+ is(beforeInputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "beforeinput" event (before gets focus)`);
+ if (beforeInputEvents.length) {
+ is(beforeInputEvents[0].validity, false,
+ `The ${tag} should be invalid at "beforeinput" event (before gets focus)`);
+ }
+ is(inputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "input" event (before gets focus)`);
+ if (inputEvents.length) {
+ is(inputEvents[0].validity, true,
+ `The ${tag} should be valid at "input" event (before gets focus)`);
+ }
+
+ target.removeEventListener("beforeinput", onBeforeInput);
+ target.removeEventListener("input", onInput);
+
+ content.innerHTML = "";
+ content.scrollTop; // Flush pending layout.
+ content.innerHTML = `${tag}${aType === "textarea" ? "</textarea>" : ""}`;
+ content.scrollTop; // Flush pending layout.
+ target = content.firstChild;
+
+ target.focus();
+ target.addEventListener("beforeinput", onBeforeInput);
+ target.addEventListener("input", onInput);
+
+ reset();
+ SpecialPowers.wrap(target).setUserInput(aValidValue);
+ is(beforeInputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "beforeinput" event (after gets focus)`);
+ if (beforeInputEvents.length) {
+ is(beforeInputEvents[0].validity, false,
+ `The ${tag} should be invalid at "beforeinput" event (after gets focus)`);
+ }
+ is(inputEvents.length, 1, `Calling ${tag}.setUserInput(${aValidValue}) should cause a "input" event (after gets focus)`);
+ if (inputEvents.length) {
+ is(inputEvents[0].validity, true,
+ `The ${tag} should be valid at "input" event (after gets focus)`);
+ }
+
+ target.removeEventListener("beforeinput", onBeforeInput);
+ target.removeEventListener("input", onInput);
+ }
+ testValueMissing("text", "abc");
+ testValueMissing("password", "abc");
+ testValueMissing("textarea", "abc");
+ testValueMissing("email", "foo@example.com");
+ testValueMissing("url", "https://example.com/");
+
+ function testEditorValueAtEachEvent(aType) {
+ let tag = aType === "textarea" ? "<textarea>" : `<input type="${aType}">`
+ let closeTag = aType === "textarea" ? "</textarea>" : "";
+ content.innerHTML = `${tag}${closeTag}`;
+ content.scrollTop; // Flush pending layout.
+ let target = content.firstChild;
+ target.value = "Old Value";
+ let description = `Setting new value of ${tag} before setting focus: `;
+ let onBeforeInput = (aEvent) => {
+ is(target.value, "Old Value",
+ `${description}The value should not have been modified at "beforeinput" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
+ };
+ let onInput = (aEvent) => {
+ is(target.value, "New Value",
+ `${description}The value should have been modified at "input" event (inputType: "${aEvent.inputType}", data: "${aEvent.data}"`);
+ };
+ target.addEventListener("beforeinput", onBeforeInput);
+ target.addEventListener("input", onInput);
+ SpecialPowers.wrap(target).setUserInput("New Value");
+
+ description = `Setting new value of ${tag} after setting focus: `;
+ target.value = "Old Value";
+ target.focus();
+ SpecialPowers.wrap(target).setUserInput("New Value");
+
+ target.removeEventListener("beforeinput", onBeforeInput);
+ target.removeEventListener("input", onInput);
+
+ // FYI: This is not realistic situation because we should do nothing
+ // while user composing IME.
+ // TODO: TextControlState should stop returning setting value as the value
+ // while committing composition.
+ description = `Setting new value of ${tag} during composition: `;
+ target.value = "";
+ target.focus();
+ synthesizeCompositionChange({
+ composition: {
+ string: "composition string",
+ clauses: [{length: 18, attr: COMPOSITION_ATTR_RAW_CLAUSE}],
+ },
+ caret: {start: 18, length: 0},
+ });
+ let onCompositionUpdate = (aEvent) => {
+ todo_is(target.value, "composition string",
+ `${description}The value should not have been modified at "compositionupdate" event yet (data: "${aEvent.data}")`);
+ };
+ let onCompositionEnd = (aEvent) => {
+ todo_is(target.value, "composition string",
+ `${description}The value should not have been modified at "compositionupdate" event yet (data: "${aEvent.data}")`);
+ };
+ onBeforeInput = (aEvent) => {
+ if (aEvent.inputType === "insertCompositionText") {
+ todo_is(target.value, "composition string",
+ `${description}The value should not have been modified at "beforeinput" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
+ } else {
+ is(target.value, "composition string",
+ `${description}The value should not have been modified at "beforeinput" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
+ }
+ };
+ onInput = (aEvent) => {
+ if (aEvent.inputType === "insertCompositionText") {
+ todo_is(target.value, "composition string",
+ `${description}The value should not have been modified at "input" event yet (inputType: "${aEvent.inputType}", data: "${aEvent.data}")`);
+ } else {
+ is(target.value, "New Value",
+ `${description}The value should have been modified at "input" event (inputType: "${aEvent.inputType}", data: "${aEvent.data}"`);
+ }
+ };
+ target.addEventListener("compositionupdate", onCompositionUpdate);
+ target.addEventListener("compositionend", onCompositionEnd);
+ target.addEventListener("beforeinput", onBeforeInput);
+ target.addEventListener("input", onInput);
+ SpecialPowers.wrap(target).setUserInput("New Value");
+ target.removeEventListener("compositionupdate", onCompositionUpdate);
+ target.removeEventListener("compositionend", onCompositionEnd);
+ target.removeEventListener("beforeinput", onBeforeInput);
+ target.removeEventListener("input", onInput);
+ }
+ testEditorValueAtEachEvent("text");
+ testEditorValueAtEachEvent("textarea");
+
+ async function testBeforeInputCancelable(aType) {
+ let tag = aType === "textarea" ? "<textarea>" : `<input type="${aType}">`
+ let closeTag = aType === "textarea" ? "</textarea>" : "";
+ for (const kShouldBeCancelable of [true, false]) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.input_event.allow_to_cancel_set_user_input", kShouldBeCancelable]],
+ });
+
+ content.innerHTML = `${tag}${closeTag}`;
+ content.scrollTop; // Flush pending layout.
+ let target = content.firstChild;
+ target.value = "Old Value";
+ let description = `Setting new value of ${tag} before setting focus (the pref ${kShouldBeCancelable ? "allows" : "disallows"} to cancel beforeinput): `;
+ let onBeforeInput = (aEvent) => {
+ is(aEvent.cancelable, kShouldBeCancelable,
+ `${description}The "beforeinput" event should be ${kShouldBeCancelable ? "cancelable" : "not be cancelable due to suppressed by the pref"}`);
+ };
+ let onInput = (aEvent) => {
+ is(aEvent.cancelable, false,
+ `${description}The value should have been modified at "input" event (inputType: "${aEvent.inputType}", data: "${aEvent.data}"`);
+ };
+ target.addEventListener("beforeinput", onBeforeInput);
+ target.addEventListener("input", onInput);
+ SpecialPowers.wrap(target).setUserInput("New Value");
+
+ description = `Setting new value of ${tag} after setting focus (the pref ${kShouldBeCancelable ? "allows" : "disallows"} to cancel beforeinput): `;
+ target.value = "Old Value";
+ target.focus();
+ SpecialPowers.wrap(target).setUserInput("New Value");
+
+ target.removeEventListener("beforeinput", onBeforeInput);
+ target.removeEventListener("input", onInput);
+ }
+
+ await SpecialPowers.clearUserPref({
+ clear: [["dom.input_event.allow_to_cancel_set_user_input"]],
+ });
+ }
+ await testBeforeInputCancelable("text");
+ await testBeforeInputCancelable("textarea");
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_autocomplete.html b/dom/html/test/forms/test_autocomplete.html
new file mode 100644
index 0000000000..c98be94eea
--- /dev/null
+++ b/dom/html/test/forms/test_autocomplete.html
@@ -0,0 +1,164 @@
+<!DOCTYPE html>
+<html>
+<!--
+Test @autocomplete on <input>/<select>/<textarea>
+-->
+<head>
+ <title>Test for @autocomplete</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<script>
+"use strict";
+
+var values = [
+ // @autocomplete content attribute, expected IDL attribute value
+
+ // Missing or empty attribute
+ [undefined, ""],
+ ["", ""],
+
+ // One token
+ ["on", "on"],
+ ["On", "on"],
+ ["off", "off"],
+ ["OFF", "off"],
+ ["name", "name"],
+ [" name ", "name"],
+ ["username", "username"],
+ [" username ", "username"],
+ ["cc-csc", ""],
+ ["one-time-code", ""],
+ ["language", ""],
+ [" language ", ""],
+ ["tel-extension", ""],
+ ["foobar", ""],
+ ["section-blue", ""],
+ [" WEBAUTHN ", "webauthn"],
+
+ // One token + WebAuthn credential type
+ ["on webauthn", ""],
+ ["off webauthn", ""],
+ ["webauthn webauthn", ""],
+ ["username WebAuthn", "username webauthn"],
+ ["current-PASSWORD webauthn", "current-password webauthn"],
+
+ // Two tokens
+ ["on off", ""],
+ ["off on", ""],
+ ["username tel", ""],
+ ["tel username ", ""],
+ [" username tel ", ""],
+ ["tel mobile", ""],
+ ["tel shipping", ""],
+ ["shipping tel", "shipping tel"],
+ ["shipPING tel", "shipping tel"],
+ ["mobile tel", "mobile tel"],
+ [" MoBiLe TeL ", "mobile tel"],
+ ["pager impp", ""],
+ ["fax tel-extension", ""],
+ ["XXX tel", ""],
+ ["XXX username", ""],
+ ["name section-blue", ""],
+ ["scetion-blue cc-name", ""],
+ ["pager language", ""],
+ ["fax url", ""],
+ ["section-blue name", "section-blue name"],
+ ["section-blue tel", "section-blue tel"],
+ ["webauthn username", ""],
+
+ // Two tokens + WebAuthn credential type
+ ["fax url webauthn", ""],
+ ["shipping tel webauthn", "shipping tel webauthn"],
+
+ // Three tokens
+ ["billing invalid tel", ""],
+ ["___ mobile tel", ""],
+ ["mobile foo tel", ""],
+ ["mobile tel foo", ""],
+ ["tel mobile billing", ""],
+ ["billing mobile tel", "billing mobile tel"],
+ [" BILLing MoBiLE tEl ", "billing mobile tel"],
+ ["billing home tel", "billing home tel"],
+ ["home section-blue tel", ""],
+ ["setion-blue work email", ""],
+ ["section-blue home address-level2", ""],
+ ["section-blue shipping name", "section-blue shipping name"],
+ ["section-blue mobile tel", "section-blue mobile tel"],
+ ["shipping webauthn tel", ""],
+
+ // Three tokens + WebAuthn credential type
+ ["invalid mobile tel webauthn", ""],
+ ["section-blue shipping name webauthn", "section-blue shipping name webauthn"],
+
+ // Four tokens
+ ["billing billing mobile tel", ""],
+ ["name section-blue shipping home", ""],
+ ["secti shipping work address-line1", ""],
+ ["section-blue shipping home name", ""],
+ ["section-blue shipping mobile tel", "section-blue shipping mobile tel"],
+ ["section-blue webauthn mobile tel", ""],
+
+ // Four tokens + WebAuthn credential type
+ ["section-blue shipping home name webauthn", ""],
+ ["section-blue shipping mobile tel webauthn", "section-blue shipping mobile tel webauthn"],
+
+ // Five tokens (invalid)
+ ["billing billing billing mobile tel", ""],
+ ["section-blue section-blue billing mobile tel", ""],
+ ["section-blue section-blue billing webauthn tel", ""],
+
+ // Five tokens + WebAuthn credential type (invalid)
+ ["billing billing billing mobile tel webauthn", ""],
+];
+
+var types = [undefined, "hidden", "text", "search"]; // Valid types for all non-multiline hints.
+
+function checkAutocompleteValues(field, type) {
+ for (var test of values) {
+ if (typeof(test[0]) === "undefined")
+ field.removeAttribute("autocomplete");
+ else
+ field.setAttribute("autocomplete", test[0]);
+ is(field.autocomplete, test[1], "Checking @autocomplete for @type=" + type + " of: " + test[0]);
+ is(field.autocomplete, test[1], "Checking cached @autocomplete for @type=" + type + " of: " + test[0]);
+ }
+}
+
+function start() {
+ var inputField = document.getElementById("input-field");
+ for (var type of types) {
+ // Switch the input type
+ if (typeof(type) === "undefined")
+ inputField.removeAttribute("type");
+ else
+ inputField.type = type;
+ checkAutocompleteValues(inputField, type || "");
+ }
+
+ var selectField = document.getElementById("select-field");
+ checkAutocompleteValues(selectField, "select");
+
+ var textarea = document.getElementById("textarea");
+ checkAutocompleteValues(textarea, "textarea");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.forms.autocomplete.formautofill", true]]}, start);
+</script>
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form>
+ <input id="input-field" />
+ <select id="select-field" />
+ <textarea id="textarea"></textarea>
+ </form>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_autocompleteinfo.html b/dom/html/test/forms/test_autocompleteinfo.html
new file mode 100644
index 0000000000..a3357ac8de
--- /dev/null
+++ b/dom/html/test/forms/test_autocompleteinfo.html
@@ -0,0 +1,206 @@
+<!DOCTYPE html>
+<html>
+<!--
+Test getAutocompleteInfo() on <input> and <select>
+-->
+<head>
+ <title>Test for getAutocompleteInfo()</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form>
+ <input id="input"/>
+ <select id="select" />
+ </form>
+</div>
+<pre id="test">
+<script>
+"use strict";
+
+var values = [
+ // Missing or empty attribute
+ [undefined, {}, ""],
+ ["", {}, ""],
+
+ // One token
+ ["on", {fieldName: "on" }, "on"],
+ ["On", {fieldName: "on" }, "on"],
+ ["off", {fieldName: "off", canAutomaticallyPersist: false}, "off" ],
+ ["name", {fieldName: "name" }, "name"],
+ [" name ", {fieldName: "name" }, "name"],
+ ["username", {fieldName: "username"}, "username"],
+ [" username ", {fieldName: "username"}, "username"],
+ ["current-password", {fieldName: "current-password", canAutomaticallyPersist: false}, "current-password"],
+ ["new-password", {fieldName: "new-password", canAutomaticallyPersist: false}, "new-password"],
+ ["cc-number", {fieldName: "cc-number", canAutomaticallyPersist: false}, "cc-number"],
+ ["cc-csc", {fieldName: "cc-csc", canAutomaticallyPersist: false}, ""],
+ ["one-time-code", {fieldName: "one-time-code", canAutomaticallyPersist: false}, ""],
+ ["language", {fieldName: "language"}, ""],
+ [" language ", {fieldName: "language"}, ""],
+ ["tel-extension", {fieldName: "tel-extension"}, ""],
+ ["foobar", {}, ""],
+ ["section-blue", {}, ""],
+ [" WEBAUTHN ", {fieldName: "webauthn", credentialType: "webauthn"}, "webauthn"],
+
+ // One token + WebAuthn credential type
+ ["on webauthn", {}, ""],
+ ["off webauthn", {}, ""],
+ ["webauthn webauthn", {}, ""],
+ ["username WebAuthn", {fieldName: "username", credentialType: "webauthn"}, "username webauthn"],
+ ["current-PASSWORD webauthn", {fieldName: "current-password", credentialType: "webauthn", canAutomaticallyPersist: false}, "current-password webauthn"],
+
+ // Two tokens
+ ["on off", {}, ""],
+ ["off on", {}, ""],
+ ["username tel", {}, ""],
+ ["tel username ", {}, ""],
+ [" username tel ", {}, ""],
+ ["tel mobile", {}, ""],
+ ["tel shipping", {}, ""],
+ ["shipping tel", {addressType: "shipping", fieldName: "tel"}, "shipping tel"],
+ ["shipPING tel", {addressType: "shipping", fieldName: "tel"}, "shipping tel"],
+ ["mobile tel", {contactType: "mobile", fieldName: "tel"}, "mobile tel"],
+ [" MoBiLe TeL ", {contactType: "mobile", fieldName: "tel"}, "mobile tel"],
+ ["pager impp", {contactType: "pager", fieldName: "impp"}, ""],
+ ["fax tel-extension", {contactType: "fax", fieldName: "tel-extension"}, ""],
+ ["XXX tel", {}, ""],
+ ["XXX username", {}, ""],
+ ["name section-blue", {}, ""],
+ ["scetion-blue cc-name", {}, ""],
+ ["pager language", {}, ""],
+ ["fax url", {}, ""],
+ ["section-blue name", {section: "section-blue", fieldName: "name"}, "section-blue name"],
+ ["section-blue tel", {section: "section-blue", fieldName: "tel"}, "section-blue tel"],
+ ["webauthn username", {}, ""],
+
+ // Two tokens + WebAuthn credential type
+ ["fax url webauthn", {}, ""],
+ ["shipping tel webauthn", {addressType: "shipping", fieldName: "tel", credentialType: "webauthn"}, "shipping tel webauthn"],
+
+ // Three tokens
+ ["billing invalid tel", {}, ""],
+ ["___ mobile tel", {}, ""],
+ ["mobile foo tel", {}, ""],
+ ["mobile tel foo", {}, ""],
+ ["tel mobile billing", {}, ""],
+ ["billing mobile tel", {addressType: "billing", contactType: "mobile", fieldName: "tel"}, "billing mobile tel"],
+ [" BILLing MoBiLE tEl ", {addressType: "billing", contactType: "mobile", fieldName: "tel"}, "billing mobile tel"],
+ ["billing home tel", {addressType: "billing", contactType: "home", fieldName: "tel"}, "billing home tel"],
+ ["home section-blue tel", {}, ""],
+ ["setion-blue work email", {}, ""],
+ ["section-blue home address-level2", {}, ""],
+ ["section-blue shipping name", {section: "section-blue", addressType: "shipping", fieldName: "name"}, "section-blue shipping name"],
+ ["section-blue mobile tel", {section: "section-blue", contactType: "mobile", fieldName: "tel"}, "section-blue mobile tel"],
+ ["shipping webauthn tel", {}, ""],
+
+ // Three tokens + WebAuthn credential type
+ ["invalid mobile tel webauthn", {}, ""],
+ ["section-blue shipping name webauthn", {section: "section-blue", addressType: "shipping", fieldName: "name", credentialType: "webauthn"}, "section-blue shipping name webauthn"],
+
+ // Four tokens
+ ["billing billing mobile tel", {}, ""],
+ ["name section-blue shipping home", {}, ""],
+ ["secti shipping work address-line1", {}, ""],
+ ["section-blue shipping home name", {}, ""],
+ ["section-blue shipping mobile tel", {section: "section-blue", addressType: "shipping", contactType: "mobile", fieldName: "tel"}, "section-blue shipping mobile tel"],
+ ["section-blue webauthn mobile tel", {}, ""],
+
+ // Four tokens + WebAuthn credential type
+ ["section-blue shipping home name webauthn", {}, ""],
+ ["section-blue shipping mobile tel webauthn", {section: "section-blue", addressType: "shipping", contactType: "mobile", fieldName: "tel", credentialType: "webauthn"}, "section-blue shipping mobile tel webauthn"],
+
+ // Five tokens (invalid)
+ ["billing billing billing mobile tel", {}, ""],
+ ["section-blue section-blue billing mobile tel", {}, ""],
+ ["section-blue section-blue billing webauthn tel", {}, ""],
+
+ // Five tokens + WebAuthn credential type (invalid)
+ ["billing billing billing mobile tel webauthn", {}, ""],
+];
+
+var autocompleteInfoFieldIds = ["input", "select"];
+var autocompleteEnabledTypes = ["hidden", "text", "search", "url", "tel",
+ "email", "password", "date", "time", "number",
+ "range", "color"];
+var autocompleteDisabledTypes = ["reset", "submit", "image", "button", "radio",
+ "checkbox", "file"];
+
+function testInputTypes() {
+ let field = document.getElementById("input");
+
+ for (var type of autocompleteEnabledTypes) {
+ testAutocomplete(field, type, true);
+ }
+
+ for (var type of autocompleteDisabledTypes) {
+ testAutocomplete(field, type, false);
+ }
+
+ // Clear input type attribute.
+ field.removeAttribute("type");
+}
+
+function testAutocompleteInfoValue(aEnabled) {
+ for (var fieldId of autocompleteInfoFieldIds) {
+ let field = document.getElementById(fieldId);
+
+ for (var test of values) {
+ if (typeof(test[0]) === "undefined")
+ field.removeAttribute("autocomplete");
+ else
+ field.setAttribute("autocomplete", test[0]);
+
+ var info = field.getAutocompleteInfo();
+ if (aEnabled) {
+ // We need to consider if getAutocompleteInfo() is valid,
+ // but @autocomplete is invalid case, because @autocomplete
+ // has smaller set of values.
+ is(field.autocomplete, test[2], "Checking @autocomplete of: " + test[0]);
+ }
+
+ is(info.section, "section" in test[1] ? test[1].section : "",
+ "Checking autocompleteInfo.section for " + field + ": " + test[0]);
+ is(info.addressType, "addressType" in test[1] ? test[1].addressType : "",
+ "Checking autocompleteInfo.addressType for " + field + ": " + test[0]);
+ is(info.contactType, "contactType" in test[1] ? test[1].contactType : "",
+ "Checking autocompleteInfo.contactType for " + field + ": " + test[0]);
+ is(info.fieldName, "fieldName" in test[1] ? test[1].fieldName : "",
+ "Checking autocompleteInfo.fieldName for " + field + ": " + test[0]);
+ is(info.credentialType, "credentialType" in test[1] ? test[1].credentialType: "",
+ "Checking autocompleteInfo.credentialType for " + field + ": " + test[0]);
+ is(info.canAutomaticallyPersist, "canAutomaticallyPersist" in test[1] ? test[1].canAutomaticallyPersist : true,
+ "Checking autocompleteInfo.canAutomaticallyPersist for " + field + ": " + test[0]);
+ }
+ }
+}
+
+function testAutocomplete(aField, aType, aEnabled) {
+ aField.type = aType;
+ if (aEnabled) {
+ ok(aField.getAutocompleteInfo() !== null, "getAutocompleteInfo shouldn't return null");
+ } else {
+ is(aField.getAutocompleteInfo(), null, "getAutocompleteInfo should return null");
+ }
+}
+
+// getAutocompleteInfo() should be able to parse all tokens as defined
+// in the spec regardless of whether dom.forms.autocomplete.formautofill pref
+// is on or off.
+add_task(async function testAutocompletePreferenceEnabled() {
+ await SpecialPowers.pushPrefEnv({"set": [["dom.forms.autocomplete.formautofill", true]]}, testInputTypes);
+ testAutocompleteInfoValue(true);
+});
+
+add_task(async function testAutocompletePreferenceDisabled() {
+ await SpecialPowers.pushPrefEnv({"set": [["dom.forms.autocomplete.formautofill", false]]}, testInputTypes);
+ testAutocompleteInfoValue(false);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_bug1039548.html b/dom/html/test/forms/test_bug1039548.html
new file mode 100644
index 0000000000..cea3cd67ef
--- /dev/null
+++ b/dom/html/test/forms/test_bug1039548.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1039548
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1039548</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1039548 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.waitForFocus(test);
+
+ var didTryToSubmit;
+ function test() {
+ var r = document.getElementById("radio");
+ r.focus();
+ didTryToSubmit = false;
+ sendKey("return");
+ ok(!didTryToSubmit, "Shouldn't have tried to submit!");
+
+ var t = document.getElementById("text");
+ t.focus();
+ didTryToSubmit = false;
+ sendKey("return");
+ ok(didTryToSubmit, "Should have tried to submit!");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1039548">Mozilla Bug 1039548</a>
+<p id="display"></p>
+<div id="content">
+
+ <form onsubmit="didTryToSubmit = true; event.preventDefault();">
+ <input type="radio" id="radio">
+ </form>
+
+ <form onsubmit="didTryToSubmit = true; event.preventDefault();">
+ <input type="text" id="text">
+ </form>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_bug1283915.html b/dom/html/test/forms/test_bug1283915.html
new file mode 100644
index 0000000000..90bffd4b20
--- /dev/null
+++ b/dom/html/test/forms/test_bug1283915.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1283915
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1283915</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1283915 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function isCursorAtEnd(field){
+ is(field.selectionStart, field.value.length);
+ is(field.selectionEnd, field.value.length);
+ }
+
+ function test() {
+ var tField = document.getElementById("textField");
+ tField.focus();
+
+ sendString("a");
+ is(tField.value, "a");
+ isCursorAtEnd(tField);
+ document.body.offsetWidth; // frame must be created after type change
+
+ sendString("b");
+ is(tField.value, "ab");
+ isCursorAtEnd(tField);
+
+ sendString("c");
+ is(tField.value, "abc");
+ isCursorAtEnd(tField);
+
+ var nField = document.getElementById("numField");
+ nField.focus();
+
+ sendString("1");
+ is(nField.value, "1");
+ document.body.offsetWidth;
+
+ sendString("2");
+ is(nField.value, "12");
+
+ sendString("3");
+ is(nField.value, "123");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForFocus(test);
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1283915">Mozilla Bug 1283915</a>
+<p id="display"></p>
+<input id="textField" type="text" oninput="if (this.type !='password') this.type = 'password';">
+<input id="numField" type="text" oninput="if (this.type !='number') this.type = 'number';">
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_bug1286509.html b/dom/html/test/forms/test_bug1286509.html
new file mode 100644
index 0000000000..638e7fe85c
--- /dev/null
+++ b/dom/html/test/forms/test_bug1286509.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1286509
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1286509</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1286509">Mozilla Bug 1286509</a>
+<p id="display"></p>
+<div id="content">
+ <input type="range" id="test_input" min="0" max="10" value="5">
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ /** Test for Bug 1286509 **/
+ SimpleTest.waitForExplicitFinish();
+ var expectedEventSequence = ['keydown', 'change', 'keyup'];
+ var eventCounts = {};
+ var expectedEventIdx = 0;
+
+ function test() {
+ var range = document.getElementById("test_input");
+ range.focus();
+ expectedEventSequence.forEach((eventName) => {
+ eventCounts[eventName] = 0;
+ range.addEventListener(eventName, (e) => {
+ ++eventCounts[eventName];
+ is(expectedEventSequence[expectedEventIdx], e.type, "Events sequence should be keydown, change, keyup");
+ expectedEventIdx = (expectedEventIdx + 1) % 3;
+ });
+ });
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowLeft");
+ synthesizeKey("KEY_ArrowRight");
+ is(eventCounts.change, 4, "Expect key up/down/left/right should trigger range input to fire change events");
+ SimpleTest.finish();
+ }
+ addLoadEvent(test);
+ </script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_button_attributes_reflection.html b/dom/html/test/forms/test_button_attributes_reflection.html
new file mode 100644
index 0000000000..de2097cb4c
--- /dev/null
+++ b/dom/html/test/forms/test_button_attributes_reflection.html
@@ -0,0 +1,144 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLButtonElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLButtonElement attributes reflection **/
+
+// .autofocus
+reflectBoolean({
+ element: document.createElement("button"),
+ attribute: "autofocus",
+});
+
+// .disabled
+reflectBoolean({
+ element: document.createElement("button"),
+ attribute: "disabled",
+});
+
+// .formAction
+reflectURL({
+ element: document.createElement("button"),
+ attribute: "formAction",
+});
+
+// .formEnctype
+reflectLimitedEnumerated({
+ element: document.createElement("button"),
+ attribute: "formEnctype",
+ validValues: [
+ "application/x-www-form-urlencoded",
+ "multipart/form-data",
+ "text/plain",
+ ],
+ invalidValues: [ "text/html", "", "tulip" ],
+ defaultValue: {
+ invalid: "application/x-www-form-urlencoded",
+ missing: "",
+ }
+});
+
+// .formMethod
+add_task(async function() {
+ reflectLimitedEnumerated({
+ element: document.createElement("button"),
+ attribute: "formMethod",
+ validValues: [ "get", "post", "dialog"],
+ invalidValues: [ "put", "", "tulip" ],
+ defaultValue: {
+ invalid: "get",
+ missing: "",
+ }
+ });
+});
+
+// .formNoValidate
+reflectBoolean({
+ element: document.createElement("button"),
+ attribute: "formNoValidate",
+});
+
+// .formTarget
+reflectString({
+ element: document.createElement("button"),
+ attribute: "formTarget",
+ otherValues: [ "_blank", "_self", "_parent", "_top" ],
+});
+
+// .name
+reflectString({
+ element: document.createElement("button"),
+ attribute: "name",
+ otherValues: [ "isindex", "_charset_" ]
+});
+
+// .type
+reflectLimitedEnumerated({
+ element: document.createElement("button"),
+ attribute: "type",
+ validValues: [ "submit", "reset", "button" ],
+ invalidValues: [ "this-is-probably-a-wrong-type", "", "tulip" ],
+ unsupportedValues: [ "menu" ],
+ defaultValue: "submit",
+});
+
+// .value
+reflectString({
+ element: document.createElement("button"),
+ attribute: "value",
+});
+
+// .willValidate
+ok("willValidate" in document.createElement("button"),
+ "willValidate should be an IDL attribute of the button element");
+is(typeof(document.createElement("button").willValidate), "boolean",
+ "button.willValidate should be a boolean");
+
+// .validity
+ok("validity" in document.createElement("button"),
+ "validity should be an IDL attribute of the button element");
+is(typeof(document.createElement("button").validity), "object",
+ "button.validity should be an object");
+ok(document.createElement("button").validity instanceof ValidityState,
+ "button.validity sohuld be an instance of ValidityState");
+
+// .validationMessage
+ok("validationMessage" in document.createElement("button"),
+ "validationMessage should be an IDL attribute of the button element");
+is(typeof(document.createElement("button").validationMessage), "string",
+ "button.validationMessage should be a string");
+
+// .checkValidity()
+ok("checkValidity" in document.createElement("button"),
+ "checkValidity() should be a method of the button element");
+is(typeof(document.createElement("button").checkValidity), "function",
+ "button.checkValidity should be a function");
+
+// .setCustomValidity()
+ok("setCustomValidity" in document.createElement("button"),
+ "setCustomValidity() should be a method of the button element");
+is(typeof(document.createElement("button").setCustomValidity), "function",
+ "button.setCustomValidity should be a function");
+
+// .labels
+ok("labels" in document.createElement("button"),
+ "button.labels should be an IDL attribute of the button element");
+is(typeof(document.createElement("button").labels), "object",
+ "button.labels should be an object");
+ok(document.createElement("button").labels instanceof NodeList,
+ "button.labels sohuld be an instance of NodeList");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_change_event.html b/dom/html/test/forms/test_change_event.html
new file mode 100644
index 0000000000..8be4554c58
--- /dev/null
+++ b/dom/html/test/forms/test_change_event.html
@@ -0,0 +1,286 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=722599
+-->
+<head>
+<title>Test for Bug 722599</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=722599">Mozilla Bug 722599</a>
+<p id="display"></p>
+<div id="content">
+<input type="file" id="fileInput"></input>
+<textarea id="textarea" onchange="++textareaChange;"></textarea>
+<input type="text" id="input_text" onchange="++textInputChange[0];"></input>
+<input type="email" id="input_email" onchange="++textInputChange[1];"></input>
+<input type="search" id="input_search" onchange="++textInputChange[2];"></input>
+<input type="tel" id="input_tel" onchange="++textInputChange[3];"></input>
+<input type="url" id="input_url" onchange="++textInputChange[4];"></input>
+<input type="password" id="input_password" onchange="++textInputChange[5];"></input>
+
+<!-- "Non-text" inputs-->
+<input type="button" id="input_button" onchange="++NonTextInputChange[0];"></input>
+<input type="submit" id="input_submit" onchange="++NonTextInputChange[1];"></input>
+<input type="image" id="input_image" onchange="++NonTextInputChange[2];"></input>
+<input type="reset" id="input_reset" onchange="++NonTextInputChange[3];"></input>
+<input type="radio" id="input_radio" onchange="++NonTextInputChange[4];"></input>
+<input type="checkbox" id="input_checkbox" onchange="++NonTextInputChange[5];"></input>
+<input type="number" id="input_number" onchange="++numberChange;"></input>
+<input type="range" id="input_range" onchange="++rangeChange;"></input>
+
+<!-- Input text with default value and blurs on focus-->
+<input type="text" id="input_text_value" onchange="++textInputValueChange"
+ onfocus="this.blur();" value="foo"></input>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Test for Bug 722599 **/
+
+ const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
+
+ var textareaChange = 0;
+ var fileInputChange = 0;
+ var textInputValueChange = 0;
+
+ var textInputTypes = ["text", "email", "search", "tel", "url", "password"];
+ var textInputChange = [0, 0, 0, 0, 0, 0];
+
+ var NonTextInputTypes = ["button", "submit", "image", "reset", "radio", "checkbox"];
+ var NonTextInputChange = [0, 0, 0, 0, 0, 0];
+
+ var numberChange = 0;
+ var rangeChange = 0;
+
+ var blurTestCalled = false; //Sentinel to prevent infinite loop.
+
+ SimpleTest.waitForExplicitFinish();
+ var MockFilePicker = SpecialPowers.MockFilePicker;
+ MockFilePicker.init(window);
+
+ function fileInputBlurTest() {
+ var btn = document.getElementById('fileInput');
+ btn.focus()
+ btn.blur();
+ is(fileInputChange, 1, "change event shouldn't be dispatched on blur for file input element(1)");
+ }
+
+ function testUserInput() {
+ //Simulating an OK click and with a file name return.
+ MockFilePicker.useBlobFile();
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+ var input = document.getElementById('fileInput');
+ input.focus();
+
+ input.addEventListener("change", function (aEvent) {
+ ++fileInputChange;
+ if (!blurTestCalled) {
+ is(fileInputChange, 1, "change event should have been dispatched on file input.");
+ blurTestCalled = true;
+ fileInputBlurTest();
+ }
+ else {
+ is(fileInputChange, 1, "change event shouldn't be dispatched on blur for file input element (2)");
+ }
+ });
+ input.click();
+ // blur the file input, we can't use blur() because of bug 760283
+ document.getElementById('input_text').focus();
+ setTimeout(testUserInput2, 0);
+ }
+
+ function testUserInput2() {
+ var input = document.getElementById('fileInput');
+ // remove it, otherwise cleanup() opens a native file picker!
+ input.remove();
+ MockFilePicker.cleanup();
+
+ //text, email, search, telephone, url & password input tests
+ for (var i = 0; i < textInputTypes.length; ++i) {
+ input = document.getElementById("input_" + textInputTypes[i]);
+ input.focus();
+ synthesizeKey("KEY_Enter");
+ is(textInputChange[i], 0, "Change event shouldn't be dispatched on " + textInputTypes[i] + " input element");
+
+ sendString("m");
+ synthesizeKey("KEY_Enter");
+ is(textInputChange[i], 1, textInputTypes[i] + " input element should have dispatched change event.");
+ }
+
+ //focus and blur text input
+ input = document.getElementById("input_text");
+ input.focus();
+ sendString("f");
+ input.blur();
+ is(textInputChange[0], 2, "text input element should have dispatched change event (2).");
+
+ // value being set while focused
+ input.focus();
+ input.value = 'foo';
+ input.blur();
+ is(textInputChange[0], 2, "text input element should not have dispatched change event (2).");
+
+ // value being set while focused after being modified manually
+ input.focus();
+ sendString("f");
+ input.value = 'bar';
+ input.blur();
+ is(textInputChange[0], 3, "text input element should have dispatched change event (3).");
+
+ //focus and blur textarea
+ var textarea = document.getElementById("textarea");
+ textarea.focus();
+ sendString("f");
+ textarea.blur();
+ is(textareaChange, 1, "Textarea element should have dispatched change event.");
+
+ // value being set while focused
+ textarea.focus();
+ textarea.value = 'foo';
+ textarea.blur();
+ is(textareaChange, 1, "textarea should not have dispatched change event (1).");
+
+ // value being set while focused after being modified manually
+ textarea.focus();
+ sendString("f");
+ textarea.value = 'bar';
+ textarea.blur();
+ is(textareaChange, 2, "textearea should have dispatched change event (2).");
+
+ //Non-text input tests:
+ for (var i = 0; i < NonTextInputTypes.length; ++i) {
+ //button, submit, image and reset input type tests.
+ if (i < 4) {
+ input = document.getElementById("input_" + NonTextInputTypes[i]);
+ input.focus();
+ input.click();
+ is(NonTextInputChange[i], 0, "Change event shouldn't be dispatched on " + NonTextInputTypes[i] + " input element");
+ input.blur();
+ is(NonTextInputChange[i], 0, "Change event shouldn't be dispatched on " + NonTextInputTypes[i] + " input element(2)");
+ }
+ //for radio and and checkboxes, we require that change event should ONLY be dispatched on setting the value.
+ else {
+ input = document.getElementById("input_" + NonTextInputTypes[i]);
+ input.focus();
+ input.click();
+ is(NonTextInputChange[i], 1, NonTextInputTypes[i] + " input element should have dispatched change event.");
+ input.blur();
+ is(NonTextInputChange[i], 1, "Change event shouldn't be dispatched on " + NonTextInputTypes[i] + " input element");
+
+ // Test that change event is not dispatched if click event is cancelled.
+ function preventDefault(e) {
+ e.preventDefault();
+ }
+ input.addEventListener("click", preventDefault);
+ input.click();
+ is(NonTextInputChange[i], 1, "Change event shouldn't be dispatched if click event is cancelled");
+ input.removeEventListener("click", preventDefault);
+ }
+ }
+
+ // Special case type=number
+ var number = document.getElementById("input_number");
+ number.focus();
+ sendString("a");
+ number.blur();
+ is(numberChange, 0, "Change event shouldn't be dispatched on number input element for key changes that don't change its value");
+ number.value = "";
+ number.focus();
+ sendString("12");
+ is(numberChange, 0, "Change event shouldn't be dispatched on number input element for keyboard input until it loses focus");
+ number.blur();
+ is(numberChange, 1, "Change event should be dispatched on number input element on blur");
+ is(number.value, "12", "Sanity check that number keys were actually handled");
+ if (isDesktop) { // up/down arrow keys not supported on android/b2g
+ number.value = "";
+ number.focus();
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_ArrowDown");
+ is(numberChange, 4, "Change event should be dispatched on number input element for up/down arrow keys (a special case)");
+ is(number.value, "1", "Sanity check that number and arrow keys were actually handled");
+ }
+
+ // Special case type=range
+ var range = document.getElementById("input_range");
+ range.focus();
+ sendString("a");
+ range.blur();
+ is(rangeChange, 0, "Change event shouldn't be dispatched on range input element for key changes that don't change its value");
+ range.focus();
+ synthesizeKey("VK_HOME");
+ is(rangeChange, 1, "Change event should be dispatched on range input element for key changes");
+ range.blur();
+ is(rangeChange, 1, "Change event shouldn't be dispatched on range input element on blur");
+ range.focus();
+ var bcr = range.getBoundingClientRect();
+ var centerOfRangeX = bcr.width / 2;
+ var centerOfRangeY = bcr.height / 2;
+ synthesizeMouse(range, centerOfRangeX - 10, centerOfRangeY, { type: "mousedown" });
+ is(rangeChange, 1, "Change event shouldn't be dispatched on range input element for mousedown");
+ synthesizeMouse(range, centerOfRangeX - 5, centerOfRangeY, { type: "mousemove" });
+ is(rangeChange, 1, "Change event shouldn't be dispatched on range input element during drag of thumb");
+ synthesizeMouse(range, centerOfRangeX, centerOfRangeY, { type: "mouseup" });
+ is(rangeChange, 2, "Change event should be dispatched on range input element at end of drag");
+ range.blur();
+ is(rangeChange, 2, "Change event shouldn't be dispatched on range input element when range loses focus after a drag");
+ synthesizeMouse(range, centerOfRangeX - 10, centerOfRangeY, {});
+ is(rangeChange, 3, "Change event should be dispatched on range input element for a click that gives the range focus");
+
+ if (isDesktop) { // up/down arrow keys not supported on android/b2g
+ synthesizeKey("KEY_ArrowUp");
+ is(rangeChange, 4, "Change event should be dispatched on range input element for key changes that change its value (KEY_ArrowUp)");
+ synthesizeKey("KEY_ArrowDown");
+ is(rangeChange, 5, "Change event should be dispatched on range input element for key changes that change its value (KEY_ArrowDown)");
+ synthesizeKey("KEY_ArrowRight");
+ is(rangeChange, 6, "Change event should be dispatched on range input element for key changes that change its value (KEY_ArrowRight)");
+ synthesizeKey("KEY_ArrowLeft");
+ is(rangeChange, 7, "Change event should be dispatched on range input element for key changes that change its value (KEY_ArrowLeft)");
+ synthesizeKey("KEY_ArrowUp", {shiftKey: true});
+ is(rangeChange, 8, "Change event should be dispatched on range input element for key changes that change its value (Shift+KEY_ArrowUp)");
+ synthesizeKey("KEY_ArrowDown", {shiftKey: true});
+ is(rangeChange, 9, "Change event should be dispatched on range input element for key changes that change its value (Shift+KEY_ArrowDown)");
+ synthesizeKey("KEY_ArrowRight", {shiftKey: true});
+ is(rangeChange, 10, "Change event should be dispatched on range input element for key changes that change its value (Shift+KEY_ArrowRight)");
+ synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
+ is(rangeChange, 11, "Change event should be dispatched on range input element for key changes that change its value (Shift+KEY_ArrowLeft)");
+ synthesizeKey("KEY_PageUp");
+ is(rangeChange, 12, "Change event should be dispatched on range input element for key changes that change its value (KEY_PageUp)");
+ synthesizeKey("KEY_PageDown");
+ is(rangeChange, 13, "Change event should be dispatched on range input element for key changes that change its value (KEY_PageDown");
+ synthesizeKey("KEY_ArrowRight", {shiftKey: true});
+ is(rangeChange, 14, "Change event should be dispatched on range input element for key changes that change its value (Shift+KEY_PageUp)");
+ synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
+ is(rangeChange, 15, "Change event should be dispatched on range input element for key changes that change its value (Shift+KEY_PageDown)");
+ }
+ //Input type change test.
+ input = document.getElementById("input_checkbox");
+ input.type = "text";
+ input.focus();
+ input.click();
+ input.blur();
+ is(NonTextInputChange[5], 1, "Change event shouldn't be dispatched for checkbox ---> text input type change");
+
+ setTimeout(testInputWithDefaultValue, 0);
+ }
+
+ function testInputWithDefaultValue() {
+ // focus and blur an input text should not trigger change event if content hasn't changed.
+ var input = document.getElementById('input_text_value');
+ input.focus();
+ is(textInputValueChange, 0, "change event shouldn't be dispatched on input text with default value");
+
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(testUserInput);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_datalist_element.html b/dom/html/test/forms/test_datalist_element.html
new file mode 100644
index 0000000000..5f05634018
--- /dev/null
+++ b/dom/html/test/forms/test_datalist_element.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for the datalist element</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <datalist>
+ </datalist>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 555840 **/
+
+function checkClassesAndAttributes()
+{
+ var d = document.getElementsByTagName('datalist');
+ is(d.length, 1, "One datalist has been found");
+
+ d = d[0];
+ ok(d instanceof HTMLDataListElement,
+ "The datalist should be instance of HTMLDataListElement");
+
+ ok('options' in d, "datalist has an options IDL attribute");
+
+ ok(d.options, "options IDL attribute is not null");
+ ok(!d.getAttribute('options'), "datalist has no options content attribute");
+
+ ok(d.options instanceof HTMLCollection,
+ "options IDL attribute should be instance of HTMLCollection");
+}
+
+function checkOptions()
+{
+ var testData = [
+ /* [ Child list, Function modifying children, Recognized options ] */
+ [['option'], null, 1],
+ [['option', 'option', 'option', 'option'], null, 4],
+ /* Disabled options are not valid. */
+ [['option'], function(d) { d.childNodes[0].disabled = true; }, 0],
+ [['option', 'option'], function(d) { d.childNodes[0].disabled = true; }, 1],
+ /* Non-option elements are not recognized. */
+ [['input'], null, 0],
+ [['input', 'option'], null, 1],
+ [['input', 'textarea'], null, 0],
+ /* .value and .label are not needed to be valid options. */
+ [['option', 'option'], function(d) { d.childNodes[0].value = 'value'; }, 2],
+ [['option', 'option'], function(d) { d.childNodes[0].label = 'label'; }, 2],
+ [['option', 'option'], function(d) { d.childNodes[0].value = 'value'; d.childNodes[0].label = 'label'; }, 2],
+ [['select'],
+ function(d) {
+ var s = d.childNodes[0];
+ s.appendChild(new Option("foo"));
+ s.appendChild(new Option("bar"));
+ },
+ 2],
+ [['select'],
+ function(d) {
+ var s = d.childNodes[0];
+ s.appendChild(new Option("foo"));
+ s.appendChild(new Option("bar"));
+ var label = document.createElement("label");
+ d.appendChild(label);
+ label.appendChild(new Option("foobar"));
+ },
+ 3],
+ [['select'],
+ function(d) {
+ var s = d.childNodes[0];
+ s.appendChild(new Option("foo"));
+ s.appendChild(new Option("bar"));
+ var label = document.createElement("label");
+ d.appendChild(label);
+ label.appendChild(new Option("foobar"));
+ s.appendChild(new Option())
+ },
+ 4],
+ [[], function(d) { d.appendChild(document.createElementNS("foo", "option")); }, 0]
+ ];
+
+ var d = document.getElementsByTagName('datalist')[0];
+ var cachedOptions = d.options;
+
+ testData.forEach(function(data) {
+ data[0].forEach(function(e) {
+ d.appendChild(document.createElement(e));
+ })
+
+ /* Modify children. */
+ if (data[1]) {
+ data[1](d);
+ }
+
+ is(d.options, cachedOptions, "Should get the same object")
+ is(d.options.length, data[2],
+ "The number of recognized options should be " + data[2])
+
+ for (var i = 0; i < d.options.length; ++i) {
+ is(d.options[i].localName, "option",
+ "Should get an option for d.options[" + i + "]")
+ }
+
+ /* Cleaning-up. */
+ d.textContent = "";
+ })
+}
+
+checkClassesAndAttributes();
+checkOptions();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_double_submit.html b/dom/html/test/forms/test_double_submit.html
new file mode 100644
index 0000000000..d27fb290a4
--- /dev/null
+++ b/dom/html/test/forms/test_double_submit.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for multiple submissions in straightline code</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+
+add_task(async function double_submit() {
+ dump("test start\n");
+ let popup = window.open("file_double_submit.html");
+ await new Promise(resolve => {
+ popup.addEventListener("load", resolve, {once: true})
+ });
+
+ let numCalls = 0;
+ popup.addEventListener("beforeunload", () => {
+ numCalls++;
+ info("beforeunload called " + numCalls + " times");
+ });
+
+ info("clicking button");
+ popup.document.querySelector("button").click();
+
+ is(numCalls, 1, "beforeunload should only fire once");
+ popup.close();
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_form_attribute-1.html b/dom/html/test/forms/test_form_attribute-1.html
new file mode 100644
index 0000000000..6735f514ae
--- /dev/null
+++ b/dom/html/test/forms/test_form_attribute-1.html
@@ -0,0 +1,473 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=588683
+-->
+<head>
+ <title>Test for form attributes 1</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=588683">Mozilla Bug 588683</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for form attributes 1 **/
+
+/**
+ * All functions take an array of forms in first argument and an array of
+ * elements in second argument.
+ * Then, it returns an array containing an array of form and an array of array
+ * of elements. The array represent the form association with elements like this:
+ * [ [ form1, form2 ], [ [ elmt1ofForm1, elmt2ofForm2 ], [ elmtofForm2 ] ] ]
+ */
+
+/**
+ * test0a and test0b are testing the regular behavior of form ownership.
+ */
+function test0a(aForms, aElements)
+{
+ // <form><element></form>
+ // <form><element></form>
+ aForms[0].appendChild(aElements[0]);
+ aForms[1].appendChild(aElements[1]);
+
+ return [[aForms[0],aForms[1]],[[aElements[0]],[aElements[1]]]];
+}
+
+function test0b(aForms, aElements)
+{
+ // <form><element><form><element></form></form>
+ aForms[0].appendChild(aElements[0]);
+ aForms[0].appendChild(aForms[1]);
+ aForms[1].appendChild(aElements[1]);
+
+ return [[aForms[0],aForms[1]],[[aElements[0]],[aElements[1]]]];
+}
+
+/**
+ * This function test that, when an element is not a descendant of a form
+ * element and has @form set to a valid form id, it's form owner is the form
+ * which has the id.
+ */
+function test1(aForms, aElements)
+{
+ // <form id='f'></form><element id='f'>
+ aForms[0].id = 'f';
+ aElements[0].setAttribute('form', 'f');
+
+ return [[aForms[0]], [[aElements[0]]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id (not it's descendant), it's form
+ * owner is the form which has the id.
+ */
+function test2(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+
+ return [[aForms[0], aForms[1]], [[aElements[0]],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id (not it's descendant), then the
+ * form attribute is removed, it does not have a form owner.
+ */
+function test3(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aElements[0].removeAttribute('form');
+
+ return [[aForms[0], aForms[1]], [[],[aElements[0]]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id (not it's descendant), then the
+ * form's id attribute is removed, it does not have a form owner.
+ */
+function test4(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aForms[0].removeAttribute('id');
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to an invalid form id, then it does not have a form
+ * owner.
+ */
+function test5(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='foo'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'foo');
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id (not it's descendant), then the
+ * form id attribute is changed to an invalid id, it does not have a form owner.
+ */
+function test6(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aElements[0].setAttribute('form', 'foo');
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to an invalid form id, then the form id attribute
+ * is changed to a valid form id, it's form owner is the form which has this id.
+ */
+function test7(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='foo'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'foo');
+ aElements[0].setAttribute('form', 'f');
+
+ return [[aForms[0], aForms[1]], [[aElements[0]],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a list of ids containing one valid form, then
+ * it does not have a form owner.
+ */
+function test8(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f foo'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f foo');
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a form id which is valid in a case insensitive
+ * way, then it does not have a form owner.
+ */
+function test9(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='F'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'F');
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a form id which is not a valid id, then it's
+ * form owner is it does not have a form owner.
+ */
+function test10(aForms, aElements)
+{
+ // <form id='F'></form><form><element form='f'></form>
+ aForms[0].id = 'F';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a form id which is not a valid id, then it's
+ * form owner is it does not have a form owner.
+ */
+function test11(aForms, aElements)
+{
+ // <form id='foo bar'></form><form><element form='foo bar'></form>
+ aForms[0].id = 'foo bar';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'foo bar');
+
+ return [[aForms[0], aForms[1]], [[aElements[0]],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id and the form id change, then
+ * it does not have a form owner.
+ */
+function test12(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aForms[0].id = 'foo';
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to an invalid form id and the form id change to a
+ * valid one, then it's form owner is the form which has the id.
+ */
+function test13(aForms, aElements)
+{
+ // <form id='foo'></form><form><element form='f'></form>
+ aForms[0].id = 'foo';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aForms[0].id = 'f';
+
+ return [[aForms[0], aForms[1]], [[aElements[0]],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id and a form with the same id is
+ * inserted before in the tree, then it's form owner is the form which has the
+ * id.
+ */
+function test14(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aForms[2].id = 'f';
+
+ document.getElementById('content').insertBefore(aForms[2], aForms[0]);
+
+ return [[aForms[0], aForms[1], aForms[2]], [[],[],[aElements[0]]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id and an element with the same id is
+ * inserted before in the tree, then it does not have a form owner.
+ */
+function test15(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aElements[1].id = 'f';
+
+ document.getElementById('content').insertBefore(aElements[1], aForms[0]);
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form
+ * element and has @form set to a valid form id and the form is removed from
+ * the tree, then it does not have a form owner.
+ */
+function test16(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ aElements[1].id = 'f';
+
+ document.getElementById('content').removeChild(aForms[0]);
+
+ return [[aForms[0], aForms[1]], [[],[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form element
+ * and has @form set to the empty string, it does not have a form owner.
+ */
+function test17(aForms, aElements)
+{
+ // <form><element form=''></form>
+ aForms[0].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', '');
+
+ return [[aForms[0]], [[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form element
+ * and has @form set to the empty string, it does not have a form owner even if
+ * it's parent has its id equals to the empty string.
+ */
+function test18(aForms, aElements)
+{
+ // <form id=''><element form=''></form>
+ aForms[0].id = '';
+ aForms[0].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', '');
+
+ return [[aForms[0]], [[]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form element
+ * and has @form set to a valid form id and the element is being moving inside
+ * it's parent, it's form owner will remain the form with the id.
+ */
+function test19(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'><element></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aForms[1].appendChild(aElements[1]);
+ aElements[0].setAttribute('form', 'f');
+ aForms[1].appendChild(aElements[0]);
+
+ return [[aForms[0],aForms[1]],[[aElements[0]],[aElements[1]]]];
+}
+
+/**
+ * This function test that, when an element is a descendant of a form element
+ * and has @form set to a valid form id and the element is being moving inside
+ * another form, it's form owner will remain the form with the id.
+ */
+function test20(aForms, aElements)
+{
+ // <form id='f'></form><form><element form='f'><element></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aForms[1].appendChild(aElements[1]);
+ aElements[0].setAttribute('form', 'f');
+ aForms[2].appendChild(aElements[0]);
+
+ return [[aForms[0],aForms[1],aForms[2]],[[aElements[0]],[aElements[1]],[]]];
+}
+
+/**
+ * This function test that when removing a form, the elements with a @form set
+ * will be correctly removed from there form owner.
+ */
+function test21(aForms, aElements)
+{
+ // <form id='f'><form><form><element form='f'></form>
+ aForms[0].id = 'f';
+ aForms[1].appendChild(aElements[0]);
+ aElements[0].setAttribute('form', 'f');
+ document.getElementById('content').removeChild(aForms[1]);
+
+ return [[aForms[0]],[[]]];
+}
+
+var functions = [
+ test0a, test0b,
+ test1, test2, test3, test4, test5, test6, test7, test8, test9,
+ test10, test11, test12, test13, test14, test15, test16, test17, test18, test19,
+ test20, test21,
+];
+
+// Global variable to have an easy access to <div id='content'>.
+var content = document.getElementById('content');
+
+// Initializing the needed elements.
+var forms = [
+ document.createElement('form'),
+ document.createElement('form'),
+ document.createElement('form'),
+];
+
+var elementNames = [
+ 'button', 'fieldset', 'input', 'label', 'object', 'output', 'select',
+ 'textarea'
+];
+
+var todoElements = [
+ ['keygen', 'Keygen'],
+];
+
+for (var e of todoElements) {
+ var node = document.createElement(e[0]);
+ var nodeString = HTMLElement.prototype.toString.apply(node);
+ nodeString = nodeString.replace(/Element[\] ].*/, "Element");
+ todo_is(nodeString, "[object HTML" + e[1] + "Element",
+ e[0] + " should not be implemented");
+}
+
+for (var name of elementNames) {
+ var elements = [
+ document.createElement(name),
+ document.createElement(name),
+ ];
+
+ for (var func of functions) {
+ // Clean-up.
+ while (content.firstChild) {
+ content.firstChild.remove();
+ }
+ for (form of forms) {
+ content.appendChild(form);
+ form.removeAttribute('id');
+ }
+ for (e of elements) {
+ content.appendChild(e);
+ e.removeAttribute('form');
+ is(e.form, null, "The element should not have a form owner");
+ }
+
+ // Calling the test.
+ var results = func(forms, elements);
+
+ // Checking the results.
+ var formsList = results[0];
+ for (var i=0; i<formsList.length; ++i) {
+ var elementsList = results[1][i];
+ if (name != 'label' && name != 'meter' && name != 'progress') {
+ is(formsList[i].elements.length, elementsList.length,
+ "The form should contain " + elementsList.length + " elements");
+ }
+ for (var j=0; j<elementsList.length; ++j) {
+ if (name != 'label' && name != 'meter' && name != 'progress') {
+ is(formsList[i].elements[j], elementsList[j],
+ "The form should contain " + elementsList[j]);
+ }
+ if (name != 'label') {
+ is(elementsList[j].form, formsList[i],
+ "The form owner should be the form associated to the list");
+ }
+ }
+ }
+ }
+
+ // Cleaning-up.
+ for (e of elements) {
+ e.remove();
+ e = null;
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_form_attribute-2.html b/dom/html/test/forms/test_form_attribute-2.html
new file mode 100644
index 0000000000..b7fe5daa87
--- /dev/null
+++ b/dom/html/test/forms/test_form_attribute-2.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=588683
+-->
+<head>
+ <title>Test for form attributes 2</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=588683">Mozilla Bug 588683</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form id='a'>
+ <form id='b'>
+ <input id='i' form='b'>
+ <script>
+ is(document.getElementById('i').form, document.getElementById('b'),
+ "While parsing, the form property should work.");
+ </script>
+ </form>
+ </form>
+ <form id='c'>
+ <form id='d'>
+ <input id='i2' form='c'>
+ <script>
+ is(document.getElementById('i2').form, document.getElementById('c'),
+ "While parsing, the form property should work.");
+ </script>
+ </form>
+ </form>
+ <!-- Let's tests without @form -->
+ <form id='e'>
+ <form id='f'>
+ <input id='i3'>
+ <script>
+ // bug 589073
+ todo_is(document.getElementById('i3').form, document.getElementById('f'),
+ "While parsing, the form property should work.");
+ </script>
+ </form>
+ </form>
+ <form id='g'>
+ <input id='i4'>
+ <script>
+ is(document.getElementById('i4').form, document.getElementById('g'),
+ "While parsing, the form property should work.");
+ </script>
+ </form>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_form_attribute-3.html b/dom/html/test/forms/test_form_attribute-3.html
new file mode 100644
index 0000000000..9ceed86716
--- /dev/null
+++ b/dom/html/test/forms/test_form_attribute-3.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=588683
+-->
+<head>
+ <title>Test for form attributes 3</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=588683">Mozilla Bug 588683</a>
+<p id="display"></p>
+<div id="content">
+ <form id='f'>
+ <input name='e1'>
+ </form>
+ <form id='f2'>
+ <input name='e2'>
+ <input id='i3' form='f'
+ onfocus="var catched=false;
+ try { e1; } catch(e) { catched=true; }
+ ok(!catched, 'e1 should be in the scope of i3');
+ catched = false;
+ try { e2; } catch(e) { catched=true; }
+ ok(catched, 'e2 should not be in the scope of i3');
+ document.getElementById('i4').focus();"
+ >
+ <input id='i4' form='f2'
+ onfocus="var catched=false;
+ try { e2; } catch(e) { catched=true; }
+ ok(!catched, 'e2 should be in the scope of i4');
+ document.getElementById('i5').focus();"
+ >
+ <input id='i5'
+ onfocus="var catched=false;
+ try { e2; } catch(e) { catched=true; }
+ ok(!catched, 'e2 should be in the scope of i5');
+ document.getElementById('i6').focus();"
+ >
+ </form>
+ <input id='i6' form='f'
+ onfocus="var catched=false;
+ try { e1; } catch(e) { catched=true; }
+ ok(!catched, 'e1 should be in the scope of i6');
+ document.getElementById('i7').focus();"
+ >
+ <input id='i7' form='f2'
+ onfocus="var catched=false;
+ try { e2; } catch(e) { catched=true; }
+ ok(!catched, 'e2 should be in the scope of i7');
+ this.blur();
+ SimpleTest.finish();"
+ >
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for form attributes 3 **/
+
+SimpleTest.waitForExplicitFinish();
+
+document.getElementById('i3').focus();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_form_attribute-4.html b/dom/html/test/forms/test_form_attribute-4.html
new file mode 100644
index 0000000000..f2228cec45
--- /dev/null
+++ b/dom/html/test/forms/test_form_attribute-4.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=588683
+-->
+<head>
+ <title>Test for form attributes 4</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=588683">Mozilla Bug 588683</a>
+<p id="display"></p>
+<div id="content" style='display:none;'>
+ <form id='f'>
+ </form>
+ <table id='t'>
+ <form id='f2'>
+ <tr><td><input id='i1'></td></tr>
+ <tr><td><input id='i2' form='f'></td></tr>
+ </form>
+ </table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for form attributes 4 **/
+
+var table = document.getElementById('t');
+var i1 = document.getElementById('i1');
+var i2 = document.getElementById('i2');
+
+is(i1.form, document.getElementById('f2'),
+ "i1 form should be it's parent");
+is(i2.form, document.getElementById('f'),
+ "i1 form should be the form with the id in @form");
+
+table.removeChild(document.getElementById('f2'));
+is(i1, document.getElementById('i1'),
+ "i1 should still be in the document");
+is(i1.form, null, "i1 should not have any form owner");
+is(i2.form, document.getElementById('f'),
+ "i1 form should be the form with the id in @form");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_form_attributes_reflection.html b/dom/html/test/forms/test_form_attributes_reflection.html
new file mode 100644
index 0000000000..0d0ef6b870
--- /dev/null
+++ b/dom/html/test/forms/test_form_attributes_reflection.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLFormElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLFormElement attributes reflection **/
+
+// .acceptCharset
+reflectString({
+ element: document.createElement("form"),
+ attribute: { idl: "acceptCharset", content: "accept-charset" },
+ otherValues: [ "ISO-8859-1", "UTF-8" ],
+});
+
+reflectURL({
+ element: document.createElement("form"),
+ attribute: "action",
+});
+
+// .autocomplete
+reflectLimitedEnumerated({
+ element: document.createElement("form"),
+ attribute: "autocomplete",
+ validValues: [ "on", "off" ],
+ invalidValues: [ "", "foo", "tulip", "default" ],
+ defaultValue: "on",
+});
+
+// .enctype
+reflectLimitedEnumerated({
+ element: document.createElement("form"),
+ attribute: "enctype",
+ validValues: [ "application/x-www-form-urlencoded", "multipart/form-data",
+ "text/plain" ],
+ invalidValues: [ "", "foo", "tulip", "multipart/foo" ],
+ defaultValue: "application/x-www-form-urlencoded"
+});
+
+// .encoding
+reflectLimitedEnumerated({
+ element: document.createElement("form"),
+ attribute: { idl: "encoding", content: "enctype" },
+ validValues: [ "application/x-www-form-urlencoded", "multipart/form-data",
+ "text/plain" ],
+ invalidValues: [ "", "foo", "tulip", "multipart/foo" ],
+ defaultValue: "application/x-www-form-urlencoded"
+});
+
+// .method
+reflectLimitedEnumerated({
+ element: document.createElement("form"),
+ attribute: "method",
+ validValues: [ "get", "post" ],
+ invalidValues: [ "", "foo", "tulip" ],
+ defaultValue: "get"
+});
+
+// .name
+reflectString({
+ element: document.createElement("form"),
+ attribute: "name",
+});
+
+// .noValidate
+reflectBoolean({
+ element: document.createElement("form"),
+ attribute: "noValidate",
+});
+
+// .target
+reflectString({
+ element: document.createElement("form"),
+ attribute: "target",
+ otherValues: [ "_blank", "_self", "_parent", "_top" ],
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_form_named_getter_dynamic.html b/dom/html/test/forms/test_form_named_getter_dynamic.html
new file mode 100644
index 0000000000..4a19768453
--- /dev/null
+++ b/dom/html/test/forms/test_form_named_getter_dynamic.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=377413
+-->
+<head>
+ <title>Test for Bug 377413</title>
+ <script type="text/javascript" src="/resources/testharness.js"></script>
+ <link rel='stylesheet' href='/resources/testharness.css'>
+ <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377413">Mozilla Bug 377413</a>
+<p id="log"></p>
+<div id="content">
+ <form>
+ <table>
+ <tbody>
+ </tbody>
+ </table>
+ </form>
+</div>
+
+<script type="text/javascript">
+
+/** Tests for Bug 377413 **/
+var tb = document.getElementsByTagName('tbody')[0];
+
+test(function(){
+ tb.innerHTML = '<tr><td><input name="fooboo"></td></tr>';
+ document.forms[0].fooboo.value = 'testme';
+ document.getElementsByTagName('table')[0].deleteRow(0);
+ assert_equals(document.forms[0].fooboo, undefined);
+}, "no element reference after deleting it with deleteRow()");
+
+test(function(){
+ var b = tb.appendChild(document.createElement('tr')).appendChild(document.createElement('td')).appendChild(document.createElement('button'));
+ b.name = b.value = 'boofoo';
+ assert_equals(document.forms[0].elements[0].value, 'boofoo');
+}, 'element value set correctly');
+
+test(function(){
+ assert_true('boofoo' in document.forms[0]);
+}, 'element name has created property on form');
+
+test(function(){
+ tb.innerHTML = '';
+ assert_false('boofoo' in document.forms[0]);
+}, "no element reference after deleting it by setting innerHTML");
+
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_formaction_attribute.html b/dom/html/test/forms/test_formaction_attribute.html
new file mode 100644
index 0000000000..0dee2f172d
--- /dev/null
+++ b/dom/html/test/forms/test_formaction_attribute.html
@@ -0,0 +1,169 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=566160
+-->
+<head>
+ <title>Test for Bug 566160</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566160">Mozilla Bug 566160</a>
+<p id="display"></p>
+<style>
+ iframe { width: 130px; height: 100px;}
+</style>
+<iframe name='frame1' id='frame1'></iframe>
+<iframe name='frame2' id='frame2'></iframe>
+<iframe name='frame3' id='frame3'></iframe>
+<iframe name='frame3bis' id='frame3bis'></iframe>
+<iframe name='frame4' id='frame4'></iframe>
+<iframe name='frame5' id='frame5'></iframe>
+<iframe name='frame6' id='frame6'></iframe>
+<iframe name='frame7' id='frame7'></iframe>
+<div id="content">
+ <!-- submit controls with formaction that are validated with a CLICK -->
+ <form target="frame1" action="FAIL.html" method="GET">
+ <input name='foo' value='foo'>
+ <input type='submit' id='is' formaction="PASS.html">
+ </form>
+ <form target="frame2" action="FAIL.html" method="GET">
+ <input name='bar' value='bar'>
+ <input type='image' id='ii' formaction="PASS.html">
+ </form>
+ <form target="frame3" action="FAIL.html" method="GET">
+ <input name='tulip' value='tulip'>
+ <button type='submit' id='bs' formaction="PASS.html">submit</button>
+ </form>
+ <form target="frame3bis" action="FAIL.html" method="GET">
+ <input name='tulipbis' value='tulipbis'>
+ <button type='submit' id='bsbis' formaction="PASS.html">submit</button>
+ </form>
+
+ <!-- submit controls with formaction that are validated with ENTER -->
+ <form target="frame4" action="FAIL.html" method="GET">
+ <input name='footulip' value='footulip'>
+ <input type='submit' id='is2' formaction="PASS.html">
+ </form>
+ <form target="frame5" action="FAIL.html" method="GET">
+ <input name='foobar' value='foobar'>
+ <input type='image' id='ii2' formaction="PASS.html">
+ </form>
+ <form target="frame6" action="FAIL.html" method="GET">
+ <input name='tulip2' value='tulip2'>
+ <button type='submit' id='bs2' formaction="PASS.html">submit</button>
+ </form>
+
+ <!-- check that when submitting a from from an element
+ which is not a submit control, @formaction isn't used -->
+ <form target='frame7' action="PASS.html" method="GET">
+ <input id='enter' name='input' value='enter' formaction="FAIL.html">
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 566160 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+const BASE_URI = `${location.origin}/tests/dom/html/test/forms/PASS.html`;
+var gTestResults = {
+ frame1: BASE_URI + "?foo=foo",
+ frame2: BASE_URI + "?bar=bar&x=0&y=0",
+ frame3: BASE_URI + "?tulip=tulip",
+ frame3bis: BASE_URI + "?tulipbis=tulipbis",
+ frame4: BASE_URI + "?footulip=footulip",
+ frame5: BASE_URI + "?foobar=foobar&x=0&y=0",
+ frame6: BASE_URI + "?tulip2=tulip2",
+ frame7: BASE_URI + "?input=enter",
+};
+
+var gPendingLoad = 0; // Has to be set after depending on the frames number.
+
+function runTests()
+{
+ // We add a load event for the frames which will be called when the forms
+ // will be submitted.
+ var frames = [ document.getElementById('frame1'),
+ document.getElementById('frame2'),
+ document.getElementById('frame3'),
+ document.getElementById('frame3bis'),
+ document.getElementById('frame4'),
+ document.getElementById('frame5'),
+ document.getElementById('frame6'),
+ document.getElementById('frame7'),
+ ];
+ gPendingLoad = frames.length;
+
+ for (var i=0; i<frames.length; i++) {
+ frames[i].setAttribute('onload', "frameLoaded(this);");
+ }
+
+ /**
+ * We are going to focus each element before interacting with either for
+ * simulating the ENTER key (synthesizeKey) or a click (synthesizeMouse) or
+ * using .click(). This because it may be needed (ENTER) and because we want
+ * to have the element visible in the iframe.
+ *
+ * Focusing the first element (id='is') is launching the tests.
+ */
+ document.getElementById('is').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('is'), 5, 5, {});
+ document.getElementById('ii').focus();
+ }, {once: true});
+
+ document.getElementById('ii').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('ii'), 5, 5, {});
+ document.getElementById('bs').focus();
+ }, {once: true});
+
+ document.getElementById('bs').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('bs'), 5, 5, {});
+ document.getElementById('bsbis').focus();
+ }, {once: true});
+
+ document.getElementById('bsbis').addEventListener('focus', function(aEvent) {
+ document.getElementById('bsbis').click();
+ document.getElementById('is2').focus();
+ }, {once: true});
+
+ document.getElementById('is2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('ii2').focus();
+ }, {once: true});
+
+ document.getElementById('ii2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('bs2').focus();
+ }, {once: true});
+
+ document.getElementById('bs2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('enter').focus();
+ }, {once: true});
+
+ document.getElementById('enter').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ }, {once: true});
+
+ document.getElementById('is').focus();
+}
+
+function frameLoaded(aFrame) {
+ // Check if formaction/action has the correct behavior.
+ is(aFrame.contentWindow.location.href, gTestResults[aFrame.name],
+ "the action attribute doesn't have the correct behavior");
+
+ if (--gPendingLoad == 0) {
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_formnovalidate_attribute.html b/dom/html/test/forms/test_formnovalidate_attribute.html
new file mode 100644
index 0000000000..2e3714d2fe
--- /dev/null
+++ b/dom/html/test/forms/test_formnovalidate_attribute.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589696
+-->
+<head>
+ <title>Test for Bug 589696</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589696">Mozilla Bug 589696</a>
+<p id="display"></p>
+<iframe style='width:50px; height: 50px;' name='t'></iframe>
+<div id="content">
+ <!-- Next forms should not submit because formnovalidate isn't set on the
+ element used for the submission. -->
+ <form target='t' action='data:text/html,'>
+ <input id='av' required>
+ <input type='submit' formnovalidate>
+ <input id='a' type='submit'>
+ </form>
+ <form target='t' action='data:text/html,'>
+ <input id='bv' type='checkbox' required>
+ <button type='submit' formnovalidate></button>
+ <button id='b' type='submit'></button>
+ </form>
+ <!-- Next form should not submit because formnovalidate only applies for
+ submit controls. -->
+ <form target='t' action='data:text/html,'>
+ <input id='c' required formnovalidate>
+ </form>
+ <!--- Next forms should submit without any validation check. -->
+ <form target='t' action='data:text/html,'>
+ <input id='dv' required>
+ <input id='d' type='submit' formnovalidate>
+ </form>
+ <form target='t' action='data:text/html,'>
+ <input id='ev' type='checkbox' required>
+ <button id='e' type='submit' formnovalidate></button>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 589696 **/
+
+document.getElementById('av').addEventListener("invalid", function(aEvent) {
+ aEvent.target.removeAttribute("invalid", arguments.callee, false);
+ ok(true, "formnovalidate should not apply on if not set on the submit " +
+ "control used for the submission");
+ document.getElementById('b').click();
+});
+
+document.getElementById('bv').addEventListener("invalid", function(aEvent) {
+ aEvent.target.removeAttribute("invalid", arguments.callee, false);
+ ok(true, "formnovalidate should not apply on if not set on the submit " +
+ "control used for the submission");
+ var c = document.getElementById('c');
+ c.focus();
+ synthesizeKey("KEY_Enter");
+});
+
+document.getElementById('c').addEventListener("invalid", function(aEvent) {
+ aEvent.target.removeAttribute("invalid", arguments.callee, false);
+ ok(true, "formnovalidate should only apply on submit controls");
+ document.getElementById('d').click();
+});
+
+document.forms[3].addEventListener("submit", function(aEvent) {
+ aEvent.target.removeAttribute("submit", arguments.callee, false);
+ ok(true, "formnovalidate applies if set on the submit control used for the submission");
+ document.getElementById('e').click();
+});
+
+document.forms[4].addEventListener("submit", function(aEvent) {
+ aEvent.target.removeAttribute("submit", arguments.callee, false);
+ ok(true, "formnovalidate applies if set on the submit control used for the submission");
+ SimpleTest.executeSoon(SimpleTest.finish);
+});
+
+/**
+ * We have to be sure invalid events behave as expected.
+ * They should be sent before the submit event so we can just create a test
+ * failure if we got one when unexpected. All of them should be caught if
+ * sent.
+ * At worst, we got random green which isn't harmful.
+ * If expected, they will be part of the chain reaction.
+ */
+function unexpectedInvalid(aEvent)
+{
+ aEvent.target.removeAttribute("invalid", unexpectedInvalid, false);
+ ok(false, "invalid event should not be sent");
+}
+
+document.getElementById('dv').addEventListener("invalid", unexpectedInvalid);
+document.getElementById('ev').addEventListener("invalid", unexpectedInvalid);
+
+/**
+ * Some submission have to be canceled. In that case, the submit events should
+ * not be sent.
+ * Same behavior as unexpected invalid events.
+ */
+function unexpectedSubmit(aEvent)
+{
+ aEvent.target.removeAttribute("submit", unexpectedSubmit, false);
+ ok(false, "submit event should not be sent");
+}
+
+document.forms[0].addEventListener("submit", unexpectedSubmit);
+document.forms[1].addEventListener("submit", unexpectedSubmit);
+document.forms[2].addEventListener("submit", unexpectedSubmit);
+
+SimpleTest.waitForExplicitFinish();
+
+// This is going to call all the tests (with a chain reaction).
+SimpleTest.waitForFocus(function() {
+ document.getElementById('a').click();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_attributes_reflection.html b/dom/html/test/forms/test_input_attributes_reflection.html
new file mode 100644
index 0000000000..348ea0f80d
--- /dev/null
+++ b/dom/html/test/forms/test_input_attributes_reflection.html
@@ -0,0 +1,271 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLInputElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLInputElement attributes reflection **/
+
+// TODO: maybe make those reflections be tested against all input types.
+
+function testWidthHeight(attr) {
+ var element = document.createElement('input');
+ is(element[attr], 0, attr + ' always returns 0 if not type=image');
+ element.setAttribute(attr, '42');
+ is(element[attr], 0, attr + ' always returns 0 if not type=image');
+ is(element.getAttribute(attr), '42');
+ element[attr] = 0;
+ is(element.getAttribute(attr), '0', 'setting ' + attr + ' changes the content attribute');
+ element[attr] = 12;
+ is(element.getAttribute(attr), '12', 'setting ' + attr + ' changes the content attribute');
+
+ element.removeAttribute(attr);
+ is(element.getAttribute(attr), null);
+
+ element = document.createElement('input');
+ element.type = 'image';
+ element.style.display = "inline";
+ document.getElementById('content').appendChild(element);
+ isnot(element[attr], 0, attr + ' represents the dimension of the element if type=image');
+
+ element.setAttribute(attr, '42');
+ isnot(element[attr], 0, attr + ' represents the dimension of the element if type=image');
+ isnot(element[attr], 42, attr + ' represents the dimension of the element if type=image');
+ is(element.getAttribute(attr), '42');
+ element[attr] = 0;
+ is(element.getAttribute(attr), '0', 'setting ' + attr + ' changes the content attribute');
+ element[attr] = 12;
+ is(element.getAttribute(attr), '12', 'setting ' + attr + ' changes the content attribute');
+
+ element.removeAttribute(attr);
+ is(element.getAttribute(attr), null);
+}
+
+// .accept
+reflectString({
+ element: document.createElement("input"),
+ attribute: "accept",
+ otherValues: [ "audio/*", "video/*", "image/*", "image/png",
+ "application/msword", "appplication/pdf" ],
+});
+
+// .alt
+reflectString({
+ element: document.createElement("input"),
+ attribute: "alt",
+});
+
+// .autocomplete
+reflectLimitedEnumerated({
+ element: document.createElement("input"),
+ attribute: "autocomplete",
+ validValues: [ "on", "off" ],
+ invalidValues: [ "", "default", "foo", "tulip" ],
+});
+
+// .autofocus
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: "autofocus",
+});
+
+// .defaultChecked
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: { idl: "defaultChecked", content: "checked" },
+});
+
+// .checked doesn't reflect a content attribute.
+
+// .dirName
+reflectString({
+ element: document.createElement("input"),
+ attribute: "dirName"
+});
+
+// .disabled
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: "disabled",
+});
+
+// TODO: form (HTMLFormElement)
+// TODO: files (FileList)
+
+// .formAction
+reflectURL({
+ element: document.createElement("button"),
+ attribute: "formAction",
+});
+
+// .formEnctype
+reflectLimitedEnumerated({
+ element: document.createElement("input"),
+ attribute: "formEnctype",
+ validValues: [ "application/x-www-form-urlencoded", "multipart/form-data",
+ "text/plain" ],
+ invalidValues: [ "", "foo", "tulip", "multipart/foo" ],
+ defaultValue: { invalid: "application/x-www-form-urlencoded", missing: "" }
+});
+
+// .formMethod
+reflectLimitedEnumerated({
+ element: document.createElement("input"),
+ attribute: "formMethod",
+ validValues: [ "get", "post" ],
+ invalidValues: [ "", "foo", "tulip" ],
+ defaultValue: { invalid: "get", missing: "" }
+});
+
+// .formNoValidate
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: "formNoValidate",
+});
+
+// .formTarget
+reflectString({
+ element: document.createElement("input"),
+ attribute: "formTarget",
+ otherValues: [ "_blank", "_self", "_parent", "_top" ],
+});
+
+// .height
+testWidthHeight('height');
+
+// .indeterminate doesn't reflect a content attribute.
+
+// TODO: list (HTMLElement)
+
+// .max
+reflectString({
+ element: document.createElement('input'),
+ attribute: 'max',
+});
+
+// .maxLength
+reflectInt({
+ element: document.createElement("input"),
+ attribute: "maxLength",
+ nonNegative: true,
+});
+
+// .min
+reflectString({
+ element: document.createElement('input'),
+ attribute: 'min',
+});
+
+// .multiple
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: "multiple",
+});
+
+// .name
+reflectString({
+ element: document.createElement("input"),
+ attribute: "name",
+ otherValues: [ "isindex", "_charset_" ],
+});
+
+// .pattern
+reflectString({
+ element: document.createElement("input"),
+ attribute: "pattern",
+ otherValues: [ "[0-9][A-Z]{3}" ],
+});
+
+// .placeholder
+reflectString({
+ element: document.createElement("input"),
+ attribute: "placeholder",
+ otherValues: [ "foo\nbar", "foo\rbar", "foo\r\nbar" ],
+});
+
+// .readOnly
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: "readOnly",
+});
+
+// .required
+reflectBoolean({
+ element: document.createElement("input"),
+ attribute: "required",
+});
+
+// .size
+reflectUnsignedInt({
+ element: document.createElement("input"),
+ attribute: "size",
+ nonZero: true,
+ defaultValue: 20,
+});
+
+// .src (URL)
+reflectURL({
+ element: document.createElement('input'),
+ attribute: 'src',
+});
+
+// .step
+reflectString({
+ element: document.createElement('input'),
+ attribute: 'step',
+});
+
+// .type
+reflectLimitedEnumerated({
+ element: document.createElement("input"),
+ attribute: "type",
+ validValues: [ "hidden", "text", "search", "tel", "url", "email", "password",
+ "checkbox", "radio", "file", "submit", "image", "reset",
+ "button", "date", "time", "number", "range", "color", "month",
+ "week", "datetime-local" ],
+ invalidValues: [ "this-is-probably-a-wrong-type", "", "tulip" ],
+ defaultValue: "text"
+});
+
+// .defaultValue
+reflectString({
+ element: document.createElement("input"),
+ attribute: { idl: "defaultValue", content: "value" },
+ otherValues: [ "foo\nbar", "foo\rbar", "foo\r\nbar" ],
+});
+
+// .value doesn't reflect a content attribute.
+
+// .valueAsDate
+is("valueAsDate" in document.createElement("input"), true,
+ "valueAsDate should be available");
+
+// Deeper check will be done with bug 763305.
+is('valueAsNumber' in document.createElement("input"), true,
+ "valueAsNumber should be available");
+
+// .selectedOption
+todo("selectedOption" in document.createElement("input"),
+ "selectedOption isn't implemented yet");
+
+// .width
+testWidthHeight('width');
+
+// .willValidate doesn't reflect a content attribute.
+// .validity doesn't reflect a content attribute.
+// .validationMessage doesn't reflect a content attribute.
+// .labels doesn't reflect a content attribute.
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_color_input_change_events.html b/dom/html/test/forms/test_input_color_input_change_events.html
new file mode 100644
index 0000000000..f97d54f66e
--- /dev/null
+++ b/dom/html/test/forms/test_input_color_input_change_events.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=885996
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1234567</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test that update() modifies the element value such as done() when it is
+ * not called as a concellation.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var MockColorPicker = SpecialPowers.MockColorPicker;
+
+ var test = runTest();
+
+ SimpleTest.waitForFocus(function() {
+ test.next();
+ });
+
+ function* runTest() {
+ MockColorPicker.init(window);
+ var element = null;
+
+ MockColorPicker.showCallback = function(picker, update) {
+ is(picker.initialColor, element.value);
+
+ var inputEvent = false;
+ var changeEvent = false;
+ element.oninput = function() {
+ inputEvent = true;
+ };
+ element.onchange = function() {
+ changeEvent = true;
+ };
+
+ if (element.dataset.type == 'update') {
+ update('#f00ba4');
+
+ is(inputEvent, true, 'input event should have been received');
+ is(changeEvent, false, 'change event should not have been received');
+
+ inputEvent = changeEvent = false;
+
+ is(element.value, '#f00ba4');
+
+ MockColorPicker.returnColor = '#f00ba7';
+ isnot(element.value, MockColorPicker.returnColor);
+ } else if (element.dataset.type == 'cancel') {
+ MockColorPicker.returnColor = '#bababa';
+ isnot(element.value, MockColorPicker.returnColor);
+ } else if (element.dataset.type == 'done') {
+ MockColorPicker.returnColor = '#098766';
+ isnot(element.value, MockColorPicker.returnColor);
+ } else if (element.dataset.type == 'noop-done') {
+ MockColorPicker.returnColor = element.value;
+ is(element.value, MockColorPicker.returnColor);
+ }
+
+ SimpleTest.executeSoon(function() {
+ if (element.dataset.type == 'cancel') {
+ isnot(element.value, MockColorPicker.returnColor);
+ is(inputEvent, false, 'no input event should have been sent');
+ is(changeEvent, false, 'no change event should have been sent');
+ } else if (element.dataset.type == 'noop-done') {
+ is(element.value, MockColorPicker.returnColor);
+ is(inputEvent, false, 'no input event should have been sent');
+ is(changeEvent, false, 'no change event should have been sent');
+ } else {
+ is(element.value, MockColorPicker.returnColor);
+ is(inputEvent, true, 'input event should have been sent');
+ is(changeEvent, true, 'change event should have been sent');
+ }
+
+ changeEvent = false;
+ element.blur();
+
+ setTimeout(function() {
+ is(changeEvent, false, "change event should not be fired on blur");
+ test.next();
+ });
+ });
+
+ return element.dataset.type == 'cancel' ? "" : MockColorPicker.returnColor;
+ };
+
+ for (var i = 0; i < document.getElementsByTagName('input').length; ++i) {
+ element = document.getElementsByTagName('input')[i];
+ element.focus();
+ synthesizeMouseAtCenter(element, {});
+ yield undefined;
+ };
+
+ MockColorPicker.cleanup();
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
+<p id="display"></p>
+<div id="content">
+ <input type='color' data-type='update'>
+ <input type='color' data-type='cancel'>
+ <input type='color' data-type='done'>
+ <input type='color' data-type='noop-done'>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_color_picker_datalist.html b/dom/html/test/forms/test_input_color_picker_datalist.html
new file mode 100644
index 0000000000..1a268c0701
--- /dev/null
+++ b/dom/html/test/forms/test_input_color_picker_datalist.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ let MockColorPicker = SpecialPowers.MockColorPicker;
+
+ MockColorPicker.init(window);
+
+ MockColorPicker.showCallback = (picker) => {
+ is(picker.defaultColors.length, 2);
+ is(picker.defaultColors[0], "#112233");
+ is(picker.defaultColors[1], "#00ffaa");
+
+ MockColorPicker.cleanup();
+ SimpleTest.finish();
+ }
+
+ let input = document.querySelector("input");
+ synthesizeMouseAtCenter(input, {});
+}
+
+SimpleTest.waitForFocus(runTest);
+</script>
+</head>
+<body>
+<input type="color" list="color-list">
+<datalist id="color-list">
+ <option value="#112233"></option>
+ <option value="black"></option> <!-- invalid -->
+ <option value="#000000" disabled></option>
+ <option value="#00FFAA"></option>
+ <option></option>
+</datalist>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_color_picker_initial.html b/dom/html/test/forms/test_input_color_picker_initial.html
new file mode 100644
index 0000000000..c7467c7520
--- /dev/null
+++ b/dom/html/test/forms/test_input_color_picker_initial.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=885996
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1234567</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test that the initial value of the nsIColorPicker is the current value of
+ the <input type='color'> element. **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var MockColorPicker = SpecialPowers.MockColorPicker;
+
+ var test = runTest();
+
+ SimpleTest.waitForFocus(function() {
+ test.next();
+ });
+
+ function* runTest() {
+ MockColorPicker.init(window);
+ var element = null;
+
+ MockColorPicker.showCallback = function(picker) {
+ is(picker.initialColor, element.value);
+ SimpleTest.executeSoon(function() {
+ test.next();
+ });
+ return "";
+ };
+
+ for (var i = 0; i < document.getElementsByTagName('input').length; ++i) {
+ element = document.getElementsByTagName('input')[i];
+ if (element.parentElement.id === 'dynamic-values') {
+ element.value = '#deadbe';
+ }
+ synthesizeMouseAtCenter(element, {});
+ yield undefined;
+ };
+
+ MockColorPicker.cleanup();
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
+<p id="display"></p>
+<div id="content">
+ <div id='valid-values'>
+ <input type='color' value='#ff00ff'>
+ <input type='color' value='#ab3275'>
+ <input type='color' value='#abcdef'>
+ <input type='color' value='#ABCDEF'>
+ </div>
+ <div id='invalid-values'>
+ <input type='color' value='ffffff'>
+ <input type='color' value='#abcdez'>
+ <input type='color' value='#0123456'>
+ </div>
+ <div id='dynamic-values'>
+ <input type='color' value='#ab4594'>
+ <input type='color' value='#984534'>
+ <input type='color' value='#f8b9a0'>
+ </div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_color_picker_popup.html b/dom/html/test/forms/test_input_color_picker_popup.html
new file mode 100644
index 0000000000..9fbebf15bc
--- /dev/null
+++ b/dom/html/test/forms/test_input_color_picker_popup.html
@@ -0,0 +1,144 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=885996
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1234567</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style> body { font-family: serif } </style>
+ <script type="application/javascript">
+
+ /** Test the behaviour of the <input type='color'> when clicking on it from
+ different ways. **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var MockColorPicker = SpecialPowers.MockColorPicker;
+
+ var test = runTest();
+ var testData = [
+ { id: 'normal', result: true },
+ { id: 'hidden', result: false },
+ { id: 'normal', type: 'untrusted', result: true },
+ { id: 'normal', type: 'prevent-default-1', result: false },
+ { id: 'normal', type: 'prevent-default-2', result: false },
+ { id: 'normal', type: 'click-method', result: true },
+ { id: 'normal', type: 'show-picker', result: true },
+ { id: 'normal', type: 'right-click', result: false },
+ { id: 'normal', type: 'middle-click', result: false },
+ { id: 'label-1', result: true },
+ { id: 'label-2', result: true },
+ { id: 'label-3', result: true },
+ { id: 'label-4', result: true },
+ { id: 'button-click', result: true },
+ { id: 'button-down', result: true },
+ { id: 'button-up', result: true },
+ { id: 'div-click', result: true },
+ { id: 'div-click-on-demand', result: true },
+ ];
+
+ SimpleTest.waitForFocus(function() {
+ test.next();
+ });
+
+ function* runTest() {
+ let currentTest = null;
+ MockColorPicker.init(window);
+ var element = null;
+
+ MockColorPicker.showCallback = function(picker) {
+ ok(currentTest.result);
+ SimpleTest.executeSoon(function() {
+ test.next();
+ });
+ return "";
+ };
+
+ while (testData.length) {
+ currentTest = testData.shift();
+ element = document.getElementById(currentTest.id);
+
+ // To make sure we can actually click on the element.
+ element.focus();
+
+ switch (currentTest.type) {
+ case 'untrusted':
+ var e = document.createEvent('MouseEvents');
+ e.initEvent('click', true, false);
+ document.getElementById(element.dispatchEvent(e));
+ break;
+ case 'prevent-default-1':
+ element.onclick = function() {
+ return false;
+ };
+ element.click();
+ element.onclick = function() {};
+ break;
+ case 'prevent-default-2':
+ element.onclick = function(event) {
+ event.preventDefault();
+ };
+ element.click();
+ element.onclick = function() {};
+ break;
+ case 'click-method':
+ element.click();
+ break;
+ case 'show-picker':
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ element.showPicker();
+ break;
+ case 'right-click':
+ synthesizeMouseAtCenter(element, { button: 2 });
+ break;
+ case 'middle-click':
+ synthesizeMouseAtCenter(element, { button: 1 });
+ break;
+ default:
+ synthesizeMouseAtCenter(element, {});
+ }
+
+ if (!currentTest.result) {
+ setTimeout(function() {
+ setTimeout(function() {
+ ok(true);
+ SimpleTest.executeSoon(function() {
+ test.next();
+ });
+ });
+ });
+ }
+ yield undefined;
+ };
+
+ MockColorPicker.cleanup();
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
+<p id="display"></p>
+<div id="content">
+ <input type='color' id='normal'>
+ <input type='color' id='hidden' hidden>
+ <label id='label-1'>foo<input type='color'></label>
+ <label id='label-2' for='labeled-2'>foo</label><input id='labeled-2' type='color'></label>
+ <label id='label-3'>foo<input type='color'></label>
+ <label id='label-4' for='labeled-4'>foo</label><input id='labeled-4' type='color'></label>
+ <input id='by-button' type='color'>
+ <button id='button-click' onclick="document.getElementById('by-button').click();">click</button>
+ <button id='button-down' onclick="document.getElementById('by-button').click();">click</button>
+ <button id='button-up' onclick="document.getElementById('by-button').click();">click</button>
+ <div id='div-click' onclick="document.getElementById('by-button').click();">click</div>
+ <div id='div-click-on-demand' onclick="var i=document.createElement('input'); i.type='color'; i.click();">click</div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_color_picker_update.html b/dom/html/test/forms/test_input_color_picker_update.html
new file mode 100644
index 0000000000..5c22b667e1
--- /dev/null
+++ b/dom/html/test/forms/test_input_color_picker_update.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=885996
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1234567</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style> body { font-family: serif } </style>
+ <script type="application/javascript">
+
+ /** Test that update() modifies the element value such as done() when it is
+ * not called as a concellation.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var MockColorPicker = SpecialPowers.MockColorPicker;
+
+ var test = runTest();
+
+ SimpleTest.waitForFocus(function() {
+ test.next();
+ });
+
+ function* runTest() {
+ MockColorPicker.init(window);
+ var element = null;
+
+ MockColorPicker.showCallback = function(picker, update) {
+ is(picker.initialColor, element.value);
+
+ if (element.dataset.type == 'update') {
+ update('#f00ba4');
+ is(element.value, '#f00ba4');
+
+ MockColorPicker.returnColor = '#f00ba7';
+ isnot(element.value, MockColorPicker.returnColor);
+ } else if (element.dataset.type == 'cancel') {
+ MockColorPicker.returnColor = '#bababa';
+ isnot(element.value, MockColorPicker.returnColor);
+ } else if (element.dataset.type == 'done') {
+ MockColorPicker.returnColor = '#098766';
+ isnot(element.value, MockColorPicker.returnColor);
+ }
+
+ SimpleTest.executeSoon(function() {
+ if (element.dataset.type == 'cancel') {
+ isnot(element.value, MockColorPicker.returnColor);
+ } else {
+ is(element.value, MockColorPicker.returnColor);
+ }
+
+ test.next();
+ });
+
+ return element.dataset.type == 'cancel' ? "" : MockColorPicker.returnColor;
+ };
+
+ for (var i = 0; i < document.getElementsByTagName('input').length; ++i) {
+ element = document.getElementsByTagName('input')[i];
+ synthesizeMouseAtCenter(element, {});
+ yield undefined;
+ };
+
+ MockColorPicker.cleanup();
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885996">Mozilla Bug 885996</a>
+<p id="display"></p>
+<div id="content">
+ <input type='color' data-type='update'>
+ <input type='color' data-type='cancel'>
+ <input type='color' data-type='done'>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_date_bad_input.html b/dom/html/test/forms/test_input_date_bad_input.html
new file mode 100644
index 0000000000..516d48263f
--- /dev/null
+++ b/dom/html/test/forms/test_input_date_bad_input.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1372369
+-->
+<head>
+ <title>Test for &lt;input type='date'&gt; bad input validity state</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input { background-color: rgb(0,0,0) !important; }
+ :valid { background-color: rgb(0,255,0) !important; }
+ :invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1372369">Mozilla Bug 1372369</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ <input type="date" id="input">
+ <form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for <input type='date'> bad input validity state **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+const DATE_BAD_INPUT_MSG = "Please enter a valid date.";
+const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
+
+function checkValidity(aElement, aIsBadInput) {
+ is(aElement.validity.valid, !aIsBadInput,
+ "validity.valid should be " + (aIsBadInput ? "false" : "true"));
+ is(aElement.validity.badInput, !!aIsBadInput,
+ "validity.badInput should be " + (aIsBadInput ? "true" : "false"));
+ is(aElement.validationMessage, aIsBadInput ? DATE_BAD_INPUT_MSG : "",
+ "validationMessage should be: " + (aIsBadInput ? DATE_BAD_INPUT_MSG : ""));
+
+ is(window.getComputedStyle(aElement).getPropertyValue('background-color'),
+ aIsBadInput ? "rgb(255, 0, 0)" : "rgb(0, 255, 0)",
+ (aIsBadInput ? ":invalid" : "valid") + " pseudo-class should apply");
+}
+
+function sendKeys(aKey) {
+ if (aKey.startsWith("KEY_")) {
+ synthesizeKey(aKey);
+ } else {
+ sendString(aKey);
+ }
+}
+
+function test() {
+ var elem = document.getElementById("input");
+
+ elem.focus();
+ sendKeys("02312017");
+ elem.blur();
+ checkValidity(elem, true);
+
+ elem.focus();
+ sendKeys("02292016");
+ elem.blur();
+ checkValidity(elem, false);
+
+ elem.focus();
+ sendKeys("06312000");
+ elem.blur();
+ checkValidity(elem, true);
+
+ // Removing some of the fields keeps the input as invalid.
+ elem.focus();
+ sendKeys("KEY_Backspace");
+ elem.blur();
+ checkValidity(elem, true);
+
+ // Removing all of the fields manually makes the input valid (but empty) again.
+ elem.focus();
+ sendKeys("KEY_ArrowRight");
+ sendKeys("KEY_Backspace");
+ sendKeys("KEY_ArrowRight");
+ sendKeys("KEY_Delete");
+ elem.blur();
+ checkValidity(elem, false);
+
+ elem.focus();
+ sendKeys("02292017");
+ elem.blur();
+ checkValidity(elem, true);
+
+ // Clearing all fields should clear bad input validity state as well.
+ elem.focus();
+ synthesizeKey("KEY_Backspace", { accelKey: true });
+ checkValidity(elem, false);
+
+ sendKeys("22334444");
+ elem.blur();
+ elem.focus();
+ synthesizeKey("KEY_Delete", { accelKey: true });
+ checkValidity(elem, false);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_date_key_events.html b/dom/html/test/forms/test_input_date_key_events.html
new file mode 100644
index 0000000000..387cb37af7
--- /dev/null
+++ b/dom/html/test/forms/test_input_date_key_events.html
@@ -0,0 +1,270 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1286182
+-->
+<head>
+ <title>Test key events for date control</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1286182">Mozilla Bug 1286182</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1804669">Mozilla Bug 1804669</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input" type="date">
+ <div id="host"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+var testData = [
+ /**
+ * keys: keys to send to the input element.
+ * initialVal: initial value set to the input element.
+ * expectedVal: expected value of the input element after sending the keys.
+ */
+ {
+ // Type 11222016, default order is month, day, year.
+ keys: ["11222016"],
+ initialVal: "",
+ expectedVal: "2016-11-22"
+ },
+ {
+ // Type 3 in the month field will automatically advance to the day field,
+ // then type 5 in the day field will automatically advance to the year
+ // field.
+ keys: ["352016"],
+ initialVal: "",
+ expectedVal: "2016-03-05"
+ },
+ {
+ // Type 13 in the month field will set it to the maximum month, which is
+ // 12.
+ keys: ["13012016"],
+ initialVal: "",
+ expectedVal: "2016-12-01"
+ },
+ {
+ // Type 00 in the month field will set it to the minimum month, which is 1.
+ keys: ["00012016"],
+ initialVal: "",
+ expectedVal: "2016-01-01"
+ },
+ {
+ // Type 33 in the day field will set it to the maximum day, which is 31.
+ keys: ["12332016"],
+ initialVal: "",
+ expectedVal: "2016-12-31"
+ },
+ {
+ // Type 00 in the day field will set it to the minimum day, which is 1.
+ keys: ["12002016"],
+ initialVal: "",
+ expectedVal: "2016-12-01"
+ },
+ {
+ // Type 275769 in the year field will set it to 0069, because the
+ // 5th digit will erase the previous 4 digits.
+ keys: ["0101275769"],
+ initialVal: "",
+ expectedVal: "0069-01-01"
+ },
+ {
+ // Type 0000 in the year field will set it to the minimum year, which is
+ // 0001.
+ keys: ["01010000"],
+ initialVal: "",
+ expectedVal: "0001-01-01"
+ },
+ {
+ // Advance to year field and decrement.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_ArrowDown"],
+ initialVal: "2016-11-25",
+ expectedVal: "2015-11-25"
+ },
+ {
+ // Right key should do the same thing as TAB key.
+ keys: ["KEY_ArrowRight", "KEY_ArrowRight", "KEY_ArrowDown"],
+ initialVal: "2016-11-25",
+ expectedVal: "2015-11-25"
+ },
+ {
+ // Advance to day field then back to month field and decrement.
+ keys: ["KEY_ArrowRight", "KEY_ArrowLeft", "KEY_ArrowDown"],
+ initialVal: "2000-05-01",
+ expectedVal: "2000-04-01"
+ },
+ {
+ // Focus starts on the first field, month in this case, and increment.
+ keys: ["KEY_ArrowUp"],
+ initialVal: "2000-03-01",
+ expectedVal: "2000-04-01"
+ },
+ {
+ // Advance to day field and decrement.
+ keys: ["KEY_Tab", "KEY_ArrowDown"],
+ initialVal: "1234-01-01",
+ expectedVal: "1234-01-31"
+ },
+ {
+ // Advance to day field and increment.
+ keys: ["KEY_Tab", "KEY_ArrowUp"],
+ initialVal: "1234-01-01",
+ expectedVal: "1234-01-02"
+ },
+ {
+ // PageUp on month field increments month by 3.
+ keys: ["KEY_PageUp"],
+ initialVal: "1999-01-01",
+ expectedVal: "1999-04-01"
+ },
+ {
+ // PageDown on month field decrements month by 3.
+ keys: ["KEY_PageDown"],
+ initialVal: "1999-01-01",
+ expectedVal: "1999-10-01"
+ },
+ {
+ // PageUp on day field increments day by 7.
+ keys: ["KEY_Tab", "KEY_PageUp"],
+ initialVal: "1999-01-01",
+ expectedVal: "1999-01-08"
+ },
+ {
+ // PageDown on day field decrements day by 7.
+ keys: ["KEY_Tab", "KEY_PageDown"],
+ initialVal: "1999-01-01",
+ expectedVal: "1999-01-25"
+ },
+ {
+ // PageUp on year field increments year by 10.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_PageUp"],
+ initialVal: "1999-01-01",
+ expectedVal: "2009-01-01"
+ },
+ {
+ // PageDown on year field decrements year by 10.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_PageDown"],
+ initialVal: "1999-01-01",
+ expectedVal: "1989-01-01"
+ },
+ {
+ // Home key on month field sets it to the minimum month, which is 01.
+ keys: ["KEY_Home"],
+ initialVal: "2016-06-01",
+ expectedVal: "2016-01-01"
+ },
+ {
+ // End key on month field sets it to the maximum month, which is 12.
+ keys: ["KEY_End"],
+ initialVal: "2016-06-01",
+ expectedVal: "2016-12-01"
+ },
+ {
+ // Home key on day field sets it to the minimum day, which is 01.
+ keys: ["KEY_Tab", "KEY_Home"],
+ initialVal: "2016-01-10",
+ expectedVal: "2016-01-01"
+ },
+ {
+ // End key on day field sets it to the maximum day, which is 31.
+ keys: ["KEY_Tab", "KEY_End"],
+ initialVal: "2016-01-10",
+ expectedVal: "2016-01-31"
+ },
+ {
+ // Home key should have no effect on year field.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_Home"],
+ initialVal: "2016-01-01",
+ expectedVal: "2016-01-01"
+ },
+ {
+ // End key should have no effect on year field.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_End"],
+ initialVal: "2016-01-01",
+ expectedVal: "2016-01-01"
+ },
+ {
+ // Incomplete value maps to empty .value.
+ keys: ["1111"],
+ initialVal: "",
+ expectedVal: ""
+ },
+ {
+ // Backspace key should clean a month field and map to empty .value.
+ keys: ["KEY_Backspace"],
+ initialVal: "2016-01-01",
+ expectedVal: ""
+ },
+ {
+ // Backspace key should clean a day field and map to empty .value.
+ keys: ["KEY_Tab", "KEY_Backspace"],
+ initialVal: "2016-01-01",
+ expectedVal: ""
+ },
+ {
+ // Backspace key should clean a year field and map to empty .value.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_Backspace"],
+ initialVal: "2016-01-01",
+ expectedVal: ""
+ },
+ {
+ // Backspace key on Calendar button should not change a value.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_Tab", "KEY_Backspace"],
+ initialVal: "2016-01-01",
+ expectedVal: "2016-01-01"
+ },
+];
+
+function sendKeys(aKeys) {
+ for (let i = 0; i < aKeys.length; i++) {
+ let key = aKeys[i];
+ if (key.startsWith("KEY_")) {
+ synthesizeKey(key);
+ } else {
+ sendString(key);
+ }
+ }
+}
+
+function test() {
+ document.querySelector("#host").attachShadow({ mode: "open" }).innerHTML = `
+ <input type="date">
+ `;
+
+ function chromeListener(e) {
+ ok(false, "Picker should not be opened when dispatching untrusted click.");
+ }
+
+ for (const elem of [document.getElementById("input"), document.getElementById("host").shadowRoot.querySelector("input")]) {
+ for (let { keys, initialVal, expectedVal } of testData) {
+ elem.focus();
+ elem.value = initialVal;
+ sendKeys(keys);
+ is(elem.value, expectedVal,
+ "Test with " + keys + ", result should be " + expectedVal);
+ elem.value = "";
+ elem.blur();
+ }
+ SpecialPowers.addChromeEventListener("MozOpenDateTimePicker",
+ chromeListener);
+ elem.click();
+ SpecialPowers.removeChromeEventListener("MozOpenDateTimePicker",
+ chromeListener);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_calendar_button.html b/dom/html/test/forms/test_input_datetime_calendar_button.html
new file mode 100644
index 0000000000..970eee9027
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_calendar_button.html
@@ -0,0 +1,179 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1479708
+-->
+<head>
+<title>Test required date/datetime-local input's Calendar button</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Created for <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1479708">Mozilla Bug 1479708</a> and updated by <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1676068">Mozilla Bug 1676068</a> and <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1865885">Mozilla Bug 1865885</a>
+<p id="display"></p>
+<div id="content">
+<input type="date" id="id_date" value="2017-06-08">
+<input type="time" id="id_time" value="10:30">
+<input type="datetime-local" id="id_datetime-local" value="2017-06-08T10:30">
+<input type="date" id="id_date_required" value="2017-06-08" required>
+<input type="time" id="id_time_required" value="10:30" required>
+<input type="datetime-local" id="id_datetime-local_required" value="2017-06-08T10:30" required>
+<input type="date" id="id_date_readonly" value="2017-06-08" readonly>
+<input type="time" id="id_time_readonly" value="10:30" readonly>
+<input type="datetime-local" id="id_datetime-local_readonly" value="2017-06-08T10:30" readonly>
+<input type="date" id="id_date_disabled" value="2017-06-08" disabled>
+<input type="time" id="id_time_disabled" value="10:30" disabled>
+<input type="datetime-local" id="id_datetime-local_disabled" value="2017-06-08T10:30" disabled>
+</div>
+<pre id="test">
+<script class="testbody">
+
+const kTypes = ["date", "time", "datetime-local"];
+
+function id_for_type(type, kind) {
+ return "id_" + type + (kind ? "_" + kind : "");
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ // Initial load.
+ assert_calendar_visible_all("");
+ assert_calendar_visible_all("required");
+ assert_calendar_hidden_all("readonly");
+ assert_calendar_hidden_all("disabled");
+
+ // Dynamic toggling.
+ test_make_readonly("");
+ test_make_editable("readonly");
+ test_disabled_field_disabled();
+
+ // Now toggle the inputs to the initial state, but while being
+ // display: none. This tests for bug 1567191.
+ for (const input of document.querySelectorAll("input")) {
+ input.style.display = "none";
+ is(input.getBoundingClientRect().width, 0, "Should be undisplayed");
+ }
+
+ test_make_readonly("readonly");
+ test_make_editable("");
+
+ // And test other toggling as well.
+ test_readonly_field_disabled();
+ test_disabled_field_disabled();
+
+ SimpleTest.finish();
+});
+
+function test_disabled_field_disabled() {
+ for (let type of kTypes) {
+ const id = id_for_type(type, "disabled");
+ const input = document.getElementById(id);
+
+ ok(input.disabled, `#${id} Should be disabled`);
+ ok(
+ is_calendar_button_hidden(id),
+ `disabled's Calendar button is hidden (${id})`
+ );
+
+ input.disabled = false;
+ ok(!input.disabled, `#${id} Should not be disabled anymore`);
+ if (type === "time") {
+ assert_calendar_hidden(id);
+ } else {
+ ok(
+ !is_calendar_button_hidden(id),
+ `enabled field's Calendar button is not hidden (${id})`
+ );
+ }
+
+ input.disabled = true; // reset to the original state.
+ }
+}
+
+function test_readonly_field_disabled() {
+ for (let type of kTypes) {
+ const id = id_for_type(type, "readonly");
+ const input = document.getElementById(id);
+
+ ok(input.readOnly, `#${id} Should be read-only`);
+ ok(is_calendar_button_hidden(id), `readonly field's Calendar button is hidden (${id})`);
+
+ input.readOnly = false;
+ ok(!input.readOnly, `#${id} Should not be read-only anymore`);
+ if (type === "time") {
+ assert_calendar_hidden(id);
+ } else {
+ ok(
+ !is_calendar_button_hidden(id),
+ `non-readonly field's Calendar button is not hidden (${id})`
+ );
+ }
+
+ input.readOnly = true; // reset to the original state.
+ }
+}
+
+function test_make_readonly(kind) {
+ for (let type of kTypes) {
+ const id = id_for_type(type, kind);
+ const input = document.getElementById(id);
+ is(input.readOnly, false, `Precondition: input #${id} is editable`);
+
+ input.readOnly = true;
+ assert_calendar_hidden(id);
+ }
+}
+
+function test_make_editable(kind) {
+ for (let type of kTypes) {
+ const id = id_for_type(type, kind);
+ const input = document.getElementById(id);
+ is(input.readOnly, true, `Precondition: input #${id} is read-only`);
+
+ input.readOnly = false;
+ if (type === "time") {
+ assert_calendar_hidden(id);
+ } else {
+ assert_calendar_visible(id);
+ }
+ }
+}
+
+function assert_calendar_visible_all(kind) {
+ for (let type of kTypes) {
+ if (type === "time") {
+ assert_calendar_hidden(id_for_type(type, kind));
+ } else {
+ assert_calendar_visible(id_for_type(type, kind));
+ }
+ }
+}
+function assert_calendar_visible(id) {
+ const isCalendarButtonHidden = is_calendar_button_hidden(id);
+ ok(!isCalendarButtonHidden, `Calendar button is not hidden on #${id}`);
+}
+
+function assert_calendar_hidden_all(kind) {
+ for (let type of kTypes) {
+ assert_calendar_hidden(id_for_type(type, kind));
+ }
+}
+
+function assert_calendar_hidden(id) {
+ const isCalendarButtonHidden = is_calendar_button_hidden(id);
+ ok(isCalendarButtonHidden, `Calendar button is hidden on #${id}`);
+}
+
+function is_calendar_button_hidden(id) {
+ const input = document.getElementById(id);
+ const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot;
+ const calendarButton = shadowRoot.getElementById("calendar-button");
+ const calendarButtonDisplay = SpecialPowers.wrap(window).getComputedStyle(calendarButton).display;
+ return calendarButtonDisplay === "none";
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_disabled_focus.html b/dom/html/test/forms/test_input_datetime_disabled_focus.html
new file mode 100644
index 0000000000..68a89b1780
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_disabled_focus.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<title>Test for bugs 1772841 and 1865885</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1772841">Mozilla Bug 1772841</a> and <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1865885">Mozilla Bug 1865885</a>
+<div id="content">
+ <!-- Disabled -->
+ <input type="date" id="date" disabled>
+ <input type="time" id="time" disabled>
+ <input type="datetime-local" id="datetime-local" disabled>
+ <fieldset id="fieldset" disabled>
+ <input type="date" id="fieldset-date">
+ <input type="time" id="fieldset-time">
+ <input type="datetime-local" id="fieldset-datetime-local">
+ </fieldset>
+
+ <!-- Dynamically disabled -->
+ <input type="date" id="date1">
+ <input type="time" id="time1">
+ <input type="datetime-local" id="datetime-local1">
+ <fieldset id="fieldset1">
+ <input type="date" id="fieldset-date1">
+ <input type="time" id="fieldset-time1">
+ <input type="datetime-local" id="fieldset-datetime-local1">
+ </fieldset>
+
+ <!-- Dynamically enabled -->
+ <input type="date" id="date2" disabled>
+ <input type="time" id="time2" disabled>
+ <input type="datetime-local" id="datetime-local2" disabled>
+ <fieldset id="fieldset2" disabled>
+ <input type="date" id="fieldset-date2">
+ <input type="time" id="fieldset-time2">
+ <input type="datetime-local" id="fieldset-datetime-local2">
+ </fieldset>
+</div>
+<script>
+ /*
+ * Test for bugs 1772841 and 1865885
+ * This test checks that when a datetime input element is disabled by itself
+ * or from its containing fieldset, it should not be focusable by click.
+ **/
+
+ add_task(async function() {
+ await SimpleTest.promiseFocus(window);
+ for (let inputId of ["time", "date", "datetime-local", "fieldset-time", "fieldset-date", "fieldset-datetime-local"]) {
+ testFocusState(inputId, /* isDisabled = */ true);
+ testDynamicChange(inputId, "1", /* isDisabling = */ true);
+ testDynamicChange(inputId, "2", /* isDisabling = */ false);
+ }
+ })
+ function testFocusState(inputId, isDisabled) {
+ let input = document.getElementById(inputId);
+
+ document.getElementById("content").click();
+ input.click();
+ if (isDisabled) {
+ isnot(document.activeElement, input, `This disabled ${inputId} input should not be focusable by click`);
+ } else {
+ // The click method won't set the focus on clicked input, thus we
+ // only check that the state is changed to enabled here
+ ok(!input.disabled, `This ${inputId} input is not disabled`);
+ }
+
+ document.getElementById("content").click();
+ synthesizeMouseAtCenter(input, {});
+ if (isDisabled) {
+ isnot(document.activeElement, input, `This disabled ${inputId} input should not be focusable by click`);
+ } else {
+ is(document.activeElement, input, `This enabled ${inputId} input should be focusable by click`);
+ }
+ }
+ function testDynamicChange(inputId, index, isDisabling) {
+ if (inputId.split("-")[0] === "fieldset") {
+ document.getElementById("fieldset" + index).disabled = isDisabling;
+ } else {
+ document.getElementById(inputId + index).disabled = isDisabling;
+ }
+ testFocusState(inputId + index, /* isDisabled = */ isDisabling);
+ }
+</script>
diff --git a/dom/html/test/forms/test_input_datetime_focus_blur.html b/dom/html/test/forms/test_input_datetime_focus_blur.html
new file mode 100644
index 0000000000..bff7b2ceb8
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_focus_blur.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
+-->
+<head>
+ <title>Test focus/blur behaviour for date/time input types</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input_time" type="time">
+ <input id="input_date" type="date">
+ <input id="input_datetime-local" type="datetime-local">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 1288591.
+ * This test checks whether date/time input types' .focus()/.blur() works
+ * correctly. This test also checks when focusing on an date/time input element,
+ * the focus is redirected to the anonymous text control, but the
+ * document.activeElement still returns date/time input element.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function testFocusBlur(type) {
+ let input = document.getElementById("input_" + type);
+ input.focus();
+
+ // The active element returns the date/time input element.
+ let activeElement = document.activeElement;
+ is(activeElement, input, "activeElement should be the date/time input element");
+ is(activeElement.localName, "input", "activeElement should be an input element");
+ is(activeElement.type, type, "activeElement should be of type " + type);
+
+ // Use FocusManager to check that the actual focus is on the anonymous
+ // text control.
+ let fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"]
+ .getService(SpecialPowers.Ci.nsIFocusManager);
+ let focusedElement = fm.focusedElement;
+ is(focusedElement.localName, "span", "focusedElement should be an span element");
+
+ input.blur();
+ isnot(document.activeElement, input, "activeElement should no longer be the datetime input element");
+}
+
+function test() {
+ for (let inputType of ["time", "date", "datetime-local"]) {
+ testFocusBlur(inputType);
+ }
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_focus_blur_events.html b/dom/html/test/forms/test_input_datetime_focus_blur_events.html
new file mode 100644
index 0000000000..2e4e918119
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_focus_blur_events.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1301306
+-->
+<head>
+<title>Test for Bug 1301306</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301306">Mozilla Bug 722599</a>
+<p id="display"></p>
+<div id="content">
+<input type="time" id="input_time" onfocus="++focusEvents[0]"
+ onblur="++blurEvents[0]" onfocusin="++focusInEvents[0]"
+ onfocusout="++focusOutEvents[0]">
+<input type="date" id="input_date" onfocus="++focusEvents[1]"
+ onblur="++blurEvents[1]" onfocusin="++focusInEvents[1]"
+ onfocusout="++focusOutEvents[1]">
+<input type="datetime-local" id="input_datetime-local" onfocus="++focusEvents[2]"
+ onblur="++blurEvents[2]" onfocusin="++focusInEvents[2]"
+ onfocusout="++focusOutEvents[2]">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/**
+ * Test for Bug 1301306.
+ * This test checks that when moving inside the time input element, e.g. jumping
+ * through the inner text boxes, does not fire extra focus/blur events.
+ **/
+
+var inputTypes = ["time", "date", "datetime-local"];
+var focusEvents = [0, 0, 0];
+var focusInEvents = [0, 0, 0];
+var focusOutEvents = [0, 0, 0];
+var blurEvents = [0, 0, 0];
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function test() {
+ for (var i = 0; i < inputTypes.length; i++) {
+ var input = document.getElementById("input_" + inputTypes[i]);
+
+ input.focus();
+ is(focusEvents[i], 1, inputTypes[i] + " input element should have dispatched focus event.");
+ is(focusInEvents[i], 1, inputTypes[i] + " input element should have dispatched focusin event.");
+ is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
+ is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
+
+ // Move around inside the input element's input box.
+ synthesizeKey("KEY_Tab");
+ is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
+ is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
+ is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
+ is(blurEvents[i], 0, inputTypes[i] + " time input element should not have dispatched blur event.");
+
+ synthesizeKey("KEY_ArrowRight");
+ is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
+ is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
+ is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
+ is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
+
+ synthesizeKey("KEY_ArrowLeft");
+ is(focusEvents[i], 1,inputTypes[i] + " input element should not have dispatched focus event.");
+ is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
+ is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
+ is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
+
+ synthesizeKey("KEY_ArrowRight");
+ is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
+ is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
+ is(focusOutEvents[i], 0, inputTypes[i] + " input element should not have dispatched focusout event.");
+ is(blurEvents[i], 0, inputTypes[i] + " input element should not have dispatched blur event.");
+
+ input.blur();
+ is(focusEvents[i], 1, inputTypes[i] + " input element should not have dispatched focus event.");
+ is(focusInEvents[i], 1, inputTypes[i] + " input element should not have dispatched focusin event.");
+ is(focusOutEvents[i], 1, inputTypes[i] + " input element should have dispatched focusout event.");
+ is(blurEvents[i], 1, inputTypes[i] + " input element should have dispatched blur event.");
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_focus_state.html b/dom/html/test/forms/test_input_datetime_focus_state.html
new file mode 100644
index 0000000000..3b771f2394
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_focus_state.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1346085
+-->
+<head>
+ <title>Test moving focus in onfocus/onblur handler</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1346085">Mozilla Bug 1346085</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input_time" type="time">
+ <input id="input_date" type="date">
+ <input id="input_dummy" type="text">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 1346085.
+ * This test checks whether date/time input types' focus state are set
+ * correctly, event when moving focus in onfocus/onblur handler.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function testFocusState(type) {
+ let input = document.getElementById("input_" + type);
+
+ input.focus();
+ let focus = document.querySelector(":focus");
+ let focusRing = document.querySelector(":-moz-focusring");
+ is(focus, input, "input should have :focus state after focus");
+ is(focusRing, input, "input should have :-moz-focusring state after focus");
+
+ input.blur();
+ focus = document.querySelector(":focus");
+ focusRing = document.querySelector(":-moz-focusring");
+ isnot(focus, input, "input should not have :focus state after blur");
+ isnot(focusRing, input, "input should not have :-moz-focusring state after blur");
+
+ input.addEventListener("focus", function() {
+ document.getElementById("input_dummy").focus();
+ }, { once: true });
+
+ input.focus();
+ focus = document.querySelector(":focus");
+ focusRing = document.querySelector(":-moz-focusring");
+ isnot(focus, input, "input should not have :focus state when moving focus in onfocus handler");
+ isnot(focusRing, input, "input should not have :-moz-focusring state when moving focus in onfocus handler");
+
+ input.addEventListener("blur", function() {
+ document.getElementById("input_dummy").focus();
+ }, { once: true });
+
+ input.blur();
+ focus = document.querySelector(":focus");
+ focusRing = document.querySelector(":-moz-focusring");
+ isnot(focus, input, "input should not have :focus state when moving focus in onblur handler");
+ isnot(focusRing, input, "input should not have :-moz-focusring state when moving focus in onblur handler");
+}
+
+function test() {
+ let inputTypes = ["time", "date"];
+
+ for (let i = 0; i < inputTypes.length; i++) {
+ testFocusState(inputTypes[i]);
+ }
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_hidden.html b/dom/html/test/forms/test_input_datetime_hidden.html
new file mode 100644
index 0000000000..7d8a6766a9
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_hidden.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1514040
+-->
+<head>
+ <title>Test construction of hidden date input type</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1514040">Mozilla Bug 1514040</a>
+<p id="display"></p>
+<div id="content">
+ <input id="date" type="date" hidden value="1947-02-28">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+let el = document.getElementById("date");
+ok(el.hidden, "element is hidden");
+is(el.value, "1947-02-28", ".value is set correctly");
+let fieldElements = Array.from(SpecialPowers.wrap(el).openOrClosedShadowRoot.querySelectorAll(".datetime-edit-field"));
+is(fieldElements[0].textContent, "02", "month is set");
+is(fieldElements[1].textContent, "28", "day is set");
+is(fieldElements[2].textContent, "1947", "year is set");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_input_change_events.html b/dom/html/test/forms/test_input_datetime_input_change_events.html
new file mode 100644
index 0000000000..63c8012252
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_input_change_events.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1370858
+-->
+<head>
+<title>Test for Bugs 1370858 and 1804881</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1370858">Mozilla Bug 1370858</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1804881">Mozilla Bug 1804881</a>
+<p id="display"></p>
+<div id="content">
+<input type="time" id="input_time" onchange="++changeEvents[0]"
+ oninput="++inputEvents[0]">
+<input type="date" id="input_date" onchange="++changeEvents[1]"
+ oninput="++inputEvents[1]">
+<input type="datetime-local" id="input_datetime-local" onchange="++changeEvents[2]"
+ oninput="++inputEvents[2]">
+</div>
+<pre id="test">
+<script class="testbody">
+
+/**
+ * Test for Bug 1370858.
+ * Test that change and input events are (not) fired for date/time inputs.
+ **/
+
+const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
+
+var inputTypes = ["time", "date", "datetime-local"];
+var changeEvents = [0, 0, 0];
+var inputEvents = [0, 0, 0];
+var values = ["10:30", "2017-06-08", "2017-06-08T10:30"];
+var expectedValues = [
+ ["09:30", "01:30", "01:25", "", "01:59", "13:59", ""],
+ ["2017-05-08", "2017-01-08", "2017-01-25", "", "2017-01-31", "2017-01-31", ""],
+ ["2017-05-08T10:30", "2017-01-08T10:30", "2017-01-25T10:30", "", "2017-01-31T10:30", "2017-01-31T10:30", ""]
+];
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function test() {
+ for (var i = 0; i < inputTypes.length; i++) {
+ var input = document.getElementById("input_" + inputTypes[i]);
+
+ is(changeEvents[i], 0, "Number of change events should be 0 at start.");
+ is(inputEvents[i], 0, "Number of input events should be 0 at start.");
+
+ // Test that change and input events are not dispatched setting .value by
+ // script.
+ input.value = values[i];
+ is(input.value, values[i], "Check that value was set correctly (0).");
+ is(changeEvents[i], 0, "Change event should not have dispatched (0).");
+ is(inputEvents[i], 0, "Input event should not have dispatched (0).");
+
+ // Test that change and input events are fired when changing the value using
+ // up/down keys.
+ input.focus();
+ synthesizeKey("KEY_ArrowDown");
+ is(input.value, expectedValues[i][0], "Check that value was set correctly (1).");
+ is(changeEvents[i], 1, "Change event should be dispatched (1).");
+ is(inputEvents[i], 1, "Input event should be dispatched (1).");
+
+ // Test that change and input events are fired when changing the value with
+ // the keyboard.
+ sendString("01");
+ // We get event per character.
+ is(input.value, expectedValues[i][1], "Check that value was set correctly (2).");
+ is(changeEvents[i], 3, "Change event should be dispatched (2).");
+ is(inputEvents[i], 3, "Input event should be dispatched (2).");
+
+ // Test that change and input events are fired when changing the value with
+ // both the numeric keyboard and digit keys.
+ synthesizeKey("2", { code: "Numpad2" });
+ synthesizeKey("5");
+ // We get event per character.
+ is(input.value, expectedValues[i][2], "Check that value was set correctly (3).");
+ is(changeEvents[i], 5, "Change event should be dispatched (3).");
+ is(inputEvents[i], 5, "Input event should be dispatched (3).");
+
+ // Test that change and input events are not fired when navigating with Tab.
+ // Return to the previously focused field (minutes, day, day).
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ is(input.value, expectedValues[i][2], "Check that value was not changed (4).");
+ is(changeEvents[i], 5, "Change event should not be dispatched (4).");
+ is(inputEvents[i], 5, "Input event should not be dispatched (4).");
+
+ // Test that change and input events are fired when using Backspace.
+ synthesizeKey("KEY_Backspace");
+ // We get event per character.
+ is(input.value, expectedValues[i][3], "Check that value was set correctly (5).");
+ is(changeEvents[i], 6, "Change event should be dispatched (5).");
+ is(inputEvents[i], 6, "Input event should be dispatched (5).");
+
+ // Test that change and input events are fired when using Home key.
+ synthesizeKey("KEY_End");
+ // We get event per character.
+ is(input.value, expectedValues[i][4], "Check that value was set correctly (6).");
+ is(changeEvents[i], 7, "Change event should be dispatched (6).");
+ is(inputEvents[i], 7, "Input event should be dispatched (6).");
+
+ // Test that change and input events are fired for time and not fired
+ // for others when changing the value with a letter key.
+ // Navigate to the next field (time of the day, year, year).
+ synthesizeKey("KEY_Tab");
+ synthesizeKey("P");
+ // We get event per character.
+ is(input.value, expectedValues[i][5], "Check that value was set correctly (7).");
+ if (i === 0) {
+ // For the time input, the time of the day should be focused and it,
+ // as an AM/PM toggle, should change to "PM" when the "p" key is pressed
+ is(changeEvents[i], 8, "Change event should be dispatched (7).");
+ is(inputEvents[i], 8, "Input event should be dispatched (7).");
+ } else {
+ // For the date and datetime inputs, the year should be focused and it,
+ // as a numeric value, should not change when the "p" key is pressed
+ is(changeEvents[i], 7, "Change event should not be dispatched (7).");
+ is(inputEvents[i], 7, "Input event should not be dispatched (7).");
+ }
+
+ // Test that change and input events are fired when clearing the value
+ // using a Ctrl/Cmd+Delete/Backspace key combination
+ let events = (i === 0) ? 9 : 8;
+ synthesizeKey("KEY_Backspace", { accelKey: true });
+ // We get one event
+ is(input.value, expectedValues[i][6], "Check that value was cleared out correctly (8).");
+ is(changeEvents[i], events, "Change event should be dispatched (8).");
+ is(inputEvents[i], events, "Input event should be dispatched (8).");
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_readonly.html b/dom/html/test/forms/test_input_datetime_readonly.html
new file mode 100644
index 0000000000..aa7b40753b
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_readonly.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<title>Test for bug 1461509</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<input id="i" type="date" value="1995-11-20" readonly required>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ let input = document.getElementById("i");
+ let value = input.value;
+
+ isnot(value, "", "should have a value");
+
+ input.focus();
+ synthesizeKey("KEY_Backspace");
+ is(input.value, value, "Value shouldn't change");
+ SimpleTest.finish();
+});
+</script>
diff --git a/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html b/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html
new file mode 100644
index 0000000000..393de9fdee
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_reset_default_value_input_change_event.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1446722
+-->
+<head>
+<title>Test for bug 1446722</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script type="application/javascript" src="utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1446722">Mozilla bug 1446722</a>
+<p id="display"></p>
+<div id="content">
+<form>
+<input type="time" id="input_time" value="10:30" onchange="++numberChangeEvents"
+ oninput="++numberInputEvents">
+<input type="date" id="input_date" value="2012-05-06" onchange="++numberChangeEvents"
+ oninput="++numberInputEvents">
+<input type="time" id="input_time2" value="11:30" onchange="++numberChangeEvents"
+ oninput="++numberInputEvents">
+<input type="date" id="input_date2" value="2014-07-08"
+ onchange="++numberChangeEvents"
+ oninput="++numberInputEvents">
+<input type="time" id="input_time3" value="12:30" onchange="++numberChangeEvents"
+ oninput="++numberInputEvents">
+<input type="date" id="input_date3" value="2014-08-09"
+ onchange="++numberChangeEvents"
+ oninput="++numberInputEvents">
+<input type="reset" id="input_reset">
+</form>
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+/**
+ * Test for bug 1446722.
+ *
+ * Test change and input events are fired for date and time inputs when the
+ * default value is reset from the date UI and the time UI.
+ * Test they are not fired when the value is changed via a script.
+ * Test clicking the reset button of a form does not fire these events.
+ **/
+
+const INPUT_FIELD_ID_PREFIX = "input_";
+
+var numberChangeEvents = 0;
+var numberInputEvents = 0;
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test_reset_in_script_does_not_trigger_change_and_input_event(
+ "time2", numberChangeEvents, numberInputEvents);
+ test_reset_in_script_does_not_trigger_change_and_input_event(
+ "date2", numberChangeEvents, numberInputEvents);
+
+ test_reset_form_does_not_trigger_change_and_input_events("time3", "14:00",
+ numberChangeEvents, numberInputEvents);
+ test_reset_form_does_not_trigger_change_and_input_events("date3", "2016-01-01",
+ numberChangeEvents, numberInputEvents);
+
+ SimpleTest.finish();
+});
+
+function test_reset_in_script_does_not_trigger_change_and_input_event(
+ inputFieldIdSuffix, oldNumberChangeEvents, oldNumberInputEvents) {
+ const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
+ var input = document.getElementById(inputFieldName);
+
+ is(input.value, input.defaultValue,
+ "Check " + inputFieldName + "'s default value is initialized correctly.");
+ is(numberChangeEvents, oldNumberChangeEvents,
+ "Check numberChangeEvents is initialized correctly for " + inputFieldName +
+ ".");
+ is(numberInputEvents, oldNumberInputEvents,
+ "Check numberInputEvents is initialized correctly for " + inputFieldName +
+ ".");
+
+ input.value = "";
+
+ is(numberChangeEvents, oldNumberChangeEvents,
+ "Change event should not be dispatched for " + inputFieldName + ".");
+ is(numberInputEvents, oldNumberInputEvents,
+ "Input event should not be dispatched for " + inputFieldName + ".");
+}
+
+function test_reset_form_does_not_trigger_change_and_input_events(
+ inputFieldIdSuffix, newValue, oldNumberChangeEvents, oldNumberInputEvents) {
+ const inputFieldName = INPUT_FIELD_ID_PREFIX + inputFieldIdSuffix;
+ const inputFieldResetButtonName = "input_reset";
+ var input = document.getElementById(inputFieldName);
+
+ is(input.value, input.defaultValue,
+ "Check " + inputFieldName + "'s default value is initialized correctly.");
+ isnot(input.defaultValue, newValue, "Check default value differs from newValue for " +
+ inputFieldName + ".");
+ is(numberChangeEvents, oldNumberChangeEvents,
+ "Check numberChangeEvents is initialized correctly for " + inputFieldName +
+ ".");
+ is(numberInputEvents, oldNumberInputEvents,
+ "Check numberInputEvents is initialized correctly for " + inputFieldName +
+ ".");
+
+ input.value = newValue;
+
+ var resetButton = document.getElementById(inputFieldResetButtonName);
+ synthesizeMouseAtCenter(resetButton, {});
+
+ is(input.value, input.defaultValue, "Check value is reset to default for " +
+ inputFieldName + ".");
+ is(numberChangeEvents, oldNumberChangeEvents,
+ "Change event should not be dispatched for " + inputFieldResetButtonName + ".");
+ is(numberInputEvents, oldNumberInputEvents,
+ "Input event should not be dispatched for " + inputFieldResetButtonName + ".");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_datetime_tabindex.html b/dom/html/test/forms/test_input_datetime_tabindex.html
new file mode 100644
index 0000000000..207a7a8a8e
--- /dev/null
+++ b/dom/html/test/forms/test_input_datetime_tabindex.html
@@ -0,0 +1,113 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
+-->
+<head>
+ <title>Test tabindex attribute for date/time input types</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
+<p id="display"></p>
+<div id="content">
+ <input id="time1" type="time" tabindex="0">
+ <input id="time2" type="time" tabindex="-1">
+ <input id="time3" type="time" tabindex="0">
+ <input id="time4" type="time" disabled>
+ <input id="date1" type="date" tabindex="0">
+ <input id="date2" type="date" tabindex="-1">
+ <input id="date3" type="date" tabindex="0">
+ <input id="date4" type="date" disabled>
+ <input id="datetime-local1" type="datetime-local" tabindex="0">
+ <input id="datetime-local2" type="datetime-local" tabindex="-1">
+ <input id="datetime-local3" type="datetime-local" tabindex="0">
+ <input id="datetime-local4" type="datetime-local" disabled>
+</div>
+<pre id="test">
+<script>
+/**
+ * Test for Bug 1288591.
+ * This test checks whether date/time input types tabindex attribute works
+ * correctly.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function checkInnerTextboxTabindex(input, tabindex) {
+ let fields = SpecialPowers.wrap(input).openOrClosedShadowRoot.querySelectorAll(".datetime-edit-field");
+
+ for (let field of fields) {
+ is(field.tabIndex, tabindex, "tabIndex in the inner textbox should be correct");
+ }
+
+}
+
+function testTabindex(type) {
+ let input1 = document.getElementById(type + "1");
+ let input2 = document.getElementById(type + "2");
+ let input3 = document.getElementById(type + "3");
+ let input4 = document.getElementById(type + "4");
+
+ input1.focus();
+ is(document.activeElement, input1,
+ "input element with tabindex=0 is focusable");
+
+ // Time input does not include a Calendar button
+ let fieldCount;
+ if (type == "datetime-local") {
+ fieldCount = 7;
+ } else if (type == "date") {
+ fieldCount = 4;
+ } else {
+ fieldCount = 3;
+ };
+
+ // Advance through inner fields.
+ for (let i = 0; i < fieldCount - 1; ++i) {
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement, input1,
+ "input element with tabindex=0 is tabbable");
+ }
+
+ // Advance to next element
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement, input3,
+ "input element with tabindex=-1 is not tabbable");
+
+ input2.focus();
+ is(document.activeElement, input2,
+ "input element with tabindex=-1 is still focusable");
+
+ checkInnerTextboxTabindex(input1, 0);
+ checkInnerTextboxTabindex(input2, -1);
+ checkInnerTextboxTabindex(input3, 0);
+
+ // Changing the tabindex attribute dynamically.
+ input3.setAttribute("tabindex", "-1");
+
+ synthesizeKey("KEY_Tab"); // need only one TAB since input2 is not tabbable
+
+ isnot(document.activeElement, input3,
+ "element with tabindex changed to -1 should not be tabbable");
+ isnot(document.activeElement, input4,
+ "disabled element should not be tabbable");
+
+ checkInnerTextboxTabindex(input3, -1);
+}
+
+function test() {
+ for (let inputType of ["time", "date", "datetime-local"]) {
+ testTabindex(inputType);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_defaultValue.html b/dom/html/test/forms/test_input_defaultValue.html
new file mode 100644
index 0000000000..03849d7f54
--- /dev/null
+++ b/dom/html/test/forms/test_input_defaultValue.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977029
+-->
+<head>
+ <title>Test for Bug 977029</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<div id="content">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=977029">Bug 977029</a>
+ <p>
+ Goal of this test is to check that modifying defaultValue and value attribute
+ of input types is working as expected.
+ </p>
+ <form>
+ <input id='a' type="color" value="#00ff00">
+ <input id='b' type="text" value="foo">
+ <input id='c' type="email" value="foo">
+ <input id='d' type="date" value="2010-09-20">
+ <input id='e' type="search" value="foo">
+ <input id='f' type="tel" value="foo">
+ <input id='g' type="url" value="foo">
+ <input id='h' type="number" value="42">
+ <input id='i' type="range" value="42" min="0" max="100">
+ <input id='j' type="time" value="17:00:25.54">
+ </form>
+</div>
+<script type="application/javascript">
+
+// [ element id | original defaultValue | another value | another default value]
+// Preferably use only valid values: the goal of this test isn't to test the
+// value sanitization algorithm (for input types which have one) as this is
+// already part of another test)
+var testData = [["a", "#00ff00", "#00aaaa", "#00ccaa"],
+ ["b", "foo", "bar", "tulip"],
+ ["c", "foo", "foo@bar.org", "tulip"],
+ ["d", "2010-09-20", "2012-09-21", ""],
+ ["e", "foo", "bar", "tulip"],
+ ["f", "foo", "bar", "tulip"],
+ ["g", "foo", "bar", "tulip"],
+ ["h", "42", "1337", "3"],
+ ["i", "42", "17", "3"],
+ ["j", "17:00:25.54", "07:00:25", "03:00:03"],
+ ];
+
+for (var data of testData) {
+ id = data[0];
+ input = document.getElementById(id);
+ originalDefaultValue = data[1];
+ is(originalDefaultValue, input.defaultValue,
+ "Default value isn't the expected one");
+ is(originalDefaultValue, input.value,
+ "input.value original value is different from defaultValue");
+ input.defaultValue = data[2]
+ is(input.defaultValue, input.value,
+ "Changing default value before value was changed should change value too");
+ input.value = data[3];
+ input.defaultValue = originalDefaultValue;
+ is(input.value, data[3],
+ "Changing default value after value was changed should not change value");
+ input.value = data[2];
+ is(originalDefaultValue, input.defaultValue,
+ "defaultValue shouldn't change when changing value");
+ input.defaultValue = data[3];
+ is(input.defaultValue, data[3],
+ "defaultValue should have changed");
+ // Change the value...
+ input.value = data[2];
+ is(input.value, data[2],
+ "value should have changed");
+ // ...then reset the form
+ input.form.reset();
+ is(input.defaultValue, input.value,
+ "reset form should bring back the default value");
+}
+</script>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_input_email.html b/dom/html/test/forms/test_input_email.html
new file mode 100644
index 0000000000..96ff939215
--- /dev/null
+++ b/dom/html/test/forms/test_input_email.html
@@ -0,0 +1,237 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=555559
+https://bugzilla.mozilla.org/show_bug.cgi?id=668817
+https://bugzilla.mozilla.org/show_bug.cgi?id=854812
+-->
+<head>
+ <title>Test for &lt;input type='email'&gt; validity</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=555559">Mozilla Bug 555559</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=668817">Mozilla Bug 668817</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=854812">Mozilla Bug 854812</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form>
+ <input type='email' name='email' id='i' oninvalid="invalidEventHandler(event);">
+ <form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for <input type='email'> validity **/
+
+var gInvalid = false;
+
+function invalidEventHandler(e)
+{
+ is(e.type, "invalid", "Invalid event type should be invalid");
+ gInvalid = true;
+}
+
+function checkValidEmailAddress(element)
+{
+ gInvalid = false;
+ ok(!element.validity.typeMismatch && !element.validity.badInput,
+ "Element should not suffer from type mismatch or bad input (with value='"+element.value+"')");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "Element should be valid");
+ ok(!gInvalid, "The invalid event should not have been thrown");
+ is(element.validationMessage, '',
+ "Validation message should be the empty string");
+ ok(element.matches(":valid"), ":valid pseudo-class should apply");
+}
+
+const VALID = 0;
+const TYPE_MISMATCH = 1 << 0;
+const BAD_INPUT = 1 << 1;
+
+function checkInvalidEmailAddress(element, failedValidityStates)
+{
+ info("Checking " + element.value);
+ gInvalid = false;
+ var expectTypeMismatch = !!(failedValidityStates & TYPE_MISMATCH);
+ var expectBadInput = !!(failedValidityStates & BAD_INPUT);
+ ok(element.validity.typeMismatch == expectTypeMismatch,
+ "Element should " + (expectTypeMismatch ? "" : "not ") + "suffer from type mismatch (with value='"+element.value+"')");
+ ok(element.validity.badInput == expectBadInput,
+ "Element should " + (expectBadInput ? "" : "not ") + "suffer from bad input (with value='"+element.value+"')");
+ ok(!element.validity.valid, "Element should not be valid");
+ ok(!element.checkValidity(), "Element should not be valid");
+ ok(gInvalid, "The invalid event should have been thrown");
+ is(element.validationMessage, "Please enter an email address.",
+ "Validation message is not valid");
+ ok(element.matches(":invalid"), ":invalid pseudo-class should apply");
+}
+
+function testEmailAddress(aElement, aValue, aMultiple, aValidityFailures)
+{
+ aElement.multiple = aMultiple;
+ aElement.value = aValue;
+
+ if (!aValidityFailures) {
+ checkValidEmailAddress(aElement);
+ } else {
+ checkInvalidEmailAddress(aElement, aValidityFailures);
+ }
+}
+
+var email = document.forms[0].elements[0];
+
+// Simple values, checking the e-mail syntax validity.
+var values = [
+ [ '' ], // The empty string shouldn't be considered as invalid.
+ [ 'foo@bar.com', VALID ],
+ [ ' foo@bar.com', VALID ],
+ [ 'foo@bar.com ', VALID ],
+ [ '\r\n foo@bar.com', VALID ],
+ [ 'foo@bar.com \n\r', VALID ],
+ [ '\n\n \r\rfoo@bar.com\n\n \r\r', VALID ],
+ [ '\n\r \n\rfoo@bar.com\n\r \n\r', VALID ],
+ [ 'tulip', TYPE_MISMATCH ],
+ // Some checks on the user part of the address.
+ [ '@bar.com', TYPE_MISMATCH ],
+ [ 'f\noo@bar.com', VALID ],
+ [ 'f\roo@bar.com', VALID ],
+ [ 'f\r\noo@bar.com', VALID ],
+ [ 'fü@foo.com', TYPE_MISMATCH ],
+ // Some checks for the domain part.
+ [ 'foo@bar', VALID ],
+ [ 'foo@b', VALID ],
+ [ 'foo@', TYPE_MISMATCH ],
+ [ 'foo@bar.', TYPE_MISMATCH ],
+ [ 'foo@foo.bar', VALID ],
+ [ 'foo@foo..bar', TYPE_MISMATCH ],
+ [ 'foo@.bar', TYPE_MISMATCH ],
+ [ 'foo@tulip.foo.bar', VALID ],
+ [ 'foo@tulip.foo-bar', VALID ],
+ [ 'foo@1.2', VALID ],
+ [ 'foo@127.0.0.1', VALID ],
+ [ 'foo@1.2.3', VALID ],
+ [ 'foo@b\nar.com', VALID ],
+ [ 'foo@b\rar.com', VALID ],
+ [ 'foo@b\r\nar.com', VALID ],
+ [ 'foo@.', TYPE_MISMATCH ],
+ [ 'foo@fü.com', VALID ],
+ [ 'foo@fu.cüm', VALID ],
+ [ 'thisUsernameIsLongerThanSixtyThreeCharactersInLengthRightAboutNow@mozilla.tld', VALID ],
+ // Long strings with UTF-8 in username.
+ [ 'this.is.email.should.be.longer.than.sixty.four.characters.föö@mözillä.tld', TYPE_MISMATCH ],
+ [ 'this-is-email-should-be-longer-than-sixty-four-characters-föö@mözillä.tld', TYPE_MISMATCH, true ],
+ // Long labels (labels greater than 63 chars long are not allowed).
+ [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss', VALID ],
+ [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', VALID ],
+ [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', VALID ],
+ [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss', VALID ],
+ [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ],
+ [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ],
+ [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ],
+ [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ],
+ // Long labels with UTF-8 (punycode encoding will increase the label to more than 63 chars).
+ [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ],
+ [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ],
+ [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', TYPE_MISMATCH | BAD_INPUT ],
+ [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', TYPE_MISMATCH | BAD_INPUT ],
+ // The domains labels (sub-domains or tld) can't start or finish with a '-'
+ [ 'foo@foo-bar', VALID ],
+ [ 'foo@-foo', TYPE_MISMATCH ],
+ [ 'foo@foo-.bar', TYPE_MISMATCH ],
+ [ 'foo@-.-', TYPE_MISMATCH ],
+ [ 'foo@fo-o.bar', VALID ],
+ [ 'foo@fo-o.-bar', TYPE_MISMATCH ],
+ [ 'foo@fo-o.bar-', TYPE_MISMATCH ],
+ [ 'foo@fo-o.-', TYPE_MISMATCH ],
+ [ 'foo@fo--o', VALID ],
+];
+
+// Multiple values, we don't check e-mail validity, only multiple stuff.
+var multipleValues = [
+ [ 'foo@bar.com, foo@bar.com', VALID ],
+ [ 'foo@bar.com,foo@bar.com', VALID ],
+ [ 'foo@bar.com,foo@bar.com,foo@bar.com', VALID ],
+ [ ' foo@bar.com , foo@bar.com ', VALID ],
+ [ '\tfoo@bar.com\t,\tfoo@bar.com\t', VALID ],
+ [ '\rfoo@bar.com\r,\rfoo@bar.com\r', VALID ],
+ [ '\nfoo@bar.com\n,\nfoo@bar.com\n', VALID ],
+ [ '\ffoo@bar.com\f,\ffoo@bar.com\f', VALID ],
+ [ '\t foo@bar.com\r,\nfoo@bar.com\f', VALID ],
+ [ 'foo@b,ar.com,foo@bar.com', TYPE_MISMATCH ],
+ [ 'foo@bar.com,foo@bar.com,', TYPE_MISMATCH ],
+ [ ' foo@bar.com , foo@bar.com , ', TYPE_MISMATCH ],
+ [ ',foo@bar.com,foo@bar.com', TYPE_MISMATCH ],
+ [ ',foo@bar.com,foo@bar.com', TYPE_MISMATCH ],
+ [ 'foo@bar.com,,,foo@bar.com', TYPE_MISMATCH ],
+ [ 'foo@bar.com;foo@bar.com', TYPE_MISMATCH ],
+ [ '<foo@bar.com>, <foo@bar.com>', TYPE_MISMATCH ],
+ [ 'foo@bar, foo@bar.com', VALID ],
+ [ 'foo@bar.com, foo', TYPE_MISMATCH ],
+ [ 'foo, foo@bar.com', TYPE_MISMATCH ],
+];
+
+/* Additional username checks. */
+
+var legalCharacters = "abcdefghijklmnopqrstuvwxyz";
+legalCharacters += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+legalCharacters += "0123456789";
+legalCharacters += "!#$%&'*+-/=?^_`{|}~.";
+
+// Add all username legal characters individually to the list.
+for (c of legalCharacters) {
+ values.push([c + "@bar.com", VALID]);
+}
+// Add the concatenation of all legal characters too.
+values.push([legalCharacters + "@bar.com", VALID]);
+
+// Add username illegal characters, the same way.
+var illegalCharacters = "()<>[]:;@\\, \t";
+for (c of illegalCharacters) {
+ values.push([illegalCharacters + "@bar.com", TYPE_MISMATCH]);
+}
+
+/* Additional domain checks. */
+
+legalCharacters = "abcdefghijklmnopqrstuvwxyz";
+legalCharacters += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+legalCharacters += "0123456789";
+
+// Add domain legal characters (except '.' and '-' because they are special).
+for (c of legalCharacters) {
+ values.push(["foo@foo.bar" + c, VALID]);
+}
+// Add the concatenation of all legal characters too.
+values.push(["foo@bar." + legalCharacters, VALID]);
+
+// Add domain illegal characters.
+illegalCharacters = "()<>[]:;@\\,!#$%&'*+/=?^_`{|}~ \t";
+for (c of illegalCharacters) {
+ values.push(['foo@foo.ba' + c + 'r', TYPE_MISMATCH]);
+}
+
+values.forEach(function([value, valid, todo]) {
+ if (todo === true) {
+ email.value = value;
+ todo_is(email.validity.valid, true, "value should be valid");
+ } else {
+ testEmailAddress(email, value, false, valid);
+ }
+});
+
+multipleValues.forEach(function([value, valid]) {
+ testEmailAddress(email, value, true, valid);
+});
+
+// Make sure setting multiple changes the value.
+email.multiple = false;
+email.value = "foo@bar.com, foo@bar.com";
+checkInvalidEmailAddress(email, TYPE_MISMATCH);
+email.multiple = true;
+checkValidEmailAddress(email);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_event.html b/dom/html/test/forms/test_input_event.html
new file mode 100644
index 0000000000..72863ca335
--- /dev/null
+++ b/dom/html/test/forms/test_input_event.html
@@ -0,0 +1,409 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=851780
+-->
+<head>
+<title>Test for input event</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=851780">Mozilla Bug 851780</a>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Test for input event. This is highly based on test_change_event.html **/
+
+ const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
+
+ let expectedInputType = "";
+ let expectedData = null;
+ let expectedBeforeInputCancelable = false;
+ function checkBeforeInputEvent(aEvent, aDescription) {
+ ok(aEvent instanceof InputEvent,
+ `"beforeinput" event should be dispatched with InputEvent interface ${aDescription}`);
+ is(aEvent.inputType, expectedInputType,
+ `inputType of "beforeinput" event should be "${expectedInputType}" ${aDescription}`);
+ is(aEvent.data, expectedData,
+ `data of "beforeinput" event should be ${expectedData} ${aDescription}`);
+ is(aEvent.dataTransfer, null,
+ `dataTransfer of "beforeinput" event should be null ${aDescription}`);
+ is(aEvent.getTargetRanges().length, 0,
+ `getTargetRanges() of "beforeinput" event should return empty array ${aDescription}`);
+ is(aEvent.cancelable, expectedBeforeInputCancelable,
+ `"beforeinput" event for "${expectedInputType}" should ${expectedBeforeInputCancelable ? "be" : "not be"} cancelable ${aDescription}`);
+ is(aEvent.bubbles, true,
+ `"beforeinput" event should always bubble ${aDescription}`);
+ }
+
+ let skipExpectedDataCheck = false;
+ function checkIfInputIsInputEvent(aEvent, aDescription) {
+ ok(aEvent instanceof InputEvent,
+ `"input" event should be dispatched with InputEvent interface ${aDescription}`);
+ is(aEvent.inputType, expectedInputType,
+ `inputType should be "${expectedInputType}" ${aDescription}`);
+ if (!skipExpectedDataCheck)
+ is(aEvent.data, expectedData, `data should be ${expectedData} ${aDescription}`);
+ else
+ info(`data is ${aEvent.data} ${aDescription}`);
+ is(aEvent.dataTransfer, null,
+ `dataTransfer should be null ${aDescription}`);
+ is(aEvent.cancelable, false,
+ `"input" event should be never cancelable ${aDescription}`);
+ is(aEvent.bubbles, true,
+ `"input" event should always bubble ${aDescription}`);
+ }
+
+ function checkIfInputIsEvent(aEvent, aDescription) {
+ ok(aEvent instanceof Event && !(aEvent instanceof UIEvent),
+ `"input" event should be dispatched with InputEvent interface ${aDescription}`);
+ is(aEvent.cancelable, false,
+ `"input" event should be never cancelable ${aDescription}`);
+ is(aEvent.bubbles, true,
+ `"input" event should always bubble ${aDescription}`);
+ }
+
+ let textareaInput = 0, textareaBeforeInput = 0;
+ let textTypes = ["text", "email", "search", "tel", "url", "password"];
+ let textBeforeInput = [0, 0, 0, 0, 0, 0];
+ let textInput = [0, 0, 0, 0, 0, 0];
+ let nonTextTypes = ["button", "submit", "image", "reset", "radio", "checkbox"];
+ let nonTextBeforeInput = [0, 0, 0, 0, 0, 0];
+ let nonTextInput = [0, 0, 0, 0, 0, 0];
+ let rangeInput = 0, rangeBeforeInput = 0;
+ let numberInput = 0, numberBeforeInput = 0;
+
+ // Don't create elements whose event listener attributes are required before enabling `beforeinput` event.
+ function init() {
+ document.getElementById("content").innerHTML =
+ `<input type="file" id="fileInput">
+ <textarea id="textarea"></textarea>
+ <input type="text" id="input_text">
+ <input type="email" id="input_email">
+ <input type="search" id="input_search">
+ <input type="tel" id="input_tel">
+ <input type="url" id="input_url">
+ <input type="password" id="input_password">
+
+ <!-- "Non-text" inputs-->
+ <input type="button" id="input_button">
+ <input type="submit" id="input_submit">
+ <input type="image" id="input_image">
+ <input type="reset" id="input_reset">
+ <input type="radio" id="input_radio">
+ <input type="checkbox" id="input_checkbox">
+ <input type="range" id="input_range">
+ <input type="number" id="input_number">`;
+
+ document.getElementById("textarea").addEventListener("beforeinput", (aEvent) => {
+ ++textareaBeforeInput;
+ checkBeforeInputEvent(aEvent, "on textarea element");
+ });
+ document.getElementById("textarea").addEventListener("input", (aEvent) => {
+ ++textareaInput;
+ checkIfInputIsInputEvent(aEvent, "on textarea element");
+ });
+
+ // These are the type were the input event apply.
+ for (let id of ["input_text", "input_email", "input_search", "input_tel", "input_url", "input_password"]) {
+ document.getElementById(id).addEventListener("beforeinput", (aEvent) => {
+ ++textBeforeInput[textTypes.indexOf(aEvent.target.type)];
+ checkBeforeInputEvent(aEvent, `on input element whose type is ${aEvent.target.type}`);
+ });
+ document.getElementById(id).addEventListener("input", (aEvent) => {
+ ++textInput[textTypes.indexOf(aEvent.target.type)];
+ checkIfInputIsInputEvent(aEvent, `on input element whose type is ${aEvent.target.type}`);
+ });
+ }
+
+ // These are the type were the input event does not apply.
+ for (let id of ["input_button", "input_submit", "input_image", "input_reset", "input_radio", "input_checkbox"]) {
+ document.getElementById(id).addEventListener("beforeinput", (aEvent) => {
+ ++nonTextBeforeInput[nonTextTypes.indexOf(aEvent.target.type)];
+ });
+ document.getElementById(id).addEventListener("input", (aEvent) => {
+ ++nonTextInput[nonTextTypes.indexOf(aEvent.target.type)];
+ checkIfInputIsEvent(aEvent, `on input element whose type is ${aEvent.target.type}`);
+ });
+ }
+
+ document.getElementById("input_range").addEventListener("beforeinput", (aEvent) => {
+ ++rangeBeforeInput;
+ });
+ document.getElementById("input_range").addEventListener("input", (aEvent) => {
+ ++rangeInput;
+ checkIfInputIsEvent(aEvent, "on input element whose type is range");
+ });
+
+ document.getElementById("input_number").addEventListener("beforeinput", (aEvent) => {
+ ++numberBeforeInput;
+ });
+ document.getElementById("input_number").addEventListener("input", (aEvent) => {
+ ++numberInput;
+ checkIfInputIsInputEvent(aEvent, "on input element whose type is number");
+ });
+ }
+
+ var MockFilePicker = SpecialPowers.MockFilePicker;
+ MockFilePicker.init(window);
+
+ function testUserInput() {
+ // Simulating an OK click and with a file name return.
+ MockFilePicker.useBlobFile();
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+ var input = document.getElementById('fileInput');
+ input.focus();
+
+ input.addEventListener("beforeinput", function (aEvent) {
+ ok(false, "beforeinput event shouldn't be dispatched on file input.");
+ });
+ input.addEventListener("input", function (aEvent) {
+ ok(true, "input event should've been dispatched on file input.");
+ checkIfInputIsEvent(aEvent, "on file input");
+ });
+
+ input.click();
+ SimpleTest.executeSoon(testUserInput2);
+ }
+
+ function testUserInput2() {
+ // Some generic checks for types that support the input event.
+ for (var i = 0; i < textTypes.length; ++i) {
+ input = document.getElementById("input_" + textTypes[i]);
+ input.focus();
+ expectedInputType = "insertLineBreak";
+ expectedData = null;
+ expectedBeforeInputCancelable = true;
+ synthesizeKey("KEY_Enter");
+ is(textBeforeInput[i], 1, "beforeinput event should've been dispatched on " + textTypes[i] + " input element");
+ is(textInput[i], 0, "input event shouldn't be dispatched on " + textTypes[i] + " input element");
+
+ expectedInputType = "insertText";
+ expectedData = "m";
+ expectedBeforeInputCancelable = true;
+ sendString("m");
+ is(textBeforeInput[i], 2, textTypes[i] + " input element should've been dispatched beforeinput event.");
+ is(textInput[i], 1, textTypes[i] + " input element should've been dispatched input event.");
+ expectedInputType = "insertLineBreak";
+ expectedData = null;
+ expectedBeforeInputCancelable = true;
+ synthesizeKey("KEY_Enter", {shiftKey: true});
+ is(textBeforeInput[i], 3, "input event should've been dispatched on " + textTypes[i] + " input element");
+ is(textInput[i], 1, "input event shouldn't be dispatched on " + textTypes[i] + " input element");
+
+ expectedInputType = "deleteContentBackward";
+ expectedData = null;
+ expectedBeforeInputCancelable = true;
+ synthesizeKey("KEY_Backspace");
+ is(textBeforeInput[i], 4, textTypes[i] + " input element should've been dispatched beforeinput event.");
+ is(textInput[i], 2, textTypes[i] + " input element should've been dispatched input event.");
+ }
+
+ // Some scenarios of value changing from script and from user input.
+ input = document.getElementById("input_text");
+ input.focus();
+ expectedInputType = "insertText";
+ expectedData = "f";
+ expectedBeforeInputCancelable = true;
+ sendString("f");
+ is(textBeforeInput[0], 5, "beforeinput event should've been dispatched");
+ is(textInput[0], 3, "input event should've been dispatched");
+ input.blur();
+ is(textBeforeInput[0], 5, "input event should not have been dispatched");
+ is(textInput[0], 3, "input event should not have been dispatched");
+
+ input.focus();
+ input.value = 'foo';
+ is(textBeforeInput[0], 5, "beforeinput event should not have been dispatched");
+ is(textInput[0], 3, "input event should not have been dispatched");
+ input.blur();
+ is(textBeforeInput[0], 5, "beforeinput event should not have been dispatched");
+ is(textInput[0], 3, "input event should not have been dispatched");
+
+ input.focus();
+ expectedInputType = "insertText";
+ expectedData = "f";
+ expectedBeforeInputCancelable = true;
+ sendString("f");
+ is(textBeforeInput[0], 6, "beforeinput event should've been dispatched");
+ is(textInput[0], 4, "input event should've been dispatched");
+ input.value = 'bar';
+ is(textBeforeInput[0], 6, "beforeinput event should not have been dispatched");
+ is(textInput[0], 4, "input event should not have been dispatched");
+ input.blur();
+ is(textBeforeInput[0], 6, "beforeinput event should not have been dispatched");
+ is(textInput[0], 4, "input event should not have been dispatched");
+
+ // Same for textarea.
+ var textarea = document.getElementById("textarea");
+ textarea.focus();
+ expectedInputType = "insertText";
+ expectedData = "f";
+ expectedBeforeInputCancelable = true;
+ sendString("f");
+ is(textareaBeforeInput, 1, "beforeinput event should've been dispatched");
+ is(textareaInput, 1, "input event should've been dispatched");
+ textarea.blur();
+ is(textareaBeforeInput, 1, "beforeinput event should not have been dispatched");
+ is(textareaInput, 1, "input event should not have been dispatched");
+
+ textarea.focus();
+ textarea.value = 'foo';
+ is(textareaBeforeInput, 1, "beforeinput event should not have been dispatched");
+ is(textareaInput, 1, "input event should not have been dispatched");
+ textarea.blur();
+ is(textareaBeforeInput, 1, "beforeinput event should not have been dispatched");
+ is(textareaInput, 1, "input event should not have been dispatched");
+
+ textarea.focus();
+ expectedInputType = "insertText";
+ expectedData = "f";
+ expectedBeforeInputCancelable = true;
+ sendString("f");
+ is(textareaBeforeInput, 2, "beforeinput event should've been dispatched");
+ is(textareaInput, 2, "input event should've been dispatched");
+ textarea.value = 'bar';
+ is(textareaBeforeInput, 2, "beforeinput event should not have been dispatched");
+ is(textareaInput, 2, "input event should not have been dispatched");
+ expectedInputType = "deleteContentBackward";
+ expectedData = null;
+ expectedBeforeInputCancelable = true;
+ synthesizeKey("KEY_Backspace");
+ is(textareaBeforeInput, 3, "beforeinput event should've been dispatched");
+ is(textareaInput, 3, "input event should've been dispatched");
+ textarea.blur();
+ is(textareaBeforeInput, 3, "beforeinput event should not have been dispatched");
+ is(textareaInput, 3, "input event should not have been dispatched");
+
+ // Non-text input tests:
+ for (var i = 0; i < nonTextTypes.length; ++i) {
+ // Button, submit, image and reset input type tests.
+ if (i < 4) {
+ input = document.getElementById("input_" + nonTextTypes[i]);
+ input.focus();
+ input.click();
+ is(nonTextBeforeInput[i], 0, "beforeinput event doesn't apply");
+ is(nonTextInput[i], 0, "input event doesn't apply");
+ input.blur();
+ is(nonTextBeforeInput[i], 0, "beforeinput event doesn't apply");
+ is(nonTextInput[i], 0, "input event doesn't apply");
+ }
+ // For radio and checkboxes, input event should be dispatched.
+ else {
+ input = document.getElementById("input_" + nonTextTypes[i]);
+ input.focus();
+ input.click();
+ is(nonTextBeforeInput[i], 0, "beforeinput event should not have been dispatched");
+ is(nonTextInput[i], 1, "input event should've been dispatched");
+ input.blur();
+ is(nonTextBeforeInput[i], 0, "beforeinput event should not have been dispatched");
+ is(nonTextInput[i], 1, "input event should not have been dispatched");
+
+ // Test that input event is not dispatched if click event is cancelled.
+ function preventDefault(e) {
+ e.preventDefault();
+ }
+ input.addEventListener("click", preventDefault);
+ input.click();
+ is(nonTextBeforeInput[i], 0, "beforeinput event shouldn't be dispatched if click event is cancelled");
+ is(nonTextInput[i], 1, "input event shouldn't be dispatched if click event is cancelled");
+ input.removeEventListener("click", preventDefault);
+ }
+ }
+
+ // Type changes.
+ var input = document.createElement('input');
+ input.type = 'text';
+ input.value = 'foo';
+ input.onbeforeinput = function () {
+ ok(false, "we shouldn't get a beforeinput event when the type changes");
+ };
+ input.oninput = function() {
+ ok(false, "we shouldn't get an input event when the type changes");
+ };
+ input.type = 'range';
+ isnot(input.value, 'foo');
+
+ // Tests for type='range'.
+ var range = document.getElementById("input_range");
+
+ range.focus();
+ sendString("a");
+ range.blur();
+ is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched on range input " +
+ "element for key changes that don't change its value");
+ is(rangeInput, 0, "input event shouldn't be dispatched on range input " +
+ "element for key changes that don't change its value");
+
+ range.focus();
+ synthesizeKey("KEY_Home");
+ is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched even for key changes");
+ is(rangeInput, 1, "input event should be dispatched for key changes");
+ range.blur();
+ is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched on blur");
+ is(rangeInput, 1, "input event shouldn't be dispatched on blur");
+
+ range.focus();
+ var bcr = range.getBoundingClientRect();
+ var centerOfRangeX = bcr.width / 2;
+ var centerOfRangeY = bcr.height / 2;
+ synthesizeMouse(range, centerOfRangeX - 10, centerOfRangeY, { type: "mousedown" });
+ is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched on mousedown if the value changes");
+ is(rangeInput, 2, "Input event should be dispatched on mousedown if the value changes");
+ synthesizeMouse(range, centerOfRangeX - 5, centerOfRangeY, { type: "mousemove" });
+ is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched during a drag");
+ is(rangeInput, 3, "Input event should be dispatched during a drag");
+ synthesizeMouse(range, centerOfRangeX, centerOfRangeY, { type: "mouseup" });
+ is(rangeBeforeInput, 0, "beforeinput event shouldn't be dispatched at the end of a drag");
+ is(rangeInput, 4, "Input event should be dispatched at the end of a drag");
+
+ // Tests for type='number'.
+ // We only test key events here since input events for mouse event changes
+ // are tested in test_input_number_mouse_events.html
+ var number = document.getElementById("input_number");
+
+ if (isDesktop) { // up/down arrow keys not supported on android
+ number.value = "";
+ number.focus();
+ // <input type="number">'s inputType value hasn't been decided, see
+ // https://github.com/w3c/input-events/issues/88
+ expectedInputType = "insertReplacementText";
+ expectedData = "1";
+ expectedBeforeInputCancelable = false;
+ synthesizeKey("KEY_ArrowUp");
+ is(numberBeforeInput, 1, "beforeinput event should be dispatched for up/down arrow key keypress");
+ is(numberInput, 1, "input event should be dispatched for up/down arrow key keypress");
+ is(number.value, "1", "sanity check value of number control after keypress");
+
+ // `data` will be the value of the input, but we can't change
+ // `expectedData` and use {repeat: 3} at the same time.
+ skipExpectedDataCheck = true;
+ synthesizeKey("KEY_ArrowDown", {repeat: 3});
+ is(numberBeforeInput, 4, "beforeinput event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated");
+ is(numberInput, 4, "input event should be dispatched for each up/down arrow key keypress event, even when rapidly repeated");
+ is(number.value, "-2", "sanity check value of number control after multiple keydown events");
+ skipExpectedDataCheck = false;
+
+ number.blur();
+ is(numberBeforeInput, 4, "beforeinput event shouldn't be dispatched on blur");
+ is(numberInput, 4, "input event shouldn't be dispatched on blur");
+ }
+
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ document.addEventListener("DOMContentLoaded", () => {
+ init();
+ SimpleTest.waitForFocus(testUserInput);
+ }, {once: true});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_file_picker.html b/dom/html/test/forms/test_input_file_picker.html
new file mode 100644
index 0000000000..296c12bb7e
--- /dev/null
+++ b/dom/html/test/forms/test_input_file_picker.html
@@ -0,0 +1,280 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for &lt;input type='file'&gt; file picker</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377624">Mozilla Bug 36619</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377624">Mozilla Bug 377624</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=565274">Mozilla Bug 565274</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=701353">Mozilla Bug 701353</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=826176">Mozilla Bug 826176</a>
+<p id="display"></p>
+<div id="content">
+ <input id='a' type='file' accept="image/*">
+ <input id='b' type='file' accept="audio/*">
+ <input id='c' type='file' accept="video/*">
+ <input id='d' type='file' accept="image/*, audio/* ">
+ <input id='e' type='file' accept=" image/*,video/*">
+ <input id='f' type='file' accept="audio/*,video/*">
+ <input id='g' type='file' accept="image/*, audio/* ,video/*">
+ <input id='h' type='file' accept="foo/baz,image/*,bogus/duh">
+ <input id='i' type='file' accept="mime/type;parameter,video/*">
+ <input id='j' type='file' accept="audio/*, audio/*, audio/*">
+ <input id='k' type="file" accept="image/gif,image/png">
+ <input id='l' type="file" accept="image/*,image/gif,image/png">
+ <input id='m' type="file" accept="image/gif,image/gif">
+ <input id='n' type="file" accept="">
+ <input id='o' type="file" accept=".test">
+ <input id='p' type="file" accept="image/gif,.csv">
+ <input id='q' type="file" accept="image/gif,.gif">
+ <input id='r' type="file" accept=".prefix,.prefixPlusSomething">
+ <input id='s' type="file" accept=".xls,.xlsx">
+ <input id='t' type="file" accept=".mp3,.wav,.flac">
+ <input id='u' type="file" accept=".xls, .xlsx">
+ <input id='v' type="file" accept=".xlsx, .xls">
+ <input id='w' type="file" accept=".xlsx; .xls">
+ <input id='x' type="file" accept=".xls, .xlsx">
+ <input id='y' type="file" accept=".xlsx, .xls">
+ <input id='z' type='file' accept="i/am,a,pathological,;,,,,test/case">
+ <input id='A' type="file" accept=".xlsx, .xls*">
+ <input id='mix-ref' type="file" accept="image/jpeg">
+ <input id='mix' type="file" accept="image/jpeg,.jpg">
+ <input id='hidden' hidden type='file'>
+ <input id='untrusted-click' type='file'>
+ <input id='prevent-default' type='file'>
+ <input id='prevent-default-false' type='file'>
+ <input id='right-click' type='file'>
+ <input id='middle-click' type='file'>
+ <input id='left-click' type='file'>
+ <label id='label-1'>foo<input type='file'></label>
+ <label id='label-2' for='labeled-2'>foo</label><input id='labeled-2' type='file'></label>
+ <label id='label-3'>foo<input type='file'></label>
+ <label id='label-4' for='labeled-4'>foo</label><input id='labeled-4' type='file'></label>
+ <input id='by-button' type='file'>
+ <button id='button-click' onclick="document.getElementById('by-button').click();">foo</button>
+ <button id='button-down' onclick="document.getElementById('by-button').click();">foo</button>
+ <button id='button-up' onclick="document.getElementById('by-button').click();">foo</button>
+ <div id='div-click' onclick="document.getElementById('by-button').click();" tabindex='1'>foo</div>
+ <div id='div-click-on-demand' onclick="var i=document.createElement('input'); i.type='file'; i.click();" tabindex='1'>foo</div>
+ <div id='div-keydown' onkeydown="document.getElementById('by-button').click();" tabindex='1'>foo</div>
+ <a id='link-click' href="javascript:document.getElementById('by-button').click();" tabindex='1'>foo</a>
+ <input id='show-picker' type='file'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * This test checks various scenarios and make sure that a file picker is being
+ * shown in all of them (minus a few exceptions).
+ * |testData| defines the tests to do and |launchNextTest| can be used to have
+ * specific behaviour for some tests. Everything else should just work.
+ */
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+// The following lists are from toolkit/content/filepicker.properties which is used by filePicker
+var imageExtensionList = "*.jpe; *.jpg; *.jpeg; *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw; *.webp; *.heic"
+
+var audioExtensionList = "*.aac; *.aif; *.flac; *.iff; *.m4a; *.m4b; *.mid; *.midi; *.mp3; *.mpa; *.mpc; *.oga; *.ogg; *.opus; *.ra; *.ram; *.snd; *.wav; *.wma"
+
+var videoExtensionList = "*.avi; *.divx; *.flv; *.m4v; *.mkv; *.mov; *.mp4; *.mpeg; *.mpg; *.ogm; *.ogv; *.ogx; *.rm; *.rmvb; *.smil; *.webm; *.wmv; *.xvid"
+
+// [ element name | number of filters | extension list or filter mask | filter index ]
+var testData = [["a", 1, MockFilePicker.filterImages, 1],
+ ["b", 1, MockFilePicker.filterAudio, 1],
+ ["c", 1, MockFilePicker.filterVideo, 1],
+ ["d", 3, imageExtensionList + "; " + audioExtensionList, 1],
+ ["e", 3, imageExtensionList + "; " + videoExtensionList, 1],
+ ["f", 3, audioExtensionList + "; " + videoExtensionList, 1],
+ ["g", 4, imageExtensionList + "; " + audioExtensionList + "; " + videoExtensionList, 1],
+ ["h", 1, MockFilePicker.filterImages, 1],
+ ["i", 1, MockFilePicker.filterVideo, 1],
+ ["j", 1, MockFilePicker.filterAudio, 1],
+ ["k", 3, "*.gif; *.png", 1],
+ ["l", 4, imageExtensionList + "; " + "*.gif; *.png", 1],
+ ["m", 1, "*.gif", 1],
+ ["n", 0, undefined, 0],
+ ["o", 1, "*.test", 1],
+ ["p", 3, "*.gif; *.csv", 1],
+ ["q", 1, "*.gif", 1],
+ ["r", 3, "*.prefix; *.prefixPlusSomething", 1],
+ ["s", 3, "*.xls; *.xlsx", 1],
+ ["t", 4, "*.mp3; *.wav; *.flac", 1],
+ ["u", 3, "*.xls; *.xlsx", 1],
+ ["v", 3, "*.xlsx; *.xls", 1],
+ ["w", 0, undefined, 0],
+ ["x", 3, "*.xls; *.xlsx", 1],
+ ["y", 3, "*.xlsx; *.xls", 1],
+ ["z", 0, undefined, 0],
+ ["A", 1, "*.xlsx", 1],
+ // Note: mix and mix-ref tests extension lists are checked differently: see SimpleTest.executeSoon below
+ ["mix-ref", undefined, undefined, undefined],
+ ["mix", 1, undefined, 1],
+ ["hidden", 0, undefined, 0],
+ ["untrusted-click", 0, undefined, 0],
+ ["prevent-default", 0, undefined, 0, true],
+ ["prevent-default-false", 0, undefined, 0, true],
+ ["right-click", 0, undefined, 0, true],
+ ["middle-click", 0, undefined, 0, true],
+ ["left-click", 0, undefined, 0],
+ ["label-1", 0, undefined, 0],
+ ["label-2", 0, undefined, 0],
+ ["label-3", 0, undefined, 0],
+ ["label-4", 0, undefined, 0],
+ ["button-click", 0, undefined, 0],
+ ["button-down", 0, undefined, 0],
+ ["button-up", 0, undefined, 0],
+ ["div-click", 0, undefined, 0],
+ ["div-click-on-demand", 0, undefined, 0],
+ ["div-keydown", 0, undefined, 0],
+ ["link-click", 0, undefined, 0],
+ ["show-picker", 0, undefined, 0],
+ ];
+
+var currentTest = 0;
+var filterAllAdded;
+var filters;
+var filterIndex;
+var mixRefExtensionList;
+
+// Make sure picker works with popup blocker enabled and no allowed events
+SpecialPowers.pushPrefEnv({'set': [["dom.popup_allowed_events", ""]]}, runTests);
+
+function launchNextTest() {
+ MockFilePicker.shown = false;
+ filterAllAdded = false;
+ filters = [];
+ filterIndex = 0;
+
+ // Focusing the element will scroll them into view so making sure the clicks
+ // will work.
+ document.getElementById(testData[currentTest][0]).focus();
+
+ if (testData[currentTest][0] == "untrusted-click") {
+ var e = document.createEvent('MouseEvents');
+ e.initEvent('click', true, false);
+ document.getElementById(testData[currentTest][0]).dispatchEvent(e);
+ // All tests that should *NOT* show a file picker.
+ } else if (testData[currentTest][0] == "prevent-default" ||
+ testData[currentTest][0] == "prevent-default-false" ||
+ testData[currentTest][0] == "right-click" ||
+ testData[currentTest][0] == "middle-click") {
+ if (testData[currentTest][0] == "right-click" ||
+ testData[currentTest][0] == "middle-click") {
+ var b = testData[currentTest][0] == "middle-click" ? 1 : 2;
+ synthesizeMouseAtCenter(document.getElementById(testData[currentTest][0]),
+ { button: b });
+ } else {
+ if (testData[currentTest][0] == "prevent-default-false") {
+ document.getElementById(testData[currentTest][0]).onclick = function() {
+ return false;
+ };
+ } else {
+ document.getElementById(testData[currentTest][0]).onclick = function(event) {
+ event.preventDefault();
+ };
+ }
+ document.getElementById(testData[currentTest][0]).click();
+ }
+
+ // Wait a bit and assume we can continue. If the file picker shows later,
+ // behaviour is uncertain but that would be a random green, no big deal...
+ setTimeout(function() {
+ ok(true, "we should be there without a file picker being opened");
+ ++currentTest;
+ launchNextTest();
+ }, 500);
+ } else if (testData[currentTest][0] == 'label-3' ||
+ testData[currentTest][0] == 'label-4') {
+ synthesizeMouse(document.getElementById(testData[currentTest][0]), 5, 5, {});
+ } else if (testData[currentTest][0] == 'button-click' ||
+ testData[currentTest][0] == 'button-down' ||
+ testData[currentTest][0] == 'button-up' ||
+ testData[currentTest][0] == 'div-click' ||
+ testData[currentTest][0] == 'div-click-on-demand' ||
+ testData[currentTest][0] == 'link-click') {
+ synthesizeMouseAtCenter(document.getElementById(testData[currentTest][0]), {});
+ } else if (testData[currentTest][0] == 'div-keydown') {
+ sendString("a");
+ } else if (testData[currentTest][0] == 'show-picker') {
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ document.getElementById(testData[currentTest][0]).showPicker();
+ } else {
+ document.getElementById(testData[currentTest][0]).click();
+ }
+}
+
+function runTests() {
+ MockFilePicker.appendFilterCallback = function(filepicker, title, val) {
+ filters.push(val);
+ };
+ MockFilePicker.appendFiltersCallback = function(filepicker, val) {
+ if (val === MockFilePicker.filterAll) {
+ filterAllAdded = true;
+ } else {
+ filters.push(val);
+ }
+ };
+ MockFilePicker.showCallback = function(filepicker) {
+ if (testData[currentTest][4]) {
+ ok(false, "we shouldn't have a file picker showing!");
+ return;
+ }
+
+ filterIndex = filepicker.filterIndex;
+ testName = testData[currentTest][0];
+ SimpleTest.executeSoon(function () {
+ ok(MockFilePicker.shown,
+ "File picker show method should have been called (" + testName + ")");
+ ok(filterAllAdded,
+ "filterAll is missing (" + testName + ")");
+ if (testName == "mix-ref") {
+ // Used only for reference for next test: nothing to be tested here
+ mixRefExtensionList = filters[0];
+ if (mixRefExtensionList == undefined) {
+ mixRefExtensionList = "";
+ }
+ } else {
+ if (testName == "mix") {
+ // Mixing mime type and file extension filters ("image/jpeg" and
+ // ".jpg" here) shouldn't restrict the list but only extend it, if file
+ // extension filter isn't a duplicate
+ ok(filters[0].includes(mixRefExtensionList),
+ "Mixing mime types and file extension filters shouldn't restrict extension list: " +
+ mixRefExtensionList + " | " + filters[0]);
+ ok(filters[0].includes("*.jpg"),
+ "Filter should contain '.jpg' extension. Filter was:" + filters[0]);
+ } else {
+ is(filters[0], testData[currentTest][2],
+ "Correct filters should have been added (" + testName + ")");
+ is(filters.length, testData[currentTest][1],
+ "appendFilters not called as often as expected (" + testName + ")");
+ }
+ is(filterIndex, testData[currentTest][3],
+ "File picker should show the correct filter index (" + testName + ")");
+ }
+
+ if (++currentTest == testData.length) {
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ } else {
+ launchNextTest();
+ }
+ });
+ };
+
+ launchNextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_hasBeenTypePassword.html b/dom/html/test/forms/test_input_hasBeenTypePassword.html
new file mode 100644
index 0000000000..ac577ae3a9
--- /dev/null
+++ b/dom/html/test/forms/test_input_hasBeenTypePassword.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1330228
+-->
+<head>
+ <title>Test input.hasBeenTypePassword</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1330228">Mozilla Bug 1330228</a>
+<script type="application/javascript">
+
+/** Test input.hasBeenTypePassword **/
+
+var gInputTestData = [
+/* type result */
+ ["tel", false],
+ ["text", false],
+ ["button", false],
+ ["checkbox", false],
+ ["file", false],
+ ["hidden", false],
+ ["reset", false],
+ ["image", false],
+ ["radio", false],
+ ["submit", false],
+ ["search", false],
+ ["email", false],
+ ["url", false],
+ ["number", false],
+ ["range", false],
+ ["date", false],
+ ["time", false],
+ ["color", false],
+ ["month", false],
+ ["week", false],
+ ["datetime-local", false],
+ ["", false],
+ // "password" must be last since we re-use the same <input>.
+ ["password", true],
+];
+
+function checkHasBeenTypePasswordValue(aInput, aResult) {
+ is(aInput.hasBeenTypePassword, aResult,
+ "hasBeenTypePassword should return " + aResult + " for " +
+ aInput.getAttribute("type"));
+}
+
+// Use SpecialPowers since the API is ChromeOnly.
+var input = SpecialPowers.wrap(document.createElement("input"));
+// Check if the method returns the correct value on the first pass.
+for (let [type, expected] of gInputTestData) {
+ input.type = type;
+ checkHasBeenTypePasswordValue(input, expected);
+}
+
+// Now do a second pass but expect `hasBeenTypePassword` to always be true now
+// that the type was 'password'.
+for (let [type] of gInputTestData) {
+ input.type = type;
+ checkHasBeenTypePasswordValue(input, true);
+}
+</script>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_hasBeenTypePassword_navigation.html b/dom/html/test/forms/test_input_hasBeenTypePassword_navigation.html
new file mode 100644
index 0000000000..70a0f8427e
--- /dev/null
+++ b/dom/html/test/forms/test_input_hasBeenTypePassword_navigation.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1330228
+-->
+<head>
+ <title>Test hasBeenTypePassword is used with bfcache</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1330228">Mozilla Bug 1330228</a>
+<p id="display">
+ <iframe id="testframe" src="file_login_fields.html"></iframe>
+</p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test hasBeenTypePassword is used with bfcache **/
+SimpleTest.waitForExplicitFinish();
+
+function afterLoad() {
+ var iframeDoc = $("testframe").contentDocument;
+
+ /* change all the form controls */
+ iframeDoc.getElementById("un").value = "username";
+ iframeDoc.getElementById("pw1").value = "password1";
+
+ // Convert pw2 to a password field temporarily to test hasBeenTypePassword.
+ // We don't want the initial or final value to be type=password or we may
+ // not test the right scenario.
+ iframeDoc.getElementById("pw2").type = "password";
+ iframeDoc.getElementById("pw2").value = "password2";
+ iframeDoc.getElementById("pw2").type = "";
+
+ /* navigate the page */
+ $("testframe").setAttribute("onload", "afterNavigation()");
+ // Use a click on an <a> so that the current page is included in session history.
+ iframeDoc.getElementById("navigate").click();
+}
+
+addLoadEvent(afterLoad);
+
+function afterNavigation() {
+ info("Navigated to a new document");
+ var iframeDoc = $("testframe").contentDocument;
+ $("testframe").setAttribute("onload", "afterBack()");
+ // Calling `history.back()` on the contentWindow from here doesn't use bfcache
+ // so call it from within the contentDocument.
+ iframeDoc.getElementById("back").click();
+}
+
+function afterBack() {
+ info("Should be back showing the first document from bfcache");
+ var iframeDoc = $("testframe").contentDocument;
+
+ is(iframeDoc.getElementById("un").value, "username",
+ "username field value remembered");
+ is(iframeDoc.getElementById("pw1").value, "",
+ "type=password field value not remembered");
+ is(iframeDoc.getElementById("pw2").value, "",
+ "former type=password field value not remembered");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_list_attribute.html b/dom/html/test/forms/test_input_list_attribute.html
new file mode 100644
index 0000000000..62a07dd91a
--- /dev/null
+++ b/dom/html/test/forms/test_input_list_attribute.html
@@ -0,0 +1,253 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=556007
+-->
+<head>
+ <title>Test for Bug 556007</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=556007">Mozilla Bug 556007</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 556007 **/
+
+function test0() {
+ var input = document.createElement("input");
+ ok("list" in input, "list should be an input element IDL attribute");
+}
+
+// Default .list returns null.
+function test1(aContent, aInput) {
+ return null;
+}
+
+// Regular test case.
+function test2(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+ aInput.setAttribute('list', 'd');
+
+ return datalist;
+}
+
+// If none of the element is in doc.
+function test3(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+
+ return null;
+}
+
+// If one of the element isn't in doc.
+function test4(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aContent.appendChild(aInput);
+ aInput.setAttribute('list', 'd');
+
+ return null;
+}
+
+// If one of the element isn't in doc.
+function test5(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aContent.appendChild(datalist);
+ aInput.setAttribute('list', 'd');
+
+ return null;
+}
+
+// If datalist is added before input.
+function test6(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aContent.appendChild(datalist);
+ aContent.appendChild(aInput);
+ aInput.setAttribute('list', 'd');
+
+ return datalist;
+}
+
+// If setAttribute is set before datalist and input are in doc.
+function test7(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+
+ aContent.appendChild(datalist);
+ aContent.appendChild(aInput);
+
+ return datalist;
+}
+
+// If setAttribute is set before datalist is in doc.
+function test8(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aContent.appendChild(aInput);
+ aInput.setAttribute('list', 'd');
+
+ aContent.appendChild(datalist);
+
+ return datalist;
+}
+
+// If setAttribute is set before datalist is created.
+function test9(aContent, aInput) {
+ aContent.appendChild(aInput);
+ aInput.setAttribute('list', 'd');
+
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+ aContent.appendChild(datalist);
+
+ return datalist;
+}
+
+// If another datalist is added _after_ the first one, with the same id.
+function test10(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+ var datalist2 = document.createElement("datalist");
+ datalist2.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+ aContent.appendChild(datalist2);
+
+ return datalist;
+}
+
+// If another datalist is added _before_ the first one with the same id.
+function test11(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+ var datalist2 = document.createElement("datalist");
+ datalist2.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+ aContent.insertBefore(datalist2, datalist);
+
+ return datalist2;
+}
+
+// If datalist changes it's id.
+function test12(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+
+ datalist.id = 'foo';
+
+ return null;
+}
+
+// If datalist is removed.
+function test13(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+ aContent.removeChild(datalist);
+
+ return null;
+}
+
+// If id contain spaces.
+function test14(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = 'a b c d';
+
+ aInput.setAttribute('list', 'a b c d');
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+
+ return datalist;
+}
+
+// If id is the empty string.
+function test15(aContent, aInput) {
+ var datalist = document.createElement("datalist");
+ datalist.id = '';
+
+ aInput.setAttribute('list', '');
+ aContent.appendChild(aInput);
+ aContent.appendChild(datalist);
+
+ return null;
+}
+
+// If the id doesn't point to a datalist.
+function test16(aContent, aInput) {
+ var input = document.createElement("input");
+ input.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+ aContent.appendChild(aInput);
+ aContent.appendChild(input);
+
+ return null;
+}
+
+// If the first element with the id isn't a datalist.
+function test17(aContent, aInput) {
+ var input = document.createElement("input");
+ input.id = 'd';
+ var datalist = document.createElement("datalist");
+ datalist.id = 'd';
+
+ aInput.setAttribute('list', 'd');
+ aContent.appendChild(aInput);
+ aContent.appendChild(input);
+ aContent.appendChild(datalist);
+
+ return null;
+}
+
+var tests = [ test1, test2, test3, test4, test5, test6, test7, test8, test9,
+ test10, test11, test12, test13, test14, test15, test16, test17 ];
+
+test0();
+
+for (var test of tests) {
+ var content = document.getElementById('content');
+
+ // Clean-up.
+ content.textContent = '';
+
+ var input = document.createElement("input");
+ var res = test(content, input);
+
+ is(input.list, res, "input.list should be " + res);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_number_data.js b/dom/html/test/forms/test_input_number_data.js
new file mode 100644
index 0000000000..9ec53f136f
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_data.js
@@ -0,0 +1,54 @@
+var tests = [
+ {
+ desc: "British English",
+ langTag: "en-GB",
+ inputWithGrouping: "123,456.78",
+ inputWithoutGrouping: "123456.78",
+ value: 123456.78,
+ },
+ {
+ desc: "Farsi",
+ langTag: "fa",
+ inputWithGrouping: "۱۲۳٬۴۵۶٫۷۸",
+ inputWithoutGrouping: "۱۲۳۴۵۶٫۷۸",
+ value: 123456.78,
+ },
+ {
+ desc: "French",
+ langTag: "fr-FR",
+ inputWithGrouping: "123 456,78",
+ inputWithoutGrouping: "123456,78",
+ value: 123456.78,
+ },
+ {
+ desc: "German",
+ langTag: "de",
+ inputWithGrouping: "123.456,78",
+ inputWithoutGrouping: "123456,78",
+ value: 123456.78,
+ },
+ // Bug 1509057 disables grouping separators for now, so this test isn't
+ // currently relevant.
+ // Extra german test to check that a locale that uses '.' as its grouping
+ // separator doesn't result in it being invalid (due to step mismatch) due
+ // to the de-localization code mishandling numbers that look like other
+ // numbers formatted for English speakers (i.e. treating this as 123.456
+ // instead of 123456):
+ //{ desc: "German (test 2)",
+ // langTag: "de", inputWithGrouping: "123.456",
+ // inputWithoutGrouping: "123456", value: 123456
+ //},
+ {
+ desc: "Hebrew",
+ langTag: "he",
+ inputWithGrouping: "123,456.78",
+ inputWithoutGrouping: "123456.78",
+ value: 123456.78,
+ },
+];
+
+var invalidTests = [
+ // Right now this will pass in a 'de' build, but not in the 'en' build that
+ // are used for testing. See bug 1216831.
+ // { desc: "Invalid German", langTag: "de", input: "12.34" }
+];
diff --git a/dom/html/test/forms/test_input_number_focus.html b/dom/html/test/forms/test_input_number_focus.html
new file mode 100644
index 0000000000..4126ecc496
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_focus.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1268556
+-->
+<head>
+ <title>Test focus behaviour for &lt;input type='number'&gt;</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #input_test_style_display {
+ display: none;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268556">Mozilla Bug 1268556</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1057858">Mozilla Bug 1057858</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input_test_redirect" type="number">
+ <input id="input_test_style_display" type="number" >
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 1268556.
+ * This test checks that when focusing on an input type=number, the focus is
+ * redirected to the anonymous text control, but the document.activeElement
+ * still returns the <input type=number>.
+ *
+ * Tests for bug 1057858.
+ * Checks that adding an element and immediately focusing it triggers exactly
+ * one "focus" event and no "blur" events. The same for switching
+ * `style.display` from `none` to `block`.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test_focus_redirects_to_text_control_but_not_for_activeElement();
+ test_add_element_and_focus_check_one_focus_event();
+ test_style_display_none_change_to_block_check_one_focus_event();
+ SimpleTest.finish();
+});
+
+function test_focus_redirects_to_text_control_but_not_for_activeElement() {
+ document.activeElement.blur();
+ var number = document.getElementById("input_test_redirect");
+ number.focus();
+
+ // The active element returns the input type=number.
+ var activeElement = document.activeElement;
+ is (activeElement, number, "activeElement should be the number element");
+ is (activeElement.localName, "input", "activeElement should be an input element");
+ is (activeElement.getAttribute("type"), "number", "activeElement should of type number");
+
+ // Use FocusManager to check that the actual focus is on the anonymous
+ // text control.
+ var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"]
+ .getService(SpecialPowers.Ci.nsIFocusManager);
+ var focusedElement = fm.focusedElement;
+ is (focusedElement.localName, "input", "focusedElement should be an input element");
+ is (focusedElement.getAttribute("type"), "number", "focusedElement should of type number");
+}
+
+var blurEventCounter = 0;
+var focusEventCounter = 0;
+
+function append_input_element_with_event_listeners_to_dom() {
+ var inputElement = document.createElement("input");
+ inputElement.type = "number";
+ inputElement.addEventListener("blur", function() { ++blurEventCounter; });
+ inputElement.addEventListener("focus", function() { ++focusEventCounter; });
+ var content = document.getElementById("content");
+ content.appendChild(inputElement);
+ return inputElement;
+}
+
+function test_add_element_and_focus_check_one_focus_event() {
+ document.activeElement.blur();
+ var inputElement = append_input_element_with_event_listeners_to_dom();
+
+ blurEventCounter = 0;
+ focusEventCounter = 0;
+ inputElement.focus();
+
+ is(blurEventCounter, 0, "After focus: no blur events observed.");
+ is(focusEventCounter, 1, "After focus: exactly one focus event observed.");
+}
+
+function test_style_display_none_change_to_block_check_one_focus_event() {
+ document.activeElement.blur();
+ var inputElement = document.getElementById("input_test_style_display");
+ inputElement.addEventListener("blur", function() { ++blurEventCounter; });
+ inputElement.addEventListener("focus", function() { ++focusEventCounter; });
+
+ blurEventCounter = 0;
+ focusEventCounter = 0;
+ inputElement.style.display = "block";
+ inputElement.focus();
+
+ is(blurEventCounter, 0, "After focus: no blur events observed.");
+ is(focusEventCounter, 1, "After focus: exactly one focus event observed.");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_number_key_events.html b/dom/html/test/forms/test_input_number_key_events.html
new file mode 100644
index 0000000000..eb537f5617
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_key_events.html
@@ -0,0 +1,238 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=935506
+-->
+<head>
+ <title>Test key events for number control</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=935506">Mozilla Bug 935506</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input" type="number">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 935506
+ * This test checks how the value of <input type=number> changes in response to
+ * key events while it is in various states.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+const defaultMinimum = "NaN";
+const defaultMaximum = "NaN";
+const defaultStep = 1;
+
+// Helpers:
+// For the sake of simplicity, we do not currently support fractional value,
+// step, etc.
+
+function getMinimum(element) {
+ return Number(element.min || defaultMinimum);
+}
+
+function getMaximum(element) {
+ return Number(element.max || defaultMaximum);
+}
+
+function getDefaultValue(element) {
+ return 0;
+}
+
+function getValue(element) {
+ return Number(element.value || getDefaultValue(element));
+}
+
+function getStep(element) {
+ if (element.step == "any") {
+ return "any";
+ }
+ var step = Number(element.step || defaultStep);
+ return step <= 0 ? defaultStep : step;
+}
+
+function getStepBase(element) {
+ return Number(element.getAttribute("min") || "NaN") ||
+ Number(element.getAttribute("value") || "NaN") || 0;
+}
+
+function hasStepMismatch(element) {
+ var value = element.value;
+ if (value == "") {
+ value = 0;
+ }
+ var step = getStep(element);
+ if (step == "any") {
+ return false;
+ }
+ return ((value - getStepBase(element)) % step) != 0;
+}
+
+function floorModulo(x, y) {
+ return (x - y * Math.floor(x / y));
+}
+
+function expectedValueAfterStepUpOrDown(stepFactor, element) {
+ var value = getValue(element);
+ if (isNaN(value)) {
+ value = 0;
+ }
+ var step = getStep(element);
+ if (step == "any") {
+ step = 1;
+ }
+
+ var minimum = getMinimum(element);
+ var maximum = getMaximum(element);
+ if (!isNaN(maximum)) {
+ // "max - (max - stepBase) % step" is the nearest valid value to max.
+ maximum = maximum - floorModulo(maximum - getStepBase(element), step);
+ }
+
+ // Cases where we are clearly going in the wrong way.
+ // We don't use ValidityState because we can be higher than the maximal
+ // allowed value and still not suffer from range overflow in the case of
+ // of the value specified in @max isn't in the step.
+ if ((value <= minimum && stepFactor < 0) ||
+ (value >= maximum && stepFactor > 0)) {
+ return value;
+ }
+
+ if (hasStepMismatch(element) &&
+ value != minimum && value != maximum) {
+ if (stepFactor > 0) {
+ value -= floorModulo(value - getStepBase(element), step);
+ } else if (stepFactor < 0) {
+ value -= floorModulo(value - getStepBase(element), step);
+ value += step;
+ }
+ }
+
+ value += step * stepFactor;
+
+ // When stepUp() is called and the value is below minimum, we should clamp on
+ // minimum unless stepUp() moves us higher than minimum.
+ if (element.validity.rangeUnderflow && stepFactor > 0 &&
+ value <= minimum) {
+ value = minimum;
+ } else if (element.validity.rangeOverflow && stepFactor < 0 &&
+ value >= maximum) {
+ value = maximum;
+ } else if (stepFactor < 0 && !isNaN(minimum)) {
+ value = Math.max(value, minimum);
+ } else if (stepFactor > 0 && !isNaN(maximum)) {
+ value = Math.min(value, maximum);
+ }
+
+ return value;
+}
+
+function expectedValAfterKeyEvent(key, element) {
+ return expectedValueAfterStepUpOrDown(key == "KEY_ArrowUp" ? 1 : -1, element);
+}
+
+function test() {
+ var elem = document.getElementById("input");
+ elem.focus();
+
+ elem.min = -5;
+ elem.max = 5;
+ elem.step = 2;
+ var defaultValue = 0;
+ var oldVal, expectedVal;
+
+ for (key of ["KEY_ArrowUp", "KEY_ArrowDown"]) {
+ // Start at middle:
+ oldVal = elem.value = -1;
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for number control with value set between min/max (" + oldVal + ")");
+
+ // Same again:
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
+
+ // Start at maximum:
+ oldVal = elem.value = elem.max;
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the maximum (" + oldVal + ")");
+
+ // Same again:
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
+
+ // Start at minimum:
+ oldVal = elem.value = elem.min;
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the minimum (" + oldVal + ")");
+
+ // Same again:
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
+
+ // Test preventDefault():
+ elem.addEventListener("keydown", evt => evt.preventDefault(), {once: true});
+ oldVal = elem.value = 0;
+ expectedVal = 0;
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for number control where scripted preventDefault() should prevent the value changing");
+
+ // Test step="any" behavior:
+ var oldStep = elem.step;
+ elem.step = "any";
+ oldVal = elem.value = 0;
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the midpoint and step='any' (" + oldVal + ")");
+ elem.step = oldStep; // restore
+
+ // Test that invalid input blocks UI initiated stepping:
+ oldVal = elem.value = "";
+ elem.select();
+ sendString("abc");
+ synthesizeKey(key);
+ is(elem.value, "", "Test " + key + " does nothing when the input is invalid");
+
+ // Test that no value does not block UI initiated stepping:
+ oldVal = elem.value = "";
+ elem.setAttribute("required", "required");
+ elem.select();
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for number control with value set to the empty string and with the 'required' attribute set");
+
+ // Same again:
+ expectedVal = expectedValAfterKeyEvent(key, elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for number control");
+
+ // Reset 'required' attribute:
+ elem.removeAttribute("required");
+ }
+
+ // Test that key events are correctly dispatched
+ elem.max = "";
+ elem.value = "";
+ sendString("7837281");
+ is(elem.value, "7837281", "Test keypress event dispatch for number control");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_number_l10n.html b/dom/html/test/forms/test_input_number_l10n.html
new file mode 100644
index 0000000000..c8202028ed
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_l10n.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=844744
+-->
+<head>
+ <title>Test localization of number control input</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="test_input_number_data.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=844744">Mozilla Bug 844744</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input" type="number" step="any">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 844744
+ * This test checks that localized input that is typed into <input type=number>
+ * is correctly handled.
+ **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ startTests();
+ SimpleTest.finish();
+});
+
+var elem;
+
+function runTest(test) {
+ elem.lang = test.langTag;
+ elem.value = 0;
+ elem.focus();
+ elem.select();
+ sendString(test.inputWithGrouping);
+ is(elem.value, "", "Test " + test.desc + " ('" + test.langTag +
+ "') localization with grouping separator");
+ elem.value = 0;
+ elem.select();
+ sendString(test.inputWithoutGrouping);
+ is(elem.valueAsNumber, test.value, "Test " + test.desc + " ('" + test.langTag +
+ "') localization without grouping separator");
+ is(elem.value, String(test.value), "Test " + test.desc + " ('" + test.langTag +
+ "') localization without grouping separator as string");
+}
+
+function runInvalidInputTest(test) {
+ elem.lang = test.langTag;
+ elem.value = 0;
+ elem.focus();
+ elem.select();
+ sendString(test.input);
+ is(elem.value, "", "Test " + test.desc + " ('" + test.langTag +
+ "') with invalid input: " + test.input);
+}
+
+function startTests() {
+ elem = document.getElementById("input");
+ for (var test of tests) {
+ runTest(test, elem);
+ }
+ for (var test of invalidTests) {
+ runInvalidInputTest(test, elem);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_number_mouse_events.html b/dom/html/test/forms/test_input_number_mouse_events.html
new file mode 100644
index 0000000000..a3e5732beb
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_mouse_events.html
@@ -0,0 +1,272 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=935501
+-->
+<head>
+ <title>Test mouse events for number</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <style>
+ input {
+ margin: 0;
+ border: 0;
+ padding: 0;
+ width: 200px;
+ box-sizing: border-box;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=935501">Mozilla Bug 935501</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input" type="number">
+</div>
+<pre id="test">
+<script>
+
+const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+/**
+ * Test for Bug 935501
+ * This test checks how the value of <input type=number> changes in response to
+ * various mouse events.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+SimpleTest.waitForFocus(function() {
+ test();
+});
+
+const kIsWin = AppConstants.platform == "win";
+const kIsLinux = AppConstants.platform == "linux";
+
+var input = document.getElementById("input");
+var inputRect = input.getBoundingClientRect();
+
+// Points over the input's spin-up and spin-down buttons (as offsets from the
+// top-left of the input's bounding client rect):
+const SPIN_UP_X = inputRect.width - 3;
+const SPIN_UP_Y = 3;
+const SPIN_DOWN_X = inputRect.width - 3;
+const SPIN_DOWN_Y = inputRect.height - 3;
+
+function checkInputEvent(aEvent, aDescription) {
+ // Probably, key operation should fire "input" event with InputEvent interface.
+ // See https://github.com/w3c/input-events/issues/88
+ ok(aEvent instanceof InputEvent, `"input" event should be dispatched with InputEvent interface on input element whose type is number ${aDescription}`);
+ is(aEvent.cancelable, false, `"input" event should be never cancelable on input element whose type is number ${aDescription}`);
+ is(aEvent.bubbles, true, `"input" event should always bubble on input element whose type is number ${aDescription}`);
+ info(`Data: ${aEvent.data}, value: ${aEvent.target.value}`);
+}
+
+function test() {
+ input.value = 0;
+
+ // Test click on spin-up button:
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
+ is(input.value, "1", "Test step-up on mousedown on spin-up button");
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mouseup" });
+ is(input.value, "1", "Test mouseup on spin-up button");
+
+ // Test click on spin-down button:
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousedown" });
+ is(input.value, "0", "Test step-down on mousedown on spin-down button");
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mouseup" });
+ is(input.value, "0", "Test mouseup on spin-down button");
+
+ // Test clicks with modifiers that mean we should ignore the click:
+ var modifiersIgnore = ["altGrKey", "fnKey"];
+ if (kIsWin || kIsLinux) {
+ modifiersIgnore.push("metaKey");
+ }
+ for (var modifier of modifiersIgnore) {
+ input.value = 0;
+ var eventParams = { type: "mousedown" };
+ eventParams[modifier] = true;
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, eventParams);
+ is(input.value, "0", "We should ignore mousedown on spin-up button with modifier " + modifier);
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mouseup" });
+ }
+
+ // Test clicks with modifiers that mean we should allow the click:
+ var modifiersAllow = ["shiftKey", "ctrlKey", "altKey"];
+ if (!modifiersIgnore.includes("metaKey")) {
+ modifiersAllow.push("metaKey");
+ }
+ for (var modifier of modifiersAllow) {
+ input.value = 0;
+ var eventParams = { type: "mousedown" };
+ eventParams[modifier] = true;
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, eventParams);
+ is(input.value, "1", "We should allow mousedown on spin-up button with modifier " + modifier);
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mouseup" });
+ }
+
+ // Test step="any" behavior:
+ input.value = 0;
+ var oldStep = input.step;
+ input.step = "any";
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
+ is(input.value, "1", "Test step-up on mousedown on spin-up button with step='any'");
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mouseup" });
+ is(input.value, "1", "Test mouseup on spin-up button with step='any'");
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousedown" });
+ is(input.value, "0", "Test step-down on mousedown on spin-down button with step='any'");
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mouseup" });
+ is(input.value, "0", "Test mouseup on spin-down button with step='any'");
+ input.step = oldStep; // restore
+
+ // Test that preventDefault() works:
+ function preventDefault(e) {
+ e.preventDefault();
+ }
+ input.value = 1;
+ input.addEventListener("mousedown", preventDefault);
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, {});
+ is(input.value, "1", "Test that preventDefault() works for click on spin-up button");
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, {});
+ is(input.value, "1", "Test that preventDefault() works for click on spin-down button");
+ input.removeEventListener("mousedown", preventDefault);
+
+ // Test for bug 1707070.
+ input.style.paddingRight = "30px";
+ input.getBoundingClientRect(); // flush layout
+
+ input.value = 0;
+ synthesizeMouse(input, SPIN_UP_X - 30, SPIN_UP_Y, { type: "mousedown" });
+ is(input.value, "1", "Spinner down works on with padding (mousedown)");
+ synthesizeMouse(input, SPIN_UP_X - 30, SPIN_UP_Y, { type: "mouseup" });
+ is(input.value, "1", "Spinner down works with padding (mouseup)");
+
+ synthesizeMouse(input, SPIN_DOWN_X - 30, SPIN_DOWN_Y, { type: "mousedown" });
+ is(input.value, "0", "Spinner works with padding (mousedown)");
+ synthesizeMouse(input, SPIN_DOWN_X - 30, SPIN_DOWN_Y, { type: "mouseup" });
+ is(input.value, "0", "Spinner works with padding (mouseup)");
+
+ input.style.paddingRight = "";
+ input.getBoundingClientRect(); // flush layout
+
+ // Run the spin tests:
+ runNextSpinTest();
+}
+
+function runNextSpinTest() {
+ var nextTest = spinTests.shift();
+ if (!nextTest) {
+ SimpleTest.finish();
+ return;
+ }
+ nextTest();
+}
+
+function waitForTick() {
+ return new Promise(SimpleTest.executeSoon);
+}
+
+const SETTIMEOUT_DELAY = 500;
+
+var spinTests = [
+ // Test spining when the mouse button is kept depressed on the spin-up
+ // button, then moved over the spin-down button:
+ function() {
+ var inputEventCount = 0;
+ input.value = 0;
+ input.addEventListener("input", async function(evt) {
+ ++inputEventCount;
+ checkInputEvent(evt, "#1");
+ if (inputEventCount == 3) {
+ is(input.value, "3", "Testing spin-up button");
+ await waitForTick();
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousemove" });
+ } else if (inputEventCount == 6) {
+ is(input.value, "0", "Testing spin direction is reversed after mouse moves from spin-up button to spin-down button");
+ input.removeEventListener("input", arguments.callee);
+ await waitForTick();
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mouseup" });
+ runNextSpinTest();
+ }
+ });
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
+ },
+
+ // Test spining when the mouse button is kept depressed on the spin-down
+ // button, then moved over the spin-up button:
+ function() {
+ var inputEventCount = 0;
+ input.value = 0;
+ input.addEventListener("input", async function(evt) {
+ ++inputEventCount;
+ checkInputEvent(evt, "#2");
+ if (inputEventCount == 3) {
+ is(input.value, "-3", "Testing spin-down button");
+ await waitForTick();
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousemove" });
+ } else if (inputEventCount == 6) {
+ is(input.value, "0", "Testing spin direction is reversed after mouse moves from spin-down button to spin-up button");
+ input.removeEventListener("input", arguments.callee);
+ await waitForTick();
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mouseup" });
+ runNextSpinTest();
+ }
+ });
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousedown" });
+ },
+
+ // Test that the spin is stopped when the mouse button is depressod on the
+ // spin-up button, then moved outside both buttons once the spin starts:
+ function() {
+ var inputEventCount = 0;
+ input.value = 0;
+ input.addEventListener("input", async function(evt) {
+ ++inputEventCount;
+ checkInputEvent(evt, "#3");
+ if (inputEventCount == 3) {
+ await waitForTick();
+ synthesizeMouse(input, -1, -1, { type: "mousemove" });
+ var eventHandler = arguments.callee;
+ setTimeout(function() {
+ is(input.value, "3", "Testing moving the mouse outside the spin buttons stops the spin");
+ is(inputEventCount, 3, "Testing moving the mouse outside the spin buttons stops the spin input events");
+ input.removeEventListener("input", eventHandler);
+ synthesizeMouse(input, -1, -1, { type: "mouseup" });
+ runNextSpinTest();
+ }, SETTIMEOUT_DELAY);
+ }
+ });
+ synthesizeMouse(input, SPIN_UP_X, SPIN_UP_Y, { type: "mousedown" });
+ },
+
+ // Test that changing the input type in the middle of a spin cancels the spin:
+ function() {
+ var inputEventCount = 0;
+ input.value = 0;
+ input.addEventListener("input", function(evt) {
+ ++inputEventCount;
+ checkInputEvent(evt, "#4");
+ if (inputEventCount == 3) {
+ input.type = "text"
+ var eventHandler = arguments.callee;
+ setTimeout(function() {
+ is(input.value, "-3", "Testing changing input type during a spin stops the spin");
+ is(inputEventCount, 3, "Testing changing input type during a spin stops the spin input events");
+ input.removeEventListener("input", eventHandler);
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mouseup" });
+ input.type = "number"; // restore
+ runNextSpinTest();
+ }, SETTIMEOUT_DELAY);
+ }
+ });
+ synthesizeMouse(input, SPIN_DOWN_X, SPIN_DOWN_Y, { type: "mousedown" });
+ }
+];
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_number_placeholder_shown.html b/dom/html/test/forms/test_input_number_placeholder_shown.html
new file mode 100644
index 0000000000..c9c2a7f515
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_placeholder_shown.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<title>Test for :placeholder-shown on input elements and invalid values.</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+input {
+ border: 1px solid purple;
+}
+input:placeholder-shown {
+ border-color: blue;
+}
+</style>
+<input type="number" placeholder="foo">
+<script>
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+ });
+
+ function test() {
+ let input = document.querySelector('input');
+ input.focus();
+ is(getComputedStyle(input).borderLeftColor, "rgb(0, 0, 255)",
+ ":placeholder-shown should apply")
+ sendString("x");
+ isnot(getComputedStyle(input).borderLeftColor, "rgb(0, 0, 255)",
+ ":placeholder-shown should not apply, even though the value is invalid")
+ }
+</script>
diff --git a/dom/html/test/forms/test_input_number_rounding.html b/dom/html/test/forms/test_input_number_rounding.html
new file mode 100644
index 0000000000..d162727557
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_rounding.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=783607
+-->
+<head>
+ <title>Test rounding behaviour for &lt;input type='number'&gt;</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783607">Mozilla Bug 783607</a>
+<p id="display"></p>
+<div id="content">
+ <input id=number type=number value=0 step=0.01 max=1>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 783607.
+ * This test checks that when <input type=number> has fractional step values,
+ * the values that a content author will see in their script will not have
+ * ugly rounding errors.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+/**
+ * We can _NOT_ generate these values by looping and simply incrementing a
+ * variable by 0.01 and stringifying it, since we'll end up with strings like
+ * "0.060000000000000005" due to the inability of binary floating point numbers
+ * to accurately represent decimal values.
+ */
+var stepVals = [
+ "0", "0.01", "0.02", "0.03", "0.04", "0.05", "0.06", "0.07", "0.08", "0.09",
+ "0.1", "0.11", "0.12", "0.13", "0.14", "0.15", "0.16", "0.17", "0.18", "0.19",
+ "0.2", "0.21", "0.22", "0.23", "0.24", "0.25", "0.26", "0.27", "0.28", "0.29",
+ "0.3", "0.31", "0.32", "0.33", "0.34", "0.35", "0.36", "0.37", "0.38", "0.39",
+ "0.4", "0.41", "0.42", "0.43", "0.44", "0.45", "0.46", "0.47", "0.48", "0.49",
+ "0.5", "0.51", "0.52", "0.53", "0.54", "0.55", "0.56", "0.57", "0.58", "0.59",
+ "0.6", "0.61", "0.62", "0.63", "0.64", "0.65", "0.66", "0.67", "0.68", "0.69",
+ "0.7", "0.71", "0.72", "0.73", "0.74", "0.75", "0.76", "0.77", "0.78", "0.79",
+ "0.8", "0.81", "0.82", "0.83", "0.84", "0.85", "0.86", "0.87", "0.88", "0.89",
+ "0.9", "0.91", "0.92", "0.93", "0.94", "0.95", "0.96", "0.97", "0.98", "0.99",
+ "1"
+];
+
+var pgUpDnVals = [
+ "0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1"
+];
+
+function test() {
+ var elem = document.getElementById("number");
+
+ elem.focus();
+
+ /**
+ * TODO:
+ * When <input type='number'> widget will have a widge we should test PAGE_UP,
+ * PAGE_DOWN, UP and DOWN keys. For the moment, there is no widget so those
+ * keys do not have any effect.
+ * The tests using those keys as marked as todo_is() hoping that at least part
+ * of them will fail when the widget will be implemented.
+ */
+
+/* No other implementations implement this, so we don't either, for now.
+ Seems like it might be nice though.
+
+ for (var i = 1; i < pgUpDnVals.length; ++i) {
+ synthesizeKey("KEY_PageUp");
+ todo_is(elem.value, pgUpDnVals[i], "Test KEY_PageUp");
+ is(elem.validity.valid, true, "Check element is valid for value " + pgUpDnVals[i]);
+ }
+
+ for (var i = pgUpDnVals.length - 2; i >= 0; --i) {
+ synthesizeKey("KEY_PageDown");
+ // TODO: this condition is there because the todo_is() below would pass otherwise.
+ if (stepVals[i] == 0) { continue; }
+ todo_is(elem.value, pgUpDnVals[i], "Test KEY_PageDown");
+ is(elem.validity.valid, true, "Check element is valid for value " + pgUpDnVals[i]);
+ }
+*/
+
+ for (var i = 1; i < stepVals.length; ++i) {
+ synthesizeKey("KEY_ArrowUp");
+ is(elem.value, stepVals[i], "Test KEY_ArrowUp");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+
+ for (var i = stepVals.length - 2; i >= 0; --i) {
+ synthesizeKey("KEY_ArrowDown");
+ // TODO: this condition is there because the todo_is() below would pass otherwise.
+ if (stepVals[i] == 0) { continue; }
+ is(elem.value, stepVals[i], "Test KEY_ArrowDown");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+
+ for (var i = 1; i < stepVals.length; ++i) {
+ elem.stepUp();
+ is(elem.value, stepVals[i], "Test stepUp()");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+
+ for (var i = stepVals.length - 2; i >= 0; --i) {
+ elem.stepDown();
+ is(elem.value, stepVals[i], "Test stepDown()");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_number_validation.html b/dom/html/test/forms/test_input_number_validation.html
new file mode 100644
index 0000000000..c19c1fde1c
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_validation.html
@@ -0,0 +1,139 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=827161
+-->
+<head>
+ <title>Test validation of number control input</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="test_input_number_data.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=827161">Mozilla Bug 827161</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input" type="number" step="0.01" oninvalid="invalidEventHandler(event);">
+ <input id="requiredinput" type="number" step="0.01" required
+ oninvalid="invalidEventHandler(event);">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 827161.
+ * This test checks that validation works correctly for <input type=number>.
+ **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ startTests();
+ SimpleTest.finish();
+});
+
+var elem;
+
+function runTest(test) {
+ elem.lang = test.langTag;
+
+ gInvalid = false; // reset
+ var desc = `${test.desc} (lang='${test.langTag}', id='${elem.id}')`;
+ elem.value = 0;
+ elem.focus();
+ elem.select();
+ sendString(test.inputWithGrouping);
+ checkIsInvalid(elem, `${desc} with grouping separator`);
+ sendChar("a");
+ checkIsInvalid(elem, `${desc} with grouping separator`);
+
+ gInvalid = false; // reset
+ elem.value = 0;
+ elem.select();
+ sendString(test.inputWithoutGrouping);
+ checkIsValid(elem, `${desc} without grouping separator`);
+ sendChar("a");
+ checkIsInvalid(elem, `${desc} without grouping separator`);
+}
+
+function runInvalidInputTest(test) {
+ elem.lang = test.langTag;
+
+ gInvalid = false; // reset
+ var desc = `${test.desc} (lang='${test.langTag}', id='${elem.id}')`;
+ elem.value = 0;
+ elem.focus();
+ elem.select();
+ sendString(test.input);
+ checkIsInvalid(elem, `${desc} with invalid input ${test.input}`);
+}
+
+function startTests() {
+ elem = document.getElementById("input");
+ for (var test of tests) {
+ runTest(test);
+ }
+ for (var test of invalidTests) {
+ runInvalidInputTest(test);
+ }
+ elem = document.getElementById("requiredinput");
+ for (var test of tests) {
+ runTest(test);
+ }
+
+ gInvalid = false; // reset
+ elem.value = "";
+ checkIsInvalidEmptyValue(elem, "empty value");
+}
+
+var gInvalid = false;
+
+function invalidEventHandler(e)
+{
+ is(e.type, "invalid", "Invalid event type should be 'invalid'");
+ gInvalid = true;
+}
+
+function checkIsValid(element, infoStr)
+{
+ ok(!element.validity.badInput,
+ "Element should not suffer from bad input for " + infoStr);
+ ok(element.validity.valid, "Element should be valid for " + infoStr);
+ ok(element.checkValidity(), "checkValidity() should return true for " + infoStr);
+ ok(!gInvalid, "The invalid event should not have been thrown for " + infoStr);
+ is(element.validationMessage, '',
+ "Validation message should be the empty string for " + infoStr);
+ ok(element.matches(":valid"), ":valid pseudo-class should apply for " + infoStr);
+}
+
+function checkIsInvalid(element, infoStr)
+{
+ ok(element.validity.badInput,
+ "Element should suffer from bad input for " + infoStr);
+ ok(!element.validity.valid, "Element should not be valid for " + infoStr);
+ ok(!element.checkValidity(), "checkValidity() should return false for " + infoStr);
+ ok(gInvalid, "The invalid event should have been thrown for " + infoStr);
+ is(element.validationMessage, "Please enter a number.",
+ "Validation message is not the expected message for " + infoStr);
+ ok(element.matches(":invalid"), ":invalid pseudo-class should apply for " + infoStr);
+}
+
+function checkIsInvalidEmptyValue(element, infoStr)
+{
+ ok(!element.validity.badInput,
+ "Element should not suffer from bad input for " + infoStr);
+ ok(element.validity.valueMissing,
+ "Element should suffer from value missing for " + infoStr);
+ ok(!element.validity.valid, "Element should not be valid for " + infoStr);
+ ok(!element.checkValidity(), "checkValidity() should return false for " + infoStr);
+ ok(gInvalid, "The invalid event should have been thrown for " + infoStr);
+ is(element.validationMessage, "Please enter a number.",
+ "Validation message is not the expected message for " + infoStr);
+ ok(element.matches(":invalid"), ":invalid pseudo-class should apply for " + infoStr);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_password_click_show_password_button.html b/dom/html/test/forms/test_input_password_click_show_password_button.html
new file mode 100644
index 0000000000..76f4e066f5
--- /dev/null
+++ b/dom/html/test/forms/test_input_password_click_show_password_button.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=502258
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 502258</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script>
+
+ SimpleTest.waitForExplicitFinish();
+
+ async function click_show_password(aId) {
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+ var element = document.getElementById(aId);
+ element.focus();
+ await new Promise(resolve => setTimeout(resolve, 0));
+ var rect = element.getBoundingClientRect();
+ var x = rect.right - 8;
+ var y = rect.top + 8;
+ wu.sendMouseEvent("mousedown", x, y, 0, 1, 0);
+ wu.sendMouseEvent("mouseup", x, y, 0, 1, 0);
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ }
+
+ async function test_show_password(aId) {
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+ var element = document.getElementById(aId);
+
+ var baseSnapshot = await snapshotWindow(window);
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ element.type = "text";
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ var typeTextSnapshot = await snapshotWindow(window);
+ results = compareSnapshots(baseSnapshot, typeTextSnapshot, true);
+ ok(results[0], aId + ": type=text should render the same as type=password that is showing the password");
+
+ // Re-setting value shouldn't change anything.
+ // eslint-disable-next-line no-self-assign
+ element.value = element.value;
+ var tmpSnapshot = await snapshotWindow(window);
+
+ results = compareSnapshots(baseSnapshot, tmpSnapshot, true);
+ ok(results[0], aId + ": re-setting the value should change nothing");
+ }
+
+ async function reset_show_password(aId, concealedSnapshot) {
+ var element = document.getElementById(aId);
+ element.type = "password";
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ var typePasswordSnapshot = await snapshotWindow(window);
+ results = compareSnapshots(concealedSnapshot, typePasswordSnapshot, true);
+ ok(results[0], aId + ": changing the type attribute should conceal the password again");
+ }
+
+ async function runTest() {
+ await SpecialPowers.pushPrefEnv({set: [["layout.forms.reveal-password-button.enabled", true]]});
+ document.getElementById("content").style.display = "";
+ document.getElementById("content").getBoundingClientRect();
+ var concealedSnapshot = await snapshotWindow(window);
+ // test1 checks that the Show Password button becomes invisible when the value becomes empty
+ document.getElementById('test1').value = "123";
+ await click_show_password('test1');
+ document.getElementById('test1').value = "";
+ // test2 checks that clicking the Show Password button unmasks the value
+ await click_show_password('test2');
+ await test_show_password('test1');
+ await test_show_password('test2');
+ // checks that changing the type attribute resets thhe revealed state
+ await reset_show_password('test2', concealedSnapshot);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForFocus(runTest);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=502258">Mozilla Bug 502258</a>
+<p id="display"></p>
+<style>input {appearance:none} .ref {display:none}</style>
+<div id="content" style="display: none">
+ <input id="test1" type=password>
+ <div style="position:relative; margin: 1em 0;">
+ <input id="test2" type=password value="123" style="position:absolute">
+ <div style="position:absolute; top:0;left:10ch; width:20ch; height:2em; background:black; pointer-events:none"></div>
+ </div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_password_show_password_button.html b/dom/html/test/forms/test_input_password_show_password_button.html
new file mode 100644
index 0000000000..09bec8ae82
--- /dev/null
+++ b/dom/html/test/forms/test_input_password_show_password_button.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=502258
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 502258</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ async function test_append_char(aId) {
+ let element = document.getElementById(aId);
+ element.focus();
+
+ let baseSnapshot = await snapshotWindow(window);
+
+ element.selectionStart = element.selectionEnd = element.value.length;
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ sendString('f');
+
+ await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+
+ let selectionAtTheEndSnapshot = await snapshotWindow(window);
+ assertSnapshots(baseSnapshot, selectionAtTheEndSnapshot, /* equal = */ false, /* fuzz = */ null, "baseSnapshot", "selectionAtTheEndSnapshot");
+
+ // Re-setting value shouldn't change anything.
+ // eslint-disable-next-line no-self-assign
+ element.value = element.value;
+ let tmpSnapshot = await snapshotWindow(window);
+
+ assertSnapshots(baseSnapshot, tmpSnapshot, /* equal = */ false, /* fuzz = */ null, "baseSnapshot", "tmpSnapshot");
+ assertSnapshots(selectionAtTheEndSnapshot, tmpSnapshot, /* equal = */ true, /* fuzz = */ null, "selectionAtTheEndSnapshot", "tmpSnapshot");
+
+ element.selectionStart = element.selectionEnd = 0;
+ element.blur();
+ }
+
+ async function runTest() {
+ await SpecialPowers.pushPrefEnv({set: [["layout.forms.reveal-password-button.enabled", true]]});
+ document.getElementById("content").style.display = "";
+ document.getElementById("content").getBoundingClientRect();
+ await test_append_char('test1');
+ await test_append_char('test2');
+ await test_append_char('test3');
+ await test_append_char('test4');
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForFocus(runTest);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=502258">Mozilla Bug 502258</a>
+<p id="display"></p>
+<style>input {appearance:none}</style>
+<div id="content" style="display: none">
+ <input id="test1" type=password>
+ <input id="test2" type=password value="123">
+ <!-- text value masked off -->
+ <div style="position:relative; margin: 1em 0;">
+ <input id="test3" type=password style="position:absolute">
+ <div style="position:absolute; top:0;left:0; width:10ch; height:2em; background:black"></div>
+ </div>
+ <br>
+ <!-- Show Password button masked off -->
+ <div style="position:relative; margin: 1em 0;">
+ <input id="test4" type=password style="position:absolute">
+ <div style="position:absolute; top:0;left:10ch; width:20ch; height:2em; background:black"></div>
+ </div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_radio_indeterminate.html b/dom/html/test/forms/test_input_radio_indeterminate.html
new file mode 100644
index 0000000000..0fe7028b1e
--- /dev/null
+++ b/dom/html/test/forms/test_input_radio_indeterminate.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=885359
+-->
+<head>
+ <title>Test for Bug 885359</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885359">Mozilla Bug 343444</a>
+<p id="display"></p>
+<form>
+ <input type="radio" id='radio1'/><br/>
+
+ <input type="radio" id="g1radio1" name="group1"/>
+ <input type="radio" id="g1radio2" name="group1"/></br>
+ <input type="radio" id="g1radio3" name="group1"/></br>
+
+ <input type="radio" id="g2radio1" name="group2"/>
+ <input type="radio" id="g2radio2" name="group2" checked/></br>
+</form>
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var radio1 = document.getElementById("radio1");
+var g1radio1 = document.getElementById("g1radio1");
+var g1radio2 = document.getElementById("g1radio2");
+var g1radio3 = document.getElementById("g1radio3");
+var g2radio1 = document.getElementById("g2radio1");
+var g2radio2 = document.getElementById("g2radio2");
+
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function verifyIndeterminateState(aElement, aIsIndeterminate, aMessage) {
+ is(aElement.mozMatchesSelector(':indeterminate'), aIsIndeterminate, aMessage);
+}
+
+function test() {
+ // Initial State.
+ verifyIndeterminateState(radio1, true,
+ "Unchecked radio in its own group (no name attribute)");
+ verifyIndeterminateState(g1radio1, true, "No selected radio in its group");
+ verifyIndeterminateState(g1radio2, true, "No selected radio in its group");
+ verifyIndeterminateState(g1radio3, true, "No selected radio in its group");
+ verifyIndeterminateState(g2radio1, false, "Selected radio in its group");
+ verifyIndeterminateState(g2radio2, false, "Selected radio in its group");
+
+ // Selecting radio buttion.
+ g1radio1.checked = true;
+ verifyIndeterminateState(g1radio1, false,
+ "Selecting a radio should affect all radios in the group");
+ verifyIndeterminateState(g1radio2, false,
+ "Selecting a radio should affect all radios in the group");
+ verifyIndeterminateState(g1radio3, false,
+ "Selecting a radio should affect all radios in the group");
+
+ // Changing the selected radio button.
+ g1radio3.checked = true;
+ verifyIndeterminateState(g1radio1, false,
+ "Selecting a radio should affect all radios in the group");
+ verifyIndeterminateState(g1radio2, false,
+ "Selecting a radio should affect all radios in the group");
+ verifyIndeterminateState(g1radio3, false,
+ "Selecting a radio should affect all radios in the group");
+
+ // Deselecting radio button.
+ g2radio2.checked = false;
+ verifyIndeterminateState(g2radio1, true,
+ "Deselecting a radio should affect all radios in the group");
+ verifyIndeterminateState(g2radio2, true,
+ "Deselecting a radio should affect all radios in the group");
+
+ // Move a selected radio button to another group.
+ g1radio3.name = "group2";
+
+ // The radios' state in the original group becomes indeterminated.
+ verifyIndeterminateState(g1radio1, true,
+ "Removing a radio from a group should affect all radios in the group");
+ verifyIndeterminateState(g1radio2, true,
+ "Removing a radio from a group should affect all radios in the group");
+
+ // The radios' state in the new group becomes determinated.
+ verifyIndeterminateState(g1radio3, false,
+ "Adding a radio from a group should affect all radios in the group");
+ verifyIndeterminateState(g2radio1, false,
+ "Adding a radio from a group should affect all radios in the group");
+ verifyIndeterminateState(g2radio2, false,
+ "Adding a radio from a group should affect all radios in the group");
+
+ // Change input type to 'text'.
+ g1radio3.type = "text";
+ verifyIndeterminateState(g1radio3, false,
+ "Input type text does not have an indeterminate state");
+ verifyIndeterminateState(g2radio1, true,
+ "Changing input type should affect all radios in the group");
+ verifyIndeterminateState(g2radio2, true,
+ "Changing input type should affect all radios in the group");
+}
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_input_radio_radiogroup.html b/dom/html/test/forms/test_input_radio_radiogroup.html
new file mode 100644
index 0000000000..62767def72
--- /dev/null
+++ b/dom/html/test/forms/test_input_radio_radiogroup.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=343444
+-->
+<head>
+ <title>Test for Bug 343444</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=343444">Mozilla Bug 343444</a>
+<p id="display"></p>
+<form>
+ <fieldset id="testradio">
+ <input type="radio" name="testradio" id="start"></input>
+ <input type="text" name="testradio"></input>
+ <input type="text" name="testradio"></input>
+ <input type="radio" name="testradio"></input>
+ <input type="text" name="testradio"></input>
+ <input type="radio" name="testradio"></input>
+ <input type="text" name="testradio"></input>
+ <input type="radio" name="testradio"></input>
+ <input type="radio" name="testradio"></input>
+ <input type="text" name="testradio"></input>
+ </fieldset>
+
+ <fieldset>
+ <input type="radio" name="testtwo" id="start2"></input>
+ <input type="radio" name="testtwo"></input>
+ <input type="radio" name="error" id="testtwo"></input>
+ <input type="radio" name="testtwo" id="end"></input>
+ </fieldset>
+
+ <fieldset>
+ <input type="radio" name="testthree" id="start3"></input>
+ <input type="radio" name="errorthree" id="testthree"></input>
+ </fieldset>
+</form>
+<script class="testbody" type="text/javascript">
+/** Test for Bug 343444 **/
+SimpleTest.waitForExplicitFinish();
+startTest();
+function startTest() {
+ document.getElementById("start").focus();
+ var count=0;
+ while (count < 2) {
+ sendKey("DOWN");
+ is(document.activeElement.type, "radio", "radioGroup should ignore non-radio input fields");
+ if (document.activeElement.id == "start") {
+ count++;
+ }
+ }
+
+ document.getElementById("start2").focus();
+ count = 0;
+ while (count < 3) {
+ is(document.activeElement.name, "testtwo",
+ "radioGroup should only contain elements with the same @name")
+ sendKey("DOWN");
+ count++;
+ }
+
+ document.getElementById("start3").focus();
+ sendKey("DOWN");
+ is(document.activeElement.name, "testthree", "we don't have an infinite-loop");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_input_radio_required.html b/dom/html/test/forms/test_input_radio_required.html
new file mode 100644
index 0000000000..ae02aab2ff
--- /dev/null
+++ b/dom/html/test/forms/test_input_radio_required.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={BUGNUMBER}
+-->
+<head>
+ <title>Test for Bug 1100535</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1100535">Mozilla Bug 1100535</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<form>
+ <input type="radio" name="a">
+</form>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ var input = document.querySelector("input");
+ input.setAttribute("required", "x");
+ input.setAttribute("required", "y");
+ is(document.forms[0].checkValidity(), false);
+ input.required = false;
+ is(document.forms[0].checkValidity(), true);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_range_attr_order.html b/dom/html/test/forms/test_input_range_attr_order.html
new file mode 100644
index 0000000000..dc3f1ac95c
--- /dev/null
+++ b/dom/html/test/forms/test_input_range_attr_order.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=841941
+-->
+<head>
+ <title>Test @min/@max/@step order for range</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841941">Mozilla Bug 841941</a>
+<p id="display"></p>
+<div id="content">
+ <input type=range value=2 max=1.5 step=0.5>
+ <input type=range value=2 step=0.5 max=1.5>
+ <input type=range value=2 max=1.5 step=0.5>
+ <input type=range value=2 step=0.5 max=1.5>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 841941
+ * This test checks that the order in which @min/@max/@step are specified in
+ * markup makes no difference to the value that <input type=range> will be
+ * given. Basically this checks that sanitization of the value does not occur
+ * until after the parser has finished with the element.
+ */
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+function test() {
+ var ranges = document.querySelectorAll("input[type=range]");
+ for (var i = 0; i < ranges.length; i++) {
+ is(ranges.item(i).value, "1.5", "Check sanitization order for range " + i);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_range_key_events.html b/dom/html/test/forms/test_input_range_key_events.html
new file mode 100644
index 0000000000..6daf572916
--- /dev/null
+++ b/dom/html/test/forms/test_input_range_key_events.html
@@ -0,0 +1,207 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=843725
+-->
+<head>
+ <title>Test key events for range</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=843725">Mozilla Bug 843725</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 843725
+ * This test checks how the value of <input type=range> changes in response to
+ * various key events while it is in various states.
+ **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+const defaultMinimum = 0;
+const defaultMaximum = 100;
+const defaultStep = 1;
+
+// Helpers:
+// For the sake of simplicity, we do not currently support fractional value,
+// step, etc.
+
+function minimum(element) {
+ return Number(element.min || defaultMinimum);
+}
+
+function maximum(element) {
+ return Number(element.max || defaultMaximum);
+}
+
+function range(element) {
+ var max = maximum(element);
+ var min = minimum(element);
+ if (max < min) {
+ return 0;
+ }
+ return max - min;
+}
+
+function defaultValue(element) {
+ return minimum(element) + range(element)/2;
+}
+
+function value(element) {
+ return Number(element.value || defaultValue(element));
+}
+
+function step(element) {
+ var stepSize = Number(element.step || defaultStep);
+ return stepSize <= 0 ? defaultStep : stepSize;
+}
+
+function clampToRange(val, element) {
+ var min = minimum(element);
+ var max = maximum(element);
+ if (max < min) {
+ return min;
+ }
+ if (val < min) {
+ return min;
+ }
+ if (val > max) {
+ return max;
+ }
+ return val;
+}
+
+// Functions used to specify expected test results:
+
+function valuePlusStep(element) {
+ return clampToRange(value(element) + step(element), element);
+}
+
+function valueMinusStep(element) {
+ return clampToRange(value(element) - step(element), element);
+}
+
+/**
+ * Returns the current value of the range plus whichever is greater of either
+ * 10% of the range or its current step value, clamped to the range's minimum/
+ * maximum. The reason for using the step if it is greater than 10% of the
+ * range is because otherwise the PgUp/PgDn keys would do nothing in that case.
+ */
+function valuePlusTenPctOrStep(element) {
+ var tenPct = range(element)/10;
+ var stp = step(element);
+ return clampToRange(value(element) + Math.max(tenPct, stp), element);
+}
+
+function valueMinusTenPctOrStep(element) {
+ var tenPct = range(element)/10;
+ var stp = step(element);
+ return clampToRange(value(element) - Math.max(tenPct, stp), element);
+}
+
+// Test table:
+
+const LTR = "ltr";
+const RTL = "rtl";
+
+var testTable = [
+ ["KEY_ArrowLeft", LTR, valueMinusStep],
+ ["KEY_ArrowLeft", RTL, valuePlusStep],
+ ["KEY_ArrowRight", LTR, valuePlusStep],
+ ["KEY_ArrowRight", RTL, valueMinusStep],
+ ["KEY_ArrowUp", LTR, valuePlusStep],
+ ["KEY_ArrowUp", RTL, valuePlusStep],
+ ["KEY_ArrowDown", LTR, valueMinusStep],
+ ["KEY_ArrowDown", RTL, valueMinusStep],
+ ["KEY_PageUp", LTR, valuePlusTenPctOrStep],
+ ["KEY_PageUp", RTL, valuePlusTenPctOrStep],
+ ["KEY_PageDown", LTR, valueMinusTenPctOrStep],
+ ["KEY_PageDown", RTL, valueMinusTenPctOrStep],
+ ["KEY_Home", LTR, minimum],
+ ["KEY_Home", RTL, minimum],
+ ["KEY_End", LTR, maximum],
+ ["KEY_End", RTL, maximum],
+]
+
+function test() {
+ var elem = document.createElement("input");
+ elem.type = "range";
+
+ var content = document.getElementById("content");
+ content.appendChild(elem);
+ elem.focus();
+
+ for (test of testTable) {
+ var [key, dir, expectedFunc] = test;
+ var oldVal, expectedVal;
+
+ elem.step = "2";
+ elem.style.direction = dir;
+ var flush = document.body.clientWidth;
+
+ // Start at middle:
+ elem.value = oldVal = defaultValue(elem);
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for " + dir + " range with value set to the midpoint (" + oldVal + ")");
+
+ // Same again:
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for " + dir + " range");
+
+ // Start at maximum:
+ elem.value = oldVal = maximum(elem);
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for " + dir + " range with value set to the maximum (" + oldVal + ")");
+
+ // Same again:
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for " + dir + " range");
+
+ // Start at minimum:
+ elem.value = oldVal = minimum(elem);
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for " + dir + " range with value set to the minimum (" + oldVal + ")");
+
+ // Same again:
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for " + dir + " range");
+
+ // Test for a step value that is greater than 10% of the range:
+ elem.step = 20;
+ elem.value = 60;
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test " + key + " for " + dir + " range with a step that is greater than 10% of the range (step=" + elem.step + ")");
+
+ // Same again:
+ expectedVal = expectedFunc(elem);
+ synthesizeKey(key);
+ is(elem.value, String(expectedVal), "Test repeat of " + key + " for " + dir + " range");
+
+ // reset step:
+ elem.step = 2;
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_range_mouse_and_touch_events.html b/dom/html/test/forms/test_input_range_mouse_and_touch_events.html
new file mode 100644
index 0000000000..5957ede81d
--- /dev/null
+++ b/dom/html/test/forms/test_input_range_mouse_and_touch_events.html
@@ -0,0 +1,240 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=846380
+-->
+<head>
+ <title>Test mouse and touch events for range</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <style>
+ /* synthesizeMouse and synthesizeFunc uses getBoundingClientRect. We set
+ * the following properties to avoid fractional values in the rect returned
+ * by getBoundingClientRect in order to avoid rounding that would occur
+ * when event coordinates are internally converted to be relative to the
+ * top-left of the element. (Such rounding would make it difficult to
+ * predict exactly what value the input should take on for events at
+ * certain coordinates.)
+ */
+ input { margin: 0 ! important; width: 200px ! important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=846380">Mozilla Bug 846380</a>
+<p id="display"></p>
+<div id="content">
+ <input id="range" type="range">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+const { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+/**
+ * Test for Bug 846380
+ * This test checks how the value of <input type=range> changes in response to
+ * various mouse and touch events.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test(synthesizeMouse, "click", "mousedown", "mousemove", "mouseup");
+ test(synthesizeTouch, "tap", "touchstart", "touchmove", "touchend");
+ SimpleTest.finish();
+});
+
+const kIsWin = AppConstants.platform == "win";
+const kIsLinux = AppConstants.platform == "linux";
+
+const MIDDLE_OF_RANGE = "50";
+const MINIMUM_OF_RANGE = "0";
+const MAXIMUM_OF_RANGE = "100";
+const QUARTER_OF_RANGE = "25";
+const THREE_QUARTERS_OF_RANGE = "75";
+
+function flush() {
+ // Flush style, specifically to flush the 'direction' property so that the
+ // browser uses the new value for thumb positioning.
+ document.body.clientWidth;
+}
+
+function test(synthesizeFunc, clickOrTap, startName, moveName, endName) {
+ var elem = document.getElementById("range");
+ elem.focus();
+ flush();
+
+ var width = parseFloat(window.getComputedStyle(elem).width);
+ var height = parseFloat(window.getComputedStyle(elem).height);
+ var borderLeft = parseFloat(window.getComputedStyle(elem).borderLeftWidth);
+ var borderTop = parseFloat(window.getComputedStyle(elem).borderTopWidth);
+ var paddingLeft = parseFloat(window.getComputedStyle(elem).paddingLeft);
+ var paddingTop = parseFloat(window.getComputedStyle(elem).paddingTop);
+
+ // Extrema for mouse/touch events:
+ var midY = height / 2 + borderTop + paddingTop;
+ var minX = borderLeft + paddingLeft;
+ var midX = minX + width / 2;
+ var maxX = minX + width;
+
+ // Test click/tap in the middle of the range:
+ elem.value = QUARTER_OF_RANGE;
+ synthesizeFunc(elem, midX, midY, {});
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + clickOrTap + " in middle of range");
+
+ // Test mouse/touch dragging of ltr range:
+ elem.value = QUARTER_OF_RANGE;
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + startName + " in middle of range");
+ synthesizeFunc(elem, minX, midY, { type: moveName });
+ is(elem.value, MINIMUM_OF_RANGE, "Test dragging of range to left of ltr range");
+
+ synthesizeFunc(elem, maxX, midY, { type: moveName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test dragging of range to right of ltr range (" + moveName + ")");
+
+ synthesizeFunc(elem, maxX, midY, { type: endName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test dragging of range to right of ltr range (" + endName + ")");
+
+ // Test mouse/touch dragging of rtl range:
+ elem.value = QUARTER_OF_RANGE;
+ elem.style.direction = "rtl";
+ flush();
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + startName + " in middle of rtl range");
+ synthesizeFunc(elem, minX, midY, { type: moveName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test dragging of range to left of rtl range");
+
+ synthesizeFunc(elem, maxX, midY, { type: moveName });
+ is(elem.value, MINIMUM_OF_RANGE, "Test dragging of range to right of rtl range (" + moveName + ")");
+
+ synthesizeFunc(elem, maxX, midY, { type: endName });
+ is(elem.value, MINIMUM_OF_RANGE, "Test dragging of range to right of rtl range (" + endName + ")");
+
+ elem.style.direction = "ltr"; // reset direction
+ flush();
+
+ // Test mouse/touch capturing by moving pointer to a position outside the range:
+ elem.value = QUARTER_OF_RANGE;
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + startName + " in middle of range");
+ synthesizeFunc(elem, maxX+100, midY, { type: moveName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test dragging of range to position outside range (" + moveName + ")");
+
+ synthesizeFunc(elem, maxX+100, midY, { type: endName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test dragging of range to position outside range (" + endName + ")");
+
+ // Test mouse/touch capturing by moving pointer to a position outside a rtl range:
+ elem.value = QUARTER_OF_RANGE;
+ elem.style.direction = "rtl";
+ flush();
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + startName + " in middle of rtl range");
+ synthesizeFunc(elem, maxX+100, midY, { type: moveName });
+ is(elem.value, MINIMUM_OF_RANGE, "Test dragging of range to position outside range (" + moveName + ")");
+
+ synthesizeFunc(elem, maxX+100, midY, { type: endName });
+ is(elem.value, MINIMUM_OF_RANGE, "Test dragging of range to position outside range (" + endName + ")");
+
+ elem.style.direction = "ltr"; // reset direction
+ flush();
+
+ // Test mouse/touch events with certain modifiers are ignored:
+ var modifiersIgnore = ["ctrlKey", "altGrKey", "fnKey"];
+ if (kIsWin || kIsLinux) {
+ modifiersIgnore.push("metaKey");
+ }
+ for (var modifier of modifiersIgnore) {
+ elem.value = QUARTER_OF_RANGE;
+ var eventParams = {};
+ eventParams[modifier] = true;
+ synthesizeFunc(elem, midX, midY, eventParams);
+ is(elem.value, QUARTER_OF_RANGE, "Test " + clickOrTap + " in the middle of range with " + modifier + " modifier key is ignored");
+ }
+
+ // Test mouse/touch events with certain modifiers are allowed:
+ var modifiersAllow = ["shiftKey", "altKey"];
+ if (!modifiersIgnore.includes("metaKey")) {
+ modifiersAllow.push("metaKey");
+ }
+ for (var modifier of modifiersAllow) {
+ elem.value = QUARTER_OF_RANGE;
+ var eventParams = {};
+ eventParams[modifier] = true;
+ synthesizeFunc(elem, midX, midY, eventParams);
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + clickOrTap + " in the middle of range with " + modifier + " modifier key is allowed");
+ }
+
+ // Test that preventDefault() works:
+ function preventDefault(e) {
+ e.preventDefault();
+ }
+ elem.value = QUARTER_OF_RANGE;
+ elem.addEventListener(startName, preventDefault);
+ synthesizeFunc(elem, midX, midY, {});
+ is(elem.value, QUARTER_OF_RANGE, "Test that preventDefault() works");
+ elem.removeEventListener(startName, preventDefault);
+
+ // Test that changing the input type in the middle of a drag cancels the drag:
+ elem.value = QUARTER_OF_RANGE;
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + startName + " in middle of range");
+ elem.type = "text";
+ is(elem.value, QUARTER_OF_RANGE, "Test that changing the input type cancels a drag");
+ synthesizeFunc(elem, midX, midY, { type: endName });
+ is(elem.value, QUARTER_OF_RANGE, "Test that changing the input type cancels a drag (after " + endName + ")");
+ elem.type = "range";
+
+ // Check that we do not drag when the mousedown/touchstart occurs outside the range:
+ elem.value = QUARTER_OF_RANGE;
+ synthesizeFunc(elem, maxX+100, midY, { type: startName });
+ is(elem.value, QUARTER_OF_RANGE, "Test " + startName + " outside range doesn't change its value");
+ synthesizeFunc(elem, midX, midY, { type: moveName });
+ is(elem.value, QUARTER_OF_RANGE, "Test dragging is not occurring when " + startName + " was outside range");
+
+ synthesizeFunc(elem, midX, midY, { type: endName });
+ is(elem.value, QUARTER_OF_RANGE, "Test dragging is not occurring when " + startName + " was outside range");
+
+ elem.focus(); // RESTORE FOCUS SO WE GET THE FOCUSED STYLE FOR TESTING OR ELSE minX/midX/maxX may be wrong!
+
+ // Check what happens when a value changing key is pressed during a drag:
+ elem.value = QUARTER_OF_RANGE;
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + startName + " in middle of range");
+ synthesizeKey("KEY_Home");
+ // The KEY_Home tests are disabled until I can figure out why they fail on Android -jwatt
+ //is(elem.value, MINIMUM_OF_RANGE, "Test KEY_Home during a drag sets the value to the minimum of the range");
+ synthesizeFunc(elem, midX+100, midY, { type: moveName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test " + moveName + " outside range after key press that occurred during a drag changes the value");
+ synthesizeFunc(elem, midX, midY, { type: moveName });
+ is(elem.value, MIDDLE_OF_RANGE, "Test " + moveName + " in middle of range");
+ synthesizeKey("KEY_Home");
+ //is(elem.value, MINIMUM_OF_RANGE, "Test KEY_Home during a drag sets the value to the minimum of the range (second time)");
+ synthesizeFunc(elem, maxX+100, midY, { type: endName });
+ is(elem.value, MAXIMUM_OF_RANGE, "Test " + endName + " outside range after key press that occurred during a drag changes the value");
+
+ function hideElement() {
+ elem.parentNode.style.display = 'none';
+ elem.parentNode.offsetLeft;
+ }
+
+ if (clickOrTap == "click") {
+ elem.addEventListener("mousedown", hideElement);
+ } else if (clickOrTap == "tap") {
+ elem.addEventListener("touchstart", hideElement);
+ }
+ synthesizeFunc(elem, midX, midY, { type: startName });
+ synthesizeFunc(elem, midX, midY, { type: endName });
+ elem.removeEventListener("mousedown", hideElement);
+ elem.removeEventListener("touchstart", hideElement);
+ ok(true, "Hiding the element during mousedown/touchstart shouldn't crash the process.");
+ elem.parentNode.style.display = "block";
+ elem.parentNode.offsetLeft;
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_range_rounding.html b/dom/html/test/forms/test_input_range_rounding.html
new file mode 100644
index 0000000000..9c3c21ce6e
--- /dev/null
+++ b/dom/html/test/forms/test_input_range_rounding.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=853525
+-->
+<head>
+ <title>Test key events for range</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=853525">Mozilla Bug 853525</a>
+<p id="display"></p>
+<div id="content">
+ <input id=range type=range value=0 step=0.01 max=1>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 853525
+ * This test checks that when <input type=range> has fractional step values,
+ * the values that a content author will see in their script will not have
+ * ugly rounding errors.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+/**
+ * We can _NOT_ generate these values by looping and simply incrementing a
+ * variable by 0.01 and stringifying it, since we'll end up with strings like
+ * "0.060000000000000005" due to the inability of binary floating point numbers
+ * to accurately represent decimal values.
+ */
+var stepVals = [
+ "0", "0.01", "0.02", "0.03", "0.04", "0.05", "0.06", "0.07", "0.08", "0.09",
+ "0.1", "0.11", "0.12", "0.13", "0.14", "0.15", "0.16", "0.17", "0.18", "0.19",
+ "0.2", "0.21", "0.22", "0.23", "0.24", "0.25", "0.26", "0.27", "0.28", "0.29",
+ "0.3", "0.31", "0.32", "0.33", "0.34", "0.35", "0.36", "0.37", "0.38", "0.39",
+ "0.4", "0.41", "0.42", "0.43", "0.44", "0.45", "0.46", "0.47", "0.48", "0.49",
+ "0.5", "0.51", "0.52", "0.53", "0.54", "0.55", "0.56", "0.57", "0.58", "0.59",
+ "0.6", "0.61", "0.62", "0.63", "0.64", "0.65", "0.66", "0.67", "0.68", "0.69",
+ "0.7", "0.71", "0.72", "0.73", "0.74", "0.75", "0.76", "0.77", "0.78", "0.79",
+ "0.8", "0.81", "0.82", "0.83", "0.84", "0.85", "0.86", "0.87", "0.88", "0.89",
+ "0.9", "0.91", "0.92", "0.93", "0.94", "0.95", "0.96", "0.97", "0.98", "0.99",
+ "1"
+];
+
+var pgUpDnVals = [
+ "0", "0.1", "0.2", "0.3", "0.4", "0.5", "0.6", "0.7", "0.8", "0.9", "1"
+];
+
+function test() {
+ var elem = document.getElementById("range");
+
+ elem.focus();
+
+ for (var i = 1; i < pgUpDnVals.length; ++i) {
+ synthesizeKey("KEY_PageUp");
+ is(elem.value, pgUpDnVals[i], "Test KEY_PageUp");
+ is(elem.validity.valid, true, "Check element is valid for value " + pgUpDnVals[i]);
+ }
+
+ for (var i = pgUpDnVals.length - 2; i >= 0; --i) {
+ synthesizeKey("KEY_PageDown");
+ is(elem.value, pgUpDnVals[i], "Test KEY_PageDown");
+ is(elem.validity.valid, true, "Check element is valid for value " + pgUpDnVals[i]);
+ }
+
+ for (var i = 1; i < stepVals.length; ++i) {
+ synthesizeKey("KEY_ArrowUp");
+ is(elem.value, stepVals[i], "Test KEY_ArrowUp");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+
+ for (var i = stepVals.length - 2; i >= 0; --i) {
+ synthesizeKey("KEY_ArrowDown");
+ is(elem.value, stepVals[i], "Test KEY_ArrowDown");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+
+ for (var i = 1; i < stepVals.length; ++i) {
+ elem.stepUp();
+ is(elem.value, stepVals[i], "Test stepUp()");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+
+ for (var i = stepVals.length - 2; i >= 0; --i) {
+ elem.stepDown();
+ is(elem.value, stepVals[i], "Test stepDown()");
+ is(elem.validity.valid, true, "Check element is valid for value " + stepVals[i]);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_sanitization.html b/dom/html/test/forms/test_input_sanitization.html
new file mode 100644
index 0000000000..474ddd621d
--- /dev/null
+++ b/dom/html/test/forms/test_input_sanitization.html
@@ -0,0 +1,585 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=549475
+-->
+<head>
+ <title>Test for Bug 549475</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=549475">Mozilla Bug 549475</a>
+<p id="display"></p>
+<pre id="test">
+<div id='content'>
+ <form>
+ </form>
+</div>
+<script type="application/javascript">
+
+SimpleTest.requestLongerTimeout(2);
+
+/**
+ * This files tests the 'value sanitization algorithm' for the various input
+ * types. Note that an input's value is affected by more than just its type's
+ * value sanitization algorithm; e.g. some type=range has actions that the user
+ * agent must perform to change the element's value to avoid underflow/overflow
+ * and step mismatch (when possible). We specifically avoid triggering these
+ * other actions here so that this test only tests the value sanitization
+ * algorithm for the various input types.
+ *
+ * XXXjwatt splitting out testing of the value sanitization algorithm and
+ * "other things" that affect .value makes it harder to know what we're testing
+ * and what we've missed, because what's included in the value sanitization
+ * algorithm and what's not is different from input type to input type. It
+ * seems to me it would be better to have a test (maybe one per type) focused
+ * on testing .value for permutations of all other inputs that can affect it.
+ * The value sanitization algorithm is just an internal spec concept after all.
+ */
+
+// We buffer up the results of sets of sub-tests, and avoid outputting log
+// entries for them all if they all pass. Otherwise, we have an enormous amount
+// of test output.
+
+var delayedTests = [];
+var anyFailedDelayedTests = false;
+
+function delayed_is(actual, expected, description)
+{
+ var result = actual == expected;
+ delayedTests.push({ actual, expected, description });
+ if (!result) {
+ anyFailedDelayedTests = true;
+ }
+}
+
+function flushDelayedTests(description)
+{
+ if (anyFailedDelayedTests) {
+ info("Outputting individual results for \"" + description + "\" due to failures in subtests");
+ for (var test of delayedTests) {
+ is(test.actual, test.expected, test.description);
+ }
+ } else {
+ ok(true, description + " (" + delayedTests.length + " subtests)");
+ }
+ delayedTests = [];
+ anyFailedDelayedTests = false;
+}
+
+// We are excluding "file" because it's too different from the other types.
+// And it has no sanitizing algorithm.
+var inputTypes =
+[
+ "text", "password", "search", "tel", "hidden", "checkbox", "radio",
+ "submit", "image", "reset", "button", "email", "url", "number", "date",
+ "time", "range", "color", "month", "week", "datetime-local"
+];
+
+var valueModeValue =
+[
+ "text", "search", "url", "tel", "email", "password", "date", "datetime",
+ "month", "week", "time", "datetime-local", "number", "range", "color",
+];
+
+function sanitizeDate(aValue)
+{
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-date-string
+ function getNumbersOfDaysInMonth(aMonth, aYear) {
+ if (aMonth === 2) {
+ return (aYear % 400 === 0 || (aYear % 100 != 0 && aYear % 4 === 0)) ? 29 : 28;
+ }
+ return (aMonth === 1 || aMonth === 3 || aMonth === 5 || aMonth === 7 ||
+ aMonth === 8 || aMonth === 10 || aMonth === 12) ? 31 : 30;
+ }
+
+ var match = /^([0-9]{4,})-([0-9]{2})-([0-9]{2})$/.exec(aValue);
+ if (!match) {
+ return "";
+ }
+ var year = Number(match[1]);
+ if (year === 0) {
+ return "";
+ }
+ var month = Number(match[2]);
+ if (month > 12 || month < 1) {
+ return "";
+ }
+ var day = Number(match[3]);
+ return 1 <= day && day <= getNumbersOfDaysInMonth(month, year) ? aValue : "";
+}
+
+function sanitizeTime(aValue)
+{
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-time-string
+ var match = /^([0-9]{2}):([0-9]{2})(.*)$/.exec(aValue);
+ if (!match) {
+ return "";
+ }
+ var hours = match[1];
+ if (hours < 0 || hours > 23) {
+ return "";
+ }
+ var minutes = match[2];
+ if (minutes < 0 || minutes > 59) {
+ return "";
+ }
+ var other = match[3];
+ if (other == "") {
+ return aValue;
+ }
+ match = /^:([0-9]{2})(.*)$/.exec(other);
+ if (!match) {
+ return "";
+ }
+ var seconds = match[1];
+ if (seconds < 0 || seconds > 59) {
+ return "";
+ }
+ var other = match[2];
+ if (other == "") {
+ return aValue;
+ }
+ match = /^.([0-9]{1,3})$/.exec(other);
+ if (!match) {
+ return "";
+ }
+ return aValue;
+}
+
+function sanitizeDateTimeLocal(aValue)
+{
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-local-date-and-time-string
+ if (aValue.length < 16) {
+ return "";
+ }
+
+ var sepIndex = aValue.indexOf("T");
+ if (sepIndex == -1) {
+ sepIndex = aValue.indexOf(" ");
+ if (sepIndex == -1) {
+ return "";
+ }
+ }
+
+ var [date, time] = aValue.split(aValue[sepIndex]);
+ if (!sanitizeDate(date)) {
+ return "";
+ }
+
+ if (!sanitizeTime(time)) {
+ return "";
+ }
+
+ // Normalize datetime-local string.
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-normalised-local-date-and-time-string
+ if (aValue[sepIndex] == " ") {
+ aValue = date + "T" + time;
+ }
+
+ if ((aValue.length - sepIndex) == 6) {
+ return aValue;
+ }
+
+ if ((aValue.length - sepIndex) > 9) {
+ var milliseconds = aValue.substring(sepIndex + 10);
+ if (Number(milliseconds) != 0) {
+ return aValue;
+ }
+ aValue = aValue.slice(0, sepIndex + 9);
+ }
+
+ var seconds = aValue.substring(sepIndex + 7);
+ if (Number(seconds) != 0) {
+ return aValue;
+ }
+ aValue = aValue.slice(0, sepIndex + 6);
+
+ return aValue;
+}
+
+function sanitizeValue(aType, aValue)
+{
+ // http://www.whatwg.org/html/#value-sanitization-algorithm
+ switch (aType) {
+ case "text":
+ case "password":
+ case "search":
+ case "tel":
+ return aValue.replace(/[\n\r]/g, "");
+ case "url":
+ case "email":
+ return aValue.replace(/[\n\r]/g, "").replace(/^[\u0020\u0009\t\u000a\u000c\u000d]+|[\u0020\u0009\t\u000a\u000c\u000d]+$/g, "");
+ case "number":
+ return isNaN(Number(aValue)) ? "" : aValue;
+ case "range":
+ var defaultMinimum = 0;
+ var defaultMaximum = 100;
+ var value = Number(aValue);
+ if (isNaN(value)) {
+ return ((defaultMaximum - defaultMinimum)/2).toString(); // "50"
+ }
+ if (value < defaultMinimum) {
+ return defaultMinimum.toString();
+ }
+ if (value > defaultMaximum) {
+ return defaultMaximum.toString();
+ }
+ return aValue;
+ case "date":
+ return sanitizeDate(aValue);
+ case "time":
+ return sanitizeTime(aValue);
+ case "month":
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-month-string
+ var match = /^([0-9]{4,})-([0-9]{2})$/.exec(aValue);
+ if (!match) {
+ return "";
+ }
+ var year = Number(match[1]);
+ if (year === 0) {
+ return "";
+ }
+ var month = Number(match[2]);
+ if (month > 12 || month < 1) {
+ return "";
+ }
+ return aValue;
+ case "week":
+ // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-week-string
+ function isLeapYear(aYear) {
+ return ((aYear % 4 == 0) && (aYear % 100 != 0)) || (aYear % 400 == 0);
+ }
+ function getDayofWeek(aYear, aMonth, aDay) { /* 0 = Sunday */
+ // Tomohiko Sakamoto algorithm.
+ var monthTable = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];
+ aYear -= Number(aMonth < 3);
+
+ return (aYear + parseInt(aYear / 4) - parseInt(aYear / 100) +
+ parseInt(aYear / 400) + monthTable[aMonth - 1] + aDay) % 7;
+ }
+ function getMaximumWeekInYear(aYear) {
+ var day = getDayofWeek(aYear, 1, 1);
+ return day == 4 || (day == 3 && isLeapYear(aYear)) ? 53 : 52;
+ }
+
+ var match = /^([0-9]{4,})-W([0-9]{2})$/.exec(aValue);
+ if (!match) {
+ return "";
+ }
+ var year = Number(match[1]);
+ if (year === 0) {
+ return "";
+ }
+ var week = Number(match[2]);
+ if (week > 53 || month < 1) {
+ return "";
+ }
+ return 1 <= week && week <= getMaximumWeekInYear(year) ? aValue : "";
+ case "datetime-local":
+ return sanitizeDateTimeLocal(aValue);
+ case "color":
+ return /^#[0-9A-Fa-f]{6}$/.exec(aValue) ? aValue.toLowerCase() : "#000000";
+ default:
+ return aValue;
+ }
+}
+
+function checkSanitizing(element, inputTypeDescription)
+{
+ var testData =
+ [
+ // For text, password, search, tel, email:
+ "\n\rfoo\n\r",
+ "foo\n\rbar",
+ " foo ",
+ " foo\n\r bar ",
+ // For url:
+ "\r\n foobar \n\r",
+ "\u000B foo \u000B",
+ "\u000A foo \u000A",
+ "\u000C foo \u000C",
+ "\u000d foo \u000d",
+ "\u0020 foo \u0020",
+ " \u0009 foo \u0009 ",
+ // For number and range:
+ "42",
+ "13.37",
+ "1.234567898765432",
+ "12foo",
+ "1e2",
+ "3E42",
+ // For date:
+ "1970-01-01",
+ "1234-12-12",
+ "1234567890-01-02",
+ "2012-12-31",
+ "2012-02-29",
+ "2000-02-29",
+ "1234",
+ "1234-",
+ "12345",
+ "1234-01",
+ "1234-012",
+ "1234-01-",
+ "12-12",
+ "999-01-01",
+ "1234-56-78-91",
+ "1234-567-78",
+ "1234--7-78",
+ "abcd-12-12",
+ "thisinotadate",
+ "2012-13-01",
+ "1234-12-42",
+ " 2012-13-01",
+ " 123-01-01",
+ "2012- 3-01",
+ "12- 10- 01",
+ " 12-0-1",
+ "2012-3-001",
+ "2012-12-00",
+ "2012-12-1r",
+ "2012-11-31",
+ "2011-02-29",
+ "2100-02-29",
+ "a2000-01-01",
+ "2000a-01-0'",
+ "20aa00-01-01",
+ "2000a2000-01-01",
+ "2000-1-1",
+ "2000-1-01",
+ "2000-01-1",
+ "2000-01-01 ",
+ "2000- 01-01",
+ "-1970-01-01",
+ "0000-00-00",
+ "0001-00-00",
+ "0000-01-01",
+ "1234-12 12",
+ "1234 12-12",
+ "1234 12 12",
+ // For time:
+ "1",
+ "10",
+ "10:",
+ "10:1",
+ "21:21",
+ ":21:21",
+ "-21:21",
+ " 21:21",
+ "21-21",
+ "21:21:",
+ "21:211",
+ "121:211",
+ "21:21 ",
+ "00:00",
+ "-1:00",
+ "24:00",
+ "00:60",
+ "01:01",
+ "23:59",
+ "99:99",
+ "8:30",
+ "19:2",
+ "19:a2",
+ "4c:19",
+ "10:.1",
+ "1.:10",
+ "13:37:42",
+ "13:37.42",
+ "13:37:42 ",
+ "13:37:42.",
+ "13:37:61.",
+ "13:37:00",
+ "13:37:99",
+ "13:37:b5",
+ "13:37:-1",
+ "13:37:.1",
+ "13:37:1.",
+ "13:37:42.001",
+ "13:37:42.001",
+ "13:37:42.abc",
+ "13:37:42.00c",
+ "13:37:42.a23",
+ "13:37:42.12e",
+ "13:37:42.1e1",
+ "13:37:42.e11",
+ "13:37:42.1",
+ "13:37:42.99",
+ "13:37:42.0",
+ "13:37:42.00",
+ "13:37:42.000",
+ "13:37:42.-1",
+ "13:37:42.1.1",
+ "13:37:42.1,1",
+ "13:37:42.",
+ "foo12:12",
+ "13:37:42.100000000000",
+ // For color
+ "#00ff00",
+ "#000000",
+ "red",
+ "#0f0",
+ "#FFFFAA",
+ "FFAABB",
+ "fFAaBb",
+ "FFAAZZ",
+ "ABCDEF",
+ "#7654321",
+ // For month
+ "1970-01",
+ "1234-12",
+ "123456789-01",
+ "2013-13",
+ "0000-00",
+ "2015-00",
+ "0001-01",
+ "1-1",
+ "888-05",
+ "2013-3",
+ "2013-may",
+ "2000-1a",
+ "2013-03-13",
+ "december",
+ "abcdef",
+ "12",
+ " 2013-03",
+ "2013 - 03",
+ "2013 03",
+ "2013/03",
+ // For week
+ "1970-W01",
+ "1970-W53",
+ "1964-W53",
+ "1900-W10",
+ "2004-W53",
+ "2065-W53",
+ "2099-W53",
+ "2010-W53",
+ "2016-W30",
+ "1900-W3",
+ "2016-w30",
+ "2016-30",
+ "16-W30",
+ "2016-Week30",
+ "2000-100",
+ "0000-W01",
+ "00-W01",
+ "123456-W05",
+ "1985-W100",
+ "week",
+ // For datetime-local
+ "1970-01-01T00:00",
+ "1970-01-01Z12:00",
+ "1970-01-01 00:00:00",
+ "1970-01-01T00:00:00.0",
+ "1970-01-01T00:00:00.00",
+ "1970-01-01T00:00:00.000",
+ "1970-01-01 00:00:00.20",
+ "1969-12-31 23:59",
+ "1969-12-31 23:59:00",
+ "1969-12-31 23:59:00.000",
+ "1969-12-31 23:59:00.30",
+ "123456-01-01T12:00",
+ "123456-01-01T12:00:00",
+ "123456-01-01T12:00:00.0",
+ "123456-01-01T12:00:00.00",
+ "123456-01-01T12:00:00.000",
+ "123456-01-01T12:00:30",
+ "123456-01-01T12:00:00.123",
+ "10000-12-31 20:00",
+ "10000-12-31 20:00:00",
+ "10000-12-31 20:00:00.0",
+ "10000-12-31 20:00:00.00",
+ "10000-12-31 20:00:00.000",
+ "10000-12-31 20:00:30",
+ "10000-12-31 20:00:00.123",
+ "2016-13-01T12:00",
+ "2016-12-32T12:00",
+ "2016-11-08 15:40:30.0",
+ "2016-11-08T15:40:30.00",
+ "2016-11-07T17:30:10",
+ "2016-12-1T12:45",
+ "2016-12-01T12:45:30.123456",
+ "2016-12-01T24:00",
+ "2016-12-01T12:88:30",
+ "2016-12-01T12:30:99",
+ "2016-12-01T12:30:100",
+ "2016-12-01",
+ "2016-12-01T",
+ "2016-Dec-01T00:00",
+ "12-05-2016T00:00",
+ "datetime-local"
+ ];
+
+ for (value of testData) {
+ element.setAttribute('value', value);
+ delayed_is(element.value, sanitizeValue(type, value),
+ "The value has not been correctly sanitized for type=" + type);
+ delayed_is(element.getAttribute('value'), value,
+ "The content value should not have been sanitized");
+
+ if (type in valueModeValue) {
+ element.setAttribute('value', 'tulip');
+ element.value = value;
+ delayed_is(element.value, sanitizeValue(type, value),
+ "The value has not been correctly sanitized for type=" + type);
+ delayed_is(element.getAttribute('value'), 'tulip',
+ "The content value should not have been sanitized");
+ }
+
+ element.setAttribute('value', '');
+ form.reset();
+ element.type = 'checkbox'; // We know this type has no sanitizing algorithm.
+ element.setAttribute('value', value);
+ delayed_is(element.value, value, "The value should not have been sanitized");
+ element.type = type;
+ delayed_is(element.value, sanitizeValue(type, value),
+ "The value has not been correctly sanitized for type=" + type);
+ delayed_is(element.getAttribute('value'), value,
+ "The content value should not have been sanitized");
+
+ element.setAttribute('value', '');
+ form.reset();
+ element.setAttribute('value', value);
+ form.reset();
+ delayed_is(element.value, sanitizeValue(type, value),
+ "The value has not been correctly sanitized for type=" + type);
+ delayed_is(element.getAttribute('value'), value,
+ "The content value should not have been sanitized");
+
+ // Cleaning-up.
+ element.setAttribute('value', '');
+ form.reset();
+ }
+
+ flushDelayedTests(inputTypeDescription);
+}
+
+for (type of inputTypes) {
+ var form = document.forms[0];
+ var element = document.createElement("input");
+ element.style.display = "none";
+ element.type = type;
+ form.appendChild(element);
+
+ checkSanitizing(element, "type=" + type + ", no frame, no editor");
+
+ element.style.display = "";
+ checkSanitizing(element, "type=" + type + ", frame, no editor");
+
+ element.focus();
+ element.blur();
+ checkSanitizing(element, "type=" + type + ", frame, editor");
+
+ element.style.display = "none";
+ checkSanitizing(element, "type=" + type + ", no frame, editor");
+
+ form.removeChild(element);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_setting_value.html b/dom/html/test/forms/test_input_setting_value.html
new file mode 100644
index 0000000000..b6ddd66d24
--- /dev/null
+++ b/dom/html/test/forms/test_input_setting_value.html
@@ -0,0 +1,619 @@
+<!DOCTYPE>
+<html>
+<head>
+ <title>Test for setting input value</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<div id="display">
+</div>
+<div id="content"><input type="text"></div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ const kSetUserInputCancelable = SpecialPowers.getBoolPref("dom.input_event.allow_to_cancel_set_user_input");
+
+ let input = document.querySelector("input[type=text]");
+
+ // Setting value during composition causes committing composition before setting the value.
+ input.focus();
+ let description = 'Setting input value at first "compositionupdate" event: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be the specified value at "compositionupdate" event (after setting the value)`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "def", `${description}input value should be the specified value at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ todo_is(input.value, "def", `${description}input value should be the specified value at "input" event`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ todo_is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value at second "compositionupdate" event: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ input.value = "def";
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ is(input.value, "def", `${description}input value should be specified value at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "def", `${description}input value should be specified value at "input" event`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value at "input" event for first composition update: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value at "input" event for second composition update: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+
+ input.value = "";
+ description = 'Setting input value and reframing at "input" event for first composition update: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing at "input" event for second composition update: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing with flushing layout at "input" event for first composition update: ';
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "", `${description}input value should not have been modified at first "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ document.documentElement.scrollTop;
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing with flushing layout at "input" event for second composition update: ';
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "ab",
+ "clauses":
+ [
+ { "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 2, "length": 0 },
+ });
+ input.addEventListener("compositionupdate", (aEvent) => {
+ is(input.value, "ab", `${description}input value should not have been modified at second "compositionupdate" event yet`);
+ }, {once: true});
+ input.addEventListener("compositionend", (aEvent) => {
+ todo_is(input.value, "abc", `${description}input value should be the composition string at "compositionend" event`);
+ }, {once: true});
+ input.addEventListener("input", (aEvent) => {
+ is(input.value, "abc", `${description}input value should be the composition string at "input" event`);
+ input.value = "def";
+ input.style.width = "1000px";
+ document.documentElement.scrollTop;
+ is(input.value, "def", `${description}input value should be the specified value at "input" event (after setting the value)`);
+ }, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "abc",
+ "clauses":
+ [
+ { "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 3, "length": 0 },
+ });
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should be set to specified value after the last "input" event`);
+ input.style.width = "";
+
+ // autocomplete and correcting misspelled word by spellchecker cause an "input" event with same path as setting input value.
+ input.value = "";
+ description = 'Setting input value at "input" event whose inputType is "insertReplacementText';
+ let inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+
+ input.value = "";
+ description = 'Setting input value and reframing at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ input.style.width = "1000px";
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and reframing with flushing layout at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ input.style.width = "1000px";
+ document.documentElement.scrollTop;
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.style.width = "";
+
+ input.value = "";
+ description = 'Setting input value and destroying the frame at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before setting value)`);
+ input.value = "def";
+ input.style.display = "none";
+ is(input.value, "def", `${description}input value should be specified value at "input" event (after setting value)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "def", `${description}input value should keep the specified value after the last "input" event`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.style.display = "inline";
+
+ input.value = "";
+ description = 'Changing input type at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before changing type)`);
+ input.type = "button";
+ is(input.value, "abc", `${description}input value should keep inserted value at "input" event (after changing type)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(input.value, "abc", `${description}input value should keep inserted value after the last "input" event`);
+ is(input.type, "button", `${description}input type should be changed correctly`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.type = "text";
+ is(input.value, "abc", `${description}input value should keep inserted value immediately after restoring the type`);
+ todo(SpecialPowers.wrap(input).hasEditor, `${description}restoring input type should create editor if it's focused element`);
+ input.blur();
+ input.focus();
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "abc", `${description}input value should keep inserted value after creating editor`);
+
+ input.value = "";
+ description = 'Changing input type and flush layout at "input" event whose inputType is "insertReplacementText';
+ inputEventFired = false;
+ input.addEventListener("input", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ inputEventFired = true;
+ is(input.value, "abc", `${description}input value should be inserted value at "input" event (before changing type)`);
+ input.type = "button";
+ input.getBoundingClientRect().height;
+ is(input.value, "abc", `${description}input value should keep inserted value at "input" event (after changing type)`);
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("abc");
+ is(input.value, "abc", `${description}input value should keep inserted value after the last "input" event`);
+ is(input.type, "button", `${description}input type should be changed correctly`);
+ ok(inputEventFired, `${description}"input" event should've been fired for setUserInput("abc")`);
+ input.type = "text";
+ is(input.value, "abc", `${description}input value should keep inserted value immediately after restoring the type`);
+ todo(SpecialPowers.wrap(input).hasEditor, `${description}restoring input type should create editor if it's focused element`);
+ input.blur();
+ input.focus();
+ is(SpecialPowers.wrap(input).editor.rootElement.firstChild.wholeText, input.value,
+ `${description}native anonymous text node should have exactly same value as value of <input> element`);
+ is(input.value, "abc", `${description}input value should keep inserted value after creating editor`);
+
+ function testSettingValueFromBeforeInput(aWithEditor, aPreventDefaultOfBeforeInput) {
+ let beforeInputEvents = [];
+ let inputEvents = [];
+ function recordEvent(aEvent) {
+ if (aEvent.type === "beforeinput") {
+ beforeInputEvents.push(aEvent);
+ } else {
+ inputEvents.push(aEvent);
+ }
+ }
+ let condition = `(${aWithEditor ? "with editor" : "without editor"}${aPreventDefaultOfBeforeInput ? ' and canceling "beforeinput" event' : ""}, the pref ${kSetUserInputCancelable ? "allows" : "disallows"} to cancel "beforeinput" event})`;
+ function Reset() {
+ beforeInputEvents = [];
+ inputEvents = [];
+ if (SpecialPowers.wrap(input).hasEditor != aWithEditor) {
+ if (aWithEditor) {
+ input.blur();
+ input.focus(); // Associate `TextEditor` with input
+ if (!SpecialPowers.wrap(input).hasEditor) {
+ ok(false, `${description}Failed to associate TextEditor with the input ${condition}`);
+ return false;
+ }
+ } else {
+ input.blur();
+ input.type = "button";
+ input.type = "text";
+ if (SpecialPowers.wrap(input).hasEditor) {
+ ok(false, `${description}Failed to disassociate TextEditor from the input ${condition}`);
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is different from Chrome (verified with spellchecker).
+ // Chrome inserts the new text to current value and selected range.
+ // It might be reasonable, but we don't touch this for now since it
+ // requires a lot of changes.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ is(inputEvents.length, 1, `${description}"input" event should be fired`);
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType,
+ "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ is(inputEvents[0].data, "def",
+ `${description}data of "input" event should be the value specified by setUserInput()`);
+ }
+ }
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" and changing the type to "button" ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ input.type = "button";
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is same as Chrome (verified with spellchecker).
+ // But this behavior is not consistent with just setting the value on Chrome.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ // Same as Chrome
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since the input element's type is changed`);
+ }
+ input.type = "text";
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText" and destroying the frame ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ input.style.display = "none";
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is same as Chrome (verified with spellchecker).
+ // But this behavior is not consistent with just setting the value on Chrome.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ // Different from Chrome
+ is(inputEvents.length, 1,
+ `${description}"input" event should be fired even if the frame of target is destroyed`);
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType,
+ "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ is(inputEvents[0].data, "def",
+ `${description}data of "input" event should be the value specified by setUserInput()`);
+ }
+ }
+ input.style.display = "inline";
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+
+ if (aWithEditor) {
+ return;
+ }
+
+ description = `Setting value from "beforeinput" event listener whose inputType is "insertReplacementText and create editor" ${condition}: `;
+ input.value = "abc";
+ if (!Reset()) {
+ return;
+ }
+ input.addEventListener("beforeinput", (aEvent) => {
+ is(aEvent.inputType, "insertReplacementText", `${description}inputType of "beforeinput" event should be "insertReplacementText"`);
+ is(aEvent.cancelable, kSetUserInputCancelable, `${description}"beforeinput" event should be cancelable unless it's suppressed by the pref`);
+ is(input.value, "abc", `${description}The value shouldn't have been modified yet at "beforeinput" event listener`);
+ input.addEventListener("beforeinput", recordEvent);
+ input.addEventListener("input", recordEvent);
+ input.value = "hig";
+ input.focus();
+ if (aPreventDefaultOfBeforeInput) {
+ aEvent.preventDefault();
+ }
+ }, {once: true});
+ SpecialPowers.wrap(input).setUserInput("def");
+ is(beforeInputEvents.length, 0, `${description}"beforeinput" event shouldn't be fired again`);
+ if (aPreventDefaultOfBeforeInput && kSetUserInputCancelable) {
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since "beforeinput" was canceled`);
+ is(inputEvents.length, 0,
+ `${description}"input" event shouldn't be fired since "beforeinput" was canceled`);
+ } else {
+ // XXX This result is different from Chrome (verified with spellchecker).
+ // Chrome inserts the new text to current value and selected range.
+ // It might be reasonable, but we don't touch this for now since it
+ // requires a lot of changes.
+ is(input.value, "hig",
+ `${description}The value should be set to the specified value in "beforeinput" event listener since the event target was already modified`);
+ is(inputEvents.length, 1, `${description}"input" event should be fired`);
+ if (inputEvents.length) {
+ is(inputEvents[0].inputType,
+ "insertReplacementText", `${description}inputType of "input" event should be "insertReplacementText"`);
+ is(inputEvents[0].data, "def",
+ `${description}data of "input" event should be the value specified by setUserInput()`);
+ }
+ }
+ input.removeEventListener("beforeinput", recordEvent);
+ input.removeEventListener("input", recordEvent);
+ }
+ // testSettingValueFromBeforeInput(true, true);
+ // testSettingValueFromBeforeInput(true, false);
+ testSettingValueFromBeforeInput(false, true);
+ testSettingValueFromBeforeInput(false, false);
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_textarea_set_value_no_scroll.html b/dom/html/test/forms/test_input_textarea_set_value_no_scroll.html
new file mode 100644
index 0000000000..79a0f3d15a
--- /dev/null
+++ b/dom/html/test/forms/test_input_textarea_set_value_no_scroll.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=829606
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 829606</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 829606 **/
+ /*
+ * This test checks that setting .value on an text field (input or textarea)
+ * doesn't scroll the field to its beginning.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+
+ var gTestRunner = null;
+
+ async function test(aElementName)
+ {
+ var element = document.getElementsByTagName(aElementName)[0];
+ element.focus();
+
+ var baseSnapshot = await snapshotWindow(window);
+
+ // This is a sanity check.
+ var s2 = await snapshotWindow(window);
+ var results = compareSnapshots(baseSnapshot, await snapshotWindow(window), true);
+ ok(results[0], "sanity check: screenshots should be the same");
+
+ element.selectionStart = element.selectionEnd = element.value.length;
+
+ setTimeout(function() {
+ sendString('f');
+
+ requestAnimationFrame(async function() {
+ var selectionAtTheEndSnapshot = await snapshotWindow(window);
+ results = compareSnapshots(baseSnapshot, selectionAtTheEndSnapshot, false);
+ ok(results[0], "after appending a character, string should have changed");
+
+ // Re-setting value shouldn't change anything.
+ // eslint-disable-next-line no-self-assign
+ element.value = element.value;
+ var tmpSnapshot = await snapshotWindow(window);
+
+ results = compareSnapshots(baseSnapshot, tmpSnapshot, false);
+ ok(results[0], "re-setting the value should change nothing");
+
+ results = compareSnapshots(selectionAtTheEndSnapshot, tmpSnapshot, true);
+ ok(results[0], "re-setting the value should change nothing");
+
+ element.selectionStart = element.selectionEnd = 0;
+ element.blur();
+
+ gTestRunner.next();
+ });
+ }, 0);
+ }
+
+ // This test checks that when a textarea has a long list of values and the
+ // textarea's value is then changed, the values are shown correctly.
+ async function testCorrectUpdateOnScroll()
+ {
+ var textarea = document.createElement('textarea');
+ textarea.rows = 5;
+ textarea.cols = 10;
+ textarea.value = 'a\nb\nc\nd';
+ document.getElementById('content').appendChild(textarea);
+
+ var baseSnapshot = await snapshotWindow(window);
+
+ textarea.value = '1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n';
+ textarea.selectionStart = textarea.selectionEnd = textarea.value.length;
+
+ var fullSnapshot = await snapshotWindow(window);
+ var results = compareSnapshots(baseSnapshot, fullSnapshot, false);
+ ok(results[0], "sanity check: screenshots should not be the same");
+
+ textarea.value = 'a\nb\nc\nd';
+
+ var tmpSnapshot = await snapshotWindow(window);
+ results = compareSnapshots(baseSnapshot, tmpSnapshot, true);
+ ok(results[0], "textarea view should look like the beginning");
+
+ setTimeout(function() {
+ gTestRunner.next();
+ }, 0);
+ }
+
+ function* runTest()
+ {
+ test('input');
+ yield undefined;
+ test('textarea');
+ yield undefined;
+ testCorrectUpdateOnScroll();
+ yield undefined;
+ SimpleTest.finish();
+ }
+
+ gTestRunner = runTest();
+
+ SimpleTest.waitForFocus(function() {
+ gTestRunner.next();
+ });;
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=829606">Mozilla Bug 829606</a>
+<p id="display"></p>
+<div id="content">
+ <textarea rows='1' cols='5' style='-moz-appearance:none;'>this is a \n long text</textarea>
+ <input size='5' value="this is a very long text" style='-moz-appearance:none;'>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_time_key_events.html b/dom/html/test/forms/test_input_time_key_events.html
new file mode 100644
index 0000000000..c738816653
--- /dev/null
+++ b/dom/html/test/forms/test_input_time_key_events.html
@@ -0,0 +1,221 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1288591
+-->
+<head>
+ <title>Test key events for time control</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288591">Mozilla Bug 1288591</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input" type="time">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+var testData = [
+ /**
+ * keys: keys to send to the input element.
+ * initialVal: initial value set to the input element.
+ * expectedVal: expected value of the input element after sending the keys.
+ */
+ {
+ // Type 16 in the hour field will automatically change time to 4PM in 12-hour clock
+ keys: ["16"],
+ initialVal: "01:00",
+ expectedVal: "16:00"
+ },
+ {
+ // Type 00 in hour field will automatically convert to 12AM in 12-hour clock
+ keys: ["00"],
+ initialVal: "03:00",
+ expectedVal: "00:00"
+ },
+ {
+ // Type hour > 23 such as 24 will automatically convert to 2
+ keys: ["24"],
+ initialVal: "04:00",
+ expectedVal: "02:00"
+ },
+ {
+ // Type 1030 and select AM.
+ keys: ["1030"],
+ initialVal: "",
+ expectedVal: "10:30"
+ },
+ {
+ // Type 3 in the hour field will automatically advance to the minute field.
+ keys: ["330"],
+ initialVal: "",
+ expectedVal: "03:30"
+ },
+ {
+ // Type 5 in the hour field will automatically advance to the minute field.
+ // Type 7 in the minute field will automatically advance to the AM/PM field.
+ keys: ["57"],
+ initialVal: "",
+ expectedVal: "05:07"
+ },
+ {
+ // Advance to AM/PM field and change to PM.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_ArrowDown"],
+ initialVal: "10:30",
+ expectedVal: "22:30"
+ },
+ {
+ // Right key should do the same thing as TAB key.
+ keys: ["KEY_ArrowRight", "KEY_ArrowRight", "KEY_ArrowDown"],
+ initialVal: "10:30",
+ expectedVal: "22:30"
+ },
+ {
+ // Advance to minute field then back to hour field and decrement.
+ keys: ["KEY_ArrowRight", "KEY_ArrowLeft", "KEY_ArrowDown"],
+ initialVal: "10:30",
+ expectedVal: "09:30"
+ },
+ {
+ // Focus starts on the first field, hour in this case, and increment.
+ keys: ["KEY_ArrowUp"],
+ initialVal: "16:00",
+ expectedVal: "17:00"
+ },
+ {
+ // Advance to minute field and decrement.
+ keys: ["KEY_Tab", "KEY_ArrowDown"],
+ initialVal: "16:00",
+ expectedVal: "16:59"
+ },
+ {
+ // Advance to minute field and increment.
+ keys: ["KEY_Tab", "KEY_ArrowUp"],
+ initialVal: "16:59",
+ expectedVal: "16:00"
+ },
+ {
+ // PageUp on hour field increments hour by 3.
+ keys: ["KEY_PageUp"],
+ initialVal: "05:00",
+ expectedVal: "08:00"
+ },
+ {
+ // PageDown on hour field decrements hour by 3.
+ keys: ["KEY_PageDown"],
+ initialVal: "05:00",
+ expectedVal: "02:00"
+ },
+ {
+ // PageUp on minute field increments minute by 10.
+ keys: ["KEY_Tab", "KEY_PageUp"],
+ initialVal: "14:00",
+ expectedVal: "14:10"
+ },
+ {
+ // PageDown on minute field decrements minute by 10.
+ keys: ["KEY_Tab", "KEY_PageDown"],
+ initialVal: "14:00",
+ expectedVal: "14:50"
+ },
+ {
+ // Home key on hour field sets it to the minimum hour, which is 1 in 12-hour
+ // clock.
+ keys: ["KEY_Home"],
+ initialVal: "03:10",
+ expectedVal: "01:10"
+ },
+ {
+ // End key on hour field sets it to the maximum hour, which is 12PM in 12-hour
+ // clock.
+ keys: ["KEY_End"],
+ initialVal: "03:10",
+ expectedVal: "12:10"
+ },
+ {
+ // Home key on minute field sets it to the minimum minute, which is 0.
+ keys: ["KEY_Tab", "KEY_Home"],
+ initialVal: "19:30",
+ expectedVal: "19:00"
+ },
+ {
+ // End key on minute field sets it to the minimum minute, which is 59.
+ keys: ["KEY_Tab", "KEY_End"],
+ initialVal: "19:30",
+ expectedVal: "19:59"
+ },
+ // Second field will show up when needed.
+ {
+ // PageUp on second field increments second by 10.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_PageUp"],
+ initialVal: "08:10:10",
+ expectedVal: "08:10:20"
+ },
+ {
+ // PageDown on second field increments second by 10.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_PageDown"],
+ initialVal: "08:10:10",
+ expectedVal: "08:10:00"
+ },
+ {
+ // Home key on second field sets it to the minimum second, which is 0.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_Home"],
+ initialVal: "16:00:30",
+ expectedVal: "16:00:00"
+ },
+ {
+ // End key on second field sets it to the minimum second, which is 59.
+ keys: ["KEY_Tab", "KEY_Tab", "KEY_End"],
+ initialVal: "16:00:30",
+ expectedVal: "16:00:59"
+ },
+ {
+ // Incomplete value maps to empty .value.
+ keys: ["1"],
+ initialVal: "",
+ expectedVal: ""
+ },
+];
+
+function sendKeys(aKeys, aElem) {
+ for (let i = 0; i < aKeys.length; i++) {
+ // Force layout flush between keys to ensure focus is correct.
+ // This shouldn't be necessary; bug 1450219 tracks this.
+ aElem.clientTop;
+ let key = aKeys[i];
+ if (key.startsWith("KEY_")) {
+ synthesizeKey(key);
+ } else {
+ sendString(key);
+ }
+ }
+}
+
+function test() {
+ var elem = document.getElementById("input");
+
+ for (let { keys, initialVal, expectedVal } of testData) {
+ elem.focus();
+ elem.value = initialVal;
+ sendKeys(keys, elem);
+ is(elem.value, expectedVal,
+ "Test with " + keys + ", result should be " + expectedVal);
+ elem.value = "";
+ elem.blur();
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_time_sec_millisec_field.html b/dom/html/test/forms/test_input_time_sec_millisec_field.html
new file mode 100644
index 0000000000..71db4942a9
--- /dev/null
+++ b/dom/html/test/forms/test_input_time_sec_millisec_field.html
@@ -0,0 +1,134 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1374967
+-->
+<head>
+ <title>Test second and millisecond fields in input type=time</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1374967">Mozilla Bug 1374967</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input1" type="time">
+ <input id="input2" type="time" value="12:30:40">
+ <input id="input3" type="time" value="12:30:40.567">
+ <input id="input4" type="time" step="1">
+ <input id="input5" type="time" step="61">
+ <input id="input6" type="time" step="120">
+ <input id="input7" type="time" step="0.01">
+ <input id="input8" type="time" step="0.001">
+ <input id="input9" type="time" step="1.001">
+ <input id="input10" type="time" min="01:30:05">
+ <input id="input11" type="time" min="01:30:05.100">
+ <input id="dummy">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ test();
+ SimpleTest.finish();
+});
+
+const NUM_OF_FIELDS_DEFAULT = 3;
+const NUM_OF_FIELDS_WITH_SECOND = NUM_OF_FIELDS_DEFAULT + 1;
+const NUM_OF_FIELDS_WITH_MILLISEC = NUM_OF_FIELDS_WITH_SECOND + 1;
+
+function countNumberOfFields(aElement) {
+ is(aElement.type, "time", "Input element type should be 'time'");
+
+ let inputRect = aElement.getBoundingClientRect();
+ let firstField_X = 15;
+ let firstField_Y = inputRect.height / 2;
+
+ // Make sure to start on the first field.
+ synthesizeMouse(aElement, firstField_X, firstField_Y, {});
+ is(document.activeElement, aElement, "Input element should be focused");
+
+ let n = 0;
+ while (document.activeElement == aElement) {
+ n++;
+ synthesizeKey("KEY_Tab");
+ }
+
+ return n;
+}
+
+function test() {
+ // Normal input time element.
+ let elem = document.getElementById("input1");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_DEFAULT, "Default input time");
+
+ // Dynamically changing the value with second part.
+ elem.value = "10:20:30";
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_SECOND,
+ "Input time after changing value with second part");
+
+ // Dynamically changing the step to 1 millisecond.
+ elem.step = "0.001";
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_MILLISEC,
+ "Input time after changing step to 1 millisecond");
+
+ // Input time with value with second part.
+ elem = document.getElementById("input2");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_SECOND,
+ "Input time with value with second part");
+
+ // Input time with value with second and millisecond part.
+ elem = document.getElementById("input3");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_MILLISEC,
+ "Input time with value with second and millisecond part");
+
+ // Input time with step set as 1 second.
+ elem = document.getElementById("input4");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_SECOND,
+ "Input time with step set as 1 second");
+
+ // Input time with step set as 61 seconds.
+ elem = document.getElementById("input5");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_SECOND,
+ "Input time with step set as 61 seconds");
+
+ // Input time with step set as 2 minutes.
+ elem = document.getElementById("input6");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_DEFAULT,
+ "Input time with step set as 2 minutes");
+
+ // Input time with step set as 10 milliseconds.
+ elem = document.getElementById("input7");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_MILLISEC,
+ "Input time with step set as 10 milliseconds");
+
+ // Input time with step set as 100 milliseconds.
+ elem = document.getElementById("input8");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_MILLISEC,
+ "Input time with step set as 100 milliseconds");
+
+ // Input time with step set as 1001 milliseconds.
+ elem = document.getElementById("input9");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_MILLISEC,
+ "Input time with step set as 1001 milliseconds");
+
+ // Input time with min with second part and default step (60 seconds). Note
+ // that step base is min, when there is a min.
+ elem = document.getElementById("input10");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_SECOND,
+ "Input time with min with second part");
+
+ // Input time with min with second and millisecond part and default step (60
+ // seconds). Note that step base is min, when there is a min.
+ elem = document.getElementById("input11");
+ is(countNumberOfFields(elem), NUM_OF_FIELDS_WITH_MILLISEC,
+ "Input time with min with second and millisecond part");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_types_pref.html b/dom/html/test/forms/test_input_types_pref.html
new file mode 100644
index 0000000000..1222e88a86
--- /dev/null
+++ b/dom/html/test/forms/test_input_types_pref.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=764481
+-->
+<head>
+ <title>Test for Bug 764481</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=764481">Mozilla Bug 764481</a>
+<p id="display"></p>
+<div id="content" style="display: none" >
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ var input = document.createElement("input");
+
+ var testData = [
+ {
+ prefs: [["dom.forms.datetime.others", false]],
+ inputType: "month",
+ expectedType: "text"
+ }, {
+ prefs: [["dom.forms.datetime.others", false]],
+ inputType: "month",
+ expectedType: "text"
+ }, {
+ prefs: [["dom.forms.datetime.others", true]],
+ inputType: "month",
+ expectedType: "month"
+ }, {
+ prefs: [["dom.forms.datetime.others", false]],
+ inputType: "week",
+ expectedType: "text"
+ }, {
+ prefs: [["dom.forms.datetime.others", false]],
+ inputType: "week",
+ expectedType: "text"
+ }, {
+ prefs: [["dom.forms.datetime.others", true]],
+ inputType: "week",
+ expectedType: "week"
+ }
+ ];
+
+ function testInputTypePreference(aData) {
+ return SpecialPowers.pushPrefEnv({'set': aData.prefs})
+ .then(() => {
+ // Change the type of input to text and then back to the tested input type,
+ // so that HTMLInputElement::ParseAttribute gets called with the pref enabled.
+ input.type = "text";
+ input.type = aData.inputType;
+ is(input.type, aData.expectedType, "input type should be '" +
+ aData.expectedType + "'' when pref " + aData.prefs + " is set");
+ is(input.getAttribute('type'), aData.inputType,
+ "input 'type' attribute should not change");
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ let promise = Promise.resolve();
+ for (let i = 0; i < testData.length; i++) {
+ let data = testData[i];
+ promise = promise.then(() => testInputTypePreference(data));
+ }
+
+ promise.catch(error => ok(false, "Promise reject: " + error))
+ .then(() => SimpleTest.finish());
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_typing_sanitization.html b/dom/html/test/forms/test_input_typing_sanitization.html
new file mode 100644
index 0000000000..fef0ebed06
--- /dev/null
+++ b/dom/html/test/forms/test_input_typing_sanitization.html
@@ -0,0 +1,217 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=765772
+-->
+<head>
+ <title>Test for Bug 765772</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug 765772</a>
+<p id="display"></p>
+<iframe name="submit_frame" style="visibility: hidden;"></iframe>
+<div id="content">
+ <form id='f' target="submit_frame" action="foo">
+ <input name=i id="i" step='any' >
+ </form>
+</div>
+<pre id="test">
+<script>
+
+/*
+ * This test checks that when a user types in some input types, it will not be
+ * in a state where the value will be un-sanitized and usable (by a script).
+ */
+
+var input = document.getElementById('i');
+var form = document.getElementById('f');
+var submitFrame = document.getElementsByTagName('iframe')[0];
+var testData = [];
+var gCurrentTest = null;
+var gValidData = [];
+var gInvalidData = [];
+
+function submitForm() {
+ form.submit();
+}
+
+function sendKeyEventToSubmitForm() {
+ sendKey("return");
+}
+
+function urlify(aStr) {
+ return aStr.replace(/:/g, '%3A');
+}
+
+function runTestsForNextInputType()
+{
+ let {done} = testRunner.next();
+ if (done) {
+ SimpleTest.finish();
+ }
+}
+
+function checkValueSubmittedIsValid()
+{
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/forms/foo?i=${urlify(gValidData[valueIndex++])}`,
+ "The submitted value should not have been sanitized");
+
+ input.value = "";
+
+ if (valueIndex >= gValidData.length) {
+ if (gCurrentTest.canHaveBadInputValidityState) {
+ // Don't run the submission tests on the invalid input if submission
+ // will be blocked by invalid input.
+ runTestsForNextInputType();
+ return;
+ }
+ valueIndex = 0;
+ submitFrame.onload = checkValueSubmittedIsInvalid;
+ testData = gInvalidData;
+ }
+ testSubmissions();
+}
+
+function checkValueSubmittedIsInvalid()
+{
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/forms/foo?i=`,
+ "The submitted value should have been sanitized");
+
+ valueIndex++;
+ input.value = "";
+
+ if (valueIndex >= gInvalidData.length) {
+ if (submitMethod == sendKeyEventToSubmitForm) {
+ runTestsForNextInputType();
+ return;
+ }
+ valueIndex = 0;
+ submitMethod = sendKeyEventToSubmitForm;
+ submitFrame.onload = checkValueSubmittedIsValid;
+ testData = gValidData;
+ }
+ testSubmissions();
+}
+
+function testSubmissions() {
+ input.focus();
+ sendString(testData[valueIndex]);
+ submitMethod();
+}
+
+var valueIndex = 0;
+var submitMethod = submitForm;
+
+SimpleTest.waitForExplicitFinish();
+
+function* runTest()
+{
+ SimpleTest.requestLongerTimeout(4);
+
+ var data = [
+ {
+ type: 'number',
+ canHaveBadInputValidityState: true,
+ validData: [
+ "42",
+ "-42", // should work for negative values
+ "42.1234",
+ "123.123456789123", // double precision
+ "1e2", // e should be usable
+ "2e1",
+ "1e-1", // value after e can be negative
+ "1E2", // E can be used instead of e
+ ],
+ invalidData: [
+ "e",
+ "e2",
+ "1e0.1",
+ "foo",
+ "42,13", // comma can't be used as a decimal separator
+ ]
+ },
+ {
+ type: 'month',
+ validData: [
+ '0001-01',
+ '2012-12',
+ '100000-01',
+ ],
+ invalidData: [
+ '1-01',
+ '-',
+ 'december',
+ '2012-dec',
+ '2012/12',
+ '2012-99',
+ '2012-1',
+ ]
+ },
+ {
+ type: 'week',
+ validData: [
+ '0001-W01',
+ '1970-W53',
+ '100000-W52',
+ '2016-W30',
+ ],
+ invalidData: [
+ '1-W01',
+ 'week',
+ '2016-30',
+ '2010-W80',
+ '2000/W30',
+ '1985-W00',
+ '1000-W'
+ ]
+ },
+ ];
+
+ for (test of data) {
+ gCurrentTest = test;
+
+ input.type = test.type;
+ gValidData = test.validData;
+ gInvalidData = test.invalidData;
+
+ for (data of gValidData) {
+ input.value = "";
+ input.focus();
+ sendString(data);
+ input.blur();
+ is(input.value, data, "valid user input should not be sanitized");
+ }
+
+ for (data of gInvalidData) {
+ input.value = "";
+ input.focus();
+ sendString(data);
+ input.blur();
+ is(input.value, "", "invalid user input should be sanitized");
+ }
+
+ input.value = '';
+
+ testData = gValidData;
+ valueIndex = 0;
+ submitFrame.onload = checkValueSubmittedIsValid;
+ testSubmissions();
+ yield undefined;
+ }
+}
+
+var testRunner = runTest();
+
+addLoadEvent(function () {
+ testRunner.next();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_untrusted_key_events.html b/dom/html/test/forms/test_input_untrusted_key_events.html
new file mode 100644
index 0000000000..78e35f525f
--- /dev/null
+++ b/dom/html/test/forms/test_input_untrusted_key_events.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for untrusted DOM KeyboardEvent on input element</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+ <input id="input">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runNextTest, window);
+
+const kTests = [
+ { type: "text", value: "foo", key: "b", expectedNewValue: "foo" },
+ { type: "number", value: "123", key: "4", expectedNewValue: "123" },
+ { type: "number", value: "123", key: KeyEvent.DOM_VK_UP, expectedNewValue: "123" },
+ { type: "number", value: "123", key: KeyEvent.DOM_VK_DOWN, expectedNewValue: "123" },
+];
+
+function sendUntrustedKeyEvent(eventType, keyCode, target) {
+ var evt = new KeyboardEvent(eventType, {
+ bubbles: true,
+ cancelable: true,
+ view: document.defaultView,
+ keyCode,
+ charCode: 0,
+ });
+ target.dispatchEvent(evt);
+}
+
+var input = document.getElementById("input");
+
+var gotEvents = {};
+
+function handleEvent(event) {
+ gotEvents[event.type] = true;
+}
+
+input.addEventListener("keydown", handleEvent);
+input.addEventListener("keyup", handleEvent);
+input.addEventListener("keypress", handleEvent);
+
+var previousTest = null;
+
+function runNextTest() {
+ if (previousTest) {
+ var msg = "For <input " + "type=" + previousTest.type + ">, ";
+ is(gotEvents.keydown, true, msg + "checking got keydown");
+ is(gotEvents.keyup, true, msg + "checking got keyup");
+ is(gotEvents.keypress, true, msg + "checking got keypress");
+ is(input.value, previousTest.expectedNewValue, msg + "checking element " +
+ " after being sent '" + previousTest.key + "' key events");
+ }
+
+ // reset flags
+ gotEvents.keydown = false;
+ gotEvents.keyup = false;
+ gotEvents.keypress = false;
+
+
+ var test = kTests.shift();
+ if (!test) {
+ SimpleTest.finish();
+ return; // We're all done
+ }
+
+ input.type = test.type;
+ input.focus(); // make sure we still have focus after type change
+ input.value = test.value;
+
+ sendUntrustedKeyEvent("keydown", test.key, input);
+ sendUntrustedKeyEvent("keyup", test.key, input);
+ sendUntrustedKeyEvent("keypress", test.key, input);
+
+ previousTest = test;
+
+ SimpleTest.executeSoon(runNextTest);
+};
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_input_url.html b/dom/html/test/forms/test_input_url.html
new file mode 100644
index 0000000000..3cdf1070bb
--- /dev/null
+++ b/dom/html/test/forms/test_input_url.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Tests for &lt;input type='url'&gt; validity</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input type='url' id='i' oninvalid='invalidEventHandler(event);'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Tests for <input type='url'> validity **/
+
+// More checks are done in test_bug551670.html.
+
+var gInvalid = false;
+
+function invalidEventHandler(e)
+{
+ is(e.type, "invalid", "Invalid event type should be invalid");
+ gInvalid = true;
+}
+
+function checkValidURL(element)
+{
+ info(`Checking ${element.value}\n`);
+ gInvalid = false;
+ ok(!element.validity.typeMismatch,
+ "Element should not suffer from type mismatch");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "Element should be valid");
+ ok(!gInvalid, "The invalid event should not have been thrown");
+ is(element.validationMessage, '',
+ "Validation message should be the empty string");
+ ok(element.matches(":valid"), ":valid pseudo-class should apply");
+}
+
+function checkInvalidURL(element)
+{
+ gInvalid = false;
+ ok(element.validity.typeMismatch,
+ "Element should suffer from type mismatch");
+ ok(!element.validity.valid, "Element should not be valid");
+ ok(!element.checkValidity(), "Element should not be valid");
+ ok(gInvalid, "The invalid event should have been thrown");
+ is(element.validationMessage, "Please enter a URL.",
+ "Validation message should be related to invalid URL");
+ ok(element.matches(":invalid"),
+ ":invalid pseudo-class should apply");
+}
+
+var url = document.getElementById('i');
+
+var values = [
+ // [ value, validity ]
+ // The empty string should be considered as valid.
+ [ "", true ],
+ [ "foo", false ],
+ [ "http://mozilla.com/", true ],
+ [ "http://mozilla.com", true ],
+ [ "http://mozil\nla\r.com/", true ],
+ [ " http://mozilla.com/ ", true ],
+ [ "\r http://mozilla.com/ \n", true ],
+ [ "file:///usr/bin/tulip", true ],
+ [ "../../bar.html", false ],
+ [ "http://mozillá.org", true ],
+ [ "https://mózillä.org", true ],
+ [ "http://mózillä.órg", true ],
+ [ "ht://mózillä.órg", true ],
+ [ "httŭ://mózillä.órg", false ],
+ [ "chrome://bookmarks", true ],
+];
+
+values.forEach(function([value, valid]) {
+ url.value = value;
+
+ if (valid) {
+ checkValidURL(url);
+ } else {
+ checkInvalidURL(url);
+ }
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_interactive_content_in_label.html b/dom/html/test/forms/test_interactive_content_in_label.html
new file mode 100644
index 0000000000..b8d9c81d51
--- /dev/null
+++ b/dom/html/test/forms/test_interactive_content_in_label.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=229925
+-->
+<head>
+ <title>Test for Bug 229925</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=229925">Mozilla Bug 229925</a>
+<p id="display"></p>
+<form action="#">
+ <label>
+ <span id="text">label</span>
+ <input type="button" id="target" value="target">
+
+ <a class="yes" href="#">a</a>
+ <audio class="yes" controls></audio>
+ <button class="yes">button</button>
+ <details class="yes">details</details>
+ <embed class="yes">embed</embed>
+ <iframe class="yes" src="data:text/plain," style="width: 16px; height: 16px;"></iframe>
+ <img class="yes" src="data:image/png," usemap="#map">
+ <input class="yes" type="text" size="4">
+ <keygen class="no">
+ <label class="yes">label</label>
+ <object class="yes" usemap="#map">object</object>
+ <select class="yes"><option>select</option></select>
+ <textarea class="yes" cols="1" rows="1"></textarea>
+ <video class="yes" controls></video>
+
+ <!-- Tests related to shadow tree. -->
+ <div id="root1"> <!-- content will be added by script below. --> </div>
+ <button><div id="root2"> <!-- content will be added by script below. --> </div></button>
+
+ <a class="no">a</a>
+ <audio class="no"></audio>
+ <img class="no" src="data:image/png,">
+ <input class="no" type="hidden">
+ <object class="no">object</object>
+ <video class="no"></video>
+
+ <span class="no" tabindex="1">tabindex</span>
+ <audio class="no" tabindex="1"></audio>
+ <img class="no" src="data:image/png," tabindex="1">
+ <input class="no" type="hidden" tabindex="1">
+ <object class="no" tabindex="1">object</object>
+ <video class="no" tabindex="1"></video>
+ </label>
+</form>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 229925 **/
+
+var target = document.getElementById("target");
+
+var yes_nodes = Array.from(document.getElementsByClassName("yes"));
+
+var root1 = document.getElementById("root1");
+root1.attachShadow({ mode: "open" }).innerHTML = "<button class=yes>button in shadow tree</button>";
+var root2 = document.getElementById("root2");
+root2.attachShadow({ mode: "open" }).innerHTML = "<div class=yes>text in shadow tree</div>";
+var yes_nodes_in_shadow_tree =
+ Array.from(root1.shadowRoot.querySelectorAll(".yes")).concat(
+ Array.from(root2.shadowRoot.querySelectorAll(".yes")));
+
+var no_nodes = Array.from(document.getElementsByClassName("no"));
+
+var target_clicked = false;
+target.addEventListener("click", function() {
+ target_clicked = true;
+});
+
+var node;
+for (node of yes_nodes) {
+ target_clicked = false;
+ node.click();
+ is(target_clicked, false, "mouse click on interactive content " + node.nodeName + " shouldn't dispatch event to label target");
+}
+
+for (node of yes_nodes_in_shadow_tree) {
+ target_clicked = false;
+ node.click();
+ is(target_clicked, false, "mouse click on content in shadow tree " + node.nodeName + " shouldn't dispatch event to label target");
+}
+
+for (node of no_nodes) {
+ target_clicked = false;
+ node.click();
+ is(target_clicked, true, "mouse click on non interactive content " + node.nodeName + " should dispatch event to label target");
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_interactive_content_in_summary.html b/dom/html/test/forms/test_interactive_content_in_summary.html
new file mode 100644
index 0000000000..f8bac77d89
--- /dev/null
+++ b/dom/html/test/forms/test_interactive_content_in_summary.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1524893
+-->
+<head>
+ <title>Test for Bug 1524893</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1524893">Mozilla Bug 1524893</a>
+
+<details id="details">
+ <summary>
+ <a class="yes" href="#">a</a>
+ <audio class="yes" controls></audio>
+ <button class="yes">button</button>
+ <details class="yes">details</details>
+ <embed class="yes">embed</embed>
+ <iframe class="yes" src="data:text/plain," style="width: 16px; height: 16px;"></iframe>
+ <img class="yes" src="data:image/png," usemap="#map">
+ <input class="yes" type="text" size="4">
+ <keygen class="no">
+ <label class="yes">label</label>
+ <object class="yes" usemap="#map">object</object>
+ <select class="yes"><option>select</option></select>
+ <textarea class="yes" cols="1" rows="1"></textarea>
+ <video class="yes" controls></video>
+
+ <!-- Tests related to shadow tree. -->
+ <div id="root1"> <!-- content will be added by script below. --> </div>
+ <button><div id="root2"> <!-- content will be added by script below. --> </div></button>
+
+ <a class="no">a</a>
+ <audio class="no"></audio>
+ <img class="no" src="data:image/png,">
+ <input class="no" type="hidden">
+ <object class="no">object</object>
+ <video class="no"></video>
+
+ <span class="no" tabindex="1">tabindex</span>
+ <audio class="no" tabindex="1"></audio>
+ <img class="no" src="data:image/png," tabindex="1">
+ <input class="no" type="hidden" tabindex="1">
+ <object class="no" tabindex="1">object</object>
+ <video class="no" tabindex="1"></video>
+ </summary>
+ <div>This is details</div>
+</details>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1524893 **/
+
+var details = document.getElementById("details");
+
+var yes_nodes = Array.from(document.getElementsByClassName("yes"));
+
+var root1 = document.getElementById("root1");
+root1.attachShadow({ mode: "open" }).innerHTML = "<button class=yes>button in shadow tree</button>";
+var root2 = document.getElementById("root2");
+root2.attachShadow({ mode: "open" }).innerHTML = "<div class=yes>text in shadow tree</div>";
+var yes_nodes_in_shadow_tree =
+ Array.from(root1.shadowRoot.querySelectorAll(".yes")).concat(
+ Array.from(root2.shadowRoot.querySelectorAll(".yes")));
+
+var no_nodes = Array.from(document.getElementsByClassName("no"));
+
+var node;
+for (node of yes_nodes) {
+ details.removeAttribute('open');
+ node.click();
+ ok(!details.hasAttribute('open'),
+ "mouse click on interactive content " + node.nodeName + " shouldn't not open details");
+}
+
+for (node of yes_nodes_in_shadow_tree) {
+ details.removeAttribute('open');
+ node.click();
+ ok(!details.hasAttribute('open'),
+ "mouse click on content in shadow tree " + node.nodeName + " shouldn't open details");
+}
+
+for (node of no_nodes) {
+ details.removeAttribute('open');
+ node.click();
+ ok(details.hasAttribute('open'),
+ "mouse click on non interactive content " + node.nodeName + " should open details");
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_label_control_attribute.html b/dom/html/test/forms/test_label_control_attribute.html
new file mode 100644
index 0000000000..efc04cd787
--- /dev/null
+++ b/dom/html/test/forms/test_label_control_attribute.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=562932
+-->
+<head>
+ <title>Test for Bug 562932</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=562932">Mozilla Bug 562932</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <!-- No @for, we have to check the content -->
+ <label id='l1'><input id='i1'></label>
+ <label id='l2'><input id='i2'><input></label>
+ <label id='l3'></label>
+ <label id='l4a'><fieldset id='f'>foo</fieldset></label>
+ <label id='l4b'><label id='l4c'><input id='i3'></label></label>
+ <label id='l4d'><label id='l4e'><input id='i3b'></label><input></label>
+
+ <!-- With @for, we do no check the content -->
+ <label id='l5' for='i1'></label>
+ <label id='l6' for='i4'></label>
+ <label id='l7' for='i4'><input></label>
+ <label id='l8' for='i1 i2'></label>
+ <label id='l9' for='i1 i2'><input></label>
+ <label id='l10' for='f'></label>
+ <label id='l11' for='i4'></label>
+ <label id='l12' for='i5'></label>
+ <label id='l13' for=''><input></label>
+ <!-- <label id='l14'> is created in script -->
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 562932 **/
+
+function checkControl(aLabelId, aElementId, aMsg)
+{
+ var element = null;
+
+ if (aElementId != null) {
+ element = document.getElementById(aElementId);
+ }
+
+ is(document.getElementById(aLabelId).control, element, aMsg);
+}
+
+ok('control' in document.createElement('label'),
+ "label element should have a control IDL attribute");
+
+checkControl('l1', 'i1', "label control should be the first form element");
+checkControl('l2', 'i2', "label control should be the first form element");
+checkControl('l3', null, "label control should be null when there is no child");
+checkControl('l4a', null, "label control should be null when there is no \
+ labelable form element child");
+checkControl('l4b', 'i3', "label control should be the first labelable element \
+ in tree order");
+checkControl('l4c', 'i3', "label control should be the first labelable element \
+ in tree order");
+checkControl('l4d', 'i3b', "label control should be the first labelable element \
+ in tree order");
+checkControl('l4e', 'i3b', "label control should be the first labelable element \
+ in tree order");
+checkControl('l5', 'i1', "label control should be the id in @for");
+checkControl('l6', null,
+ "label control should be null if the id in @for is not valid");
+checkControl('l7', null,
+ "label control should be null if the id in @for is not valid");
+checkControl('l8', null,
+ "label control should be null if there are more than one id in @for");
+checkControl('l9', null,
+ "label control should be null if there are more than one id in @for");
+checkControl('l10', null, "label control should be null if the id in @for \
+ is not an id from a labelable form element");
+
+var inputOutOfDocument = document.createElement('input');
+inputOutOfDocument.id = 'i4';
+checkControl('l11', null, "label control should be null if the id in @for \
+ is not an id from an element in the document");
+
+var inputInDocument = document.createElement('input');
+inputInDocument.id = 'i5';
+document.getElementById('content').appendChild(inputInDocument);
+checkControl('l12', 'i5', "label control should be the id in @for");
+
+checkControl('l13', null, "label control should be null if the id in @for \
+ is empty");
+
+var labelOutOfDocument = document.createElement('label');
+labelOutOfDocument.htmlFor = 'i1';
+is(labelOutOfDocument.control, null, "out of document label shouldn't \
+ labelize a form control");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_label_input_controls.html b/dom/html/test/forms/test_label_input_controls.html
new file mode 100644
index 0000000000..fe9410b608
--- /dev/null
+++ b/dom/html/test/forms/test_label_input_controls.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=597650
+-->
+<head>
+ <title>Test for Bug 597650</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=597650">Mozilla Bug 597650</a>
+ <p id="display"></p>
+ <div id="content">
+ <label id="l">
+ <input id="h"></input>
+ <input type="text" id="i"></input>
+ </label>
+ <label id="lh" for="h"></label>
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ /** Test for Bug 597650 **/
+ label = document.getElementById("l");
+ labelForH = document.getElementById("lh");
+ inputI = document.getElementById("i");
+ inputH = document.getElementById("h");
+
+ var labelableTypes = ["text", "search", "tel", "url", "email", "password",
+ "datetime", "date", "month", "week", "time",
+ "number", "range", "color", "checkbox", "radio",
+ "file", "submit", "image", "reset", "button"];
+ var nonLabelableTypes = ["hidden"];
+
+ for (var i in labelableTypes) {
+ test(labelableTypes[i], true);
+ }
+
+ for (var i in nonLabelableTypes) {
+ test(nonLabelableTypes[i], false);
+ }
+
+ function test(type, isLabelable) {
+ inputH.type = type;
+ if (isLabelable) {
+ testControl(label, inputH, type, true);
+ testControl(labelForH, inputH, type, true);
+ } else {
+ testControl(label, inputI, type, false);
+ testControl(labelForH, null, type, false);
+
+ inputH.type = "text";
+ testControl(label, inputH, "text", true);
+ testControl(labelForH, inputH, "text", true);
+
+ inputH.type = type;
+ testControl(label, inputI, type, false);
+ testControl(labelForH, null, type, false);
+
+ label.removeChild(inputH);
+ testControl(label, inputI, "text", true);
+
+ var element = document.createElement('input');
+ element.type = type;
+ label.insertBefore(element, inputI);
+ testControl(label, inputI, "text", true);
+ }
+ }
+
+ function testControl(label, control, type, labelable) {
+ if (labelable) {
+ is(label.control, control, "Input controls of type " + type
+ + " should be labeled");
+ } else {
+ is(label.control, control, "Input controls of type " + type
+ + " should be ignored by <label>");
+ }
+ }
+ </script>
+ </pre>
+ </body>
+</html>
+
diff --git a/dom/html/test/forms/test_max_attribute.html b/dom/html/test/forms/test_max_attribute.html
new file mode 100644
index 0000000000..f6e9c9bd8e
--- /dev/null
+++ b/dom/html/test/forms/test_max_attribute.html
@@ -0,0 +1,473 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=635499
+-->
+<head>
+ <title>Test for Bug 635499</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635499">Mozilla Bug 635499</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 635499 **/
+
+var data = [
+ { type: 'hidden', apply: false },
+ { type: 'text', apply: false },
+ { type: 'search', apply: false },
+ { type: 'tel', apply: false },
+ { type: 'url', apply: false },
+ { type: 'email', apply: false },
+ { type: 'password', apply: false },
+ { type: 'date', apply: true },
+ { type: 'month', apply: true },
+ { type: 'week', apply: true },
+ { type: 'time', apply: true },
+ { type: 'datetime-local', apply: true },
+ { type: 'number', apply: true },
+ { type: 'range', apply: true },
+ { type: 'color', apply: false },
+ { type: 'checkbox', apply: false },
+ { type: 'radio', apply: false },
+ { type: 'file', apply: false },
+ { type: 'submit', apply: false },
+ { type: 'image', apply: false },
+ { type: 'reset', apply: false },
+ { type: 'button', apply: false },
+];
+
+var input = document.createElement("input");
+document.getElementById('content').appendChild(input);
+
+/**
+ * @aValidity - boolean indicating whether the element is expected to be valid
+ * (aElement.validity.valid is true) or not. The value passed is ignored and
+ * overridden with true if aApply is false.
+ * @aApply - boolean indicating whether the min/max attributes apply to this
+ * element type.
+ * @aRangeApply - A boolean that's set to true if the current input type is a
+ * "[candidate] for constraint validation" and it "[has] range limitations"
+ * per http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#selector-in-range
+ * (in other words, one of the pseudo classes :in-range and :out-of-range
+ * should apply (which, depends on aValidity)).
+ * Else (neither :in-range or :out-of-range should match) set to false.
+ */
+function checkValidity(aElement, aValidity, aApply, aRangeApply)
+{
+ aValidity = aApply ? aValidity : true;
+
+ is(aElement.validity.valid, aValidity,
+ "element validity should be " + aValidity);
+ is(aElement.validity.rangeOverflow, !aValidity,
+ "element overflow status should be " + !aValidity);
+ var overflowMsg =
+ (aElement.type == "date" || aElement.type == "time" ||
+ aElement.type == "month" || aElement.type == "week" ||
+ aElement.type == "datetime-local") ?
+ ("Please select a value that is no later than " + aElement.max + ".") :
+ ("Please select a value that is no more than " + aElement.max + ".");
+ is(aElement.validationMessage,
+ aValidity ? "" : overflowMsg, "Checking range overflow validation message");
+
+ is(aElement.matches(":valid"), aElement.willValidate && aValidity,
+ (aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
+ is(aElement.matches(":invalid"), aElement.willValidate && !aValidity,
+ (aElement.wil && aValidity) ? ":invalid shouldn't apply" : "valid should apply");
+
+ if (!aRangeApply) {
+ ok(!aElement.matches(":in-range"), ":in-range should not match");
+ ok(!aElement.matches(":out-of-range"),
+ ":out-of-range should not match");
+ } else {
+ is(aElement.matches(":in-range"), aValidity,
+ ":in-range matches status should be " + aValidity);
+ is(aElement.matches(":out-of-range"), !aValidity,
+ ":out-of-range matches status should be " + !aValidity);
+ }
+}
+
+for (var test of data) {
+ input.type = test.type;
+ var apply = test.apply;
+
+ // The element should be valid. Range should not apply when @min and @max are
+ // undefined, except if the input type is 'range' (since that type has a
+ // default minimum and maximum).
+ if (input.type == 'range') {
+ checkValidity(input, true, apply, true);
+ } else {
+ checkValidity(input, true, apply, false);
+ }
+ checkValidity(input, true, apply, test.type == 'range');
+
+ switch (input.type) {
+ case 'hidden':
+ case 'text':
+ case 'search':
+ case 'password':
+ case 'url':
+ case 'tel':
+ case 'email':
+ case 'number':
+ case 'checkbox':
+ case 'radio':
+ case 'file':
+ case 'submit':
+ case 'reset':
+ case 'button':
+ case 'image':
+ case 'color':
+ input.max = '-1';
+ break;
+ case 'date':
+ input.max = '2012-06-27';
+ break;
+ case 'time':
+ input.max = '02:20';
+ break;
+ case 'range':
+ // range is special, since setting max to -1 will make it invalid since
+ // it's default would then be 0, meaning it suffers from overflow.
+ input.max = '-1';
+ checkValidity(input, false, apply, apply);
+ // Now make it something that won't cause an error below:
+ input.max = '10';
+ break;
+ case 'month':
+ input.max = '2016-12';
+ break;
+ case 'week':
+ input.max = '2016-W39';
+ break;
+ case 'datetime-local':
+ input.max = '2016-12-31T23:59:59';
+ break;
+ default:
+ ok(false, 'please, add a case for this new type (' + input.type + ')');
+ }
+
+ checkValidity(input, true, apply, apply);
+
+ switch (input.type) {
+ case 'text':
+ case 'hidden':
+ case 'search':
+ case 'password':
+ case 'tel':
+ case 'radio':
+ case 'checkbox':
+ case 'reset':
+ case 'button':
+ case 'submit':
+ case 'image':
+ input.value = '0';
+ checkValidity(input, true, apply, apply);
+ break;
+ case 'url':
+ input.value = 'http://mozilla.org';
+ checkValidity(input, true, apply, apply);
+ break;
+ case 'email':
+ input.value = 'foo@bar.com';
+ checkValidity(input, true, apply, apply);
+ break;
+ case 'file':
+ var file = new File([''], '635499_file');
+
+ SpecialPowers.wrap(input).mozSetFileArray([file]);
+ checkValidity(input, true, apply, apply);
+
+ break;
+ case 'date':
+ input.max = '2012-06-27';
+ input.value = '2012-06-26';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2012-06-27';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2012-06-28';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '2012-06-30';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2012-07-05';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-01-01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '20120-01-01';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '0050-01-01';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '0049-01-01';
+ checkValidity(input, true, apply, apply);
+
+ input.max = '';
+ checkValidity(input, true, apply, false);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, false);
+
+ break;
+ case 'number':
+ input.max = '2';
+ input.value = '1';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '3';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '5';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '42';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '';
+ checkValidity(input, true, apply, false);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, false);
+
+ // Check that we correctly convert input.max to a double in validationMessage.
+ if (input.type == 'number') {
+ input.max = "4.333333333333333333333333333333333331";
+ input.value = "5";
+ is(input.validationMessage,
+ "Please select a value that is no more than 4.33333333333333.",
+ "validation message");
+ }
+
+ break;
+ case 'range':
+ input.max = '2';
+ input.value = '1';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '3';
+ checkValidity(input, true, apply, apply);
+
+ is(input.value, input.max, "the value should have been set to max");
+
+ input.max = '5';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '42';
+ checkValidity(input, true, apply, apply);
+
+ is(input.value, input.max, "the value should have been set to max");
+
+ input.max = '';
+ checkValidity(input, true, apply, apply);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ // Check that we correctly convert input.max to a double in validationMessage.
+ input.step = 'any';
+ input.min = 5;
+ input.max = 0.6666666666666666;
+ input.value = 1;
+ is(input.validationMessage,
+ "Please select a value that is no more than 0.666666666666667.",
+ "validation message")
+
+ break;
+ case 'time':
+ // Don't worry about that.
+ input.step = 'any';
+
+ input.max = '10:10';
+ input.value = '10:09';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '10:10';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '10:10:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '10:10:00.000';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '10:11';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '10:10:00.001';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '01:00:00.01';
+ input.value = '01:00:00.001';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '01:00:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '01:00:00.1';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '';
+ checkValidity(input, true, apply, false);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, false);
+
+ break;
+ case 'month':
+ input.value = '2016-06';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-12';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-01';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '2017-07';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-12';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '20160-01';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '0050-01';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '0049-12';
+ checkValidity(input, true, apply, apply);
+
+ input.max = '';
+ checkValidity(input, true, apply, false);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, false);
+
+ break;
+ case 'week':
+ input.value = '2016-W01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-W39';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-W01';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '2017-W01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-W52';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-W01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2100-W01';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '0050-W01';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '0049-W52';
+ checkValidity(input, true, apply, apply);
+
+ input.max = '';
+ checkValidity(input, true, apply, false);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, false);
+
+ break;
+ case 'datetime-local':
+ input.value = '2016-01-01T12:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-12-31T23:59:59';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-12-31T23:59:59.123';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '2017-01-01T10:00';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '2017-01-01T10:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-01-01T10:00:30';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-01-01T12:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2100-01-01T12:00';
+ checkValidity(input, false, apply, apply);
+
+ input.max = '0050-12-31T23:59:59.999';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '0050-12-31T23:59:59';
+ checkValidity(input, true, apply, apply);
+
+ input.max = '';
+ checkValidity(input, true, apply, false);
+
+ input.max = 'foo';
+ checkValidity(input, true, apply, false);
+
+ break;
+ }
+
+ // Cleaning up,
+ input.removeAttribute('max');
+ input.value = '';
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_maxlength_attribute.html b/dom/html/test/forms/test_maxlength_attribute.html
new file mode 100644
index 0000000000..bd76e277e5
--- /dev/null
+++ b/dom/html/test/forms/test_maxlength_attribute.html
@@ -0,0 +1,129 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=345624
+-->
+<head>
+ <title>Test for Bug 345624</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input, textarea { background-color: rgb(0,0,0) !important; }
+ :-moz-any(input,textarea):valid { background-color: rgb(0,255,0) !important; }
+ :-moz-any(input,textarea):invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345624">Mozilla Bug 345624</a>
+<p id="display"></p>
+<div id="content">
+ <input id='i'>
+ <textarea id='t'></textarea>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 345624 **/
+
+/**
+ * This test is checking only tooLong related features
+ * related to constraint validation.
+ */
+
+function checkTooLongValidity(element)
+{
+ element.value = "foo";
+ ok(!element.validity.tooLong,
+ "Element should not be too long when maxlength is not set");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.maxLength = 1;
+ ok(!element.validity.tooLong,
+ "Element should not be too long unless the user edits it");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.focus();
+
+ synthesizeKey("KEY_Backspace");
+ is(element.value, "fo", "value should have changed");
+ ok(element.validity.tooLong,
+ "Element should be too long after a user edit that does not make it short enough");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ ok(!element.validity.valid, "Element should be invalid");
+ ok(!element.checkValidity(), "The element should not be valid");
+ is(element.validationMessage,
+ "Please shorten this text to 1 characters or less (you are currently using 2 characters).",
+ "The validation message text is not correct");
+
+ synthesizeKey("KEY_Backspace");
+ is(element.value, "f", "value should have changed");
+ ok(!element.validity.tooLong,
+ "Element should not be too long after a user edit makes it short enough");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+
+ element.maxLength = 2;
+ ok(!element.validity.tooLong,
+ "Element should remain valid if maxlength changes but maxlength > length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+
+ element.maxLength = 1;
+ ok(!element.validity.tooLong,
+ "Element should remain valid if maxlength changes but maxlength = length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.maxLength = 0;
+ ok(element.validity.tooLong,
+ "Element should become invalid if maxlength changes and maxlength < length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ ok(!element.validity.valid, "Element should be invalid");
+ ok(!element.checkValidity(), "The element should not be valid");
+ is(element.validationMessage,
+ "Please shorten this text to 0 characters or less (you are currently using 1 characters).",
+ "The validation message text is not correct");
+
+ element.maxLength = 1;
+ ok(!element.validity.tooLong,
+ "Element should become valid if maxlength changes and maxlength = length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.value = "test";
+ ok(!element.validity.tooLong,
+ "Element should stay valid after programmatic edit (even if value is too long)");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.setCustomValidity("custom message");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ is(element.validationMessage, "custom message",
+ "Custom message should be shown instead of too long one");
+}
+
+checkTooLongValidity(document.getElementById('i'));
+checkTooLongValidity(document.getElementById('t'));
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_meter_element.html b/dom/html/test/forms/test_meter_element.html
new file mode 100644
index 0000000000..5e1073d53d
--- /dev/null
+++ b/dom/html/test/forms/test_meter_element.html
@@ -0,0 +1,376 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=657938
+-->
+<head>
+ <title>Test for <meter></title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=657938">Mozilla Bug 657938</a>
+<p id="display"></p>
+<iframe name="submit_frame" style="visibility: hidden;"></iframe>
+<div id="content" style="visibility: hidden;">
+ <form id='f' method='get' target='submit_frame' action='foo'>
+ <meter id='m' value=0.5></meter>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for <meter> **/
+
+function checkFormIDLAttribute(aElement)
+{
+ is('form' in aElement, false, "<meter> shouldn't have a form attribute");
+}
+
+function checkAttribute(aElement, aAttribute, aNewValue, aExpectedValueForIDL)
+{
+ var expectedValueForIDL = aNewValue;
+ var expectedValueForContent = String(aNewValue);
+
+ if (aExpectedValueForIDL !== undefined) {
+ expectedValueForIDL = aExpectedValueForIDL;
+ }
+
+ if (aNewValue != null) {
+ aElement.setAttribute(aAttribute, aNewValue);
+ is(aElement.getAttribute(aAttribute), expectedValueForContent,
+ aAttribute + " content attribute should be " + expectedValueForContent);
+ is(aElement[aAttribute], expectedValueForIDL,
+ aAttribute + " IDL attribute should be " + expectedValueForIDL);
+
+ if (parseFloat(aNewValue) == aNewValue) {
+ aElement[aAttribute] = aNewValue;
+ is(aElement.getAttribute(aAttribute), expectedValueForContent,
+ aAttribute + " content attribute should be " + expectedValueForContent);
+ is(aElement[aAttribute], parseFloat(expectedValueForIDL),
+ aAttribute + " IDL attribute should be " + parseFloat(expectedValueForIDL));
+ }
+ } else {
+ aElement.removeAttribute(aAttribute);
+ is(aElement.getAttribute(aAttribute), null,
+ aAttribute + " content attribute should be null");
+ is(aElement[aAttribute], expectedValueForIDL,
+ aAttribute + " IDL attribute should be " + expectedValueForIDL);
+ }
+}
+
+function checkValueAttribute()
+{
+ var tests = [
+ // value has to be a valid float, its default value is 0.0 otherwise.
+ [ null, 0.0 ],
+ [ 'foo', 0.0 ],
+ // If value < 0.0, 0.0 is used instead.
+ [ -1.0, 0.0 ],
+ // If value >= max, max is used instead (max default value is 1.0).
+ [ 2.0, 1.0 ],
+ [ 1.0, 0.5, 0.5 ],
+ [ 10.0, 5.0, 5.0 ],
+ [ 13.37, 13.37, 42.0 ],
+ // If value <= min, min is used instead (min default value is 0.0).
+ [ 0.5, 1.0, 10.0 ,1.0 ],
+ [ 10.0, 13.37, 42.0 , 13.37],
+ // Regular reflection.
+ [ 0.0 ],
+ [ 0.5 ],
+ [ 1.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('meter');
+
+ for (var test of tests) {
+ if (test[2]) {
+ element.setAttribute('max', test[2]);
+ }
+
+ if (test[3]) {
+ element.setAttribute('min', test[3]);
+ }
+
+ checkAttribute(element, 'value', test[0], test[1]);
+
+ element.removeAttribute('max');
+ element.removeAttribute('min');
+ }
+}
+
+function checkMinAttribute()
+{
+ var tests = [
+ // min default value is 0.0.
+ [ null, 0.0 ],
+ [ 'foo', 0.0 ],
+ // Regular reflection.
+ [ 0.5 ],
+ [ 1.0 ],
+ [ 2.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('meter');
+
+ for (var test of tests) {
+ checkAttribute(element, 'min', test[0], test[1]);
+ }
+}
+
+function checkMaxAttribute()
+{
+ var tests = [
+ // max default value is 1.0.
+ [ null, 1.0 ],
+ [ 'foo', 1.0 ],
+ // If value <= min, min is used instead.
+ [ -1.0, 0.0 ],
+ [ 0.0, 0.5, 0.5 ],
+ [ 10.0, 15.0, 15.0 ],
+ [ 42, 42, 13.37 ],
+ // Regular reflection.
+ [ 0.5 ],
+ [ 1.0 ],
+ [ 2.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('meter');
+
+ for (var test of tests) {
+ if (test[2]) {
+ element.setAttribute('min', test[2]);
+ }
+
+ checkAttribute(element, 'max', test[0], test[1]);
+
+ element.removeAttribute('min');
+ }
+}
+
+function checkLowAttribute()
+{
+ var tests = [
+ // low default value is min (min default value is 0.0).
+ [ null, 0.0 ],
+ [ 'foo', 0.0 ],
+ [ 'foo', 1.0, 1.0],
+ // If low <= min, min is used instead.
+ [ -1.0, 0.0 ],
+ [ 0.0, 0.5, 0.5 ],
+ [ 10.0, 15.0, 15.0, 42.0 ],
+ [ 42.0, 42.0, 13.37, 100.0 ],
+ // If low >= max, max is used instead.
+ [ 2.0, 1.0 ],
+ [ 10.0, 5.0 , 0.5, 5.0 ],
+ [ 13.37, 13.37, 0.0, 42.0 ],
+ // Regular reflection.
+ [ 0.0 ],
+ [ 0.5 ],
+ [ 1.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('meter');
+
+ for (var test of tests) {
+ if (test[2]) {
+ element.setAttribute('min', test[2]);
+ }
+ if (test[3]) {
+ element.setAttribute('max', test[3]);
+ }
+
+ checkAttribute(element, 'low', test[0], test[1]);
+
+ element.removeAttribute('min');
+ element.removeAttribute('max');
+ }
+}
+
+function checkHighAttribute()
+{
+ var tests = [
+ // high default value is max (max default value is 1.0).
+ [ null, 1.0 ],
+ [ 'foo', 1.0 ],
+ [ 'foo', 42.0, 0.0, 42.0],
+ // If high <= min, min is used instead.
+ [ -1.0, 0.0 ],
+ [ 0.0, 0.5, 0.5 ],
+ [ 10.0, 15.0, 15.0, 42.0 ],
+ [ 42.0, 42.0, 13.37, 100.0 ],
+ // If high >= max, max is used instead.
+ [ 2.0, 1.0 ],
+ [ 10.0, 5.0 , 0.5, 5.0 ],
+ [ 13.37, 13.37, 0.0, 42.0 ],
+ // Regular reflection.
+ [ 0.0 ],
+ [ 0.5 ],
+ [ 1.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('meter');
+
+ for (var test of tests) {
+ if (test[2]) {
+ element.setAttribute('min', test[2]);
+ }
+ if (test[3]) {
+ element.setAttribute('max', test[3]);
+ }
+
+ checkAttribute(element, 'high', test[0], test[1]);
+
+ element.removeAttribute('min');
+ element.removeAttribute('max');
+ }
+}
+
+function checkOptimumAttribute()
+{
+ var tests = [
+ // opt default value is (max-min)/2 (thus default value is 0.5).
+ [ null, 0.5 ],
+ [ 'foo', 0.5 ],
+ [ 'foo', 2.0, 1.0, 3.0],
+ // If opt <= min, min is used instead.
+ [ -1.0, 0.0 ],
+ [ 0.0, 0.5, 0.5 ],
+ [ 10.0, 15.0, 15.0, 42.0 ],
+ [ 42.0, 42.0, 13.37, 100.0 ],
+ // If opt >= max, max is used instead.
+ [ 2.0, 1.0 ],
+ [ 10.0, 5.0 , 0.5, 5.0 ],
+ [ 13.37, 13.37, 0.0, 42.0 ],
+ // Regular reflection.
+ [ 0.0 ],
+ [ 0.5 ],
+ [ 1.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('meter');
+
+ for (var test of tests) {
+ if (test[2]) {
+ element.setAttribute('min', test[2]);
+ }
+ if (test[3]) {
+ element.setAttribute('max', test[3]);
+ }
+
+ checkAttribute(element, 'optimum', test[0], test[1]);
+
+ element.removeAttribute('min');
+ element.removeAttribute('max');
+ }
+}
+
+function checkFormListedElement(aElement)
+{
+ is(document.forms[0].elements.length, 0, "the form should have no element");
+}
+
+function checkLabelable(aElement)
+{
+ var content = document.getElementById('content');
+ var label = document.createElement('label');
+
+ content.appendChild(label);
+ label.appendChild(aElement);
+ is(label.control, aElement, "meter should be labelable");
+
+ // Cleaning-up.
+ content.removeChild(label);
+ content.appendChild(aElement);
+}
+
+function checkNotResetableAndFormSubmission(aElement)
+{
+ // Creating an input element to check the submission worked.
+ var form = document.forms[0];
+ var input = document.createElement('input');
+
+ input.name = 'a';
+ input.value = 'tulip';
+ form.appendChild(input);
+
+ // Setting values.
+ aElement.value = 42.0;
+ aElement.max = 100.0;
+
+ document.getElementsByName('submit_frame')[0].addEventListener("load", function() {
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/forms/foo?a=tulip`,
+ "The meter element value should not be submitted");
+
+ checkNotResetable();
+ }, {once: true});
+
+ form.submit();
+}
+
+function checkNotResetable()
+{
+ // Try to reset the form.
+ var form = document.forms[0];
+ var element = document.getElementById('m');
+
+ element.value = 3.0;
+ element.max = 42.0;
+
+ form.reset();
+
+ SimpleTest.executeSoon(function() {
+ is(element.value, 3.0, "meter.value should not have changed");
+ is(element.max, 42.0, "meter.max should not have changed");
+
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+var m = document.getElementById('m');
+
+ok(m instanceof HTMLMeterElement,
+ "The meter element should be instance of HTMLMeterElement");
+is(m.constructor, HTMLMeterElement,
+ "The meter element constructor should be HTMLMeterElement");
+
+// There is no such attribute.
+checkFormIDLAttribute(m);
+
+checkValueAttribute();
+
+checkMinAttribute();
+
+checkMaxAttribute();
+
+checkLowAttribute();
+
+checkHighAttribute();
+
+checkOptimumAttribute();
+
+checkFormListedElement(m);
+
+checkLabelable(m);
+
+checkNotResetableAndFormSubmission(m);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_meter_pseudo-classes.html b/dom/html/test/forms/test_meter_pseudo-classes.html
new file mode 100644
index 0000000000..e317a58405
--- /dev/null
+++ b/dom/html/test/forms/test_meter_pseudo-classes.html
@@ -0,0 +1,169 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660238
+-->
+<head>
+ <title>Test for Bug 660238</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770238">Mozilla Bug 660238</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 660238 **/
+
+function checkOptimum(aElement, aValue, aOptimum, expectedResult)
+{
+ var errorString = expectedResult
+ ? "value attribute should be in the optimum region"
+ : "value attribute should not be in the optimum region";
+
+ aElement.setAttribute('value', aValue);
+ aElement.setAttribute('optimum', aOptimum);
+ is(aElement.matches(":-moz-meter-optimum"),
+ expectedResult, errorString);
+}
+
+function checkSubOptimum(aElement, aValue, aOptimum, expectedResult)
+{
+ var errorString = "value attribute should be in the suboptimal region";
+ if (!expectedResult) {
+ errorString = "value attribute should not be in the suboptimal region";
+ }
+ aElement.setAttribute('value', aValue);
+ aElement.setAttribute('optimum', aOptimum);
+ is(aElement.matches(":-moz-meter-sub-optimum"),
+ expectedResult, errorString);
+}
+
+function checkSubSubOptimum(aElement, aValue, aOptimum, expectedResult)
+{
+ var errorString = "value attribute should be in the sub-suboptimal region";
+ if (!expectedResult) {
+ errorString = "value attribute should not be in the sub-suboptimal region";
+ }
+ aElement.setAttribute('value', aValue);
+ aElement.setAttribute('optimum', aOptimum);
+ is(aElement.matches(":-moz-meter-sub-sub-optimum"),
+ expectedResult, errorString);
+}
+
+function checkMozMatchesSelector()
+{
+ var element = document.createElement('meter');
+ // all tests realised with default values for min and max (0 and 1)
+ // low = 0.3 and high = 0.7
+ element.setAttribute('low', 0.3);
+ element.setAttribute('high', 0.7);
+
+ var tests = [
+ /*
+ * optimum = 0.0 =>
+ * optimum region = [ 0.0, 0.3 [
+ * suboptimal region = [ 0.3, 0.7 ]
+ * sub-suboptimal region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 0.0, true, false, false ],
+ [ 0.1, 0.0, true, false, false ],
+ [ 0.3, 0.0, false, true, false ],
+ [ 0.5, 0.0, false, true, false ],
+ [ 0.7, 0.0, false, true, false ],
+ [ 0.8, 0.0, false, false, true ],
+ [ 1.0, 0.0, false, false, true ],
+ /*
+ * optimum = 0.1 =>
+ * optimum region = [ 0.0, 0.3 [
+ * suboptimal region = [ 0.3, 0.7 ]
+ * sub-suboptimal region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 0.1, true, false, false ],
+ [ 0.1, 0.1, true, false, false ],
+ [ 0.3, 0.1, false, true, false ],
+ [ 0.5, 0.1, false, true, false ],
+ [ 0.7, 0.1, false, true, false ],
+ [ 0.8, 0.1, false, false, true ],
+ [ 1.0, 0.1, false, false, true ],
+ /*
+ * optimum = 0.3 =>
+ * suboptimal region = [ 0.0, 0.3 [
+ * optimum region = [ 0.3, 0.7 ]
+ * suboptimal region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 0.3, false, true, false ],
+ [ 0.1, 0.3, false, true, false ],
+ [ 0.3, 0.3, true, false, false ],
+ [ 0.5, 0.3, true, false, false ],
+ [ 0.7, 0.3, true, false, false ],
+ [ 0.8, 0.3, false, true, false ],
+ [ 1.0, 0.3, false, true, false ],
+ /*
+ * optimum = 0.5 =>
+ * suboptimal region = [ 0.0, 0.3 [
+ * optimum region = [ 0.3, 0.7 ]
+ * suboptimal region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 0.5, false, true, false ],
+ [ 0.1, 0.5, false, true, false ],
+ [ 0.3, 0.5, true, false, false ],
+ [ 0.5, 0.5, true, false, false ],
+ [ 0.7, 0.5, true, false, false ],
+ [ 0.8, 0.5, false, true, false ],
+ [ 1.0, 0.5, false, true, false ],
+ /*
+ * optimum = 0.7 =>
+ * suboptimal region = [ 0.0, 0.3 [
+ * optimum region = [ 0.3, 0.7 ]
+ * suboptimal region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 0.7, false, true, false ],
+ [ 0.1, 0.7, false, true, false ],
+ [ 0.3, 0.7, true, false, false ],
+ [ 0.5, 0.7, true, false, false ],
+ [ 0.7, 0.7, true, false, false ],
+ [ 0.8, 0.7, false, true, false ],
+ [ 1.0, 0.7, false, true, false ],
+ /*
+ * optimum = 0.8 =>
+ * sub-suboptimal region = [ 0.0, 0.3 [
+ * suboptimal region = [ 0.3, 0.7 ]
+ * optimum region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 0.8, false, false, true ],
+ [ 0.1, 0.8, false, false, true ],
+ [ 0.3, 0.8, false, true, false ],
+ [ 0.5, 0.8, false, true, false ],
+ [ 0.7, 0.8, false, true, false ],
+ [ 0.8, 0.8, true, false, false ],
+ [ 1.0, 0.8, true, false, false ],
+ /*
+ * optimum = 1.0 =>
+ * sub-suboptimal region = [ 0.0, 0.3 [
+ * suboptimal region = [ 0.3, 0.7 ]
+ * optimum region = ] 0.7, 1.0 ]
+ */
+ [ 0.0, 1.0, false, false, true ],
+ [ 0.1, 1.0, false, false, true ],
+ [ 0.3, 1.0, false, true, false ],
+ [ 0.5, 1.0, false, true, false ],
+ [ 0.7, 1.0, false, true, false ],
+ [ 0.8, 1.0, true, false, false ],
+ [ 1.0, 1.0, true, false, false ],
+ ];
+
+ for (var test of tests) {
+ checkOptimum(element, test[0], test[1], test[2]);
+ checkSubOptimum(element, test[0], test[1], test[3]);
+ checkSubSubOptimum(element, test[0], test[1], test[4]);
+ }
+}
+
+checkMozMatchesSelector();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_min_attribute.html b/dom/html/test/forms/test_min_attribute.html
new file mode 100644
index 0000000000..a603a37d29
--- /dev/null
+++ b/dom/html/test/forms/test_min_attribute.html
@@ -0,0 +1,473 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=635553
+-->
+<head>
+ <title>Test for Bug 635553</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635499">Mozilla Bug 635499</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 635553 **/
+
+var data = [
+ { type: 'hidden', apply: false },
+ { type: 'text', apply: false },
+ { type: 'search', apply: false },
+ { type: 'tel', apply: false },
+ { type: 'url', apply: false },
+ { type: 'email', apply: false },
+ { type: 'password', apply: false },
+ { type: 'date', apply: true },
+ { type: 'month', apply: true },
+ { type: 'week', apply: true },
+ { type: 'time', apply: true },
+ { type: 'datetime-local', apply: true },
+ { type: 'number', apply: true },
+ { type: 'range', apply: true },
+ { type: 'color', apply: false },
+ { type: 'checkbox', apply: false },
+ { type: 'radio', apply: false },
+ { type: 'file', apply: false },
+ { type: 'submit', apply: false },
+ { type: 'image', apply: false },
+ { type: 'reset', apply: false },
+ { type: 'button', apply: false },
+];
+
+var input = document.createElement("input");
+document.getElementById('content').appendChild(input);
+
+/**
+ * @aValidity - boolean indicating whether the element is expected to be valid
+ * (aElement.validity.valid is true) or not. The value passed is ignored and
+ * overridden with true if aApply is false.
+ * @aApply - boolean indicating whether the min/max attributes apply to this
+ * element type.
+ * @aRangeApply - A boolean that's set to true if the current input type is a
+ * "[candidate] for constraint validation" and it "[has] range limitations"
+ * per http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#selector-in-range
+ * (in other words, one of the pseudo classes :in-range and :out-of-range
+ * should apply (which, depends on aValidity)).
+ * Else (neither :in-range or :out-of-range should match) set to false.
+ */
+function checkValidity(aElement, aValidity, aApply, aRangeApply)
+{
+ aValidity = aApply ? aValidity : true;
+
+ is(aElement.validity.valid, aValidity,
+ "element validity should be " + aValidity);
+ is(aElement.validity.rangeUnderflow, !aValidity,
+ "element underflow status should be " + !aValidity);
+ var underflowMsg =
+ (aElement.type == "date" || aElement.type == "time" ||
+ aElement.type == "month" || aElement.type == "week" ||
+ aElement.type == "datetime-local") ?
+ ("Please select a value that is no earlier than " + aElement.min + ".") :
+ ("Please select a value that is no less than " + aElement.min + ".");
+ is(aElement.validationMessage,
+ aValidity ? "" : underflowMsg, "Checking range underflow validation message");
+
+ is(aElement.matches(":valid"), aElement.willValidate && aValidity,
+ (aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
+ is(aElement.matches(":invalid"), aElement.willValidate && !aValidity,
+ (aElement.wil && aValidity) ? ":invalid shouldn't apply" : "valid should apply");
+
+ if (!aRangeApply) {
+ ok(!aElement.matches(":in-range"), ":in-range should not match");
+ ok(!aElement.matches(":out-of-range"),
+ ":out-of-range should not match");
+ } else {
+ is(aElement.matches(":in-range"), aValidity,
+ ":in-range matches status should be " + aValidity);
+ is(aElement.matches(":out-of-range"), !aValidity,
+ ":out-of-range matches status should be " + !aValidity);
+ }
+}
+
+for (var test of data) {
+ input.type = test.type;
+ var apply = test.apply;
+
+ if (test.todo) {
+ todo_is(input.type, test.type, test.type + " isn't implemented yet");
+ continue;
+ }
+
+ // The element should be valid. Range should not apply when @min and @max are
+ // undefined, except if the input type is 'range' (since that type has a
+ // default minimum and maximum).
+ if (input.type == 'range') {
+ checkValidity(input, true, apply, true);
+ } else {
+ checkValidity(input, true, apply, false);
+ }
+
+ switch (input.type) {
+ case 'hidden':
+ case 'text':
+ case 'search':
+ case 'password':
+ case 'url':
+ case 'tel':
+ case 'email':
+ case 'number':
+ case 'checkbox':
+ case 'radio':
+ case 'file':
+ case 'submit':
+ case 'reset':
+ case 'button':
+ case 'image':
+ case 'color':
+ input.min = '999';
+ break;
+ case 'date':
+ input.min = '2012-06-27';
+ break;
+ case 'time':
+ input.min = '20:20';
+ break;
+ case 'range':
+ // range is special, since setting min to 999 will make it invalid since
+ // it's default maximum is 100, its value would be 999, and it would
+ // suffer from overflow.
+ break;
+ case 'month':
+ input.min = '2016-06';
+ break;
+ case 'week':
+ input.min = '2016-W39';
+ break;
+ case 'datetime-local':
+ input.min = '2017-01-01T00:00';
+ break;
+ default:
+ ok(false, 'please, add a case for this new type (' + input.type + ')');
+ }
+
+ // The element should still be valid and range should apply if it can.
+ checkValidity(input, true, apply, apply);
+
+ switch (input.type) {
+ case 'text':
+ case 'hidden':
+ case 'search':
+ case 'password':
+ case 'tel':
+ case 'radio':
+ case 'checkbox':
+ case 'reset':
+ case 'button':
+ case 'submit':
+ case 'image':
+ case 'color':
+ input.value = '0';
+ checkValidity(input, true, apply, apply);
+ break;
+ case 'url':
+ input.value = 'http://mozilla.org';
+ checkValidity(input, true, apply, apply);
+ break;
+ case 'email':
+ input.value = 'foo@bar.com';
+ checkValidity(input, true, apply, apply);
+ break;
+ case 'file':
+ var file = new File([''], '635499_file');
+
+ SpecialPowers.wrap(input).mozSetFileArray([file]);
+ checkValidity(input, true, apply, apply);
+
+ break;
+ case 'date':
+ input.value = '2012-06-28';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2012-06-27';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2012-06-26';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '2012-02-29';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2012-02-28';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-01-01';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '20120-01-01';
+ checkValidity(input, true, apply, apply);
+
+ input.min = '0050-01-01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '0049-01-01';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '';
+ checkValidity(input, true, apply, false);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, false);
+ break;
+ case 'number':
+ input.min = '0';
+ input.value = '1';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '0';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '-1';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '-1';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '-42';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '';
+ checkValidity(input, true, apply, false);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, false);
+
+ // Check that we correctly convert input.min to a double in
+ // validationMessage.
+ input.min = "4.333333333333333333333333333333333331";
+ input.value = "2";
+ is(input.validationMessage,
+ "Please select a value that is no less than 4.33333333333333.",
+ "validation message");
+ break;
+ case 'range':
+ input.min = '0';
+ input.value = '1';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '0';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '-1';
+ checkValidity(input, true, apply, apply);
+
+ is(input.value, input.min, "the value should have been set to min");
+
+ input.min = '-1';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '-42';
+ checkValidity(input, true, apply, apply);
+
+ is(input.value, input.min, "the value should have been set to min");
+
+ input.min = '';
+ checkValidity(input, true, apply, true);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, true);
+
+ // We don't check the conversion of input.min to a double in
+ // validationMessage for 'range' since range will always clamp the value
+ // up to at least the minimum (so we will never see the min in a
+ // validationMessage).
+
+ break;
+ case 'time':
+ // Don't worry about that.
+ input.step = 'any';
+
+ input.min = '20:20';
+ input.value = '20:20:01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '20:20:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '10:00';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '20:20:00.001';
+ input.value = '20:20';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '00:00';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '23:59';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '20:20:01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '20:20:00.01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '20:20:00.1';
+ checkValidity(input, true, apply, apply);
+
+ input.min = '00:00:00';
+ input.value = '01:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '00:00:00.000';
+ checkValidity(input, true, apply, apply);
+
+ input.min = '';
+ checkValidity(input, true, apply, false);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, false);
+ break;
+ case 'month':
+ input.value = '2016-07';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-06';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-05';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '2016-01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2015-12';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-01';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '10000-01';
+ checkValidity(input, true, apply, apply);
+
+ input.min = '0010-01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '0001-01';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '';
+ checkValidity(input, true, apply, false);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, false);
+ break;
+ case 'week':
+ input.value = '2016-W40';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-W39';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-W38';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '2016-W01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2015-W53';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-W01';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '10000-01';
+ checkValidity(input, true, apply, apply);
+
+ input.min = '0010-W01';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '0001-W01';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '';
+ checkValidity(input, true, apply, false);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, false);
+ break;
+ case 'datetime-local':
+ input.value = '2017-12-31T23:59';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-01-01T00:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2017-01-01T00:00:00.123';
+ checkValidity(input, true, apply, apply);
+
+ input.value = 'foo';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2016-12-31T23:59';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '2016-01-01T00:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '2015-12-31T23:59';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '1000-01-01T00:00';
+ checkValidity(input, false, apply, apply);
+
+ input.value = '10000-01-01T00:00';
+ checkValidity(input, true, apply, apply);
+
+ input.min = '0010-01-01T12:00';
+ checkValidity(input, true, apply, apply);
+
+ input.value = '0010-01-01T10:00';
+ checkValidity(input, false, apply, apply);
+
+ input.min = '';
+ checkValidity(input, true, apply, false);
+
+ input.min = 'foo';
+ checkValidity(input, true, apply, false);
+ break;
+ default:
+ ok(false, 'write tests for ' + input.type);
+ }
+
+ // Cleaning up,
+ input.removeAttribute('min');
+ input.value = '';
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_minlength_attribute.html b/dom/html/test/forms/test_minlength_attribute.html
new file mode 100644
index 0000000000..154343a512
--- /dev/null
+++ b/dom/html/test/forms/test_minlength_attribute.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=345624
+-->
+<head>
+ <title>Test for Bug 345624</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input, textarea { background-color: rgb(0,0,0) !important; }
+ :-moz-any(input,textarea):valid { background-color: rgb(0,255,0) !important; }
+ :-moz-any(input,textarea):invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345624">Mozilla Bug 345624</a>
+<p id="display"></p>
+<div id="content">
+ <input id='i'>
+ <textarea id='t'></textarea>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 345624 **/
+
+/**
+ * This test is checking only tooShort related features
+ * related to constraint validation.
+ */
+
+function checkTooShortValidity(element)
+{
+ element.value = "foo";
+ ok(!element.validity.tooShort,
+ "Element should not be too short when minlength is not set");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.minLength = 5;
+ ok(!element.validity.tooShort,
+ "Element should not be too short unless the user edits it");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.focus();
+
+ sendString("o");
+ is(element.value, "fooo", "value should have changed");
+ ok(element.validity.tooShort,
+ "Element should be too short after a user edit that does not make it short enough");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ ok(!element.validity.valid, "Element should be invalid");
+ ok(!element.checkValidity(), "The element should not be valid");
+ is(element.validationMessage,
+ "Please use at least 5 characters (you are currently using 4 characters).",
+ "The validation message text is not correct");
+
+ sendString("o");
+ is(element.value, "foooo", "value should have changed");
+ ok(!element.validity.tooShort,
+ "Element should not be too short after a user edit makes it long enough");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+
+ element.minLength = 2;
+ ok(!element.validity.tooShort,
+ "Element should remain valid if minlength changes but minlength < length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+
+ element.minLength = 1;
+ ok(!element.validity.tooShort,
+ "Element should remain valid if minlength changes but minlength = length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.minLength = 6;
+ ok(element.validity.tooShort,
+ "Element should become invalid if minlength changes and minlength > length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ ok(!element.validity.valid, "Element should be invalid");
+ ok(!element.checkValidity(), "The element should not be valid");
+ is(element.validationMessage,
+ "Please use at least 6 characters (you are currently using 5 characters).",
+ "The validation message text is not correct");
+
+ element.minLength = 5;
+ ok(!element.validity.tooShort,
+ "Element should become valid if minlength changes and minlength = length");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.value = "test";
+ ok(!element.validity.tooShort,
+ "Element should stay valid after programmatic edit (even if value is too short)");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "The element should be valid");
+
+ element.setCustomValidity("custom message");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ is(element.validationMessage, "custom message",
+ "Custom message should be shown instead of too short one");
+}
+
+checkTooShortValidity(document.getElementById('i'));
+checkTooShortValidity(document.getElementById('t'));
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_mozistextfield.html b/dom/html/test/forms/test_mozistextfield.html
new file mode 100644
index 0000000000..3f92a3d05d
--- /dev/null
+++ b/dom/html/test/forms/test_mozistextfield.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=565538
+-->
+<head>
+ <title>Test for Bug 565538</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=565538">Mozilla Bug 565538</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 565538 **/
+
+var gElementTestData = [
+/* element result */
+ ['input', true],
+ ['button', false],
+ ['fieldset', false],
+ ['label', false],
+ ['option', false],
+ ['optgroup', false],
+ ['output', false],
+ ['legend', false],
+ ['select', false],
+ ['textarea', false],
+ ['object', false],
+];
+
+var gInputTestData = [
+/* type result */
+ ['password', true],
+ ['tel', true],
+ ['text', true],
+ ['button', false],
+ ['checkbox', false],
+ ['file', false],
+ ['hidden', false],
+ ['reset', false],
+ ['image', false],
+ ['radio', false],
+ ['submit', false],
+ ['search', true],
+ ['email', true],
+ ['url', true],
+ ['number', false],
+ ['range', false],
+ ['date', false],
+ ['time', false],
+ ['color', false],
+ ['month', false],
+ ['week', false],
+ ['datetime-local', false],
+];
+
+function checkMozIsTextFieldDefined(aElement, aResult)
+{
+ var element = document.createElement(aElement);
+
+ var msg = "mozIsTextField should be "
+ if (aResult) {
+ msg += "defined";
+ } else {
+ msg += "undefined";
+ }
+
+ is('mozIsTextField' in element, aResult, msg);
+}
+
+function checkMozIsTextFieldValue(aInput, aResult)
+{
+ is(aInput.mozIsTextField(false), aResult,
+ "mozIsTextField(false) should return " + aResult);
+
+ if (aInput.type == 'password') {
+ ok(!aInput.mozIsTextField(true),
+ "mozIsTextField(true) should return false for password");
+ } else {
+ is(aInput.mozIsTextField(true), aResult,
+ "mozIsTextField(true) should return " + aResult);
+ }
+}
+
+function checkMozIsTextFieldValueTodo(aInput, aResult)
+{
+ todo_is(aInput.mozIsTextField(false), aResult,
+ "mozIsTextField(false) should return " + aResult);
+ todo_is(aInput.mozIsTextField(true), aResult,
+ "mozIsTextField(true) should return " + aResult);
+}
+
+// Check if the method is defined for the correct elements.
+for (data of gElementTestData) {
+ checkMozIsTextFieldDefined(data[0], data[1]);
+}
+
+// Check if the method returns the correct value.
+var input = document.createElement('input');
+for (data of gInputTestData) {
+ input.type = data[0];
+ checkMozIsTextFieldValue(input, data[1]);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_novalidate_attribute.html b/dom/html/test/forms/test_novalidate_attribute.html
new file mode 100644
index 0000000000..dcea207838
--- /dev/null
+++ b/dom/html/test/forms/test_novalidate_attribute.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=556013
+-->
+<head>
+ <title>Test for Bug 556013</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=556013">Mozilla Bug 556013</a>
+<p id="display"></p>
+<iframe style='width:50px; height: 50px;' name='t'></iframe>
+<div id="content">
+ <form target='t' action='data:text/html,' novalidate>
+ <input id='av' required>
+ <input id='a' type='submit'>
+ </form>
+ <form target='t' action='data:text/html,' novalidate>
+ <input id='bv' type='checkbox' required>
+ <button id='b' type='submit'></button>
+ </form>
+ <form target='t' action='data:text/html,' novalidate>
+ <input id='c' required>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 556013 **/
+
+/**
+ * novalidate should prevent form validation, thus not blocking form submission.
+ *
+ * NOTE: if the MozInvalidForm event doesn't get prevented default, the form
+ * submission will never be blocked and this test might be a false-positive but
+ * that should not be a problem. We will remove the check for MozInvalidForm
+ * event, see bug 587671.
+ */
+document.forms[0].addEventListener("submit", function(aEvent) {
+ ok(true, "novalidate has been correctly used for first form");
+ document.getElementById('b').click();
+}, {once: true});
+
+document.forms[1].addEventListener("submit", function(aEvent) {
+ ok(true, "novalidate has been correctly used for second form");
+ var c = document.getElementById('c');
+ c.focus();
+ synthesizeKey("KEY_Enter");
+}, {once: true});
+
+document.forms[2].addEventListener("submit", function(aEvent) {
+ ok(true, "novalidate has been correctly used for third form");
+ SimpleTest.executeSoon(SimpleTest.finish);
+}, {once: true});
+
+/**
+ * We have to be sure invalid events are not send too.
+ * They should be sent before the submit event so we can just create a test
+ * failure if we got one. All of them should be catched if sent.
+ * At worst, we got random green which isn't harmful.
+ */
+function invalidHandling(aEvent)
+{
+ aEvent.target.removeEventListener("invalid", invalidHandling);
+ ok(false, "invalid event should not be sent");
+}
+
+document.getElementById('av').addEventListener("invalid", invalidHandling);
+document.getElementById('bv').addEventListener("invalid", invalidHandling);
+document.getElementById('c').addEventListener("invalid", invalidHandling);
+
+SimpleTest.waitForExplicitFinish();
+
+// This is going to call all the tests (with a chain reaction).
+SimpleTest.waitForFocus(function() {
+ document.getElementById('a').click();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_option_disabled.html b/dom/html/test/forms/test_option_disabled.html
new file mode 100644
index 0000000000..421e4546be
--- /dev/null
+++ b/dom/html/test/forms/test_option_disabled.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=759666
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for HTMLOptionElement disabled attribute and pseudo-class</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=759666">Mozilla Bug 759666</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLOptionElement disabled attribute and pseudo-class **/
+
+var testCases = [
+ // Static checks.
+ { html: "<option></option>",
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<option disabled></option>",
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<optgroup><option></option></otpgroup>",
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup><option disabled></option></optgroup>",
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<optgroup disabled><option disabled></option></optgroup>",
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<optgroup disabled><option></option></optgroup>",
+ result: { attr: null, idl: false, pseudo: true } },
+ { html: "<optgroup><optgroup disabled><option></option></optgroup></optgroup>",
+ result: { attr: null, idl: false, pseudo: true } },
+ { html: "<optgroup disabled><optgroup><option></option></optgroup></optgroup>",
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
+ result: { attr: "", idl: true, pseudo: true } },
+
+ // Dynamic checks: changing disable value.
+ { html: "<option></option>",
+ modifier(c) { c.querySelector('option').disabled = true; },
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<option disabled></option>",
+ modifier(c) { c.querySelector('option').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup><option></option></otpgroup>",
+ modifier(c) { c.querySelector('optgroup').disabled = true; },
+ result: { attr: null, idl: false, pseudo: true } },
+ { html: "<optgroup><option disabled></option></optgroup>",
+ modifier(c) { c.querySelector('option').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup disabled><option disabled></option></optgroup>",
+ modifier(c) { c.querySelector('optgroup').disabled = false; },
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<optgroup disabled><option disabled></option></optgroup>",
+ modifier(c) { c.querySelector('option').disabled = false; },
+ result: { attr: null, idl: false, pseudo: true } },
+ { html: "<optgroup disabled><option disabled></option></optgroup>",
+ modifier(c) { c.querySelector('optgroup').disabled = c.querySelector('option').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup disabled><option></option></optgroup>",
+ modifier(c) { c.querySelector('optgroup').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup><optgroup disabled><option></option></optgroup></optgroup>",
+ modifier(c) { c.querySelector('optgroup[disabled]').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup disabled><optgroup><option></option></optgroup></optgroup>",
+ modifier(c) { c.querySelector('optgroup[disabled]').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
+ modifier(c) { c.querySelector('optgroup').disabled = false; },
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
+ modifier(c) { c.querySelector('option').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
+ modifier(c) { c.querySelector('option').disabled = c.querySelector('option').disabled = false; },
+ result: { attr: null, idl: false, pseudo: false } },
+
+ // Dynamic checks: moving option element.
+ { html: "<optgroup id='a'><option></option></optgroup><optgroup id='b'></optgroup>",
+ modifier(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
+ result: { attr: null, idl: false, pseudo: false } },
+ { html: "<optgroup id='a'><option disabled></option></optgroup><optgroup id='b'></optgroup>",
+ modifier(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
+ result: { attr: "", idl: true, pseudo: true } },
+ { html: "<optgroup id='a'><option></option></optgroup><optgroup disabled id='b'></optgroup>",
+ modifier(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
+ result: { attr: null, idl: false, pseudo: true } },
+ { html: "<optgroup disabled id='a'><option></option></optgroup><optgroup id='b'></optgroup>",
+ modifier(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
+ result: { attr: null, idl: false, pseudo: false } },
+];
+
+var content = document.getElementById('content');
+
+testCases.forEach(function(testCase) {
+ var result = testCase.result;
+
+ content.innerHTML = testCase.html;
+
+ if (testCase.modifier !== undefined) {
+ testCase.modifier(content);
+ }
+
+ var option = content.querySelector('option');
+ is(option.getAttribute('disabled'), result.attr, "disabled content attribute value should be " + result.attr);
+ is(option.disabled, result.idl, "disabled idl attribute value should be " + result.idl);
+ is(option.matches(":disabled"), result.pseudo, ":disabled state should be " + result.pseudo);
+ is(option.matches(":enabled"), !result.pseudo, ":enabled state should be " + !result.pseudo);
+
+ content.innerHTML = "";
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_option_index_attribute.html b/dom/html/test/forms/test_option_index_attribute.html
new file mode 100644
index 0000000000..f15520e5e6
--- /dev/null
+++ b/dom/html/test/forms/test_option_index_attribute.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+See those bugs:
+https://bugzilla.mozilla.org/show_bug.cgi?id=720385
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for option.index</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=720385">Mozilla Bug 720385</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <datalist>
+ <option></option>
+ <option></option>
+ </datalist>
+ <select>
+ <option></option>
+ <foo>
+ <option></option>
+ <optgroup>
+ <option></option>
+ </optgroup>
+ <option></option>
+ </foo>
+ <option></option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 720385 **/
+
+var initialIndexes = [ 0, 0, 0, 1, 2, 3, 4 ];
+var options = document.getElementsByTagName('option');
+
+is(options.length, initialIndexes.length,
+ "Must have " + initialIndexes.length +" options");
+
+for (var i=0; i<options.length; ++i) {
+ is(options[i].index, initialIndexes[i], "test");
+}
+
+var o = document.createElement('option');
+is(o.index, 0, "option outside of a document have index=0");
+
+document.body.appendChild(o);
+is(o.index, 0, "option outside of a select have index=0");
+
+var datalist = document.getElementsByTagName('datalist')[0];
+
+datalist.appendChild(o);
+is(o.index, 0, "option outside of a select have index=0");
+
+datalist.removeChild(o);
+is(o.index, 0, "option outside of a select have index=0");
+
+var select = document.getElementsByTagName('select')[0];
+
+select.appendChild(o);
+is(o.index, 5, "option inside a select have an index");
+
+select.removeChild(select.options[0]);
+is(o.index, 4, "option inside a select have an index");
+
+select.insertBefore(o, select.options[0]);
+is(o.index, 0, "option inside a select have an index");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_option_text.html b/dom/html/test/forms/test_option_text.html
new file mode 100644
index 0000000000..3afe3e786a
--- /dev/null
+++ b/dom/html/test/forms/test_option_text.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>HTMLOptionElement.text</title>
+<link rel=author title=Ms2ger href="mailto:Ms2ger@gmail.com">
+<link rel=help href="http://www.whatwg.org/html/#dom-option-text">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createElement("font"))
+ .appendChild(document.createTextNode(" font "));
+ assert_equals(option.text, "font");
+}, "option.text should recurse");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElement("script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before after");
+}, "option.text should not recurse into HTML script elements");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before after");
+}, "option.text should not recurse into SVG script elements");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElementNS("http://www.w3.org/1998/Math/MathML", "script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before script after");
+}, "option.text should recurse into MathML script elements");
+test(function() {
+ var option = document.createElement("option");
+ option.appendChild(document.createTextNode(" before "));
+ option.appendChild(document.createElementNS(null, "script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" after "));
+ assert_equals(option.text, "before script after");
+}, "option.text should recurse into null script elements");
+test(function() {
+ var option = document.createElement("option");
+ var span = option.appendChild(document.createElement("span"));
+ span.appendChild(document.createTextNode(" Some "));
+ span.appendChild(document.createElement("script"))
+ .appendChild(document.createTextNode(" script "));
+ option.appendChild(document.createTextNode(" Text "));
+ assert_equals(option.text, "Some Text");
+}, "option.text should work if a child of the option ends with a script");
+</script>
diff --git a/dom/html/test/forms/test_output_element.html b/dom/html/test/forms/test_output_element.html
new file mode 100644
index 0000000000..ab11443d83
--- /dev/null
+++ b/dom/html/test/forms/test_output_element.html
@@ -0,0 +1,182 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=346485
+-->
+<head>
+ <title>Test for Bug 346485</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ frameLoaded = function() {
+ is(frames.submit_frame.location.href, "about:blank",
+ "Blank frame loaded");
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=346485">Mozilla Bug 346485</a>
+<p id="display"></p>
+<iframe name="submit_frame" onload="frameLoaded()" style="visibility: hidden;"></iframe>
+<div id="content" style="display: none">
+ <form id='f' method='get' target='submit_frame' action='foo'>
+ <input name='a' id='a'>
+ <input name='b' id='b'>
+ <output id='o' for='a b' name='output-name'>tulip</output>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 346485 **/
+
+function checkNameAttribute(element)
+{
+ is(element.name, "output-name", "Output name IDL attribute is not correct");
+ is(element.getAttribute('name'), "output-name",
+ "Output name content attribute is not correct");
+}
+
+function checkValueAndDefaultValueIDLAttribute(element)
+{
+ is(element.value, element.textContent,
+ "The value IDL attribute should act like the textContent IDL attribute");
+
+ element.value = "foo";
+ is(element.value, "foo", "Value should be 'foo'");
+
+ is(element.defaultValue, "tulip", "Default defaultValue is 'tulip'");
+
+ element.defaultValue = "bar";
+ is(element.defaultValue, "bar", "defaultValue should be 'bar'");
+
+ // More complex situation.
+ element.textContent = 'foo';
+ var b = document.createElement('b');
+ b.textContent = 'bar'
+ element.appendChild(b);
+ is(element.value, element.textContent,
+ "The value IDL attribute should act like the textContent IDL attribute");
+}
+
+function checkValueModeFlag(element)
+{
+ /**
+ * The value mode flag is the flag used to know if value should represent the
+ * textContent or the default value.
+ */
+ // value mode flag should be 'value'
+ isnot(element.defaultValue, element.value,
+ "When value is set, defaultValue keeps its value");
+
+ var f = document.getElementById('f');
+ f.reset();
+ // value mode flag should be 'default'
+ is(element.defaultValue, element.value, "When reset, defaultValue=value");
+ is(element.textContent, element.defaultValue,
+ "textContent should contain the defaultValue");
+}
+
+function checkDescendantChanged(element)
+{
+ /**
+ * Whenever a descendant is changed if the value mode flag is value,
+ * the default value should be the textContent value.
+ */
+ element.defaultValue = 'tulip';
+ element.value = 'foo';
+
+ // set value mode flag to 'default'
+ var f = document.getElementById('f');
+ f.reset();
+
+ is(element.textContent, element.defaultValue,
+ "textContent should contain the defaultValue");
+ element.textContent = "bar";
+ is(element.textContent, element.defaultValue,
+ "textContent should contain the defaultValue");
+}
+
+function checkFormIDLAttribute(element)
+{
+ is(element.form, document.getElementById('f'),
+ "form IDL attribute is invalid");
+}
+
+function checkHtmlForIDLAttribute(element)
+{
+ is(String(element.htmlFor), 'a b',
+ "htmlFor IDL attribute should reflect the for content attribute");
+
+ // DOMTokenList is tested in another bug so we just test assignation
+ element.htmlFor.value = 'a b c';
+ is(String(element.htmlFor), 'a b c', "htmlFor should have changed");
+}
+
+function submitForm()
+{
+ // Setting the values for the submit.
+ document.getElementById('o').value = 'foo';
+ document.getElementById('a').value = 'afield';
+ document.getElementById('b').value = 'bfield';
+
+ frameLoaded = checkFormSubmission;
+
+ // This will call checkFormSubmission() which is going to call ST.finish().
+ document.getElementById('f').submit();
+}
+
+function checkFormSubmission()
+{
+ /**
+ * All elements values have been set just before the submission.
+ * The input elements values should be in the submit url but the ouput
+ * element value should not appear.
+ */
+
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/forms/foo?a=afield&b=bfield`,
+ "The output element value should not be submitted");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ reflectString({
+ element: document.createElement("output"),
+ attribute: "name",
+ });
+
+ var o = document.getElementsByTagName('output');
+ is(o.length, 1, "There should be one output element");
+
+ o = o[0];
+ ok(o instanceof HTMLOutputElement,
+ "The output should be instance of HTMLOutputElement");
+
+ o = document.getElementById('o');
+ ok(o instanceof HTMLOutputElement,
+ "The output should be instance of HTMLOutputElement");
+
+ is(o.type, "output", "Output type IDL attribute should be 'output'");
+
+ checkNameAttribute(o);
+
+ checkValueAndDefaultValueIDLAttribute(o);
+
+ checkValueModeFlag(o);
+
+ checkDescendantChanged(o);
+
+ checkFormIDLAttribute(o);
+
+ checkHtmlForIDLAttribute(o);
+
+ submitForm();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_pattern_attribute.html b/dom/html/test/forms/test_pattern_attribute.html
new file mode 100644
index 0000000000..71d79c1def
--- /dev/null
+++ b/dom/html/test/forms/test_pattern_attribute.html
@@ -0,0 +1,324 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=345512
+-->
+<head>
+ <title>Test for Bug 345512</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input { background-color: rgb(0,0,0) !important; }
+ input:valid { background-color: rgb(0,255,0) !important; }
+ input:invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345512">Mozilla Bug 345512</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input id='i' pattern="tulip" oninvalid="invalidEventHandler(event);">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 345512 **/
+
+var gInvalid = false;
+
+function invalidEventHandler(e)
+{
+ is(e.type, "invalid", "Invalid event type should be invalid");
+ gInvalid = true;
+}
+
+function completeValidityCheck(element, alwaysValid, isBarred)
+{
+ // Check when pattern matches.
+ if (element.type == 'email') {
+ element.pattern = ".*@bar.com";
+ element.value = "foo@bar.com";
+ } else if (element.type == 'url') {
+ element.pattern = "http://.*\\.com$";
+ element.value = "http://mozilla.com";
+ } else if (element.type == 'file') {
+ element.pattern = "foo";
+ SpecialPowers.wrap(element).mozSetFileArray([new File(["foo"], "foo")]);
+ } else {
+ element.pattern = "foo";
+ element.value = "foo";
+ }
+
+ checkValidPattern(element, true, isBarred);
+
+ // Check when pattern does not match.
+
+ if (element.type == 'email') {
+ element.pattern = ".*@bar.com";
+ element.value = "foo@foo.com";
+ } else if (element.type == 'url') {
+ element.pattern = "http://.*\\.com$";
+ element.value = "http://mozilla.org";
+ } else if (element.type == 'file') {
+ element.pattern = "foo";
+ SpecialPowers.wrap(element).mozSetFileArray([new File(["bar"], "bar")]);
+ } else {
+ element.pattern = "foo";
+ element.value = "bar";
+ }
+
+ if (!alwaysValid) {
+ checkInvalidPattern(element, true);
+ } else {
+ checkValidPattern(element, true, isBarred);
+ }
+}
+
+function checkValidPattern(element, completeCheck, isBarred)
+{
+ if (completeCheck) {
+ gInvalid = false;
+
+ ok(!element.validity.patternMismatch,
+ "Element should not suffer from pattern mismatch");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "Element should be valid");
+ ok(!gInvalid, "Invalid event shouldn't have been thrown");
+ is(element.validationMessage, '',
+ "Validation message should be the empty string");
+ if (element.type != 'radio' && element.type != 'checkbox') {
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ isBarred ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
+ "The pseudo-class is not correctly applied");
+ }
+ } else {
+ ok(!element.validity.patternMismatch,
+ "Element should not suffer from pattern mismatch");
+ }
+}
+
+function checkInvalidPattern(element, completeCheck)
+{
+ if (completeCheck) {
+ gInvalid = false;
+
+ ok(element.validity.patternMismatch,
+ "Element should suffer from pattern mismatch");
+ ok(!element.validity.valid, "Element should not be valid");
+ ok(!element.checkValidity(), "Element should not be valid");
+ ok(gInvalid, "Invalid event should have been thrown");
+ is(element.validationMessage,
+ "Please match the requested format.",
+ "Validation message is not valid");
+ } else {
+ ok(element.validity.patternMismatch,
+ "Element should suffer from pattern mismatch");
+ }
+
+ if (element.type != 'radio' && element.type != 'checkbox') {
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ }
+}
+
+function checkSyntaxError(element)
+{
+ ok(!element.validity.patternMismatch,
+ "On SyntaxError, element should not suffer");
+}
+
+function checkPatternValidity(element)
+{
+ element.pattern = "foo";
+
+ element.value = '';
+ checkValidPattern(element);
+
+ element.value = "foo";
+ checkValidPattern(element);
+
+ element.value = "bar";
+ checkInvalidPattern(element);
+
+ element.value = "foobar";
+ checkInvalidPattern(element);
+
+ element.value = "foofoo";
+ checkInvalidPattern(element);
+
+ element.pattern = "foo\"bar";
+ element.value = "foo\"bar";
+ checkValidPattern(element);
+
+ element.value = 'foo"bar';
+ checkValidPattern(element);
+
+ element.pattern = "foo'bar";
+ element.value = "foo\'bar";
+ checkValidPattern(element);
+
+ element.pattern = "foo\\(bar";
+ element.value = "foo(bar";
+ checkValidPattern(element);
+
+ element.value = "foo";
+ checkInvalidPattern(element);
+
+ element.pattern = "foo\\)bar";
+ element.value = "foo)bar";
+ checkValidPattern(element);
+
+ element.value = "foo";
+ checkInvalidPattern(element);
+
+ // Check for 'i' flag disabled. Should be case sensitive.
+ element.value = "Foo";
+ checkInvalidPattern(element);
+
+ // We can't check for the 'g' flag because we only test, we don't execute.
+ // We can't check for the 'm' flag because .value shouldn't contain line breaks.
+
+ // We need '\\\\' because '\\' will produce '\\' and we want to escape the '\'
+ // for the regexp.
+ element.pattern = "foo\\\\bar";
+ element.value = "foo\\bar";
+ checkValidPattern(element);
+
+ // We may want to escape the ' in the pattern, but this is a SyntaxError
+ // when unicode flag is set.
+ element.pattern = "foo\\'bar";
+ element.value = "foo'bar";
+ checkSyntaxError(element);
+ element.value = "baz";
+ checkSyntaxError(element);
+
+ // We should check the pattern attribute do not pollute |RegExp.lastParen|.
+ is(RegExp.lastParen, "", "RegExp.lastParen should be the empty string");
+
+ element.pattern = "(foo)";
+ element.value = "foo";
+ checkValidPattern(element);
+ is(RegExp.lastParen, "", "RegExp.lastParen should be the empty string");
+
+ // That may sound weird but the empty string is a valid pattern value.
+ element.pattern = "";
+ element.value = "";
+ checkValidPattern(element);
+
+ element.value = "foo";
+ checkInvalidPattern(element);
+
+ // Checking some complex patterns. As we are using js regexp mechanism, these
+ // tests doesn't aim to test the regexp mechanism.
+ element.pattern = "\\d{2}\\s\\d{2}\\s\\d{4}"
+ element.value = "01 01 2010"
+ checkValidPattern(element);
+
+ element.value = "01/01/2010"
+ checkInvalidPattern(element);
+
+ element.pattern = "[0-9a-zA-Z]([\\-.\\w]*[0-9a-zA-Z_+])*@([0-9a-zA-Z][\\-\\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9}";
+ element.value = "foo@bar.com";
+ checkValidPattern(element);
+
+ element.value = "...@bar.com";
+ checkInvalidPattern(element);
+
+ element.pattern = "^(?:\\w{3,})$";
+ element.value = "foo";
+ checkValidPattern(element);
+
+ element.value = "f";
+ checkInvalidPattern(element);
+
+ // If @title is specified, it should be added in the validation message.
+ if (element.type == 'email') {
+ element.pattern = "foo@bar.com"
+ element.value = "bar@foo.com";
+ } else if (element.type == 'url') {
+ element.pattern = "http://mozilla.com";
+ element.value = "http://mozilla.org";
+ } else {
+ element.pattern = "foo";
+ element.value = "bar";
+ }
+ element.title = "this is an explanation of the regexp";
+ is(element.validationMessage,
+ "Please match the requested format: " + element.title + ".",
+ "Validation message is not valid");
+ element.title = "";
+ is(element.validationMessage,
+ "Please match the requested format.",
+ "Validation message is not valid");
+
+ element.pattern = "foo";
+ if (element.type == 'email') {
+ element.value = "bar@foo.com";
+ } else if (element.type == 'url') {
+ element.value = "http://mozilla.org";
+ } else {
+ element.value = "bar";
+ }
+ checkInvalidPattern(element);
+
+ element.removeAttribute('pattern');
+ checkValidPattern(element, true);
+
+ // Unicode pattern
+ for (var pattern of ["\\u{1F438}{2}", "\u{1F438}{2}",
+ "\\uD83D\\uDC38{2}", "\uD83D\uDC38{2}",
+ "\u{D83D}\u{DC38}{2}"]) {
+ element.pattern = pattern;
+
+ element.value = "\u{1F438}\u{1F438}";
+ checkValidPattern(element);
+
+ element.value = "\uD83D\uDC38\uD83D\uDC38";
+ checkValidPattern(element);
+
+ element.value = "\uD83D\uDC38\uDC38";
+ checkInvalidPattern(element);
+ }
+
+ element.pattern = "\\u{D83D}\\u{DC38}{2}";
+
+ element.value = "\u{1F438}\u{1F438}";
+ checkInvalidPattern(element);
+
+ element.value = "\uD83D\uDC38\uD83D\uDC38";
+ checkInvalidPattern(element);
+
+ element.value = "\uD83D\uDC38\uDC38";
+ checkInvalidPattern(element);
+}
+
+var input = document.getElementById('i');
+
+// |validTypes| are the types which accept @pattern
+// and |invalidTypes| are the ones which do not accept it.
+var validTypes = Array('text', 'password', 'search', 'tel', 'email', 'url');
+var barredTypes = Array('hidden', 'reset', 'button');
+var invalidTypes = Array('checkbox', 'radio', 'file', 'number', 'range', 'date',
+ 'time', 'color', 'submit', 'image', 'month', 'week',
+ 'datetime-local');
+
+for (type of validTypes) {
+ input.type = type;
+ completeValidityCheck(input, false);
+ checkPatternValidity(input);
+}
+
+for (type of barredTypes) {
+ input.type = type;
+ completeValidityCheck(input, true, true);
+}
+
+for (type of invalidTypes) {
+ input.type = type;
+ completeValidityCheck(input, true);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_preserving_metadata_between_reloads.html b/dom/html/test/forms/test_preserving_metadata_between_reloads.html
new file mode 100644
index 0000000000..07ca05f7ce
--- /dev/null
+++ b/dom/html/test/forms/test_preserving_metadata_between_reloads.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test preserving metadata between page reloads</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+ </head>
+<body>
+<p id="display"></p>
+<div id="content">
+ <iframe id="test-frame" width="800px" height="600px" srcdoc='
+ <html>
+ <body>
+ <h3>Bug 1635224: Preserve mLastValueChangeWasInteractive between reloads</h3>
+ <div>
+ <form>
+ <textarea id="maxlen-textarea" maxlength="2" rows="2" cols="10"></textarea><br/>
+ <input id="maxlen-inputtext" type="text" maxlength="2"><br/>
+ <textarea id="minlen-textarea" minlength="8" rows="2" cols="10"></textarea><br/>
+ <input id="minlen-inputtext" type="text" minlength="8"><br/>
+ </form>
+ </div>
+ </body>
+ </html>
+'></iframe>
+</div>
+
+<pre id="test">
+<script>
+ SimpleTest.waitForExplicitFinish()
+ const Ci = SpecialPowers.Ci;
+ const str = "aaaaa";
+
+ function afterLoad() {
+ SimpleTest.waitForFocus(async function () {
+ await SpecialPowers.pushPrefEnv({"set": [["editor.truncate_user_pastes", false]]});
+ var iframeDoc = $("test-frame").contentDocument;
+ var src = iframeDoc.getElementById("src");
+
+ function test(fieldId, callback) {
+ var field = iframeDoc.getElementById(fieldId);
+ field.focus();
+ SimpleTest.waitForClipboard(str,
+ function () {
+ SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(str);
+ },
+ function () {
+ synthesizeKey("v", { accelKey: true });
+ is(field.value, "aaaaa", "the value of " + fieldId + " was entered correctly");
+ is(field.checkValidity(), false, "the validity of " + fieldId + " should be false");
+ $("test-frame").contentWindow.location.reload();
+ is(field.value, "aaaaa", "the value of " + fieldId + " persisted correctly");
+ is(field.checkValidity(), false, "the validity of " + fieldId + " should be false after reload");
+ callback();
+ },
+ function () {
+ ok(false, "Failed to copy the string");
+ SimpleTest.finish();
+ }
+ );
+ }
+
+ function runNextTest() {
+ if (fieldIds.length) {
+ var currentFieldId = fieldIds.shift();
+ test(currentFieldId, runNextTest);
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ var fieldIds = ["maxlen-textarea", "maxlen-inputtext", "minlen-textarea", "minlen-inputtext"];
+ runNextTest();
+ });
+ }
+ addLoadEvent(afterLoad);
+</script>
+</pre>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/html/test/forms/test_progress_element.html b/dom/html/test/forms/test_progress_element.html
new file mode 100644
index 0000000000..065adf94ea
--- /dev/null
+++ b/dom/html/test/forms/test_progress_element.html
@@ -0,0 +1,307 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514437
+https://bugzilla.mozilla.org/show_bug.cgi?id=633913
+-->
+<head>
+ <title>Test for progress element content and layout</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=514437">Mozilla Bug 514437</a>
+and
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633913">Mozilla Bug 633913</a>
+<p id="display"></p>
+<iframe name="submit_frame" style="visibility: hidden;"></iframe>
+<div id="content" style="visibility: hidden;">
+ <form id='f' method='get' target='submit_frame' action='foo'>
+ <progress id='p'></progress>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.expectAssertions(0, 1);
+
+/** Test for progress element content and layout **/
+
+function checkFormIDLAttribute(aElement)
+{
+ is("form" in aElement, false, "<progress> shouldn't have a form attribute");
+}
+
+function checkAttribute(aElement, aAttribute, aNewValue, aExpectedValueForIDL)
+{
+ var expectedValueForIDL = aNewValue;
+ var expectedValueForContent = String(aNewValue);
+
+ if (aExpectedValueForIDL !== undefined) {
+ expectedValueForIDL = aExpectedValueForIDL;
+ }
+
+ if (aNewValue != null) {
+ aElement.setAttribute(aAttribute, aNewValue);
+ is(aElement.getAttribute(aAttribute), expectedValueForContent,
+ aAttribute + " content attribute should be " + expectedValueForContent);
+ is(aElement[aAttribute], expectedValueForIDL,
+ aAttribute + " IDL attribute should be " + expectedValueForIDL);
+
+ if (parseFloat(aNewValue) == aNewValue) {
+ aElement[aAttribute] = aNewValue;
+ is(aElement.getAttribute(aAttribute), expectedValueForContent,
+ aAttribute + " content attribute should be " + expectedValueForContent);
+ is(aElement[aAttribute], parseFloat(expectedValueForIDL),
+ aAttribute + " IDL attribute should be " + parseFloat(expectedValueForIDL));
+ }
+ } else {
+ aElement.removeAttribute(aAttribute);
+ is(aElement.getAttribute(aAttribute), null,
+ aAttribute + " content attribute should be null");
+ is(aElement[aAttribute], expectedValueForIDL,
+ aAttribute + " IDL attribute should be " + expectedValueForIDL);
+ }
+}
+
+function checkValueAttribute()
+{
+ var tests = [
+ // value has to be a valid float, its default value is 0.0 otherwise.
+ [ null, 0.0 ],
+ [ 'fo', 0.0 ],
+ // If value < 0.0, 0.0 is used instead.
+ [ -1.0, 0.0 ],
+ // If value >= max, max is used instead (max default value is 1.0).
+ [ 2.0, 1.0 ],
+ [ 1.0, 0.5, 0.5 ],
+ [ 10.0, 5.0, 5.0 ],
+ [ 13.37, 13.37, 42.0 ],
+ // Regular reflection.
+ [ 0.0 ],
+ [ 0.5 ],
+ [ 1.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('progress');
+
+ for (var test of tests) {
+ if (test[2]) {
+ element.setAttribute('max', test[2]);
+ }
+
+ checkAttribute(element, 'value', test[0], test[1]);
+
+ element.removeAttribute('max');
+ }
+}
+
+function checkMaxAttribute()
+{
+ var tests = [
+ // max default value is 1.0.
+ [ null, 1.0 ],
+ // If value <= 0.0, 1.0 is used instead.
+ [ 0.0, 1.0 ],
+ [ -1.0, 1.0 ],
+ // Regular reflection.
+ [ 0.5 ],
+ [ 1.0 ],
+ [ 2.0 ],
+ // Check double-precision value.
+ [ 0.234567898765432 ],
+ ];
+
+ var element = document.createElement('progress');
+
+ for (var test of tests) {
+ checkAttribute(element, 'max', test[0], test[1]);
+ }
+}
+
+function checkPositionAttribute()
+{
+ function checkPositionValue(aElement, aValue, aMax, aExpected) {
+ if (aValue != null) {
+ aElement.setAttribute('value', aValue);
+ } else {
+ aElement.removeAttribute('value');
+ }
+
+ if (aMax != null) {
+ aElement.setAttribute('max', aMax);
+ } else {
+ aElement.removeAttribute('max');
+ }
+
+ is(aElement.position, aExpected, "position IDL attribute should be " + aExpected);
+ }
+
+ var tests = [
+ // value has to be defined (indeterminate state).
+ [ null, null, -1.0 ],
+ [ null, 1.0, -1.0 ],
+ // value has to be defined to a valid float (indeterminate state).
+ [ 'foo', 1.0, -1.0 ],
+ // If value < 0.0, 0.0 is used instead.
+ [ -1.0, 1.0, 0.0 ],
+ // If value >= max, max is used instead.
+ [ 2.0, 1.0, 1.0 ],
+ // If max isn't present, max is set to 1.0.
+ [ 1.0, null, 1.0 ],
+ // If max isn't a valid float, max is set to 1.0.
+ [ 1.0, 'foo', 1.0 ],
+ // If max isn't > 0, max is set to 1.0.
+ [ 1.0, -1.0, 1.0 ],
+ // A few simple and valid values.
+ [ 0.0, 1.0, 0.0 ],
+ [ 0.1, 1.0, 0.1/1.0 ],
+ [ 0.1, 2.0, 0.1/2.0 ],
+ [ 10, 50, 10/50 ],
+ // Values implying .position is a double.
+ [ 1.0, 3.0, 1.0/3.0 ],
+ [ 0.1, 0.7, 0.1/0.7 ],
+ ];
+
+ var element = document.createElement('progress');
+
+ for (var test of tests) {
+ checkPositionValue(element, test[0], test[1], test[2], test[3]);
+ }
+}
+
+function checkIndeterminatePseudoClass()
+{
+ function checkIndeterminate(aElement, aValue, aMax, aIndeterminate) {
+ if (aValue != null) {
+ aElement.setAttribute('value', aValue);
+ } else {
+ aElement.removeAttribute('value');
+ }
+
+ if (aMax != null) {
+ aElement.setAttribute('max', aMax);
+ } else {
+ aElement.removeAttribute('max');
+ }
+
+ is(aElement.matches("progress:indeterminate"), aIndeterminate,
+ "<progress> indeterminate state should be " + aIndeterminate);
+ }
+
+ var tests = [
+ // Indeterminate state: (value is undefined, or not a float)
+ // value has to be defined (indeterminate state).
+ [ null, null, true ],
+ [ null, 1.0, true ],
+ [ 'foo', 1.0, true ],
+ // Determined state:
+ [ -1.0, 1.0, false ],
+ [ 2.0, 1.0, false ],
+ [ 1.0, null, false ],
+ [ 1.0, 'foo', false ],
+ [ 1.0, -1.0, false ],
+ [ 0.0, 1.0, false ],
+ ];
+
+ var element = document.createElement('progress');
+
+ for (var test of tests) {
+ checkIndeterminate(element, test[0], test[1], test[2]);
+ }
+}
+
+function checkFormListedElement(aElement)
+{
+ is(document.forms[0].elements.length, 0, "the form should have no element");
+}
+
+function checkLabelable(aElement)
+{
+ var content = document.getElementById('content');
+ var label = document.createElement('label');
+
+ content.appendChild(label);
+ label.appendChild(aElement);
+ is(label.control, aElement, "progress should be labelable");
+
+ // Cleaning-up.
+ content.removeChild(label);
+ content.appendChild(aElement);
+}
+
+function checkNotResetableAndFormSubmission(aElement)
+{
+ // Creating an input element to check the submission worked.
+ var form = document.forms[0];
+ var input = document.createElement('input');
+
+ input.name = 'a';
+ input.value = 'tulip';
+ form.appendChild(input);
+
+ // Setting values.
+ aElement.value = 42.0;
+ aElement.max = 100.0;
+
+ document.getElementsByName('submit_frame')[0].addEventListener("load", function() {
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/forms/foo?a=tulip`,
+ "The progress element value should not be submitted");
+
+ checkNotResetable();
+ }, {once: true});
+
+ form.submit();
+}
+
+function checkNotResetable()
+{
+ // Try to reset the form.
+ var form = document.forms[0];
+ var element = document.getElementById('p');
+
+ element.value = 3.0;
+ element.max = 42.0;
+
+ form.reset();
+
+ SimpleTest.executeSoon(function() {
+ is(element.value, 3.0, "progress.value should not have changed");
+ is(element.max, 42.0, "progress.max should not have changed");
+
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+var p = document.getElementById('p');
+
+ok(p instanceof HTMLProgressElement,
+ "The progress element should be instance of HTMLProgressElement");
+is(p.constructor, HTMLProgressElement,
+ "The progress element constructor should be HTMLProgressElement");
+
+checkFormIDLAttribute(p);
+
+checkValueAttribute();
+
+checkMaxAttribute();
+
+checkPositionAttribute();
+
+checkIndeterminatePseudoClass();
+
+checkFormListedElement(p);
+
+checkLabelable(p);
+
+checkNotResetableAndFormSubmission(p);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_radio_in_label.html b/dom/html/test/forms/test_radio_in_label.html
new file mode 100644
index 0000000000..7e8a232cc3
--- /dev/null
+++ b/dom/html/test/forms/test_radio_in_label.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=229925
+-->
+<head>
+ <title>Test for Bug 229925</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=229925">Mozilla Bug 229925</a>
+<p id="display"></p>
+<form>
+ <label>
+ <span id="s1">LABEL</span>
+ <input type="radio" name="rdo" value="1" id="r1" onmousedown="document.body.appendChild(document.createTextNode('down'));">
+ <input type="radio" name="rdo" value="2" id="r2" checked="checked">
+ </label>
+</form>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 229925 **/
+SimpleTest.waitForExplicitFinish();
+var r1 = document.getElementById("r1");
+var r2 = document.getElementById("r2");
+var s1 = document.getElementById("s1");
+startTest();
+function startTest() {
+ r1.click();
+ ok(r1.checked,
+ "The first radio input element should be checked by clicking the element");
+ r2.click();
+ ok(r2.checked,
+ "The second radio input element should be checked by clicking the element");
+ s1.click();
+ ok(r1.checked,
+ "The first radio input element should be checked by clicking other element");
+
+ r1.focus();
+ synthesizeKey("KEY_ArrowLeft");
+ ok(r2.checked,
+ "The second radio input element should be checked by key");
+ synthesizeKey("KEY_ArrowLeft");
+ ok(r1.checked,
+ "The first radio input element should be checked by key");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_radio_radionodelist.html b/dom/html/test/forms/test_radio_radionodelist.html
new file mode 100644
index 0000000000..8761c22b58
--- /dev/null
+++ b/dom/html/test/forms/test_radio_radionodelist.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=779723
+-->
+<head>
+ <title>Test for Bug 779723</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=779723">Mozilla Bug 779723</a>
+<p id="display"></p>
+<form>
+ <input type="checkbox" name="rdo" value="0" id="r0" checked="checked">
+ <input type="radio" name="rdo" id="r1">
+ <input type="radio" name="rdo" id="r2" value="2">
+</form>
+<script class="testbody" type="text/javascript">
+/** Test for Bug 779723 **/
+
+var rdoList = document.forms[0].elements.namedItem('rdo');
+is(rdoList.value, "", "The value attribute should be empty");
+
+document.getElementById('r2').checked = true;
+is(rdoList.value, "2", "The value attribute should be 2");
+
+document.getElementById('r1').checked = true;
+is(rdoList.value, "on", "The value attribute should be on");
+
+document.getElementById('r1').value = 1;
+is(rdoList.value, "1", "The value attribute should be 1");
+
+is(rdoList.value, document.getElementById('r1').value,
+ "The value attribute should be equal to the first checked radio input element's value");
+ok(!document.getElementById('r2').checked,
+ "The second radio input element should not be checked");
+
+rdoList.value = '2';
+is(rdoList.value, document.getElementById('r2').value,
+ "The value attribute should be equal to the second radio input element's value");
+ok(document.getElementById('r2').checked,
+ "The second radio input element should be checked");
+
+rdoList.value = '3';
+is(rdoList.value, document.getElementById('r2').value,
+ "The value attribute should be the second radio input element's value");
+ok(document.getElementById('r2').checked,
+ "The second radio input element should be checked");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_reportValidation_preventDefault.html b/dom/html/test/forms/test_reportValidation_preventDefault.html
new file mode 100644
index 0000000000..3f3b99d140
--- /dev/null
+++ b/dom/html/test/forms/test_reportValidation_preventDefault.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1088761
+-->
+<head>
+ <title>Test for Bug 1088761</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input, textarea, fieldset, button, select, output, object { background-color: rgb(0,0,0) !important; }
+ :valid { background-color: rgb(0,255,0) !important; }
+ :invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1088761">Mozilla Bug 1088761</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <fieldset id='f' oninvalid="invalidEventHandler(event, true);"></fieldset>
+ <input id='i' required oninvalid="invalidEventHandler(event, true);">
+ <button id='b' oninvalid="invalidEventHandler(event, true);"></button>
+ <select id='s' required oninvalid="invalidEventHandler(event, true);"></select>
+ <textarea id='t' required oninvalid="invalidEventHandler(event, true);"></textarea>
+ <output id='o' oninvalid="invalidEventHandler(event, true);"></output>
+ <object id='obj' oninvalid="invalidEventHandler(event, true);"></object>
+</div>
+<div id="content2" style="display: none">
+ <fieldset id='f2' oninvalid="invalidEventHandler(event, false);"></fieldset>
+ <input id='i2' required oninvalid="invalidEventHandler(event, false);">
+ <button id='b2' oninvalid="invalidEventHandler(event, false);"></button>
+ <select id='s2' required oninvalid="invalidEventHandler(event, false);"></select>
+ <textarea id='t2' required oninvalid="invalidEventHandler(event, false);"></textarea>
+ <output id='o2' oninvalid="invalidEventHandler(event, false);"></output>
+ <object id='obj2' oninvalid="invalidEventHandler(event, false);"></object>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1088761 **/
+
+var gInvalid = false;
+
+function invalidEventHandler(aEvent, isPreventDefault)
+{
+ if (isPreventDefault) {
+ aEvent.preventDefault();
+ }
+
+ is(aEvent.type, "invalid", "Invalid event type should be invalid");
+ ok(!aEvent.bubbles, "Invalid event should not bubble");
+ ok(aEvent.cancelable, "Invalid event should be cancelable");
+ gInvalid = true;
+}
+
+function checkReportValidityForInvalid(element)
+{
+ gInvalid = false;
+ ok(!element.reportValidity(), "reportValidity() should return false when the element is not valid");
+ ok(gInvalid, "Invalid event should have been handled");
+}
+
+function checkReportValidityForValid(element)
+{
+ gInvalid = false;
+ ok(element.reportValidity(), "reportValidity() should return true when the element is valid");
+ ok(!gInvalid, "Invalid event shouldn't have been handled");
+}
+
+checkReportValidityForInvalid(document.getElementById('i'));
+checkReportValidityForInvalid(document.getElementById('s'));
+checkReportValidityForInvalid(document.getElementById('t'));
+
+checkReportValidityForInvalid(document.getElementById('i2'));
+checkReportValidityForInvalid(document.getElementById('s2'));
+checkReportValidityForInvalid(document.getElementById('t2'));
+
+checkReportValidityForValid(document.getElementById('o'));
+checkReportValidityForValid(document.getElementById('obj'));
+checkReportValidityForValid(document.getElementById('f'));
+
+checkReportValidityForValid(document.getElementById('o2'));
+checkReportValidityForValid(document.getElementById('obj2'));
+checkReportValidityForValid(document.getElementById('f2'));
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_required_attribute.html b/dom/html/test/forms/test_required_attribute.html
new file mode 100644
index 0000000000..a95a5cc339
--- /dev/null
+++ b/dom/html/test/forms/test_required_attribute.html
@@ -0,0 +1,416 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=345822
+-->
+<head>
+ <title>Test for Bug 345822</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345822">Mozilla Bug 345822</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 345822 **/
+
+function checkNotSufferingFromBeingMissing(element, doNotApply)
+{
+ ok(!element.validity.valueMissing,
+ "Element should not suffer from value missing");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "Element should be valid");
+ is(element.validationMessage, "",
+ "Validation message should be the empty string");
+
+ if (doNotApply) {
+ ok(!element.matches(':valid'), ":valid should not apply");
+ ok(!element.matches(':invalid'), ":invalid should not apply");
+ } else {
+ ok(element.matches(':valid'), ":valid should apply");
+ ok(!element.matches(':invalid'), ":invalid should not apply");
+ }
+}
+
+function checkSufferingFromBeingMissing(element)
+{
+ ok(element.validity.valueMissing, "Element should suffer from value missing");
+ ok(!element.validity.valid, "Element should not be valid");
+ ok(!element.checkValidity(), "Element should not be valid");
+
+ if (element.type == 'checkbox')
+ {
+ is(element.validationMessage,
+ "Please check this box if you want to proceed.",
+ "Validation message is wrong");
+ }
+ else if (element.type == 'radio')
+ {
+ is(element.validationMessage,
+ "Please select one of these options.",
+ "Validation message is wrong");
+ }
+ else if (element.type == 'file')
+ {
+ is(element.validationMessage,
+ "Please select a file.",
+ "Validation message is wrong");
+ }
+ else if (element.type == 'number')
+ {
+ is(element.validationMessage,
+ "Please enter a number.",
+ "Validation message is wrong");
+ }
+ else // text fields
+ {
+ is(element.validationMessage,
+ "Please fill out this field.",
+ "Validation message is wrong");
+ }
+
+ ok(!element.matches(':valid'), ":valid should apply");
+ ok(element.matches(':invalid'), ":invalid should not apply");
+}
+
+function checkTextareaRequiredValidity()
+{
+ var element = document.createElement('textarea');
+ document.forms[0].appendChild(element);
+
+ SpecialPowers.wrap(element).value = '';
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.required = true;
+ checkSufferingFromBeingMissing(element);
+
+ element.readOnly = true;
+ checkNotSufferingFromBeingMissing(element, true);
+
+ element.readOnly = false;
+ checkSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).value = 'foo';
+ checkNotSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).value = '';
+ checkSufferingFromBeingMissing(element);
+
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.focus();
+ element.required = true;
+ SpecialPowers.wrap(element).value = 'foobar';
+ element.blur();
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).value = '';
+ element.form.reportValidity();
+ checkSufferingFromBeingMissing(element);
+
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ // TODO: for the moment, a textarea outside of a document is mutable.
+ SpecialPowers.wrap(element).value = ''; // To make -moz-ui-valid apply.
+ element.required = false;
+ document.forms[0].removeChild(element);
+ checkNotSufferingFromBeingMissing(element);
+}
+
+function checkInputRequiredNotApply(type, isBarred)
+{
+ var element = document.createElement('input');
+ element.type = type;
+ document.forms[0].appendChild(element);
+
+ SpecialPowers.wrap(element).value = '';
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element, isBarred);
+
+ element.required = true;
+ checkNotSufferingFromBeingMissing(element, isBarred);
+
+ element.required = false;
+
+ document.forms[0].removeChild(element);
+ checkNotSufferingFromBeingMissing(element, isBarred);
+}
+
+function checkInputRequiredValidity(type)
+{
+ var element = document.createElement('input');
+ element.type = type;
+ document.forms[0].appendChild(element);
+
+ SpecialPowers.wrap(element).value = '';
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.required = true;
+ checkSufferingFromBeingMissing(element);
+
+ element.readOnly = true;
+ checkNotSufferingFromBeingMissing(element, true);
+
+ element.readOnly = false;
+ checkSufferingFromBeingMissing(element);
+
+ if (element.type == 'email') {
+ SpecialPowers.wrap(element).value = 'foo@bar.com';
+ } else if (element.type == 'url') {
+ SpecialPowers.wrap(element).value = 'http://mozilla.org/';
+ } else if (element.type == 'number') {
+ SpecialPowers.wrap(element).value = '42';
+ } else if (element.type == 'date') {
+ SpecialPowers.wrap(element).value = '2010-10-10';
+ } else if (element.type == 'time') {
+ SpecialPowers.wrap(element).value = '21:21';
+ // TODO: Bug 1864327. This test is wrong, and needs fixing properly.
+ // eslint-disable-next-line no-cond-assign
+ } else if (element.type = 'month') {
+ SpecialPowers.wrap(element).value = '2010-10';
+ } else {
+ SpecialPowers.wrap(element).value = 'foo';
+ }
+ checkNotSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).value = '';
+ checkSufferingFromBeingMissing(element);
+
+ element.focus();
+ element.required = true;
+ SpecialPowers.wrap(element).value = 'foobar';
+ element.blur();
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).value = '';
+ element.form.reportValidity();
+ checkSufferingFromBeingMissing(element);
+
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ element.required = true;
+ SpecialPowers.wrap(element).value = ''; // To make :-moz-ui-valid apply.
+ checkSufferingFromBeingMissing(element);
+ document.forms[0].removeChild(element);
+ // Removing the child changes nothing about whether it's valid
+ checkSufferingFromBeingMissing(element);
+}
+
+function checkInputRequiredValidityForCheckbox()
+{
+ var element = document.createElement('input');
+ element.type = 'checkbox';
+ document.forms[0].appendChild(element);
+
+ element.checked = false;
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.required = true;
+ checkSufferingFromBeingMissing(element);
+
+ element.checked = true;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.checked = false;
+ checkSufferingFromBeingMissing(element);
+
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.focus();
+ element.required = true;
+ element.checked = true;
+ element.blur();
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ element.required = true;
+ element.checked = false;
+ element.form.reportValidity();
+ checkSufferingFromBeingMissing(element);
+
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ element.required = true;
+ element.checked = false;
+ document.forms[0].removeChild(element);
+ checkSufferingFromBeingMissing(element);
+}
+
+function checkInputRequiredValidityForRadio()
+{
+ var element = document.createElement('input');
+ element.type = 'radio';
+ element.name = 'test'
+ document.forms[0].appendChild(element);
+
+ element.checked = false;
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.required = true;
+ checkSufferingFromBeingMissing(element);
+
+ element.checked = true;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.checked = false;
+ checkSufferingFromBeingMissing(element);
+
+ // A required radio button should not suffer from value missing if another
+ // radio button from the same group is checked.
+ var element2 = document.createElement('input');
+ element2.type = 'radio';
+ element2.name = 'test';
+
+ element2.checked = true;
+ element2.required = false;
+ document.forms[0].appendChild(element2);
+
+ // Adding a checked radio should make required radio in the group not
+ // suffering from being missing.
+ checkNotSufferingFromBeingMissing(element);
+
+ element.checked = false;
+ element2.checked = false;
+ checkSufferingFromBeingMissing(element);
+
+ // The other radio button should not be disabled.
+ // A disabled checked radio button in the radio group
+ // is enough to not suffer from value missing.
+ element2.checked = true;
+ element2.disabled = true;
+ checkNotSufferingFromBeingMissing(element);
+
+ // If a radio button is not required but another radio button is required in
+ // the same group, the not required radio button should suffer from value
+ // missing.
+ element2.disabled = false;
+ element2.checked = false;
+ element.required = false;
+ element2.required = true;
+ checkSufferingFromBeingMissing(element);
+ checkSufferingFromBeingMissing(element2);
+
+ element.checked = true;
+ checkNotSufferingFromBeingMissing(element2);
+
+ // The checked radio is not in the group anymore, element2 should be invalid.
+ element.form.removeChild(element);
+ checkNotSufferingFromBeingMissing(element);
+ checkSufferingFromBeingMissing(element2);
+
+ element2.focus();
+ element2.required = true;
+ element2.checked = true;
+ element2.blur();
+ element2.form.reset();
+ checkSufferingFromBeingMissing(element2);
+
+ element2.required = true;
+ element2.checked = false;
+ element2.form.reportValidity();
+ checkSufferingFromBeingMissing(element2);
+
+ element2.form.reset();
+ checkSufferingFromBeingMissing(element2);
+
+ element2.required = true;
+ element2.checked = false;
+ document.forms[0].removeChild(element2);
+ checkSufferingFromBeingMissing(element2);
+}
+
+function checkInputRequiredValidityForFile()
+{
+ var element = document.createElement('input');
+ element.type = 'file'
+ document.forms[0].appendChild(element);
+
+ var file = new File([""], "345822_file");
+
+ SpecialPowers.wrap(element).value = "";
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.required = true;
+ checkSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).mozSetFileArray([file]);
+ checkNotSufferingFromBeingMissing(element);
+
+ SpecialPowers.wrap(element).value = "";
+ checkSufferingFromBeingMissing(element);
+
+ element.required = false;
+ checkNotSufferingFromBeingMissing(element);
+
+ element.focus();
+ SpecialPowers.wrap(element).mozSetFileArray([file]);
+ element.required = true;
+ element.blur();
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ element.required = true;
+ SpecialPowers.wrap(element).value = '';
+ element.form.reportValidity();
+ checkSufferingFromBeingMissing(element);
+
+ element.form.reset();
+ checkSufferingFromBeingMissing(element);
+
+ element.required = true;
+ SpecialPowers.wrap(element).value = '';
+ document.forms[0].removeChild(element);
+ checkSufferingFromBeingMissing(element);
+}
+
+checkTextareaRequiredValidity();
+
+// The require attribute behavior depend of the input type.
+// First of all, checks for types that make the element barred from
+// constraint validation.
+var typeBarredFromConstraintValidation = ["hidden", "button", "reset"];
+for (type of typeBarredFromConstraintValidation) {
+ checkInputRequiredNotApply(type, true);
+}
+
+// Then, checks for the types which do not use the required attribute.
+var typeRequireNotApply = ['range', 'color', 'submit', 'image'];
+for (type of typeRequireNotApply) {
+ checkInputRequiredNotApply(type, false);
+}
+
+// Now, checking for all types which accept the required attribute.
+var typeRequireApply = ["text", "password", "search", "tel", "email", "url",
+ "number", "date", "time", "month", "week",
+ "datetime-local"];
+
+for (type of typeRequireApply) {
+ checkInputRequiredValidity(type);
+}
+
+checkInputRequiredValidityForCheckbox();
+checkInputRequiredValidityForRadio();
+checkInputRequiredValidityForFile();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_restore_form_elements.html b/dom/html/test/forms/test_restore_form_elements.html
new file mode 100644
index 0000000000..be22a29b7b
--- /dev/null
+++ b/dom/html/test/forms/test_restore_form_elements.html
@@ -0,0 +1,174 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=737851
+-->
+<head>
+ <meta charset="utf-8">
+
+ <title>Test for Bug 737851</title>
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=737851">Mozilla Bug 737851</a>
+
+<p id="display"></p>
+
+
+<div id="content">
+
+ <iframe id="frame" width="800px" height="600px" srcdoc='
+ <html>
+ <body style="display:none;">
+
+ <h3>Checking persistence of inputs through js inserts and moves</h3>
+ <div id="test">
+ <input id="a"/>
+ <input id="b"/>
+ <form id="form1">
+ <input id="c"/>
+ <input id="d"/>
+ </form>
+ <form id="form2">
+ <input id="radio1" type="radio" name="radio"/>
+ <input type="radio" name="radio"/>
+ <input type="radio" name="radio"/>
+ <input type="radio" name="radio"/>
+ </form>
+ <input id="e"/>
+ </div>
+
+ <h3>Bug 728798: checking persistence of inputs when forward-using @form</h3>
+ <div>
+ <input id="728798-a" form="728798-form" name="a"/>
+ <form id="728798-form">
+ <input id="728798-b" form="728798-form" name="b"/>
+ <input id="728798-c" name="c"/>
+ </form>
+ <input id="728798-d" form="728798-form" name="d"/>
+ </div>
+
+ </body>
+ </html>
+ '></iframe>
+
+</div>
+
+
+<pre id="test">
+<script type="text/javascript">
+
+var frameElem = document.getElementById("frame");
+var frame = frameElem.contentWindow;
+
+
+/* -- Main test run -- */
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ shuffle();
+ fill();
+ frameElem.addEventListener("load", function() {
+ shuffle();
+ checkAllFields();
+ SimpleTest.finish();
+ });
+ frame.location.reload();
+})
+
+
+/* -- Input fields js changes and moves -- */
+
+function shuffle() {
+ var framedoc = frame.document;
+
+ // Insert a button (toplevel)
+ var btn = framedoc.createElement("button");
+ var testdiv = framedoc.getElementById("test");
+ testdiv.insertBefore(btn, framedoc.getElementById("b"));
+
+ // Insert a dynamically generated input (in a form)
+ var newInput = framedoc.createElement("input");
+ newInput.setAttribute("id","c0");
+ var form1 = framedoc.getElementById("form1");
+ form1.insertBefore(newInput, form1.firstChild);
+
+ // Move an input around
+ var inputD = framedoc.getElementById("d");
+ var form2 = framedoc.getElementById("form2");
+ form2.insertBefore(inputD, form2.firstChild)
+
+ // Clone an existing input
+ var inputE2 = framedoc.getElementById("e").cloneNode(true);
+ inputE2.setAttribute("id","e2");
+ testdiv.appendChild(inputE2);
+}
+
+
+/* -- Input fields fill & check -- */
+
+/* Values entered in the input fields (by id) */
+
+var fieldValues = {
+ 'a':'simple input',
+ 'b':'moved by inserting a button before (no form)',
+ 'c0':'dynamically generated input',
+ 'c':'moved by inserting an input before (in a form)',
+ 'd':'moved from a form to another',
+ 'e':'the original',
+ 'e2':'the clone',
+ '728798-a':'before the form',
+ '728798-b':'from within the form',
+ '728798-c':'no form attribute in the form',
+ '728798-d':'after the form'
+}
+
+/* Fields for which the input is changed, and corresponding value
+ (clone and creation, same behaviour as webkit) */
+
+var changedFields = {
+ // dynamically generated input field not preserved
+ 'c0':'',
+ // cloned input field is restored with the value of the original
+ 'e2':fieldValues.e
+}
+
+/* Simulate user input by entering the values */
+
+function fill() {
+ for (id in fieldValues) {
+ frame.document.getElementById(id).value = fieldValues[id];
+ }
+ // an input is inserted before the radios (that may move the selected one by 1)
+ frame.document.getElementById('radio1').checked = true;
+}
+
+/* Check that all the fields are as they have been entered */
+
+function checkAllFields() {
+
+ for (id in fieldValues) {
+ var fieldValue = frame.document.getElementById(id).value;
+ if (changedFields[id] === undefined) {
+ is(fieldValue, fieldValues[id],
+ "Field "+id+" should be restored after reload");
+ } else {
+ is(fieldValue, changedFields[id],
+ "Field "+id+" normally gets a different value after reload");
+ }
+ }
+
+ ok(frame.document.getElementById('radio1').checked,
+ "Radio button radio1 should be restored after reload")
+
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_save_restore_custom_elements.html b/dom/html/test/forms/test_save_restore_custom_elements.html
new file mode 100644
index 0000000000..489ad0ca2f
--- /dev/null
+++ b/dom/html/test/forms/test_save_restore_custom_elements.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1556358
+-->
+
+<head>
+ <title>Test for Bug 1556358</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1556358">Mozilla Bug 1556358</a>
+ <p id="display"></p>
+ <div id="content">
+ <iframe src="save_restore_custom_elements_sample.html"></iframe>
+ </div>
+ <script type="application/javascript">
+ /** Test for Bug 1556358 **/
+
+ function formDataWith(...entries) {
+ const formData = new FormData();
+ for (let [key, value] of entries) {
+ formData.append(key, value);
+ }
+ return formData;
+ }
+
+ const states = [
+ "test state",
+ new File(["state"], "state.txt"),
+ formDataWith(["1", "state"], ["2", new Blob(["state_blob"])]),
+ null,
+ undefined,
+ ];
+ const values = [
+ "test value",
+ new File(["value"], "value.txt"),
+ formDataWith(["1", "value"], ["2", new Blob(["value_blob"])]),
+ "null state",
+ "both value and state",
+ ];
+
+ add_task(async () => {
+ const frame = document.querySelector("iframe");
+ const elementTags = ["c-e", "upgraded-ce"];
+
+ // Set the custom element values.
+ for (const tags of elementTags) {
+ [...frame.contentDocument.querySelectorAll(tags)]
+ .forEach((e, i) => {
+ e.set(states[i], values[i]);
+ });
+ }
+
+ await new Promise(resolve => {
+ frame.addEventListener("load", resolve);
+ frame.contentWindow.location.reload();
+ });
+
+ for (const tag of elementTags) {
+ // Retrieve the restored values.
+ const ceStates =
+ [...frame.contentDocument.querySelectorAll(tag)].map((e) => e.state);
+ is(ceStates.length, 5, "Should have 5 custom element states");
+
+ const [restored, original] = [ceStates, states];
+ is(restored[0], original[0], "Value should be restored");
+
+ const file = restored[1];
+ isnot(file, original[1], "Restored file object differs from original object.");
+ is(file.name, original[1].name, "File name should be restored");
+ is(await file.text(), await original[1].text(), "File text should be restored");
+
+ const formData = restored[2];
+ isnot(formData, original[2], "Restored formdata object differs from original object.");
+ is(formData.get("1"), original[2].get("1"), "Form data string should be restored");
+ is(await formData.get("2").text(), await original[2].get("2").text(), "Form data blob should be restored");
+
+ isnot(restored[3], original[3], "Null values don't get restored");
+ is(restored[3], undefined, "Null values don't get restored");
+
+ is(restored[4], "both value and state", "Undefined state should be set to value");
+ }
+ });
+ </script>
+</body>
+
+</html>
diff --git a/dom/html/test/forms/test_save_restore_radio_groups.html b/dom/html/test/forms/test_save_restore_radio_groups.html
new file mode 100644
index 0000000000..c5ef924a0e
--- /dev/null
+++ b/dom/html/test/forms/test_save_restore_radio_groups.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=350022
+-->
+<head>
+ <title>Test for Bug 350022</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=350022">Mozilla Bug 350022</a>
+<p id="display"></p>
+<div id="content"><!-- style="display: none">-->
+ <iframe src="save_restore_radio_groups.sjs"></iframe>
+ <iframe src="save_restore_radio_groups.sjs"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 350022 **/
+
+function checkRadioGroup(aFrame, aResults)
+{
+ var radios = frames[aFrame].document.getElementsByTagName('input');
+
+ is(radios.length, aResults.length,
+ "Radio group should have " + aResults.length + "elements");
+
+ for (var i=0; i<aResults.length; ++i) {
+ is(radios[i].checked, aResults[i],
+ "Radio checked state should be " + aResults[i]);
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ /**
+ * We have two iframes each containing one radio button group.
+ * We are going to change the selected radio button in one group.
+ * Then, both iframes will be reloaded and the new groups will have another
+ * radio checked by default.
+ * For the first group (which had a selection change), nothing should change.
+ * For the second, the selected radio button should change.
+ */
+ checkRadioGroup(0, [true, false, false]);
+ checkRadioGroup(1, [true, false, false]);
+
+ frames[0].document.getElementsByTagName('input')[2].checked = true;
+ checkRadioGroup(0, [false, false, true]);
+
+ framesElts = document.getElementsByTagName('iframe');
+ framesElts[0].addEventListener("load", function() {
+ checkRadioGroup(0, [false, false, true]);
+
+ framesElts[1].addEventListener("load", function() {
+ checkRadioGroup(1, [false, true, false]);
+ SimpleTest.finish();
+ }, {once: true});
+
+ frames[1].location.reload();
+ }, {once: true});
+
+ frames[0].location.reload();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_select_change_event.html b/dom/html/test/forms/test_select_change_event.html
new file mode 100644
index 0000000000..ec3ed58c5e
--- /dev/null
+++ b/dom/html/test/forms/test_select_change_event.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265968
+-->
+<head>
+ <title>Test for Bug 1265968</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265968">Mozilla Bug 1265968</a>
+<p id="display"></p>
+<div id="content">
+ <select id="select" onchange="++selectChange;">
+ <option>one</option>
+ <option>two</option>
+ <option>three</option>
+ <option>four</option>
+ <option>five</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+ var select = document.getElementById("select");
+ var selectChange = 0;
+ var expectedChange = 0;
+
+ select.focus();
+ for (var i = 1; i < select.length; i++) {
+ synthesizeKey("KEY_ArrowDown");
+ is(select.options[i].selected, true, "Option should be selected");
+ is(selectChange, ++expectedChange, "Down key should fire change event.");
+ }
+
+ // We are at the end of the list, going down should not fire change event.
+ synthesizeKey("KEY_ArrowDown");
+ is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+ for (var i = select.length - 2; i >= 0; i--) {
+ synthesizeKey("KEY_ArrowUp");
+ is(select.options[i].selected, true, "Option should be selected");
+ is(selectChange, ++expectedChange, "Up key should fire change event.");
+ }
+
+ // We are at the top of the list, going up should not fire change event.
+ synthesizeKey("KEY_ArrowUp");
+ is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_select_input_change_event.html b/dom/html/test/forms/test_select_input_change_event.html
new file mode 100644
index 0000000000..fcf384e423
--- /dev/null
+++ b/dom/html/test/forms/test_select_input_change_event.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265968
+-->
+<head>
+ <title>Test for Bug 1024350</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1024350">Mozilla Bug 1024350</a>
+<p id="display"></p>
+<div id="content">
+ <select oninput='++selectInput;' onchange="++selectChange;">
+ <option>one</option>
+ </select>
+ <select oninput='++selectInput;' onchange="++selectChange;">
+ <option>one</option>
+ <option>two</option>
+ </select>
+ <select multiple size='1' oninput='++selectInput;' onchange="++selectChange;">
+ <option>one</option>
+ </select>
+ <select multiple oninput='++selectInput;' onchange="++selectChange;">
+ <option>one</option>
+ <option>two</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+ var selectSingleOneItem = document.getElementsByTagName('select')[0];
+ var selectSingle = document.getElementsByTagName('select')[1];
+ var selectMultipleOneItem = document.getElementsByTagName('select')[2];
+ var selectMultiple = document.getElementsByTagName('select')[3];
+
+ var selectChange = 0;
+ var selectInput = 0;
+ var expectedChange = 0;
+ var expectedInput = 0;
+
+ selectSingleOneItem.focus();
+ synthesizeKey("KEY_ArrowDown");
+ is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+ is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+ synthesizeKey("KEY_ArrowUp");
+ is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+ is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+ selectSingle.focus();
+ for (var i = 1; i < selectSingle.length; i++) {
+ synthesizeKey("KEY_ArrowDown");
+
+ is(selectSingle.options[i].selected, true, "Option should be selected");
+ is(selectInput, ++expectedInput, "Down key should fire input event.");
+ is(selectChange, ++expectedChange, "Down key should fire change event.");
+ }
+
+ // We are at the end of the list, going down should not fire change event.
+ synthesizeKey("KEY_ArrowDown");
+ is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+ is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+ for (var i = selectSingle.length - 2; i >= 0; i--) {
+ synthesizeKey("KEY_ArrowUp");
+
+ is(selectSingle.options[i].selected, true, "Option should be selected");
+ is(selectInput, ++expectedInput, "Up key should fire input event.");
+ is(selectChange, ++expectedChange, "Up key should fire change event.");
+ }
+
+ // We are at the top of the list, going up should not fire change event.
+ synthesizeKey("KEY_ArrowUp");
+ is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+ is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+ selectMultipleOneItem.focus();
+ synthesizeKey("KEY_ArrowDown");
+ is(selectInput, ++expectedInput, "Down key should fire input event when reaching end of the list.");
+ is(selectChange, ++expectedChange, "Down key should fire change event when reaching end of the list.");
+
+ synthesizeKey("KEY_ArrowDown");
+ is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+ is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+ synthesizeKey("KEY_ArrowUp");
+ is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+ is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+ selectMultiple.focus();
+ for (var i = 0; i < selectMultiple.length; i++) {
+ synthesizeKey("KEY_ArrowDown");
+
+ is(selectMultiple.options[i].selected, true, "Option should be selected");
+ is(selectInput, ++expectedInput, "Down key should fire input event.");
+ is(selectChange, ++expectedChange, "Down key should fire change event.");
+ }
+
+ // We are at the end of the list, going down should not fire change event.
+ synthesizeKey("KEY_ArrowDown");
+ is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+ is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+ for (var i = selectMultiple.length - 2; i >= 0; i--) {
+ synthesizeKey("KEY_ArrowUp");
+
+ is(selectMultiple.options[i].selected, true, "Option should be selected");
+ is(selectInput, ++expectedInput, "Up key should fire input event.");
+ is(selectChange, ++expectedChange, "Up key should fire change event.");
+ }
+
+ // We are at the top of the list, going up should not fire change event.
+ synthesizeKey("KEY_ArrowUp");
+ is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+ is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_select_selectedOptions.html b/dom/html/test/forms/test_select_selectedOptions.html
new file mode 100644
index 0000000000..745e0ba4f3
--- /dev/null
+++ b/dom/html/test/forms/test_select_selectedOptions.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=596681
+-->
+<head>
+ <title>Test for HTMLSelectElement.selectedOptions</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=596681">Mozilla Bug 596681</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLSelectElement's selectedOptions attribute.
+ *
+ * selectedOptions is a live list of the options that have selectedness of true
+ * (not the selected content attribute).
+ *
+ * See http://www.whatwg.org/html/#dom-select-selectedoptions
+ **/
+
+function checkSelectedOptions(size, elements)
+{
+ is(selectedOptions.length, size,
+ "select should have " + size + " selected options");
+ for (let i = 0; i < size; ++i) {
+ ok(selectedOptions[i], "selected option is valid");
+ if (selectedOptions[i]) {
+ is(selectedOptions[i].value, elements[i].value, "selected options are correct");
+ }
+ }
+}
+
+let select = document.createElement("select");
+document.body.appendChild(select);
+let selectedOptions = select.selectedOptions;
+
+ok("selectedOptions" in select,
+ "select element should have a selectedOptions IDL attribute");
+
+ok(select.selectedOptions instanceof HTMLCollection,
+ "selectedOptions should be an HTMLCollection instance");
+
+let option1 = document.createElement("option");
+let option2 = document.createElement("option");
+let option3 = document.createElement("option");
+option1.id = "option1";
+option1.value = "option1";
+option2.value = "option2";
+option3.value = "option3";
+
+checkSelectedOptions(0, null);
+
+select.add(option1, null);
+is(selectedOptions.namedItem("option1").value, "option1", "named getter works");
+checkSelectedOptions(1, [option1]);
+
+select.add(option2, null);
+checkSelectedOptions(1, [option1]);
+
+select.options[1].selected = true;
+checkSelectedOptions(1, [option2]);
+
+select.multiple = true;
+checkSelectedOptions(1, [option2]);
+
+select.options[0].selected = true;
+checkSelectedOptions(2, [option1, option2]);
+
+option1.selected = false;
+// Usinig selected directly on the option should work.
+checkSelectedOptions(1, [option2]);
+
+select.remove(1);
+select.add(option2, 0);
+select.options[0].selected = true;
+select.options[1].selected = true;
+// Should be in tree order.
+checkSelectedOptions(2, [option2, option1]);
+
+select.add(option3, null);
+checkSelectedOptions(2, [option2, option1]);
+
+select.options[2].selected = true;
+checkSelectedOptions(3, [option2, option1, option3]);
+
+select.length = 0;
+option1.selected = false;
+option2.selected = false;
+option3.selected = false;
+var optgroup1 = document.createElement("optgroup");
+optgroup1.appendChild(option1);
+optgroup1.appendChild(option2);
+select.add(optgroup1)
+var optgroup2 = document.createElement("optgroup");
+optgroup2.appendChild(option3);
+select.add(optgroup2);
+
+checkSelectedOptions(0, null);
+
+option2.selected = true;
+checkSelectedOptions(1, [option2]);
+
+option3.selected = true;
+checkSelectedOptions(2, [option2, option3]);
+
+optgroup1.removeChild(option2);
+checkSelectedOptions(1, [option3]);
+
+document.body.removeChild(select);
+option1.selected = true;
+checkSelectedOptions(2, [option1, option3]);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_select_validation.html b/dom/html/test/forms/test_select_validation.html
new file mode 100644
index 0000000000..6d02aa0746
--- /dev/null
+++ b/dom/html/test/forms/test_select_validation.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=942321
+-->
+<head>
+ <title>Test for Bug 942321</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942321">Mozilla Bug 942321</a>
+<p id="display"></p>
+<form id="form" href="">
+ <select required id="testselect">
+ <option id="placeholder" value="" selected>placeholder</option>
+ <option value="test" id="actualvalue">test</option>
+ <select>
+ <input type="submit" />
+</form>
+<script class="testbody" type="text/javascript">
+/** Test for Bug 942321 **/
+var option = document.getElementById("actualvalue");
+option.selected = true;
+is(form.checkValidity(), true, "Select is required and should be valid");
+
+var placeholder = document.getElementById("placeholder");
+placeholder.selected = true;
+is(form.checkValidity(), false, "Select is required and should be invalid");
+
+placeholder.value = "not-invalid-anymore";
+is(form.checkValidity(), true, "Select is required and should be valid when option's value is changed by javascript");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/forms/test_set_range_text.html b/dom/html/test/forms/test_set_range_text.html
new file mode 100644
index 0000000000..f85014ae77
--- /dev/null
+++ b/dom/html/test/forms/test_set_range_text.html
@@ -0,0 +1,242 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=850364
+-->
+<head>
+<title>Tests for Bug 850364 && Bug 918940</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=850364">Mozilla Bug 850364</a>
+<p id="display"></p>
+<div id="content">
+
+<!-- "SetRangeText() supported types"-->
+<input type="text" id="input_text"></input>
+<input type="search" id="input_search"></input>
+<input type="url" id="input_url"></input>
+<input type="tel" id="input_tel"></input>
+<input type="password" id="input_password"></input>
+<textarea id="input_textarea"></textarea>
+
+<!-- "SetRangeText() non-supported types" -->
+<input type="button" id="input_button"></input>
+<input type="submit" id="input_submit"></input>
+<input type="image" id="input_image"></input>
+<input type="reset" id="input_reset"></input>
+<input type="radio" id="input_radio"></input>
+<input type="checkbox" id="input_checkbox"></input>
+<input type="range" id="input_range"></input>
+<input type="file" id="input_file"></input>
+<input type="email" id="input_email"></input>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Tests for Bug 850364 && Bug 918940**/
+
+ var SupportedTypes = ["text", "search", "url", "tel", "password", "textarea"];
+ var NonSupportedTypes = ["button", "submit", "image", "reset", "radio",
+ "checkbox", "range", "file", "email"];
+
+ SimpleTest.waitForExplicitFinish();
+
+ function TestInputs() {
+
+ var opThrows, elem, i, msg;
+
+ //Non-supported types should throw
+ for (i = 0; i < NonSupportedTypes.length; ++i) {
+ opThrows = false;
+ msg = "input_" + NonSupportedTypes[i];
+ elem = document.getElementById(msg);
+ elem.focus();
+ try {
+ elem.setRangeText("abc");
+ } catch (ex) {
+ opThrows = true;
+ }
+ ok(opThrows, msg + " should throw InvalidStateError");
+ }
+
+ var numOfSelectCalls = 0, expectedNumOfSelectCalls = 0;
+ //Supported types should not throw
+ for (i = 0; i < SupportedTypes.length; ++i) {
+ opThrows = false;
+ msg = "input_" + SupportedTypes[i];
+ elem = document.getElementById(msg);
+ elem.focus();
+ try {
+ elem.setRangeText("abc");
+ } catch (ex) {
+ opThrows = true;
+ }
+ is(opThrows, false, msg + " should not throw InvalidStateError");
+
+ elem.addEventListener("select", function (aEvent) {
+ ok(true, "select event should be fired for " + aEvent.target.id);
+ if (++numOfSelectCalls == expectedNumOfSelectCalls) {
+ SimpleTest.finish();
+ } else if (numOfSelectCalls > expectedNumOfSelectCalls) {
+ ok(false, "Too many select events were fired");
+ }
+ });
+
+ elem.addEventListener("input", function (aEvent) {
+ ok(false, "input event should NOT be fired for " + + aEvent.target.id);
+ });
+
+ var test = " setRange(replacement), shrink";
+ elem.value = "0123456789ABCDEF";
+ elem.setSelectionRange(1, 6);
+ elem.setRangeText("xyz");
+ is(elem.value, "0xyz6789ABCDEF", msg + test);
+ is(elem.selectionStart, 1, msg + test);
+ is(elem.selectionEnd, 4, msg + test);
+ elem.setRangeText("mnk");
+ is(elem.value, "0mnk6789ABCDEF", msg + test);
+ expectedNumOfSelectCalls += 2;
+
+ test = " setRange(replacement), expand";
+ elem.value = "0123456789ABCDEF";
+ elem.setSelectionRange(1, 2);
+ elem.setRangeText("xyz");
+ is(elem.value, "0xyz23456789ABCDEF", msg + test);
+ is(elem.selectionStart, 1, msg + test);
+ is(elem.selectionEnd, 4, msg + test);
+ elem.setRangeText("mnk");
+ is(elem.value, "0mnk23456789ABCDEF", msg + test);
+ expectedNumOfSelectCalls += 2;
+
+ test = " setRange(replacement) pure insertion at start";
+ elem.value = "0123456789ABCDEF";
+ elem.setSelectionRange(0, 0);
+ elem.setRangeText("xyz");
+ is(elem.value, "xyz0123456789ABCDEF", msg + test);
+ is(elem.selectionStart, 0, msg + test);
+ is(elem.selectionEnd, 0, msg + test);
+ elem.setRangeText("mnk");
+ is(elem.value, "mnkxyz0123456789ABCDEF", msg + test);
+ expectedNumOfSelectCalls += 1;
+
+ test = " setRange(replacement) pure insertion in the middle";
+ elem.value = "0123456789ABCDEF";
+ elem.setSelectionRange(4, 4);
+ elem.setRangeText("xyz");
+ is(elem.value, "0123xyz456789ABCDEF", msg + test);
+ is(elem.selectionStart, 4, msg + test);
+ is(elem.selectionEnd, 4, msg + test);
+ elem.setRangeText("mnk");
+ is(elem.value, "0123mnkxyz456789ABCDEF", msg + test);
+ expectedNumOfSelectCalls += 1;
+
+ test = " setRange(replacement) pure insertion at the end";
+ elem.value = "0123456789ABCDEF";
+ elem.setSelectionRange(16, 16);
+ elem.setRangeText("xyz");
+ is(elem.value, "0123456789ABCDEFxyz", msg + test);
+ is(elem.selectionStart, 16, msg + test);
+ is(elem.selectionEnd, 16, msg + test);
+ elem.setRangeText("mnk");
+ is(elem.value, "0123456789ABCDEFmnkxyz", msg + test);
+
+ //test SetRange(replacement, start, end, mode) with start > end
+ try {
+ elem.setRangeText("abc", 20, 4);
+ } catch (ex) {
+ opThrows = (ex.name == "IndexSizeError" && ex.code == DOMException.INDEX_SIZE_ERR);
+ }
+ is(opThrows, true, msg + " should throw IndexSizeError");
+
+ //test SelectionMode 'select'
+ elem.value = "0123456789ABCDEF";
+ elem.setRangeText("xyz", 4, 9, "select");
+ is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
+ is(elem.selectionStart, 4, msg + ".selectionStart == 4, with \"select\"");
+ is(elem.selectionEnd, 7, msg + ".selectionEnd == 7, with \"select\"");
+ expectedNumOfSelectCalls += 1;
+
+ elem.setRangeText("pqm", 6, 25, "select");
+ is(elem.value, "0123xypqm", msg + ".value == \"0123xypqm\"");
+ is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"select\"");
+ is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"select\"");
+ expectedNumOfSelectCalls += 1;
+
+ //test SelectionMode 'start'
+ elem.value = "0123456789ABCDEF";
+ elem.setRangeText("xyz", 4, 9, "start");
+ is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
+ is(elem.selectionStart, 4, msg + ".selectionStart == 4, with \"start\"");
+ is(elem.selectionEnd, 4, msg + ".selectionEnd == 4, with \"start\"");
+ expectedNumOfSelectCalls += 1;
+
+ elem.setRangeText("pqm", 6, 25, "start");
+ is(elem.value, "0123xypqm", msg + ".value == \"0123xypqm\"");
+ is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"start\"");
+ is(elem.selectionEnd, 6, msg + ".selectionEnd == 6, with \"start\"");
+ expectedNumOfSelectCalls += 1;
+
+ //test SelectionMode 'end'
+ elem.value = "0123456789ABCDEF";
+ elem.setRangeText("xyz", 4, 9, "end");
+ is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
+ is(elem.selectionStart, 7, msg + ".selectionStart == 7, with \"end\"");
+ is(elem.selectionEnd, 7, msg + ".selectionEnd == 7, with \"end\"");
+ expectedNumOfSelectCalls += 1;
+
+ elem.setRangeText("pqm", 6, 25, "end");
+ is(elem.value, "0123xypqm", msg + ".value == \"0123xypqm\"");
+ is(elem.selectionStart, 9, msg + ".selectionStart == 9, with \"end\"");
+ is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"end\"");
+ expectedNumOfSelectCalls += 1;
+
+ //test SelectionMode 'preserve' (default)
+
+ //subcase: selection{Start|End} > end
+ elem.value = "0123456789";
+ elem.setSelectionRange(6, 9);
+ elem.setRangeText("Z", 1, 2, "preserve");
+ is(elem.value, "0Z23456789", msg + ".value == \"0Z23456789\"");
+ is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"preserve\"");
+ is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"preserve\"");
+ expectedNumOfSelectCalls += 1;
+
+ //subcase: selection{Start|End} < end
+ elem.value = "0123456789";
+ elem.setSelectionRange(4, 5);
+ elem.setRangeText("QRST", 2, 9, "preserve");
+ is(elem.value, "01QRST9", msg + ".value == \"01QRST9\"");
+ is(elem.selectionStart, 2, msg + ".selectionStart == 2, with \"preserve\"");
+ is(elem.selectionEnd, 6, msg + ".selectionEnd == 6, with \"preserve\"");
+ expectedNumOfSelectCalls += 2;
+
+ //subcase: selectionStart > end, selectionEnd < end
+ elem.value = "0123456789";
+ elem.setSelectionRange(8, 4);
+ elem.setRangeText("QRST", 1, 5);
+ is(elem.value, "0QRST56789", msg + ".value == \"0QRST56789\"");
+ is(elem.selectionStart, 1, msg + ".selectionStart == 1, with \"default\"");
+ is(elem.selectionEnd, 5, msg + ".selectionEnd == 5, with \"default\"");
+ expectedNumOfSelectCalls += 2;
+
+ //subcase: selectionStart < end, selectionEnd > end
+ elem.value = "0123456789";
+ elem.setSelectionRange(4, 9);
+ elem.setRangeText("QRST", 2, 6);
+ is(elem.value, "01QRST6789", msg + ".value == \"01QRST6789\"");
+ is(elem.selectionStart, 2, msg + ".selectionStart == 2, with \"default\"");
+ is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"default\"");
+ expectedNumOfSelectCalls += 2;
+ }
+ }
+
+ addLoadEvent(TestInputs);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_step_attribute.html b/dom/html/test/forms/test_step_attribute.html
new file mode 100644
index 0000000000..f0af250c06
--- /dev/null
+++ b/dom/html/test/forms/test_step_attribute.html
@@ -0,0 +1,1060 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=635553
+-->
+<head>
+ <title>Test for Bug 635553</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635499">Mozilla Bug 635499</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 635553 **/
+
+var data = [
+ { type: 'hidden', apply: false },
+ { type: 'text', apply: false },
+ { type: 'search', apply: false },
+ { type: 'tel', apply: false },
+ { type: 'url', apply: false },
+ { type: 'email', apply: false },
+ { type: 'password', apply: false },
+ { type: 'date', apply: true },
+ { type: 'month', apply: true },
+ { type: 'week', apply: true },
+ { type: 'time', apply: true },
+ { type: 'datetime-local', apply: true },
+ { type: 'number', apply: true },
+ { type: 'range', apply: true },
+ { type: 'color', apply: false },
+ { type: 'checkbox', apply: false },
+ { type: 'radio', apply: false },
+ { type: 'file', apply: false },
+ { type: 'submit', apply: false },
+ { type: 'image', apply: false },
+ { type: 'reset', apply: false },
+ { type: 'button', apply: false },
+];
+
+function getFreshElement(type) {
+ var elmt = document.createElement('input');
+ elmt.type = type;
+ return elmt;
+}
+
+function checkValidity(aElement, aValidity, aApply, aData)
+{
+ aValidity = aApply ? aValidity : true;
+
+ is(aElement.validity.valid, aValidity,
+ "element validity should be " + aValidity);
+ is(aElement.validity.stepMismatch, !aValidity,
+ "element step mismatch status should be " + !aValidity);
+
+ if (aValidity) {
+ is(aElement.validationMessage, "", "There should be no validation message.");
+ } else {
+ if (aElement.validity.rangeUnderflow) {
+ var underflowMsg =
+ (aElement.type == "date" || aElement.type == "time") ?
+ ("Please select a value that is no earlier than " + aElement.min + ".") :
+ ("Please select a value that is no less than " + aElement.min + ".");
+ is(aElement.validationMessage, underflowMsg,
+ "Checking range underflow validation message.");
+ } else if (aData.low == aData.high) {
+ is(aElement.validationMessage, "Please select a valid value. " +
+ "The nearest valid value is " + aData.low + ".",
+ "There should be a validation message.");
+ } else {
+ is(aElement.validationMessage, "Please select a valid value. " +
+ "The two nearest valid values are " + aData.low + " and " + aData.high + ".",
+ "There should be a validation message.");
+ }
+ }
+
+ is(aElement.matches(":valid"), aElement.willValidate && aValidity,
+ (aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
+ is(aElement.matches(":invalid"), aElement.willValidate && !aValidity,
+ (aElement.wil && aValidity) ? ":invalid shouldn't apply" : "valid should apply");
+}
+
+for (var test of data) {
+ var input = getFreshElement(test.type);
+ var apply = test.apply;
+
+ if (test.todo) {
+ todo_is(input.type, test.type, test.type + " isn't implemented yet");
+ continue;
+ }
+
+ // The element should be valid, there should be no step mismatch.
+ checkValidity(input, true, apply);
+
+ // Checks to do for all types that support step:
+ // - check for @step=0,
+ // - check for @step behind removed,
+ // - check for @step being 'any' with different case variations.
+ switch (input.type) {
+ case 'text':
+ case 'hidden':
+ case 'search':
+ case 'password':
+ case 'tel':
+ case 'radio':
+ case 'checkbox':
+ case 'reset':
+ case 'button':
+ case 'submit':
+ case 'image':
+ case 'color':
+ input.value = '0';
+ checkValidity(input, true, apply);
+ break;
+ case 'url':
+ input.value = 'http://mozilla.org';
+ checkValidity(input, true, apply);
+ break;
+ case 'email':
+ input.value = 'foo@bar.com';
+ checkValidity(input, true, apply);
+ break;
+ case 'file':
+ var file = new File([''], '635499_file');
+
+ SpecialPowers.wrap(input).mozSetFileArray([file]);
+ checkValidity(input, true, apply);
+
+ break;
+ case 'date':
+ // For date, the step is calulated on the timestamp since 1970-01-01
+ // which mean that for all dates prior to the epoch, this timestamp is < 0
+ // and the behavior might differ, therefore we have to test for these cases.
+
+ // When step is invalid, every date is valid
+ input.step = 0;
+ input.value = '2012-07-05';
+ checkValidity(input, true, apply);
+
+ input.step = 'foo';
+ input.value = '1970-01-01';
+ checkValidity(input, true, apply);
+
+ input.step = '-1';
+ input.value = '1969-12-12';
+ checkValidity(input, true, apply);
+
+ input.removeAttribute('step');
+ input.value = '1500-01-01';
+ checkValidity(input, true, apply);
+
+ input.step = 'any';
+ input.value = '1966-12-12';
+ checkValidity(input, true, apply);
+
+ input.step = 'ANY';
+ input.value = '2013-02-03';
+ checkValidity(input, true, apply);
+
+ // When min is set to a valid date, there is a step base.
+ input.min = '2008-02-28';
+ input.step = '2';
+ input.value = '2008-03-01';
+ checkValidity(input, true, apply);
+
+ input.value = '2008-02-29';
+ checkValidity(input, false, apply, { low: "2008-02-28", high: "2008-03-01" });
+
+ input.min = '2008-02-27';
+ input.value = '2008-02-28';
+ checkValidity(input, false, apply, { low: "2008-02-27", high: "2008-02-29" });
+
+ input.min = '2009-02-27';
+ input.value = '2009-02-28';
+ checkValidity(input, false, apply, { low: "2009-02-27", high: "2009-03-01" });
+
+ input.min = '2009-02-01';
+ input.step = '1.1';
+ input.value = '2009-02-02';
+ checkValidity(input, true, apply);
+
+ // Without any step attribute the date is valid
+ input.removeAttribute('step');
+ checkValidity(input, true, apply);
+
+ input.min = '1950-01-01';
+ input.step = '366';
+ input.value = '1951-01-01';
+ checkValidity(input, false, apply, { low: "1950-01-01", high: "1951-01-02" });
+
+ input.min = '1951-01-01';
+ input.step = '365';
+ input.value = '1952-01-01';
+ checkValidity(input, true, apply);
+
+ input.step = '0.9';
+ input.value = '1951-01-02';
+ is(input.step, '0.9', "check that step value is unchanged");
+ checkValidity(input, true, apply);
+
+ input.step = '0.4';
+ input.value = '1951-01-02';
+ is(input.step, '0.4', "check that step value is unchanged");
+ checkValidity(input, true, apply);
+
+ input.step = '1.5';
+ input.value = '1951-01-02';
+ is(input.step, '1.5', "check that step value is unchanged");
+ checkValidity(input, false, apply, { low: "1951-01-01", high: "1951-01-03" });
+
+ input.value = '1951-01-08';
+ checkValidity(input, false, apply, { low: "1951-01-07", high: "1951-01-09" });
+
+ input.step = '3000';
+ input.min= '1968-01-01';
+ input.value = '1968-05-12';
+ checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
+
+ input.value = '1971-01-01';
+ checkValidity(input, false, apply, { low: "1968-01-01", high: "1976-03-19" });
+
+ input.value = '1991-01-01';
+ checkValidity(input, false, apply, { low: "1984-06-05", high: "1992-08-22" });
+
+ input.value = '1984-06-05';
+ checkValidity(input, true, apply);
+
+ input.value = '1992-08-22';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1991-01-01';
+ input.value = '1991-01-01';
+ checkValidity(input, true, apply);
+
+ input.value = '1991-01-02';
+ checkValidity(input, false, apply, { low: "1991-01-01", high: "1991-01-03" });
+
+ input.value = '1991-01-03';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1969-12-20';
+ input.value = '1969-12-20';
+ checkValidity(input, true, apply);
+
+ input.value = '1969-12-21';
+ checkValidity(input, false, apply, { low: "1969-12-20", high: "1969-12-22" });
+
+ input.value = '1969-12-22';
+ checkValidity(input, true, apply);
+
+ break;
+ case 'number':
+ // When step=0, the allowed step is 1.
+ input.step = '0';
+ input.value = '1.2';
+ checkValidity(input, false, apply, { low: 1, high: 2 });
+
+ input.value = '1';
+ checkValidity(input, true, apply);
+
+ input.value = '0';
+ checkValidity(input, true, apply);
+
+ // When step is NaN, the allowed step value is 1.
+ input.step = 'foo';
+ input.value = '1';
+ checkValidity(input, true, apply);
+
+ input.value = '1.5';
+ checkValidity(input, false, apply, { low: 1, high: 2 });
+
+ // When step is negative, the allowed step value is 1.
+ input.step = '-0.1';
+ checkValidity(input, false, apply, { low: 1, high: 2 });
+
+ input.value = '1';
+ checkValidity(input, true, apply);
+
+ // When step is missing, the allowed step value is 1.
+ input.removeAttribute('step');
+ input.value = '1.5';
+ checkValidity(input, false, apply, { low: 1, high: 2 });
+
+ input.value = '1';
+ checkValidity(input, true, apply);
+
+ // When step is 'any', all values are fine wrt to step.
+ input.step = 'any';
+ checkValidity(input, true, apply);
+
+ input.step = 'aNy';
+ input.value = '1337';
+ checkValidity(input, true, apply);
+
+ input.step = 'AnY';
+ input.value = '0.1';
+ checkValidity(input, true, apply);
+
+ input.step = 'ANY';
+ input.value = '-13.37';
+ checkValidity(input, true, apply);
+
+ // When min is set to a valid float, there is a step base.
+ input.min = '1';
+ input.step = '2';
+ input.value = '3';
+ checkValidity(input, true, apply);
+
+ input.value = '2';
+ checkValidity(input, false, apply, { low: 1, high: 3 });
+
+ input.removeAttribute('step'); // step = 1
+ input.min = '0.5';
+ input.value = '5.5';
+ checkValidity(input, true, apply);
+
+ input.value = '1';
+ checkValidity(input, false, apply, { low: 0.5, high: 1.5 });
+
+ input.min = '-0.1';
+ input.step = '1';
+ input.value = '0.9';
+ checkValidity(input, true, apply);
+
+ input.value = '0.1';
+ checkValidity(input, false, apply, { low: -0.1, high: 0.9 });
+
+ // When min is set to NaN, there is no step base (step base=0 actually).
+ input.min = 'foo';
+ input.step = '1';
+ input.value = '1';
+ checkValidity(input, true, apply);
+
+ input.value = '0.5';
+ checkValidity(input, false, apply, { low: 0, high: 1 });
+
+ input.min = '';
+ input.value = '1';
+ checkValidity(input, true, apply);
+
+ input.value = '0.5';
+ checkValidity(input, false, apply, { low: 0, high: 1 });
+
+ input.removeAttribute('min');
+
+ // If value isn't a number, the element isn't invalid.
+ input.value = '';
+ checkValidity(input, true, apply);
+
+ // Regular situations.
+ input.step = '2';
+ input.value = '1.5';
+ checkValidity(input, false, apply, { low: 0, high: 2 });
+
+ input.value = '42.0';
+ checkValidity(input, true, apply);
+
+ input.step = '0.1';
+ input.value = '-0.1';
+ checkValidity(input, true, apply);
+
+ input.step = '2';
+ input.removeAttribute('min');
+ input.max = '10';
+ input.value = '-9';
+ checkValidity(input, false, apply, {low: -10, high: -8});
+
+ // If there is a value defined but no min, the step base is the value.
+ input = getFreshElement(test.type);
+ input.setAttribute('value', '1');
+ input.step = 2;
+ checkValidity(input, true, apply);
+
+ input.value = 3;
+ checkValidity(input, true, apply);
+
+ input.value = 2;
+ checkValidity(input, false, apply, {low: 1, high: 3});
+
+ // Should also work with defaultValue.
+ input = getFreshElement(test.type);
+ input.defaultValue = 1;
+ input.step = 2;
+ checkValidity(input, true, apply);
+
+ input.value = 3;
+ checkValidity(input, true, apply);
+
+ input.value = 2;
+ checkValidity(input, false, apply, {low: 1, high: 3});
+
+ // Rounding issues.
+ input = getFreshElement(test.type);
+ input.min = 0.1;
+ input.step = 0.2;
+ input.value = 0.3;
+ checkValidity(input, true, apply);
+
+ // Check that when the higher value is higher than max, we don't show it.
+ input = getFreshElement(test.type);
+ input.step = '2';
+ input.min = '1';
+ input.max = '10.9';
+ input.value = '10';
+
+ is(input.validationMessage, "Please select a valid value. " +
+ "The nearest valid value is 9.",
+ "The validation message should not include the higher value.");
+ break;
+ case 'range':
+ // Range is special in that it clamps to valid values, so it is much
+ // rarer for it to be invalid.
+
+ // When step=0, the allowed value step is 1.
+ input.step = '0';
+ input.value = '1.2';
+ is(input.value, '1', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.value = '1';
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '0';
+ is(input.value, '0', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ // When step is NaN, the allowed step value is 1.
+ input.step = 'foo';
+ input.value = '1';
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '1.5';
+ is(input.value, '2', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ // When step is negative, the allowed step value is 1.
+ input.step = '-0.1';
+ is(input.value, '2', "check that the value still coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '1';
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ // When step is missing, the allowed step value is 1.
+ input.removeAttribute('step');
+ input.value = '1.5';
+ is(input.value, '2', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.value = '1';
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ // When step is 'any', all values are fine wrt to step.
+ input.step = 'any';
+ checkValidity(input, true, apply);
+
+ input.step = 'aNy';
+ input.value = '97';
+ is(input.value, '97', "check that the value for step=aNy is unchanged");
+ checkValidity(input, true, apply);
+
+ input.step = 'AnY';
+ input.value = '0.1';
+ is(input.value, '0.1', "check that a positive fractional value with step=AnY is unchanged");
+ checkValidity(input, true, apply);
+
+ input.step = 'ANY';
+ input.min = -100;
+ input.value = '-13.37';
+ is(input.value, '-13.37', "check that a negative fractional value with step=ANY is unchanged");
+ checkValidity(input, true, apply);
+
+ // When min is set to a valid float, there is a step base.
+ input.min = '1'; // the step base
+ input.step = '2';
+ input.value = '3';
+ is(input.value, '3', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '2';
+ is(input.value, '3', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.value = '1.99';
+ is(input.value, '1', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.removeAttribute('step'); // step = 1
+ input.min = '0.5'; // step base
+ input.value = '5.5';
+ is(input.value, '5.5', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '1';
+ is(input.value, '1.5', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.min = '-0.1'; // step base
+ input.step = '1';
+ input.value = '0.9';
+ is(input.value, '0.9', "the value should be a valid step");
+ checkValidity(input, true, apply);
+
+ input.value = '0.1';
+ is(input.value, '-0.1', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ // When min is set to NaN, the step base is the value.
+ input.min = 'foo';
+ input.step = '1';
+ input.value = '1';
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '0.5';
+ is(input.value, '1', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.min = '';
+ input.value = '1';
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = '0.5';
+ is(input.value, '1', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.removeAttribute('min');
+
+ // Test when the value isn't a number
+ input.value = '';
+ is(input.value, '50', "value be should default to the value midway between the minimum (0) and the maximum (100)");
+ checkValidity(input, true, apply);
+
+ // Regular situations.
+ input.step = '2';
+ input.value = '1.5';
+ is(input.value, '2', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.value = '42.0';
+ is(input.value, '42.0', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.step = '0.1';
+ input.value = '-0.1';
+ is(input.value, '0', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.step = '2';
+ input.removeAttribute('min');
+ input.max = '10';
+ input.value = '-9';
+ is(input.value, '0', "check the value is clamped to the minimum's default of zero");
+ checkValidity(input, true, apply);
+
+ // If @value is defined but not @min, the step base is @value.
+ input = getFreshElement(test.type);
+ input.setAttribute('value', '1');
+ input.step = 2;
+ is(input.value, '1', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ input.value = 3;
+ is(input.value, '3', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = 2;
+ is(input.value, '3', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ // Should also work with defaultValue.
+ input = getFreshElement(test.type);
+ input.defaultValue = 1;
+ input.step = 2;
+ is(input.value, '1', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = 3;
+ is(input.value, '3', "check that the value coincides with a step");
+ checkValidity(input, true, apply);
+
+ input.value = 2;
+ is(input.value, '3', "check that the value changes to the nearest valid step, choosing the higher step if both are equally close");
+ checkValidity(input, true, apply);
+
+ // Check contrived error case where there are no valid steps in range:
+ // No @min, so the step base is the default minimum, zero, the valid
+ // range is 0-1, -1 gets clamped to zero.
+ input = getFreshElement(test.type);
+ input.step = '3';
+ input.max = '1';
+ input.defaultValue = '-1';
+ is(input.value, '0', "the value should have been clamped to the default minimum, zero");
+ checkValidity(input, false, apply, {low: -1, high: -1});
+
+ // Check that when the closest of the two steps that the value is between
+ // is greater than the maximum we sanitize to the lower step.
+ input = getFreshElement(test.type);
+ input.step = '2';
+ input.min = '1';
+ input.max = '10.9';
+ input.value = '10.8'; // closest step in 11, but 11 > maximum
+ is(input.value, '9', "check that the value coincides with a step");
+
+ // The way that step base is defined, the converse (the value not being
+ // on a step, and the nearest step being a value that would be underflow)
+ // is not possible, so nothing to test there.
+
+ is(input.validationMessage, "",
+ "The validation message should be empty.");
+ break;
+ case 'time':
+ // Tests invalid step values. That defaults to step = 1 minute (60).
+ var values = [ '0', '-1', 'foo', 'any', 'ANY', 'aNy' ];
+ for (var value of values) {
+ input.step = value;
+ input.value = '19:06:00';
+ checkValidity(input, true, apply);
+ input.value = '19:06:51';
+ if (value.toLowerCase() != 'any') {
+ checkValidity(input, false, apply, {low: '19:06', high: '19:07'});
+ } else {
+ checkValidity(input, true, apply);
+ }
+ }
+
+ // No step means that we use the default step value.
+ input.removeAttribute('step');
+ input.value = '19:06:00';
+ checkValidity(input, true, apply);
+ input.value = '19:06:51';
+ checkValidity(input, false, apply, {low: '19:06', high: '19:07'});
+
+ var tests = [
+ // With step=1, we allow values by the second.
+ { step: '1', value: '19:11:01', min: '00:00', result: true },
+ { step: '1', value: '19:11:01.001', min: '00:00', result: false,
+ low: '19:11:01', high: '19:11:02' },
+ { step: '1', value: '19:11:01.1', min: '00:00', result: false,
+ low: '19:11:01', high: '19:11:02' },
+ // When step >= 86400000, only the minimum value is valid.
+ // This is actually @value if there is no @min.
+ { step: '86400000', value: '00:00', result: true },
+ { step: '86400000', value: '00:01', result: true },
+ { step: '86400000', value: '00:00', min: '00:01', result: false },
+ { step: '86400000', value: '00:01', min: '00:00', result: false,
+ low: '00:00', high: '00:00' },
+ // When step < 1, it should just work.
+ { step: '0.1', value: '15:05:05.1', min: '00:00', result: true },
+ { step: '0.1', value: '15:05:05.101', min: '00:00', result: false,
+ low: '15:05:05.100', high: '15:05:05.200' },
+ { step: '0.2', value: '15:05:05.2', min: '00:00', result: true },
+ { step: '0.2', value: '15:05:05.1', min: '00:00', result: false,
+ low: '15:05:05', high: '15:05:05.200' },
+ { step: '0.01', value: '15:05:05.01', min: '00:00', result: true },
+ { step: '0.01', value: '15:05:05.011', min: '00:00', result: false,
+ low: '15:05:05.010', high: '15:05:05.020' },
+ { step: '0.02', value: '15:05:05.02', min: '00:00', result: true },
+ { step: '0.02', value: '15:05:05.01', min: '00:00', result: false,
+ low: '15:05:05', high: '15:05:05.020' },
+ { step: '0.002', value: '15:05:05.002', min: '00:00', result: true },
+ { step: '0.002', value: '15:05:05.001', min: '00:00', result: false,
+ low: '15:05:05', high: '15:05:05.002' },
+ // When step<=0.001, any value is allowed.
+ { step: '0.001', value: '15:05:05.001', min: '00:00', result: true },
+ { step: '0.001', value: '15:05:05', min: '00:00', result: true },
+ { step: '0.000001', value: '15:05:05', min: '00:00', result: true },
+ // This value has conversion to double issues.
+ { step: '0.0000001', value: '15:05:05', min: '00:00', result: true },
+ // Some random values.
+ { step: '100', value: '15:06:40', min: '00:00', result: true },
+ { step: '100', value: '15:05:05.010', min: '00:00', result: false,
+ low: '15:05', high: '15:06:40' },
+ { step: '3600', value: '15:00', min: '00:00', result: true },
+ { step: '3600', value: '15:14', min: '00:00', result: false,
+ low: '15:00', high: '16:00' },
+ { step: '7200', value: '14:00', min: '00:00', result: true },
+ { step: '7200', value: '15:14', min: '00:00', result: false,
+ low: '14:00', high: '16:00' },
+ { step: '7260', value: '14:07', min: '00:00', result: true },
+ { step: '7260', value: '15:14', min: '00:00', result: false,
+ low: '14:07', high: '16:08' },
+ ];
+
+ var type = test.type;
+ for (var test of tests) {
+ var input = getFreshElement(type);
+ input.step = test.step;
+ input.setAttribute('value', test.value);
+ if (test.min !== undefined) {
+ input.min = test.min;
+ }
+
+ if (test.todo) {
+ todo(input.validity.valid, test.result,
+ "This test should fail for the moment because of precission issues");
+ continue;
+ }
+
+ if (test.result) {
+ checkValidity(input, true, apply);
+ } else {
+ checkValidity(input, false, apply,
+ { low: test.low, high: test.high });
+ }
+ }
+
+ break;
+ case 'month':
+ // When step is invalid, every date is valid
+ input.step = 0;
+ input.value = '2016-07';
+ checkValidity(input, true, apply);
+
+ input.step = 'foo';
+ input.value = '1970-01';
+ checkValidity(input, true, apply);
+
+ input.step = '-1';
+ input.value = '1970-01';
+ checkValidity(input, true, apply);
+
+ input.removeAttribute('step');
+ input.value = '1500-01';
+ checkValidity(input, true, apply);
+
+ input.step = 'any';
+ input.value = '1966-12';
+ checkValidity(input, true, apply);
+
+ input.step = 'ANY';
+ input.value = '2013-02';
+ checkValidity(input, true, apply);
+
+ // When min is set to a valid month, there is a step base.
+ input.min = '2000-01';
+ input.step = '2';
+ input.value = '2000-03';
+ checkValidity(input, true, apply);
+
+ input.value = '2000-02';
+ checkValidity(input, false, apply, { low: "2000-01", high: "2000-03" });
+
+ input.min = '2012-12';
+ input.value = '2013-01';
+ checkValidity(input, false, apply, { low: "2012-12", high: "2013-02" });
+
+ input.min = '2010-10';
+ input.value = '2010-11';
+ checkValidity(input, false, apply, { low: "2010-10", high: "2010-12" });
+
+ input.min = '2010-01';
+ input.step = '1.1';
+ input.value = '2010-02';
+ checkValidity(input, true, apply);
+
+ input.min = '2010-05';
+ input.step = '1.9';
+ input.value = '2010-06';
+ checkValidity(input, false, apply, { low: "2010-05", high: "2010-07" });
+
+ // Without any step attribute the date is valid
+ input.removeAttribute('step');
+ checkValidity(input, true, apply);
+
+ input.min = '1950-01';
+ input.step = '13';
+ input.value = '1951-01';
+ checkValidity(input, false, apply, { low: "1950-01", high: "1951-02" });
+
+ input.min = '1951-01';
+ input.step = '12';
+ input.value = '1952-01';
+ checkValidity(input, true, apply);
+
+ input.step = '0.9';
+ input.value = '1951-02';
+ checkValidity(input, true, apply);
+
+ input.step = '1.5';
+ input.value = '1951-04';
+ checkValidity(input, false, apply, { low: "1951-03", high: "1951-05" });
+
+ input.value = '1951-08';
+ checkValidity(input, false, apply, { low: "1951-07", high: "1951-09" });
+
+ input.step = '300';
+ input.min= '1968-01';
+ input.value = '1968-05';
+ checkValidity(input, false, apply, { low: "1968-01", high: "1993-01" });
+
+ input.value = '1971-01';
+ checkValidity(input, false, apply, { low: "1968-01", high: "1993-01" });
+
+ input.value = '1994-01';
+ checkValidity(input, false, apply, { low: "1993-01", high: "2018-01" });
+
+ input.value = '2018-01';
+ checkValidity(input, true, apply);
+
+ input.value = '2043-01';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1991-01';
+ input.value = '1991-01';
+ checkValidity(input, true, apply);
+
+ input.value = '1991-02';
+ checkValidity(input, false, apply, { low: "1991-01", high: "1991-03" });
+
+ input.value = '1991-03';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1969-12';
+ input.value = '1969-12';
+ checkValidity(input, true, apply);
+
+ input.value = '1970-01';
+ checkValidity(input, false, apply, { low: "1969-12", high: "1970-02" });
+
+ input.value = '1970-02';
+ checkValidity(input, true, apply);
+
+ break;
+ case 'week':
+ // When step is invalid, every week is valid
+ input.step = 0;
+ input.value = '2016-W30';
+ checkValidity(input, true, apply);
+
+ input.step = 'foo';
+ input.value = '1970-W01';
+ checkValidity(input, true, apply);
+
+ input.step = '-1';
+ input.value = '1970-W01';
+ checkValidity(input, true, apply);
+
+ input.removeAttribute('step');
+ input.value = '1500-W01';
+ checkValidity(input, true, apply);
+
+ input.step = 'any';
+ input.value = '1966-W52';
+ checkValidity(input, true, apply);
+
+ input.step = 'ANY';
+ input.value = '2013-W10';
+ checkValidity(input, true, apply);
+
+ // When min is set to a valid week, there is a step base.
+ input.min = '2000-W01';
+ input.step = '2';
+ input.value = '2000-W03';
+ checkValidity(input, true, apply);
+
+ input.value = '2000-W02';
+ checkValidity(input, false, apply, { low: "2000-W01", high: "2000-W03" });
+
+ input.min = '2012-W52';
+ input.value = '2013-W01';
+ checkValidity(input, false, apply, { low: "2012-W52", high: "2013-W02" });
+
+ input.min = '2010-W01';
+ input.step = '1.1';
+ input.value = '2010-W02';
+ checkValidity(input, true, apply);
+
+ input.min = '2010-W05';
+ input.step = '1.9';
+ input.value = '2010-W06';
+ checkValidity(input, false, apply, { low: "2010-W05", high: "2010-W07" });
+
+ // Without any step attribute the week is valid
+ input.removeAttribute('step');
+ checkValidity(input, true, apply);
+
+ input.min = '1950-W01';
+ input.step = '53';
+ input.value = '1951-W01';
+ checkValidity(input, false, apply, { low: "1950-W01", high: "1951-W02" });
+
+ input.min = '1951-W01';
+ input.step = '52';
+ input.value = '1952-W01';
+ checkValidity(input, true, apply);
+
+ input.step = '0.9';
+ input.value = '1951-W02';
+ checkValidity(input, true, apply);
+
+ input.step = '1.5';
+ input.value = '1951-W04';
+ checkValidity(input, false, apply, { low: "1951-W03", high: "1951-W05" });
+
+ input.value = '1951-W20';
+ checkValidity(input, false, apply, { low: "1951-W19", high: "1951-W21" });
+
+ input.step = '300';
+ input.min= '1968-W01';
+ input.value = '1968-W05';
+ checkValidity(input, false, apply, { low: "1968-W01", high: "1973-W40" });
+
+ input.value = '1971-W01';
+ checkValidity(input, false, apply, { low: "1968-W01", high: "1973-W40" });
+
+ input.value = '1975-W01';
+ checkValidity(input, false, apply, { low: "1973-W40", high: "1979-W27" });
+
+ input.value = '1985-W14';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1991-W01';
+ input.value = '1991-W01';
+ checkValidity(input, true, apply);
+
+ input.value = '1991-W02';
+ checkValidity(input, false, apply, { low: "1991-W01", high: "1991-W03" });
+
+ input.value = '1991-W03';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1969-W52';
+ input.value = '1969-W52';
+ checkValidity(input, true, apply);
+
+ input.value = '1970-W01';
+ checkValidity(input, false, apply, { low: "1969-W52", high: "1970-W02" });
+
+ input.value = '1970-W02';
+ checkValidity(input, true, apply);
+
+ break;
+ case 'datetime-local':
+ // When step is invalid, every datetime is valid
+ input.step = 0;
+ input.value = '2017-02-06T12:00';
+ checkValidity(input, true, apply);
+
+ input.step = 'foo';
+ input.value = '1970-01-01T00:00';
+ checkValidity(input, true, apply);
+
+ input.step = '-1';
+ input.value = '1969-12-12 00:10';
+ checkValidity(input, true, apply);
+
+ input.removeAttribute('step');
+ input.value = '1500-01-01T12:00';
+ checkValidity(input, true, apply);
+
+ input.step = 'any';
+ input.value = '1966-12-12T12:00';
+ checkValidity(input, true, apply);
+
+ input.step = 'ANY';
+ input.value = '2017-01-01 12:00';
+ checkValidity(input, true, apply);
+
+ // When min is set to a valid datetime, there is a step base.
+ input.min = '2017-01-01T00:00:00';
+ input.step = '2';
+ input.value = '2017-01-01T00:00:02';
+ checkValidity(input, true, apply);
+
+ input.value = '2017-01-01T00:00:03';
+ checkValidity(input, false, apply,
+ { low: "2017-01-01T00:00:02", high: "2017-01-01T00:00:04" });
+
+ input.min = '2017-01-01T00:00:05';
+ input.value = '2017-01-01T00:00:08';
+ checkValidity(input, false, apply,
+ { low: "2017-01-01T00:00:07", high: "2017-01-01T00:00:09" });
+
+ input.min = '2000-01-01T00:00';
+ input.step = '120';
+ input.value = '2000-01-01T00:02';
+ checkValidity(input, true, apply);
+
+ // Without any step attribute the datetime is valid
+ input.removeAttribute('step');
+ checkValidity(input, true, apply);
+
+ input.min = '1950-01-01T00:00';
+ input.step = '129600'; // 1.5 day
+ input.value = '1950-01-02T00:00';
+ checkValidity(input, false, apply,
+ { low: "1950-01-01T00:00", high: "1950-01-02T12:00" });
+
+ input.step = '259200'; // 3 days
+ input.value = '1950-01-04T12:00';
+ checkValidity(input, false, apply,
+ { low: "1950-01-04T00:00", high: "1950-01-07T00:00" });
+
+ input.value = '1950-01-10T00:00';
+ checkValidity(input, true, apply);
+
+ input.step = '0.5'; // half a second
+ input.value = '1950-01-01T00:00:00.123';
+ checkValidity(input, false, apply,
+ { low: "1950-01-01T00:00", high: "1950-01-01T00:00:00.500" });
+
+ input.value = '2000-01-01T12:30:30.600';
+ checkValidity(input, false, apply,
+ { low: "2000-01-01T12:30:30.500", high: "2000-01-01T12:30:31" });
+
+ input.value = '1950-01-05T00:00:00.500';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1991-01-01T12:00';
+ input.value = '1991-01-01T12:00';
+ checkValidity(input, true, apply);
+
+ input.value = '1991-01-01T12:00:03';
+ checkValidity(input, false, apply,
+ { low: "1991-01-01T12:00:02.100", high: "1991-01-01T12:00:04.200" });
+
+ input.value = '1991-01-01T12:00:06.3';
+ checkValidity(input, true, apply);
+
+ input.step = '2.1';
+ input.min = '1969-12-20T10:00:05';
+ input.value = '1969-12-20T10:00:05';
+ checkValidity(input, true, apply);
+
+ input.value = '1969-12-20T10:00:08';
+ checkValidity(input, false, apply,
+ { low: "1969-12-20T10:00:07.100", high: "1969-12-20T10:00:09.200" });
+
+ input.value = '1969-12-20T10:00:09.200';
+ checkValidity(input, true, apply);
+
+ break;
+ default:
+ ok(false, "Implement the tests for <input type='" + test.type + " >");
+ break;
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_stepup_stepdown.html b/dom/html/test/forms/test_stepup_stepdown.html
new file mode 100644
index 0000000000..8ad7fbfeee
--- /dev/null
+++ b/dom/html/test/forms/test_stepup_stepdown.html
@@ -0,0 +1,1137 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=636627
+-->
+<head>
+ <title>Test for Bug 636627</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=636627">Mozilla Bug 636627</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 636627 **/
+
+/**
+ * This test is testing stepDown() and stepUp().
+ */
+
+function checkPresence()
+{
+ var input = document.createElement('input');
+ is('stepDown' in input, true, 'stepDown() should be an input function');
+ is('stepUp' in input, true, 'stepUp() should be an input function');
+}
+
+function checkAvailability()
+{
+ var testData =
+ [
+ ["text", false],
+ ["password", false],
+ ["search", false],
+ ["telephone", false],
+ ["email", false],
+ ["url", false],
+ ["hidden", false],
+ ["checkbox", false],
+ ["radio", false],
+ ["file", false],
+ ["submit", false],
+ ["image", false],
+ ["reset", false],
+ ["button", false],
+ ["number", true],
+ ["range", true],
+ ["date", true],
+ ["time", true],
+ ["month", true],
+ ["week", true],
+ ["datetime-local", true],
+ ["color", false],
+ ];
+
+ var element = document.createElement("input");
+ element.setAttribute('value', '0');
+
+ for (data of testData) {
+ var exceptionCaught = false;
+ element.type = data[0];
+ try {
+ element.stepDown();
+ } catch (e) {
+ exceptionCaught = true;
+ }
+ is(exceptionCaught, !data[1], "stepDown() availability is not correct");
+
+ exceptionCaught = false;
+ try {
+ element.stepUp();
+ } catch (e) {
+ exceptionCaught = true;
+ }
+ is(exceptionCaught, !data[1], "stepUp() availability is not correct");
+ }
+}
+
+function checkStepDown()
+{
+ // This testData is very similar to the one in checkStepUp with some changes
+ // relative to stepDown.
+ var testData = [
+ /* Initial value | step | min | max | stepDown arg | final value | exception */
+ { type: 'number', data: [
+ // Regular case.
+ [ '1', null, null, null, null, '0', false ],
+ // Argument testing.
+ [ '1', null, null, null, 1, '0', false ],
+ [ '9', null, null, null, 9, '0', false ],
+ [ '1', null, null, null, -1, '2', false ],
+ [ '1', null, null, null, 0, '1', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '1', null, null, null, 1.1, '0', false ],
+ // With step values.
+ [ '1', '0.5', null, null, null, '0.5', false ],
+ [ '1', '0.25', null, null, 4, '0', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '1', '0', null, null, null, '0', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '1', '-1', null, null, null, '0', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '1', 'foo', null, null, null, '0', false ],
+ // Min values testing.
+ [ '1', '1', 'foo', null, null, '0', false ],
+ [ '1', null, '-10', null, null, '0', false ],
+ [ '1', null, '0', null, null, '0', false ],
+ [ '1', null, '10', null, null, '1', false ],
+ [ '1', null, '2', null, null, '1', false ],
+ [ '1', null, '1', null, null, '1', false ],
+ // Max values testing.
+ [ '1', '1', null, 'foo', null, '0', false ],
+ [ '1', null, null, '10', null, '0', false ],
+ [ '1', null, null, '0', null, '0', false ],
+ [ '1', null, null, '-10', null, '-10', false ],
+ [ '1', null, null, '1', null, '0', false ],
+ [ '5', null, null, '3', '3', '2', false ],
+ [ '5', '2', '-6', '3', '2', '2', false ],
+ [ '-3', '5', '-10', '-3', null, '-5', false ],
+ // Step mismatch.
+ [ '1', '2', '-2', null, null, '0', false ],
+ [ '3', '2', '-2', null, null, '2', false ],
+ [ '3', '2', '-2', null, '2', '0', false ],
+ [ '3', '2', '-2', null, '-2', '6', false ],
+ [ '1', '2', '-6', null, null, '0', false ],
+ [ '1', '2', '-2', null, null, '0', false ],
+ [ '1', '3', '-6', null, null, '0', false ],
+ [ '2', '3', '-6', null, null, '0', false ],
+ [ '2', '3', '1', null, null, '1', false ],
+ [ '5', '3', '1', null, null, '4', false ],
+ [ '3', '2', '-6', null, null, '2', false ],
+ [ '5', '2', '-6', null, null, '4', false ],
+ [ '6', '2', '1', null, null, '5', false ],
+ [ '8', '3', '1', null, null, '7', false ],
+ [ '9', '2', '-10', null, null, '8', false ],
+ [ '7', '3', '-10', null, null, '5', false ],
+ [ '-2', '3', '-10', null, null, '-4', false ],
+ // Clamping.
+ [ '0', '2', '-1', null, null, '-1', false ],
+ [ '10', '2', '0', '4', '10', '0', false ],
+ [ '10', '2', '0', '4', '5', '0', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '-1', false ],
+ [ '', '2', null, null, null, '-2', false ],
+ [ '', '2', '3', null, null, '3', false ],
+ [ '', null, '3', null, null, '3', false ],
+ [ '', '2', '3', '8', null, '3', false ],
+ [ '', null, '-10', '10', null, '-1', false ],
+ [ '', '3', '-10', '10', null, '-1', false ],
+ // With step = 'any'.
+ [ '0', 'any', null, null, 1, null, true ],
+ [ '0', 'ANY', null, null, 1, null, true ],
+ [ '0', 'AnY', null, null, 1, null, true ],
+ [ '0', 'aNy', null, null, 1, null, true ],
+ // With @value = step base.
+ [ '1', '2', null, null, null, '-1', false ],
+ ]},
+ { type: 'range', data: [
+ // Regular case.
+ [ '1', null, null, null, null, '0', false ],
+ // Argument testing.
+ [ '1', null, null, null, 1, '0', false ],
+ [ '9', null, null, null, 9, '0', false ],
+ [ '1', null, null, null, -1, '2', false ],
+ [ '1', null, null, null, 0, '1', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '1', null, null, null, 1.1, '0', false ],
+ // With step values.
+ [ '1', '0.5', null, null, null, '0.5', false ],
+ [ '1', '0.25', null, null, 4, '0', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '1', '0', null, null, null, '0', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '1', '-1', null, null, null, '0', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '1', 'foo', null, null, null, '0', false ],
+ // Min values testing.
+ [ '1', '1', 'foo', null, null, '0', false ],
+ [ '1', null, '-10', null, null, '0', false ],
+ [ '1', null, '0', null, null, '0', false ],
+ [ '1', null, '10', null, null, '10', false ],
+ [ '1', null, '2', null, null, '2', false ],
+ [ '1', null, '1', null, null, '1', false ],
+ // Max values testing.
+ [ '1', '1', null, 'foo', null, '0', false ],
+ [ '1', null, null, '10', null, '0', false ],
+ [ '1', null, null, '0', null, '0', false ],
+ [ '1', null, null, '-10', null, '0', false ],
+ [ '1', null, null, '1', null, '0', false ],
+ [ '5', null, null, '3', '3', '0', false ],
+ [ '5', '2', '-6', '3', '2', '-2', false ],
+ [ '-3', '5', '-10', '-3', null, '-10', false ],
+ // Step mismatch.
+ [ '1', '2', '-2', null, null, '0', false ],
+ [ '3', '2', '-2', null, null, '2', false ],
+ [ '3', '2', '-2', null, '2', '0', false ],
+ [ '3', '2', '-2', null, '-2', '8', false ],
+ [ '1', '2', '-6', null, null, '0', false ],
+ [ '1', '2', '-2', null, null, '0', false ],
+ [ '1', '3', '-6', null, null, '-3', false ],
+ [ '2', '3', '-6', null, null, '0', false ],
+ [ '2', '3', '1', null, null, '1', false ],
+ [ '5', '3', '1', null, null, '1', false ],
+ [ '3', '2', '-6', null, null, '2', false ],
+ [ '5', '2', '-6', null, null, '4', false ],
+ [ '6', '2', '1', null, null, '5', false ],
+ [ '8', '3', '1', null, null, '4', false ],
+ [ '9', '2', '-10', null, null, '8', false ],
+ [ '7', '3', '-10', null, null, '5', false ],
+ [ '-2', '3', '-10', null, null, '-4', false ],
+ // Clamping.
+ [ '0', '2', '-1', null, null, '-1', false ],
+ [ '10', '2', '0', '4', '10', '0', false ],
+ [ '10', '2', '0', '4', '5', '0', false ],
+ // value = "" (default will be 50).
+ [ '', null, null, null, null, '49', false ],
+ // With step = 'any'.
+ [ '0', 'any', null, null, 1, null, true ],
+ [ '0', 'ANY', null, null, 1, null, true ],
+ [ '0', 'AnY', null, null, 1, null, true ],
+ [ '0', 'aNy', null, null, 1, null, true ],
+ // With @value = step base.
+ [ '1', '2', null, null, null, '1', false ],
+ ]},
+ { type: 'date', data: [
+ // Regular case.
+ [ '2012-07-09', null, null, null, null, '2012-07-08', false ],
+ // Argument testing.
+ [ '2012-07-09', null, null, null, 1, '2012-07-08', false ],
+ [ '2012-07-09', null, null, null, 5, '2012-07-04', false ],
+ [ '2012-07-09', null, null, null, -1, '2012-07-10', false ],
+ [ '2012-07-09', null, null, null, 0, '2012-07-09', false ],
+ // Month/Year wrapping.
+ [ '2012-08-01', null, null, null, 1, '2012-07-31', false ],
+ [ '1969-01-02', null, null, null, 4, '1968-12-29', false ],
+ [ '1969-01-01', null, null, null, -365, '1970-01-01', false ],
+ [ '2012-02-29', null, null, null, -1, '2012-03-01', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '2012-01-02', null, null, null, 1.1, '2012-01-01', false ],
+ [ '2012-01-02', null, null, null, 1.9, '2012-01-01', false ],
+ // With step values.
+ [ '2012-01-03', '0.5', null, null, null, '2012-01-02', false ],
+ [ '2012-01-02', '0.5', null, null, null, '2012-01-01', false ],
+ [ '2012-01-01', '2', null, null, null, '2011-12-30', false ],
+ [ '2012-01-02', '0.25',null, null, 4, '2011-12-29', false ],
+ [ '2012-01-15', '1.1', '2012-01-01', null, 1, '2012-01-14', false ],
+ [ '2012-01-12', '1.1', '2012-01-01', null, 2, '2012-01-10', false ],
+ [ '2012-01-23', '1.1', '2012-01-01', null, 10, '2012-01-13', false ],
+ [ '2012-01-23', '1.1', '2012-01-01', null, 11, '2012-01-12', false ],
+ [ '1968-01-12', '1.1', '1968-01-01', null, 8, '1968-01-04', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2012-01-02', '0', null, null, null, '2012-01-01', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2012-01-02', '-1', null, null, null, '2012-01-01', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2012-01-02', 'foo', null, null, null, '2012-01-01', false ],
+ // Min values testing.
+ [ '2012-01-03', '1', 'foo', null, 2, '2012-01-01', false ],
+ [ '2012-01-02', '1', '2012-01-01', null, null, '2012-01-01', false ],
+ [ '2012-01-01', '1', '2012-01-01', null, null, '2012-01-01', false ],
+ [ '2012-01-01', '1', '2012-01-10', null, 1, '2012-01-01', false ],
+ [ '2012-01-05', '3', '2012-01-01', null, null, '2012-01-04', false ],
+ [ '1969-01-01', '5', '1969-01-01', '1969-01-02', null, '1969-01-01', false ],
+ // Max values testing.
+ [ '2012-01-02', '1', null, 'foo', null, '2012-01-01', false ],
+ [ '2012-01-02', null, null, '2012-01-05', null, '2012-01-01', false ],
+ [ '2012-01-03', null, null, '2012-01-03', null, '2012-01-02', false ],
+ [ '2012-01-07', null, null, '2012-01-04', 4, '2012-01-03', false ],
+ [ '2012-01-07', '2', null, '2012-01-04', 3, '2012-01-01', false ],
+ // Step mismatch.
+ [ '2012-01-04', '2', '2012-01-01', null, null, '2012-01-03', false ],
+ [ '2012-01-06', '2', '2012-01-01', null, 2, '2012-01-03', false ],
+ [ '2012-01-05', '2', '2012-01-04', '2012-01-08', null, '2012-01-04', false ],
+ [ '1970-01-04', '2', null, null, null, '1970-01-02', false ],
+ [ '1970-01-09', '3', null, null, null, '1970-01-06', false ],
+ // Clamping.
+ [ '2012-05-01', null, null, '2012-01-05', null, '2012-01-05', false ],
+ [ '1970-01-05', '2', '1970-01-02', '1970-01-05', null, '1970-01-04', false ],
+ [ '1970-01-01', '5', '1970-01-02', '1970-01-09', 10, '1970-01-01', false ],
+ [ '1970-01-07', '5', '1969-12-27', '1970-01-06', 2, '1970-01-01', false ],
+ [ '1970-03-08', '3', '1970-02-01', '1970-02-07', 15, '1970-02-01', false ],
+ [ '1970-01-10', '3', '1970-01-01', '1970-01-06', 2, '1970-01-04', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1969-12-31', false ],
+ // With step = 'any'.
+ [ '2012-01-01', 'any', null, null, 1, null, true ],
+ [ '2012-01-01', 'ANY', null, null, 1, null, true ],
+ [ '2012-01-01', 'AnY', null, null, 1, null, true ],
+ [ '2012-01-01', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'time', data: [
+ // Regular case.
+ [ '16:39', null, null, null, null, '16:38', false ],
+ // Argument testing.
+ [ '16:40', null, null, null, 1, '16:39', false ],
+ [ '16:40', null, null, null, 5, '16:35', false ],
+ [ '16:40', null, null, null, -1, '16:41', false ],
+ [ '16:40', null, null, null, 0, '16:40', false ],
+ // hour/minutes/seconds wrapping.
+ [ '05:00', null, null, null, null, '04:59', false ],
+ [ '05:00:00', 1, null, null, null, '04:59:59', false ],
+ [ '05:00:00', 0.1, null, null, null, '04:59:59.900', false ],
+ [ '05:00:00', 0.01, null, null, null, '04:59:59.990', false ],
+ [ '05:00:00', 0.001, null, null, null, '04:59:59.999', false ],
+ // stepDown() on '00:00' gives '23:59'.
+ [ '00:00', null, null, null, 1, '23:59', false ],
+ [ '00:00', null, null, null, 3, '23:57', false ],
+ // Some random step values..
+ [ '16:56', '0.5', null, null, null, '16:55:59.500', false ],
+ [ '16:56', '2', null, null, null, '16:55:58', false ],
+ [ '16:56', '0.25',null, null, 4, '16:55:59', false ],
+ [ '16:57', '1.1', '16:00', null, 1, '16:56:59.900', false ],
+ [ '16:57', '1.1', '16:00', null, 2, '16:56:58.800', false ],
+ [ '16:57', '1.1', '16:00', null, 10, '16:56:50', false ],
+ [ '16:57', '1.1', '16:00', null, 11, '16:56:48.900', false ],
+ [ '16:57', '1.1', '16:00', null, 8, '16:56:52.200', false ],
+ // Invalid @step, means that we use the default value.
+ [ '17:01', '0', null, null, null, '17:00', false ],
+ [ '17:01', '-1', null, null, null, '17:00', false ],
+ [ '17:01', 'foo', null, null, null, '17:00', false ],
+ // Min values testing.
+ [ '17:02', '60', 'foo', null, 2, '17:00', false ],
+ [ '17:10', '60', '17:09', null, null, '17:09', false ],
+ [ '17:10', '60', '17:10', null, null, '17:10', false ],
+ [ '17:10', '60', '17:30', null, 1, '17:10', false ],
+ [ '17:10', '180', '17:05', null, null, '17:08', false ],
+ [ '17:10', '300', '17:10', '17:11', null, '17:10', false ],
+ // Max values testing.
+ [ '17:15', '60', null, 'foo', null, '17:14', false ],
+ [ '17:15', null, null, '17:20', null, '17:14', false ],
+ [ '17:15', null, null, '17:15', null, '17:14', false ],
+ [ '17:15', null, null, '17:13', 4, '17:11', false ],
+ [ '17:15', '120', null, '17:13', 3, '17:09', false ],
+ // Step mismatch.
+ [ '17:19', '120', '17:10', null, null, '17:18', false ],
+ [ '17:19', '120', '17:10', null, 2, '17:16', false ],
+ [ '17:19', '120', '17:18', '17:25', null, '17:18', false ],
+ [ '17:19', '120', null, null, null, '17:17', false ],
+ [ '17:19', '180', null, null, null, '17:16', false ],
+ // Clamping.
+ [ '17:22', null, null, '17:11', null, '17:11', false ],
+ [ '17:22', '120', '17:20', '17:22', null, '17:20', false ],
+ [ '17:22', '300', '17:12', '17:20', 10, '17:12', false ],
+ [ '17:22', '300', '17:18', '17:20', 2, '17:18', false ],
+ [ '17:22', '180', '17:00', '17:20', 15, '17:00', false ],
+ [ '17:22', '180', '17:10', '17:20', 2, '17:16', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '23:59', false ],
+ // With step = 'any'.
+ [ '17:26', 'any', null, null, 1, null, true ],
+ [ '17:26', 'ANY', null, null, 1, null, true ],
+ [ '17:26', 'AnY', null, null, 1, null, true ],
+ [ '17:26', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'month', data: [
+ // Regular case.
+ [ '2016-08', null, null, null, null, '2016-07', false ],
+ // Argument testing.
+ [ '2016-08', null, null, null, 1, '2016-07', false ],
+ [ '2016-08', null, null, null, 5, '2016-03', false ],
+ [ '2016-08', null, null, null, -1, '2016-09', false ],
+ [ '2016-08', null, null, null, 0, '2016-08', false ],
+ // Month/Year wrapping.
+ [ '2016-01', null, null, null, 1, '2015-12', false ],
+ [ '1969-02', null, null, null, 4, '1968-10', false ],
+ [ '1969-01', null, null, null, -12, '1970-01', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '2016-08', null, null, null, 1.1, '2016-07', false ],
+ [ '2016-01', null, null, null, 1.9, '2015-12', false ],
+ // With step values.
+ [ '2016-03', '0.5', null, null, null, '2016-02', false ],
+ [ '2016-03', '2', null, null, null, '2016-01', false ],
+ [ '2016-03', '0.25',null, null, 4, '2015-11', false ],
+ [ '2016-12', '1.1', '2016-01', null, 1, '2016-11', false ],
+ [ '2016-12', '1.1', '2016-01', null, 2, '2016-10', false ],
+ [ '2016-12', '1.1', '2016-01', null, 10, '2016-02', false ],
+ [ '2016-12', '1.1', '2016-01', null, 12, '2016-01', false ],
+ [ '1968-12', '1.1', '1968-01', null, 8, '1968-04', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2016-02', '0', null, null, null, '2016-01', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2016-02', '-1', null, null, null, '2016-01', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2016-02', 'foo', null, null, null, '2016-01', false ],
+ // Min values testing.
+ [ '2016-03', '1', 'foo', null, 2, '2016-01', false ],
+ [ '2016-02', '1', '2016-01', null, null, '2016-01', false ],
+ [ '2016-01', '1', '2016-01', null, null, '2016-01', false ],
+ [ '2016-01', '1', '2016-01', null, 1, '2016-01', false ],
+ [ '2016-05', '3', '2016-01', null, null, '2016-04', false ],
+ [ '1969-01', '5', '1969-01', '1969-02', null, '1969-01', false ],
+ // Max values testing.
+ [ '2016-02', '1', null, 'foo', null, '2016-01', false ],
+ [ '2016-02', null, null, '2016-05', null, '2016-01', false ],
+ [ '2016-03', null, null, '2016-03', null, '2016-02', false ],
+ [ '2016-07', null, null, '2016-04', 4, '2016-03', false ],
+ [ '2016-07', '2', null, '2016-04', 3, '2016-01', false ],
+ // Step mismatch.
+ [ '2016-04', '2', '2016-01', null, null, '2016-03', false ],
+ [ '2016-06', '2', '2016-01', null, 2, '2016-03', false ],
+ [ '2016-05', '2', '2016-04', '2016-08', null, '2016-04', false ],
+ [ '1970-04', '2', null, null, null, '1970-02', false ],
+ [ '1970-09', '3', null, null, null, '1970-06', false ],
+ // Clamping.
+ [ '2016-05', null, null, '2016-01', null, '2016-01', false ],
+ [ '1970-05', '2', '1970-02', '1970-05', null, '1970-04', false ],
+ [ '1970-01', '5', '1970-02', '1970-09', 10, '1970-01', false ],
+ [ '1970-07', '5', '1969-12', '1970-10', 2, '1969-12', false ],
+ [ '1970-08', '3', '1970-01', '1970-07', 15, '1970-01', false ],
+ [ '1970-10', '3', '1970-01', '1970-06', 2, '1970-04', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1969-12', false ],
+ // With step = 'any'.
+ [ '2016-01', 'any', null, null, 1, null, true ],
+ [ '2016-01', 'ANY', null, null, 1, null, true ],
+ [ '2016-01', 'AnY', null, null, 1, null, true ],
+ [ '2016-01', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'week', data: [
+ // Regular case.
+ [ '2016-W40', null, null, null, null, '2016-W39', false ],
+ // Argument testing.
+ [ '2016-W40', null, null, null, 1, '2016-W39', false ],
+ [ '2016-W40', null, null, null, 5, '2016-W35', false ],
+ [ '2016-W40', null, null, null, -1, '2016-W41', false ],
+ [ '2016-W40', null, null, null, 0, '2016-W40', false ],
+ // Week/Year wrapping.
+ [ '2016-W01', null, null, null, 1, '2015-W53', false ],
+ [ '1969-W02', null, null, null, 4, '1968-W50', false ],
+ [ '1969-W01', null, null, null, -52, '1970-W01', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '2016-W40', null, null, null, 1.1, '2016-W39', false ],
+ [ '2016-W01', null, null, null, 1.9, '2015-W53', false ],
+ // With step values.
+ [ '2016-W03', '0.5', null, null, null, '2016-W02', false ],
+ [ '2016-W03', '2', null, null, null, '2016-W01', false ],
+ [ '2016-W03', '0.25', null, null, 4, '2015-W52', false ],
+ [ '2016-W52', '1.1', '2016-W01', null, 1, '2016-W51', false ],
+ [ '2016-W52', '1.1', '2016-W01', null, 2, '2016-W50', false ],
+ [ '2016-W52', '1.1', '2016-W01', null, 10, '2016-W42', false ],
+ [ '2016-W52', '1.1', '2016-W01', null, 52, '2016-W01', false ],
+ [ '1968-W52', '1.1', '1968-W01', null, 8, '1968-W44', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2016-W02', '0', null, null, null, '2016-W01', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2016-W02', '-1', null, null, null, '2016-W01', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2016-W02', 'foo', null, null, null, '2016-W01', false ],
+ // Min values testing.
+ [ '2016-W03', '1', 'foo', null, 2, '2016-W01', false ],
+ [ '2016-W02', '1', '2016-01', null, null, '2016-W01', false ],
+ [ '2016-W01', '1', '2016-W01', null, null, '2016-W01', false ],
+ [ '2016-W01', '1', '2016-W01', null, 1, '2016-W01', false ],
+ [ '2016-W05', '3', '2016-W01', null, null, '2016-W04', false ],
+ [ '1969-W01', '5', '1969-W01', '1969-W02', null, '1969-W01', false ],
+ // Max values testing.
+ [ '2016-W02', '1', null, 'foo', null, '2016-W01', false ],
+ [ '2016-W02', null, null, '2016-W05', null, '2016-W01', false ],
+ [ '2016-W03', null, null, '2016-W03', null, '2016-W02', false ],
+ [ '2016-W07', null, null, '2016-W04', 4, '2016-W03', false ],
+ [ '2016-W07', '2', null, '2016-W04', 3, '2016-W01', false ],
+ // Step mismatch.
+ [ '2016-W04', '2', '2016-W01', null, null, '2016-W03', false ],
+ [ '2016-W06', '2', '2016-W01', null, 2, '2016-W03', false ],
+ [ '2016-W05', '2', '2016-W04', '2016-W08', null, '2016-W04', false ],
+ [ '1970-W04', '2', null, null, null, '1970-W02', false ],
+ [ '1970-W09', '3', null, null, null, '1970-W06', false ],
+ // Clamping.
+ [ '2016-W05', null, null, '2016-W01', null, '2016-W01', false ],
+ [ '1970-W05', '2', '1970-W02', '1970-W05', null, '1970-W04', false ],
+ [ '1970-W01', '5', '1970-W02', '1970-W09', 10, '1970-W01', false ],
+ [ '1970-W07', '5', '1969-W52', '1970-W10', 2, '1969-W52', false ],
+ [ '1970-W08', '3', '1970-W01', '1970-W07', 15, '1970-W01', false ],
+ [ '1970-W10', '3', '1970-W01', '1970-W06', 2, '1970-W04', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1970-W01', false ],
+ // With step = 'any'.
+ [ '2016-W01', 'any', null, null, 1, null, true ],
+ [ '2016-W01', 'ANY', null, null, 1, null, true ],
+ [ '2016-W01', 'AnY', null, null, 1, null, true ],
+ [ '2016-W01', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'datetime-local', data: [
+ // Regular case.
+ [ '2017-02-07T09:30', null, null, null, null, '2017-02-07T09:29', false ],
+ // Argument testing.
+ [ '2017-02-07T09:30', null, null, null, 1, '2017-02-07T09:29', false ],
+ [ '2017-02-07T09:30', null, null, null, 5, '2017-02-07T09:25', false ],
+ [ '2017-02-07T09:30', null, null, null, -1, '2017-02-07T09:31', false ],
+ [ '2017-02-07T09:30', null, null, null, 0, '2017-02-07T09:30', false ],
+ // hour/minutes/seconds wrapping.
+ [ '2000-01-01T05:00', null, null, null, null, '2000-01-01T04:59', false ],
+ [ '2000-01-01T05:00:00', 1, null, null, null, '2000-01-01T04:59:59', false ],
+ [ '2000-01-01T05:00:00', 0.1, null, null, null, '2000-01-01T04:59:59.900', false ],
+ [ '2000-01-01T05:00:00', 0.01, null, null, null, '2000-01-01T04:59:59.990', false ],
+ [ '2000-01-01T05:00:00', 0.001, null, null, null, '2000-01-01T04:59:59.999', false ],
+ // month/year wrapping.
+ [ '2012-08-01T12:00', null, null, null, 1440, '2012-07-31T12:00', false ],
+ [ '1969-01-02T12:00', null, null, null, 5760, '1968-12-29T12:00', false ],
+ [ '1969-12-31T00:00', null, null, null, -1440, '1970-01-01T00:00', false ],
+ [ '2012-02-29T00:00', null, null, null, -1440, '2012-03-01T00:00', false ],
+ // stepDown() on '00:00' gives '23:59'.
+ [ '2017-02-07T00:00', null, null, null, 1, '2017-02-06T23:59', false ],
+ [ '2017-02-07T00:00', null, null, null, 3, '2017-02-06T23:57', false ],
+ // Some random step values..
+ [ '2017-02-07T16:07', '0.5', null, null, null, '2017-02-07T16:06:59.500', false ],
+ [ '2017-02-07T16:07', '2', null, null, null, '2017-02-07T16:06:58', false ],
+ [ '2017-02-07T16:07', '0.25', null, null, 4, '2017-02-07T16:06:59', false ],
+ [ '2017-02-07T16:07', '1.1', '2017-02-07T16:00', null, 1, '2017-02-07T16:06:59.100', false ],
+ [ '2017-02-07T16:07', '1.1', '2017-02-07T16:00', null, 2, '2017-02-07T16:06:58', false ],
+ [ '2017-02-07T16:07', '1.1', '2017-02-07T16:00', null, 10, '2017-02-07T16:06:49.200', false ],
+ [ '2017-02-07T16:07', '129600', '2017-02-01T00:00', null, 2, '2017-02-05T12:00', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2017-02-07T10:15', '0', null, null, null, '2017-02-07T10:14', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2017-02-07T10:15', '-1', null, null, null, '2017-02-07T10:14', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2017-02-07T10:15', 'foo', null, null, null, '2017-02-07T10:14', false ],
+ // Min values testing.
+ [ '2012-02-02T17:02', '60', 'foo', null, 2, '2012-02-02T17:00', false ],
+ [ '2012-02-02T17:10', '60', '2012-02-02T17:09', null, null, '2012-02-02T17:09', false ],
+ [ '2012-02-02T17:10', '60', '2012-02-02T17:10', null, null, '2012-02-02T17:10', false ],
+ [ '2012-02-02T17:10', '60', '2012-02-02T17:30', null, 1, '2012-02-02T17:10', false ],
+ [ '2012-02-02T17:10', '180', '2012-02-02T17:05', null, null, '2012-02-02T17:08', false ],
+ [ '2012-02-03T20:05', '86400', '2012-02-02T17:05', null, null, '2012-02-03T17:05', false ],
+ [ '2012-02-03T18:00', '129600', '2012-02-01T00:00', null, null, '2012-02-02T12:00', false ],
+ // Max values testing.
+ [ '2012-02-02T17:15', '60', null, 'foo', null, '2012-02-02T17:14', false ],
+ [ '2012-02-02T17:15', null, null, '2012-02-02T17:20', null, '2012-02-02T17:14', false ],
+ [ '2012-02-02T17:15', null, null, '2012-02-02T17:15', null, '2012-02-02T17:14', false ],
+ [ '2012-02-02T17:15', null, null, '2012-02-02T17:13', 4, '2012-02-02T17:11', false ],
+ [ '2012-02-02T17:15', '120', null, '2012-02-02T17:13', 3, '2012-02-02T17:09', false ],
+ [ '2012-02-03T20:05', '86400', null, '2012-02-03T20:05', null, '2012-02-02T20:05', false ],
+ [ '2012-02-03T18:00', '129600', null, '2012-02-03T20:00', null, '2012-02-02T06:00', false ],
+ // Step mismatch.
+ [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, null, '2017-02-07T17:18', false ],
+ [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, 2, '2017-02-07T17:16', false ],
+ [ '2017-02-07T17:19', '120', '2017-02-07T17:18', '2017-02-07T17:25', null, '2017-02-07T17:18', false ],
+ [ '2017-02-07T17:19', '120', null, null, null, '2017-02-07T17:17', false ],
+ [ '2017-02-07T17:19', '180', null, null, null, '2017-02-07T17:16', false ],
+ [ '2017-02-07T17:19', '172800', '2017-02-02T17:19', '2017-02-10T17:19', null, '2017-02-06T17:19', false ],
+ // Clamping.
+ [ '2017-02-07T17:22', null, null, '2017-02-07T17:11', null, '2017-02-07T17:11', false ],
+ [ '2017-02-07T17:22', '120', '2017-02-07T17:20', '2017-02-07T17:22', null, '2017-02-07T17:20', false ],
+ [ '2017-02-07T17:22', '300', '2017-02-07T17:12', '2017-02-07T17:20', 10, '2017-02-07T17:12', false ],
+ [ '2017-02-07T17:22', '300', '2017-02-07T17:18', '2017-02-07T17:20', 2, '2017-02-07T17:18', false ],
+ [ '2017-02-07T17:22', '600', '2017-02-02T17:00', '2017-02-07T17:00', 15, '2017-02-07T15:00', false ],
+ [ '2017-02-07T17:22', '600', '2017-02-02T17:00', '2017-02-07T17:00', 2, '2017-02-07T17:00', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1969-12-31T23:59', false ],
+ // With step = 'any'.
+ [ '2017-02-07T15:20', 'any', null, null, 1, null, true ],
+ [ '2017-02-07T15:20', 'ANY', null, null, 1, null, true ],
+ [ '2017-02-07T15:20', 'AnY', null, null, 1, null, true ],
+ [ '2017-02-07T15:20', 'aNy', null, null, 1, null, true ],
+ ]},
+ ];
+
+ for (var test of testData) {
+ for (var data of test.data) {
+ var element = document.createElement("input");
+ element.type = test.type;
+
+ if (data[1] != null) {
+ element.step = data[1];
+ }
+
+ if (data[2] != null) {
+ element.min = data[2];
+ }
+
+ if (data[3] != null) {
+ element.max = data[3];
+ }
+
+ // Set 'value' last for type=range, because the final sanitized value
+ // after setting 'step', 'min' and 'max' can be affected by the order in
+ // which those attributes are set. Setting 'value' last makes it simpler
+ // to reason about what the final value should be.
+ if (data[0] != null) {
+ element.setAttribute('value', data[0]);
+ }
+
+ var exceptionCaught = false;
+ try {
+ if (data[4] != null) {
+ element.stepDown(data[4]);
+ } else {
+ element.stepDown();
+ }
+
+ is(element.value, data[5], "The value for type=" + test.type + " should be " + data[5]);
+ } catch (e) {
+ exceptionCaught = true;
+ is(element.value, data[0], e.name + "The value should not have changed");
+ is(e.name, 'InvalidStateError',
+ "It should be a InvalidStateError exception.");
+ } finally {
+ is(exceptionCaught, data[6], "exception status should be " + data[6]);
+ }
+ }
+ }
+}
+
+function checkStepUp()
+{
+ // This testData is very similar to the one in checkStepDown with some changes
+ // relative to stepUp.
+ var testData = [
+ /* Initial value | step | min | max | stepUp arg | final value | exception */
+ { type: 'number', data: [
+ // Regular case.
+ [ '1', null, null, null, null, '2', false ],
+ // Argument testing.
+ [ '1', null, null, null, 1, '2', false ],
+ [ '9', null, null, null, 9, '18', false ],
+ [ '1', null, null, null, -1, '0', false ],
+ [ '1', null, null, null, 0, '1', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '1', null, null, null, 1.1, '2', false ],
+ // With step values.
+ [ '1', '0.5', null, null, null, '1.5', false ],
+ [ '1', '0.25', null, null, 4, '2', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '1', '0', null, null, null, '2', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '1', '-1', null, null, null, '2', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '1', 'foo', null, null, null, '2', false ],
+ // Min values testing.
+ [ '1', '1', 'foo', null, null, '2', false ],
+ [ '1', null, '-10', null, null, '2', false ],
+ [ '1', null, '0', null, null, '2', false ],
+ [ '1', null, '10', null, null, '10', false ],
+ [ '1', null, '2', null, null, '2', false ],
+ [ '1', null, '1', null, null, '2', false ],
+ [ '0', null, '4', null, '5', '5', false ],
+ [ '0', '2', '5', null, '3', '5', false ],
+ // Max values testing.
+ [ '1', '1', null, 'foo', null, '2', false ],
+ [ '1', null, null, '10', null, '2', false ],
+ [ '1', null, null, '0', null, '1', false ],
+ [ '1', null, null, '-10', null, '1', false ],
+ [ '1', null, null, '1', null, '1', false ],
+ [ '-3', '5', '-10', '-3', null, '-3', false ],
+ // Step mismatch.
+ [ '1', '2', '0', null, null, '2', false ],
+ [ '1', '2', '0', null, '2', '4', false ],
+ [ '8', '2', null, '9', null, '8', false ],
+ [ '-3', '2', '-6', null, null, '-2', false ],
+ [ '9', '3', '-10', null, null, '11', false ],
+ [ '7', '3', '-10', null, null, '8', false ],
+ [ '7', '3', '5', null, null, '8', false ],
+ [ '9', '4', '3', null, null, '11', false ],
+ [ '-2', '3', '-6', null, null, '0', false ],
+ [ '7', '3', '6', null, null, '9', false ],
+ // Clamping.
+ [ '1', '2', '0', '3', null, '2', false ],
+ [ '0', '5', '1', '8', '10', '6', false ],
+ [ '-9', '3', '-8', '-1', '5', '-2', false ],
+ [ '-9', '3', '8', '15', '15', '14', false ],
+ [ '-1', '3', '-1', '4', '3', '2', false ],
+ [ '-3', '2', '-6', '-2', null, '-2', false ],
+ [ '-3', '2', '-6', '-1', null, '-2', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1', false ],
+ [ '', null, null, null, null, '1', false ],
+ [ '', '2', null, null, null, '2', false ],
+ [ '', '2', '3', null, null, '3', false ],
+ [ '', null, '3', null, null, '3', false ],
+ [ '', '2', '3', '8', null, '3', false ],
+ [ '', null, '-10', '10', null, '1', false ],
+ [ '', '3', '-10', '10', null, '2', false ],
+ // With step = 'any'.
+ [ '0', 'any', null, null, 1, null, true ],
+ [ '0', 'ANY', null, null, 1, null, true ],
+ [ '0', 'AnY', null, null, 1, null, true ],
+ [ '0', 'aNy', null, null, 1, null, true ],
+ // With @value = step base.
+ [ '1', '2', null, null, null, '3', false ],
+ ]},
+ { type: 'range', data: [
+ // Regular case.
+ [ '1', null, null, null, null, '2', false ],
+ // Argument testing.
+ [ '1', null, null, null, 1, '2', false ],
+ [ '9', null, null, null, 9, '18', false ],
+ [ '1', null, null, null, -1, '0', false ],
+ [ '1', null, null, null, 0, '1', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '1', null, null, null, 1.1, '2', false ],
+ // With step values.
+ [ '1', '0.5', null, null, null, '1.5', false ],
+ [ '1', '0.25', null, null, 4, '2', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '1', '0', null, null, null, '2', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '1', '-1', null, null, null, '2', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '1', 'foo', null, null, null, '2', false ],
+ // Min values testing.
+ [ '1', '1', 'foo', null, null, '2', false ],
+ [ '1', null, '-10', null, null, '2', false ],
+ [ '1', null, '0', null, null, '2', false ],
+ [ '1', null, '10', null, null, '11', false ],
+ [ '1', null, '2', null, null, '3', false ],
+ [ '1', null, '1', null, null, '2', false ],
+ [ '0', null, '4', null, '5', '9', false ],
+ [ '0', '2', '5', null, '3', '11', false ],
+ // Max values testing.
+ [ '1', '1', null, 'foo', null, '2', false ],
+ [ '1', null, null, '10', null, '2', false ],
+ [ '1', null, null, '0', null, '0', false ],
+ [ '1', null, null, '-10', null, '0', false ],
+ [ '1', null, null, '1', null, '1', false ],
+ [ '-3', '5', '-10', '-3', null, '-5', false ],
+ // Step mismatch.
+ [ '1', '2', '0', null, null, '4', false ],
+ [ '1', '2', '0', null, '2', '6', false ],
+ [ '8', '2', null, '9', null, '8', false ],
+ [ '-3', '2', '-6', null, null, '0', false ],
+ [ '9', '3', '-10', null, null, '11', false ],
+ [ '7', '3', '-10', null, null, '11', false ],
+ [ '7', '3', '5', null, null, '11', false ],
+ [ '9', '4', '3', null, null, '15', false ],
+ [ '-2', '3', '-6', null, null, '0', false ],
+ [ '7', '3', '6', null, null, '9', false ],
+ // Clamping.
+ [ '1', '2', '0', '3', null, '2', false ],
+ [ '0', '5', '1', '8', '10', '6', false ],
+ [ '-9', '3', '-8', '-1', '5', '-2', false ],
+ [ '-9', '3', '8', '15', '15', '14', false ],
+ [ '-1', '3', '-1', '4', '3', '2', false ],
+ [ '-3', '2', '-6', '-2', null, '-2', false ],
+ [ '-3', '2', '-6', '-1', null, '-2', false ],
+ // value = "" (default will be 50).
+ [ '', null, null, null, null, '51', false ],
+ // With step = 'any'.
+ [ '0', 'any', null, null, 1, null, true ],
+ [ '0', 'ANY', null, null, 1, null, true ],
+ [ '0', 'AnY', null, null, 1, null, true ],
+ [ '0', 'aNy', null, null, 1, null, true ],
+ // With @value = step base.
+ [ '1', '2', null, null, null, '3', false ],
+ ]},
+ { type: 'date', data: [
+ // Regular case.
+ [ '2012-07-09', null, null, null, null, '2012-07-10', false ],
+ // Argument testing.
+ [ '2012-07-09', null, null, null, 1, '2012-07-10', false ],
+ [ '2012-07-09', null, null, null, 9, '2012-07-18', false ],
+ [ '2012-07-09', null, null, null, -1, '2012-07-08', false ],
+ [ '2012-07-09', null, null, null, 0, '2012-07-09', false ],
+ // Month/Year wrapping.
+ [ '2012-07-31', null, null, null, 1, '2012-08-01', false ],
+ [ '1968-12-29', null, null, null, 4, '1969-01-02', false ],
+ [ '1970-01-01', null, null, null, -365, '1969-01-01', false ],
+ [ '2012-03-01', null, null, null, -1, '2012-02-29', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '2012-01-01', null, null, null, 1.1, '2012-01-02', false ],
+ [ '2012-01-01', null, null, null, 1.9, '2012-01-02', false ],
+ // With step values.
+ [ '2012-01-01', '0.5', null, null, null, '2012-01-02', false ],
+ [ '2012-01-01', '2', null, null, null, '2012-01-03', false ],
+ [ '2012-01-01', '0.25', null, null, 4, '2012-01-05', false ],
+ [ '2012-01-01', '1.1', '2012-01-01', null, 1, '2012-01-02', false ],
+ [ '2012-01-01', '1.1', '2012-01-01', null, 2, '2012-01-03', false ],
+ [ '2012-01-01', '1.1', '2012-01-01', null, 10, '2012-01-11', false ],
+ [ '2012-01-01', '1.1', '2012-01-01', null, 11, '2012-01-12', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2012-01-01', '0', null, null, null, '2012-01-02', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2012-01-01', '-1', null, null, null, '2012-01-02', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2012-01-01', 'foo', null, null, null, '2012-01-02', false ],
+ // Min values testing.
+ [ '2012-01-01', '1', 'foo', null, null, '2012-01-02', false ],
+ [ '2012-01-01', null, '2011-12-01', null, null, '2012-01-02', false ],
+ [ '2012-01-01', null, '2012-01-02', null, null, '2012-01-02', false ],
+ [ '2012-01-01', null, '2012-01-01', null, null, '2012-01-02', false ],
+ [ '2012-01-01', null, '2012-01-04', null, 4, '2012-01-05', false ],
+ [ '2012-01-01', '2', '2012-01-04', null, 3, '2012-01-06', false ],
+ // Max values testing.
+ [ '2012-01-01', '1', null, 'foo', 2, '2012-01-03', false ],
+ [ '2012-01-01', '1', null, '2012-01-10', 1, '2012-01-02', false ],
+ [ '2012-01-02', null, null, '2012-01-01', null, '2012-01-02', false ],
+ [ '2012-01-02', null, null, '2012-01-02', null, '2012-01-02', false ],
+ [ '1969-01-02', '5', '1969-01-01', '1969-01-02', null, '1969-01-02', false ],
+ // Step mismatch.
+ [ '2012-01-02', '2', '2012-01-01', null, null, '2012-01-03', false ],
+ [ '2012-01-02', '2', '2012-01-01', null, 2, '2012-01-05', false ],
+ [ '2012-01-05', '2', '2012-01-01', '2012-01-06', null, '2012-01-05', false ],
+ [ '1970-01-02', '2', null, null, null, '1970-01-04', false ],
+ [ '1970-01-05', '3', null, null, null, '1970-01-08', false ],
+ [ '1970-01-03', '3', null, null, null, '1970-01-06', false ],
+ [ '1970-01-03', '3', '1970-01-02', null, null, '1970-01-05', false ],
+ // Clamping.
+ [ '2012-01-01', null, '2012-01-31', null, null, '2012-01-31', false ],
+ [ '1970-01-02', '2', '1970-01-01', '1970-01-04', null, '1970-01-03', false ],
+ [ '1970-01-01', '5', '1970-01-02', '1970-01-09', 10, '1970-01-07', false ],
+ [ '1969-12-28', '5', '1969-12-29', '1970-01-06', 3, '1970-01-03', false ],
+ [ '1970-01-01', '3', '1970-02-01', '1970-02-07', 15, '1970-02-07', false ],
+ [ '1970-01-01', '3', '1970-01-01', '1970-01-06', 2, '1970-01-04', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1970-01-02', false ],
+ // With step = 'any'.
+ [ '2012-01-01', 'any', null, null, 1, null, true ],
+ [ '2012-01-01', 'ANY', null, null, 1, null, true ],
+ [ '2012-01-01', 'AnY', null, null, 1, null, true ],
+ [ '2012-01-01', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'time', data: [
+ // Regular case.
+ [ '16:39', null, null, null, null, '16:40', false ],
+ // Argument testing.
+ [ '16:40', null, null, null, 1, '16:41', false ],
+ [ '16:40', null, null, null, 5, '16:45', false ],
+ [ '16:40', null, null, null, -1, '16:39', false ],
+ [ '16:40', null, null, null, 0, '16:40', false ],
+ // hour/minutes/seconds wrapping.
+ [ '04:59', null, null, null, null, '05:00', false ],
+ [ '04:59:59', 1, null, null, null, '05:00', false ],
+ [ '04:59:59.900', 0.1, null, null, null, '05:00', false ],
+ [ '04:59:59.990', 0.01, null, null, null, '05:00', false ],
+ [ '04:59:59.999', 0.001, null, null, null, '05:00', false ],
+ // stepUp() on '23:59' gives '00:00'.
+ [ '23:59', null, null, null, 1, '00:00', false ],
+ [ '23:59', null, null, null, 3, '00:02', false ],
+ // Some random step values..
+ [ '16:56', '0.5', null, null, null, '16:56:00.500', false ],
+ [ '16:56', '2', null, null, null, '16:56:02', false ],
+ [ '16:56', '0.25',null, null, 4, '16:56:01', false ],
+ [ '16:57', '1.1', '16:00', null, 1, '16:57:01', false ],
+ [ '16:57', '1.1', '16:00', null, 2, '16:57:02.100', false ],
+ [ '16:57', '1.1', '16:00', null, 10, '16:57:10.900', false ],
+ [ '16:57', '1.1', '16:00', null, 11, '16:57:12', false ],
+ [ '16:57', '1.1', '16:00', null, 8, '16:57:08.700', false ],
+ // Invalid @step, means that we use the default value.
+ [ '17:01', '0', null, null, null, '17:02', false ],
+ [ '17:01', '-1', null, null, null, '17:02', false ],
+ [ '17:01', 'foo', null, null, null, '17:02', false ],
+ // Min values testing.
+ [ '17:02', '60', 'foo', null, 2, '17:04', false ],
+ [ '17:10', '60', '17:09', null, null, '17:11', false ],
+ [ '17:10', '60', '17:10', null, null, '17:11', false ],
+ [ '17:10', '60', '17:30', null, 1, '17:30', false ],
+ [ '17:10', '180', '17:05', null, null, '17:11', false ],
+ [ '17:10', '300', '17:10', '17:11', null,'17:10', false ],
+ // Max values testing.
+ [ '17:15', '60', null, 'foo', null, '17:16', false ],
+ [ '17:15', null, null, '17:20', null, '17:16', false ],
+ [ '17:15', null, null, '17:15', null, '17:15', false ],
+ [ '17:15', null, null, '17:13', 4, '17:15', false ],
+ [ '17:15', '120', null, '17:13', 3, '17:15', false ],
+ // Step mismatch.
+ [ '17:19', '120', '17:10', null, null, '17:20', false ],
+ [ '17:19', '120', '17:10', null, 2, '17:22', false ],
+ [ '17:19', '120', '17:18', '17:25', null, '17:20', false ],
+ [ '17:19', '120', null, null, null, '17:21', false ],
+ [ '17:19', '180', null, null, null, '17:22', false ],
+ // Clamping.
+ [ '17:22', null, null, '17:11', null, '17:22', false ],
+ [ '17:22', '120', '17:20', '17:22', null, '17:22', false ],
+ [ '17:22', '300', '17:12', '17:20', 10, '17:22', false ],
+ [ '17:22', '300', '17:18', '17:20', 2, '17:22', false ],
+ [ '17:22', '180', '17:00', '17:20', 15, '17:22', false ],
+ [ '17:22', '180', '17:10', '17:20', 2, '17:22', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '00:01', false ],
+ // With step = 'any'.
+ [ '17:26', 'any', null, null, 1, null, true ],
+ [ '17:26', 'ANY', null, null, 1, null, true ],
+ [ '17:26', 'AnY', null, null, 1, null, true ],
+ [ '17:26', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'month', data: [
+ // Regular case.
+ [ '2016-08', null, null, null, null, '2016-09', false ],
+ // Argument testing.
+ [ '2016-08', null, null, null, 1, '2016-09', false ],
+ [ '2016-08', null, null, null, 9, '2017-05', false ],
+ [ '2016-08', null, null, null, -1, '2016-07', false ],
+ [ '2016-08', null, null, null, 0, '2016-08', false ],
+ // Month/Year wrapping.
+ [ '2015-12', null, null, null, 1, '2016-01', false ],
+ [ '1968-12', null, null, null, 4, '1969-04', false ],
+ [ '1970-01', null, null, null, -12, '1969-01', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '2016-01', null, null, null, 1.1, '2016-02', false ],
+ [ '2016-01', null, null, null, 1.9, '2016-02', false ],
+ // With step values.
+ [ '2016-01', '0.5', null, null, null, '2016-02', false ],
+ [ '2016-01', '2', null, null, null, '2016-03', false ],
+ [ '2016-01', '0.25', null, null, 4, '2016-05', false ],
+ [ '2016-01', '1.1', '2016-01', null, 1, '2016-02', false ],
+ [ '2016-01', '1.1', '2016-01', null, 2, '2016-03', false ],
+ [ '2016-01', '1.1', '2016-01', null, 10, '2016-11', false ],
+ [ '2016-01', '1.1', '2016-01', null, 11, '2016-12', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2016-01', '0', null, null, null, '2016-02', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2016-01', '-1', null, null, null, '2016-02', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2016-01', 'foo', null, null, null, '2016-02', false ],
+ // Min values testing.
+ [ '2016-01', '1', 'foo', null, null, '2016-02', false ],
+ [ '2016-01', null, '2015-12', null, null, '2016-02', false ],
+ [ '2016-01', null, '2016-02', null, null, '2016-02', false ],
+ [ '2016-01', null, '2016-01', null, null, '2016-02', false ],
+ [ '2016-01', null, '2016-04', null, 4, '2016-05', false ],
+ [ '2016-01', '2', '2016-04', null, 3, '2016-06', false ],
+ // Max values testing.
+ [ '2016-01', '1', null, 'foo', 2, '2016-03', false ],
+ [ '2016-01', '1', null, '2016-02', 1, '2016-02', false ],
+ [ '2016-02', null, null, '2016-01', null, '2016-02', false ],
+ [ '2016-02', null, null, '2016-02', null, '2016-02', false ],
+ [ '1969-02', '5', '1969-01', '1969-02', null, '1969-02', false ],
+ // Step mismatch.
+ [ '2016-02', '2', '2016-01', null, null, '2016-03', false ],
+ [ '2016-02', '2', '2016-01', null, 2, '2016-05', false ],
+ [ '2016-05', '2', '2016-01', '2016-06', null, '2016-05', false ],
+ [ '1970-02', '2', null, null, null, '1970-04', false ],
+ [ '1970-05', '3', null, null, null, '1970-08', false ],
+ [ '1970-03', '3', null, null, null, '1970-06', false ],
+ [ '1970-03', '3', '1970-02', null, null, '1970-05', false ],
+ // Clamping.
+ [ '2016-01', null, '2016-12', null, null, '2016-12', false ],
+ [ '1970-02', '2', '1970-01', '1970-04', null, '1970-03', false ],
+ [ '1970-01', '5', '1970-02', '1970-09', 10, '1970-07', false ],
+ [ '1969-11', '5', '1969-12', '1970-06', 3, '1970-05', false ],
+ [ '1970-01', '3', '1970-02', '1971-07', 15, '1971-05', false ],
+ [ '1970-01', '3', '1970-01', '1970-06', 2, '1970-04', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1970-02', false ],
+ // With step = 'any'.
+ [ '2016-01', 'any', null, null, 1, null, true ],
+ [ '2016-01', 'ANY', null, null, 1, null, true ],
+ [ '2016-01', 'AnY', null, null, 1, null, true ],
+ [ '2016-01', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'week', data: [
+ // Regular case.
+ [ '2016-W40', null, null, null, null, '2016-W41', false ],
+ // Argument testing.
+ [ '2016-W40', null, null, null, 1, '2016-W41', false ],
+ [ '2016-W40', null, null, null, 20, '2017-W08', false ],
+ [ '2016-W40', null, null, null, -1, '2016-W39', false ],
+ [ '2016-W40', null, null, null, 0, '2016-W40', false ],
+ // Week/Year wrapping.
+ [ '2015-W53', null, null, null, 1, '2016-W01', false ],
+ [ '1968-W52', null, null, null, 4, '1969-W04', false ],
+ [ '1970-W01', null, null, null, -52, '1969-W01', false ],
+ // Float values are rounded to integer (1.1 -> 1).
+ [ '2016-W01', null, null, null, 1.1, '2016-W02', false ],
+ [ '2016-W01', null, null, null, 1.9, '2016-W02', false ],
+ // With step values.
+ [ '2016-W01', '0.5', null, null, null, '2016-W02', false ],
+ [ '2016-W01', '2', null, null, null, '2016-W03', false ],
+ [ '2016-W01', '0.25', null, null, 4, '2016-W05', false ],
+ [ '2016-W01', '1.1', '2016-01', null, 1, '2016-W02', false ],
+ [ '2016-W01', '1.1', '2016-01', null, 2, '2016-W03', false ],
+ [ '2016-W01', '1.1', '2016-01', null, 10, '2016-W11', false ],
+ [ '2016-W01', '1.1', '2016-01', null, 20, '2016-W21', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2016-W01', '0', null, null, null, '2016-W02', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2016-W01', '-1', null, null, null, '2016-W02', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2016-W01', 'foo', null, null, null, '2016-W02', false ],
+ // Min values testing.
+ [ '2016-W01', '1', 'foo', null, null, '2016-W02', false ],
+ [ '2016-W01', null, '2015-W53', null, null, '2016-W02', false ],
+ [ '2016-W01', null, '2016-W02', null, null, '2016-W02', false ],
+ [ '2016-W01', null, '2016-W01', null, null, '2016-W02', false ],
+ [ '2016-W01', null, '2016-W04', null, 4, '2016-W05', false ],
+ [ '2016-W01', '2', '2016-W04', null, 3, '2016-W06', false ],
+ // Max values testing.
+ [ '2016-W01', '1', null, 'foo', 2, '2016-W03', false ],
+ [ '2016-W01', '1', null, '2016-W02', 1, '2016-W02', false ],
+ [ '2016-W02', null, null, '2016-W01', null, '2016-W02', false ],
+ [ '2016-W02', null, null, '2016-W02', null, '2016-W02', false ],
+ [ '1969-W02', '5', '1969-W01', '1969-W02', null, '1969-W02', false ],
+ // Step mismatch.
+ [ '2016-W02', '2', '2016-W01', null, null, '2016-W03', false ],
+ [ '2016-W02', '2', '2016-W01', null, 2, '2016-W05', false ],
+ [ '2016-W05', '2', '2016-W01', '2016-W06', null, '2016-W05', false ],
+ [ '1970-W02', '2', null, null, null, '1970-W04', false ],
+ [ '1970-W05', '3', null, null, null, '1970-W08', false ],
+ [ '1970-W03', '3', null, null, null, '1970-W06', false ],
+ [ '1970-W03', '3', '1970-W02', null, null, '1970-W05', false ],
+ // Clamping.
+ [ '2016-W01', null, '2016-W52', null, null, '2016-W52', false ],
+ [ '1970-W02', '2', '1970-W01', '1970-W04', null, '1970-W03', false ],
+ [ '1970-W01', '5', '1970-W02', '1970-W09', 10, '1970-W07', false ],
+ [ '1969-W50', '5', '1969-W52', '1970-W06', 3, '1970-W05', false ],
+ [ '1970-W01', '3', '1970-W02', '1971-W07', 15, '1970-W44', false ],
+ [ '1970-W01', '3', '1970-W01', '1970-W06', 2, '1970-W04', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1970-W02', false ],
+ // With step = 'any'.
+ [ '2016-W01', 'any', null, null, 1, null, true ],
+ [ '2016-W01', 'ANY', null, null, 1, null, true ],
+ [ '2016-W01', 'AnY', null, null, 1, null, true ],
+ [ '2016-W01', 'aNy', null, null, 1, null, true ],
+ ]},
+ { type: 'datetime-local', data: [
+ // Regular case.
+ [ '2017-02-07T17:09', null, null, null, null, '2017-02-07T17:10', false ],
+ // Argument testing.
+ [ '2017-02-07T17:10', null, null, null, 1, '2017-02-07T17:11', false ],
+ [ '2017-02-07T17:10', null, null, null, 5, '2017-02-07T17:15', false ],
+ [ '2017-02-07T17:10', null, null, null, -1, '2017-02-07T17:09', false ],
+ [ '2017-02-07T17:10', null, null, null, 0, '2017-02-07T17:10', false ],
+ // hour/minutes/seconds wrapping.
+ [ '2000-01-01T04:59', null, null, null, null, '2000-01-01T05:00', false ],
+ [ '2000-01-01T04:59:59', 1, null, null, null, '2000-01-01T05:00', false ],
+ [ '2000-01-01T04:59:59.900', 0.1, null, null, null, '2000-01-01T05:00', false ],
+ [ '2000-01-01T04:59:59.990', 0.01, null, null, null, '2000-01-01T05:00', false ],
+ [ '2000-01-01T04:59:59.999', 0.001, null, null, null, '2000-01-01T05:00', false ],
+ // month/year wrapping.
+ [ '2012-07-31T12:00', null, null, null, 1440, '2012-08-01T12:00', false ],
+ [ '1968-12-29T12:00', null, null, null, 5760, '1969-01-02T12:00', false ],
+ [ '1970-01-01T00:00', null, null, null, -1440, '1969-12-31T00:00', false ],
+ [ '2012-03-01T00:00', null, null, null, -1440, '2012-02-29T00:00', false ],
+ // stepUp() on '23:59' gives '00:00'.
+ [ '2017-02-07T23:59', null, null, null, 1, '2017-02-08T00:00', false ],
+ [ '2017-02-07T23:59', null, null, null, 3, '2017-02-08T00:02', false ],
+ // Some random step values..
+ [ '2017-02-07T17:40', '0.5', null, null, null, '2017-02-07T17:40:00.500', false ],
+ [ '2017-02-07T17:40', '2', null, null, null, '2017-02-07T17:40:02', false ],
+ [ '2017-02-07T17:40', '0.25', null, null, 4, '2017-02-07T17:40:01', false ],
+ [ '2017-02-07T17:40', '1.1', '2017-02-07T17:00', null, 1, '2017-02-07T17:40:00.200', false ],
+ [ '2017-02-07T17:40', '1.1', '2017-02-07T17:00', null, 2, '2017-02-07T17:40:01.300', false ],
+ [ '2017-02-07T17:40', '1.1', '2017-02-07T17:00', null, 10, '2017-02-07T17:40:10.100', false ],
+ [ '2017-02-07T17:40', '129600', '2017-02-01T00:00', null, 2, '2017-02-10T00:00', false ],
+ // step = 0 isn't allowed (-> step = 1).
+ [ '2017-02-07T17:39', '0', null, null, null, '2017-02-07T17:40', false ],
+ // step < 0 isn't allowed (-> step = 1).
+ [ '2017-02-07T17:39', '-1', null, null, null, '2017-02-07T17:40', false ],
+ // step = NaN isn't allowed (-> step = 1).
+ [ '2017-02-07T17:39', 'foo', null, null, null, '2017-02-07T17:40', false ],
+ // Min values testing.
+ [ '2012-02-02T17:00', '60', 'foo', null, 2, '2012-02-02T17:02', false ],
+ [ '2012-02-02T17:10', '60', '2012-02-02T17:10', null, null, '2012-02-02T17:11', false ],
+ [ '2012-02-02T17:10', '60', '2012-02-02T17:30', null, 1, '2012-02-02T17:30', false ],
+ [ '2012-02-02T17:10', '180', '2012-02-02T17:05', null, null, '2012-02-02T17:11', false ],
+ [ '2012-02-02T17:10', '86400', '2012-02-02T17:05', null, null, '2012-02-03T17:05', false ],
+ [ '2012-02-02T17:10', '129600', '2012-02-01T00:00', null, null, '2012-02-04T00:00', false ],
+ // Max values testing.
+ [ '2012-02-02T17:15', '60', null, 'foo', null, '2012-02-02T17:16', false ],
+ [ '2012-02-02T17:15', null, null, '2012-02-02T17:20', null, '2012-02-02T17:16', false ],
+ [ '2012-02-02T17:15', null, null, '2012-02-02T17:15', null, '2012-02-02T17:15', false ],
+ [ '2012-02-02T17:15', null, null, '2012-02-02T17:13', 4, '2012-02-02T17:15', false ],
+ [ '2012-02-02T20:05', '86400', null, '2012-02-03T20:05', null, '2012-02-03T20:05', false ],
+ [ '2012-02-02T18:00', '129600', null, '2012-02-04T20:00', null, '2012-02-04T06:00', false ],
+ // Step mismatch.
+ [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, null, '2017-02-07T17:20', false ],
+ [ '2017-02-07T17:19', '120', '2017-02-07T17:10', null, 2, '2017-02-07T17:22', false ],
+ [ '2017-02-07T17:19', '120', '2017-02-07T17:18', '2017-02-07T17:25', null, '2017-02-07T17:20', false ],
+ [ '2017-02-07T17:19', '120', null, null, null, '2017-02-07T17:21', false ],
+ [ '2017-02-07T17:19', '180', null, null, null, '2017-02-07T17:22', false ],
+ [ '2017-02-03T17:19', '172800', '2017-02-02T17:19', '2017-02-10T17:19', null, '2017-02-04T17:19', false ],
+ // Clamping.
+ [ '2017-02-07T17:22', null, null, '2017-02-07T17:11', null, '2017-02-07T17:22', false ],
+ [ '2017-02-07T17:22', '120', '2017-02-07T17:20', '2017-02-07T17:22', null, '2017-02-07T17:22', false ],
+ [ '2017-02-07T17:22', '300', '2017-02-07T17:12', '2017-02-07T17:20', 10, '2017-02-07T17:22', false ],
+ [ '2017-02-07T17:22', '300', '2017-02-07T17:18', '2017-02-07T17:20', 2, '2017-02-07T17:22', false ],
+ [ '2017-02-06T17:22', '600', '2017-02-02T17:00', '2017-02-07T17:20', 15, '2017-02-06T19:50', false ],
+ [ '2017-02-06T17:22', '600', '2017-02-02T17:10', '2017-02-07T17:20', 2, '2017-02-06T17:40', false ],
+ // value = "" (NaN).
+ [ '', null, null, null, null, '1970-01-01T00:01', false ],
+ // With step = 'any'.
+ [ '2017-02-07T17:30', 'any', null, null, 1, null, true ],
+ [ '2017-02-07T17:30', 'ANY', null, null, 1, null, true ],
+ [ '2017-02-07T17:30', 'AnY', null, null, 1, null, true ],
+ [ '2017-02-07T17:30', 'aNy', null, null, 1, null, true ],
+ ]},
+ ];
+
+ for (var test of testData) {
+ for (var data of test.data) {
+ var element = document.createElement("input");
+ element.type = test.type;
+
+ if (data[1] != null) {
+ element.step = data[1];
+ }
+
+ if (data[2] != null) {
+ element.min = data[2];
+ }
+
+ if (data[3] != null) {
+ element.max = data[3];
+ }
+
+ // Set 'value' last for type=range, because the final sanitized value
+ // after setting 'step', 'min' and 'max' can be affected by the order in
+ // which those attributes are set. Setting 'value' last makes it simpler
+ // to reason about what the final value should be.
+ if (data[0] != null) {
+ element.setAttribute('value', data[0]);
+ }
+
+ var exceptionCaught = false;
+ try {
+ if (data[4] != null) {
+ element.stepUp(data[4]);
+ } else {
+ element.stepUp();
+ }
+
+ is(element.value, data[5], "The value for type=" + test.type + " should be " + data[5]);
+ } catch (e) {
+ exceptionCaught = true;
+ is(element.value, data[0], e.name + "The value should not have changed");
+ is(e.name, 'InvalidStateError',
+ "It should be a InvalidStateError exception.");
+ } finally {
+ is(exceptionCaught, data[6], "exception status should be " + data[6]);
+ }
+ }
+ }
+}
+
+checkPresence();
+checkAvailability();
+
+checkStepDown();
+checkStepUp();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_submit_invalid_file.html b/dom/html/test/forms/test_submit_invalid_file.html
new file mode 100644
index 0000000000..68b5e44877
--- /dev/null
+++ b/dom/html/test/forms/test_submit_invalid_file.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=702949
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test invalid file submission</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=702949">Mozilla Bug 702949</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form action='http://mochi.test:8888/chrome/dom/html/test/forms/submit_invalid_file.sjs' method='post' target='result'
+ enctype='multipart/form-data'>
+ <input type='file' name='file'>
+ </form>
+ <iframe name='result'></iframe>
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+ /*
+ * Test invalid file submission by submitting a file that has been deleted
+ * from the file system before the form has been submitted.
+ * The form submission triggers a sjs file that shows its output in a frame.
+ * That means the test might time out if it fails.
+ */
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ var { FileUtils } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/FileUtils.sys.mjs"
+ );
+
+ var i = document.getElementsByTagName('input')[0];
+
+ var file = FileUtils.getDir("TmpD", []);
+ file.append("testfile");
+ file.createUnique(SpecialPowers.Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
+
+ SpecialPowers.wrap(i).value = file.path;
+ file.remove(/* recursive = */ false);
+
+ document.getElementsByName('result')[0].addEventListener('load', function() {
+ is(window.frames[0].document.body.textContent, "SUCCESS");
+ SimpleTest.finish();
+ });
+ document.forms[0].submit();
+ });
+</script>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_textarea_attributes_reflection.html b/dom/html/test/forms/test_textarea_attributes_reflection.html
new file mode 100644
index 0000000000..925f97e751
--- /dev/null
+++ b/dom/html/test/forms/test_textarea_attributes_reflection.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLTextAreaElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLTextAreaElement attributes reflection **/
+
+// .autofocus
+reflectBoolean({
+ element: document.createElement("textarea"),
+ attribute: "autofocus",
+});
+
+//.cols
+reflectUnsignedInt({
+ element: document.createElement("textarea"),
+ attribute: "cols",
+ nonZero: true,
+ defaultValue: 20,
+ fallback: true,
+});
+
+//.dirname
+reflectString({
+ element: document.createElement("textarea"),
+ attribute: "dirName"
+})
+
+// .disabled
+reflectBoolean({
+ element: document.createElement("textarea"),
+ attribute: "disabled",
+});
+
+// TODO: form (HTMLFormElement)
+
+// .maxLength
+reflectInt({
+ element: document.createElement("textarea"),
+ attribute: "maxLength",
+ nonNegative: true,
+});
+
+// .name
+reflectString({
+ element: document.createElement("textarea"),
+ attribute: "name",
+ otherValues: [ "isindex", "_charset_" ],
+});
+
+// .placeholder
+reflectString({
+ element: document.createElement("textarea"),
+ attribute: "placeholder",
+ otherValues: [ "foo\nbar", "foo\rbar", "foo\r\nbar" ],
+});
+
+// .readOnly
+reflectBoolean({
+ element: document.createElement("textarea"),
+ attribute: "readOnly",
+});
+
+// .required
+reflectBoolean({
+ element: document.createElement("textarea"),
+ attribute: "required",
+});
+
+// .rows
+reflectUnsignedInt({
+ element: document.createElement("textarea"),
+ attribute: "rows",
+ nonZero: true,
+ defaultValue: 2,
+ fallback: true,
+});
+
+// .wrap
+// TODO: make it an enumerated attributes limited to only known values, bug 670869.
+reflectString({
+ element: document.createElement("textarea"),
+ attribute: "wrap",
+ otherValues: [ "soft", "hard" ],
+});
+
+// .type doesn't reflect a content attribute.
+// .defaultValue doesn't reflect a content attribute.
+// .value doesn't reflect a content attribute.
+// .textLength doesn't reflect a content attribute.
+// .willValidate doesn't reflect a content attribute.
+// .validity doesn't reflect a content attribute.
+// .validationMessage doesn't reflect a content attribute.
+// .labels doesn't reflect a content attribute.
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_validation.html b/dom/html/test/forms/test_validation.html
new file mode 100644
index 0000000000..666d4a45c0
--- /dev/null
+++ b/dom/html/test/forms/test_validation.html
@@ -0,0 +1,343 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=345624
+-->
+<head>
+ <title>Test for Bug 345624</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input, textarea, fieldset, button, select, output, object { background-color: rgb(0,0,0) !important; }
+ :valid { background-color: rgb(0,255,0) !important; }
+ :invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=345624">Mozilla Bug 345624</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <fieldset id='f'></fieldset>
+ <input id='i' oninvalid="invalidEventHandler(event);">
+ <button id='b' oninvalid="invalidEventHandler(event);"></button>
+ <select id='s' oninvalid="invalidEventHandler(event);"></select>
+ <textarea id='t' oninvalid="invalidEventHandler(event);"></textarea>
+ <output id='o' oninvalid="invalidEventHandler(event);"></output>
+ <object id='obj'></object>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 345624 **/
+
+var gInvalid = false;
+
+function invalidEventHandler(aEvent)
+{
+ function checkInvalidEvent(event)
+ {
+ is(event.type, "invalid", "Invalid event type should be invalid");
+ ok(!event.bubbles, "Invalid event should not bubble");
+ ok(event.cancelable, "Invalid event should be cancelable");
+ }
+
+ checkInvalidEvent(aEvent);
+
+ gInvalid = true;
+}
+
+function checkConstraintValidationAPIExist(element)
+{
+ ok('willValidate' in element, "willValidate is not available in the DOM");
+ ok('validationMessage' in element, "validationMessage is not available in the DOM");
+ ok('validity' in element, "validity is not available in the DOM");
+
+ if ('validity' in element) {
+ validity = element.validity;
+ ok('valueMissing' in validity, "validity.valueMissing is not available in the DOM");
+ ok('typeMismatch' in validity, "validity.typeMismatch is not available in the DOM");
+ ok('badInput' in validity, "validity.badInput is not available in the DOM");
+ ok('patternMismatch' in validity, "validity.patternMismatch is not available in the DOM");
+ ok('tooLong' in validity, "validity.tooLong is not available in the DOM");
+ ok('rangeUnderflow' in validity, "validity.rangeUnderflow is not available in the DOM");
+ ok('rangeOverflow' in validity, "validity.rangeOverflow is not available in the DOM");
+ ok('stepMismatch' in validity, "validity.stepMismatch is not available in the DOM");
+ ok('customError' in validity, "validity.customError is not available in the DOM");
+ ok('valid' in validity, "validity.valid is not available in the DOM");
+ }
+}
+
+function checkConstraintValidationAPIDefaultValues(element)
+{
+ // Not checking willValidate because the default value depends of the element
+
+ is(element.validationMessage, "", "validationMessage default value should be empty string");
+
+ ok(!element.validity.valueMissing, "The element should not suffer from a constraint validation");
+ ok(!element.validity.typeMismatch, "The element should not suffer from a constraint validation");
+ ok(!element.validity.badInput, "The element should not suffer from a constraint validation");
+ ok(!element.validity.patternMismatch, "The element should not suffer from a constraint validation");
+ ok(!element.validity.tooLong, "The element should not suffer from a constraint validation");
+ ok(!element.validity.rangeUnderflow, "The element should not suffer from a constraint validation");
+ ok(!element.validity.rangeOverflow, "The element should not suffer from a constraint validation");
+ ok(!element.validity.stepMismatch, "The element should not suffer from a constraint validation");
+ ok(!element.validity.customError, "The element should not suffer from a constraint validation");
+ ok(element.validity.valid, "The element should be valid by default");
+
+ ok(element.checkValidity(), "The element should be valid by default");
+}
+
+function checkDefaultPseudoClass()
+{
+ is(window.getComputedStyle(document.getElementById('f'))
+ .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+ ":valid should apply");
+
+ is(window.getComputedStyle(document.getElementById('o'))
+ .getPropertyValue('background-color'), "rgb(0, 0, 0)",
+ "Nor :valid and :invalid should apply");
+
+ is(window.getComputedStyle(document.getElementById('obj'))
+ .getPropertyValue('background-color'), "rgb(0, 0, 0)",
+ "Nor :valid and :invalid should apply");
+
+ is(window.getComputedStyle(document.getElementById('s'))
+ .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+ ":valid pseudo-class should apply");
+
+ is(window.getComputedStyle(document.getElementById('i'))
+ .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+ ":valid pseudo-class should apply");
+
+ is(window.getComputedStyle(document.getElementById('t'))
+ .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+ ":valid pseudo-class should apply");
+
+ is(window.getComputedStyle(document.getElementById('b'))
+ .getPropertyValue('background-color'), "rgb(0, 255, 0)",
+ ":valid pseudo-class should apply");
+}
+
+function checkSpecificWillValidate()
+{
+ // fieldset, output, object (TODO) and select elements
+ ok(!document.getElementById('f').willValidate, "Fielset element should be barred from constraint validation");
+ ok(!document.getElementById('obj').willValidate, "Object element should be barred from constraint validation");
+ ok(!document.getElementById('o').willValidate, "Output element should be barred from constraint validation");
+ ok(document.getElementById('s').willValidate, "Select element should not be barred from constraint validation");
+
+ // input element
+ i = document.getElementById('i');
+ i.type = "hidden";
+ ok(!i.willValidate, "Hidden state input should be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ i.type = "reset";
+ ok(!i.willValidate, "Reset button state input should be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ i.type = "button";
+ ok(!i.willValidate, "Button state input should be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ i.type = "image";
+ ok(i.willValidate, "Image state input should not be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid and :invalid should apply");
+ i.type = "submit";
+ ok(i.willValidate, "Submit state input should not be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid and :invalid should apply");
+ i.type = "number";
+ ok(i.willValidate, "Number state input should not be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+ i.type = "";
+ i.readOnly = 'true';
+ ok(!i.willValidate, "Readonly input should be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ i.removeAttribute('readOnly');
+ ok(i.willValidate, "Default input element should not be barred from constraint validation");
+ is(window.getComputedStyle(i).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+
+ // button element
+ b = document.getElementById('b');
+ b.type = "reset";
+ ok(!b.willValidate, "Reset state button should be barred from constraint validation");
+ is(window.getComputedStyle(b).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ b.type = "button";
+ ok(!b.willValidate, "Button state button should be barred from constraint validation");
+ is(window.getComputedStyle(b).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ b.type = "submit";
+ ok(b.willValidate, "Submit state button should not be barred from constraint validation");
+ is(window.getComputedStyle(b).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid and :invalid should apply");
+ b.type = "";
+ ok(b.willValidate, "Default button element should not be barred from constraint validation");
+ is(window.getComputedStyle(b).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+
+ // textarea element
+ t = document.getElementById('t');
+ t.readOnly = true;
+ ok(!t.willValidate, "Readonly textarea should be barred from constraint validation");
+ is(window.getComputedStyle(t).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+ t.removeAttribute('readOnly');
+ ok(t.willValidate, "Default textarea element should not be barred from constraint validation");
+ is(window.getComputedStyle(t).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+
+ // TODO: PROGRESS
+ // TODO: METER
+}
+
+function checkCommonWillValidate(element)
+{
+ // Not checking the default value because it has been checked previously.
+
+ element.disabled = true;
+ ok(!element.willValidate, "Disabled element should be barred from constraint validation");
+
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
+
+ element.removeAttribute('disabled');
+
+ // TODO: If an element has a datalist element ancestor, it is barred from constraint validation.
+}
+
+function checkCustomError(element, isBarred)
+{
+ element.setCustomValidity("message");
+ if (!isBarred) {
+ is(element.validationMessage, "message",
+ "When the element has a custom validity message, validation message should return it");
+ } else {
+ is(element.validationMessage, "",
+ "An element barred from constraint validation can't have a validation message");
+ }
+ ok(element.validity.customError, "The element should suffer from a custom error");
+ ok(!element.validity.valid, "The element should not be valid with a custom error");
+
+ if (element.tagName == "FIELDSET") {
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ isBarred ? "rgb(0, 255, 0)" : "rgb(255, 0, 0)",
+ ":invalid pseudo-classs should apply to " + element.tagName);
+ }
+ else {
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ isBarred ? "rgb(0, 0, 0)" : "rgb(255, 0, 0)",
+ ":invalid pseudo-classs should apply to " + element.tagName);
+ }
+
+ element.setCustomValidity("");
+ is(element.validationMessage, "", "The element should not have a validation message when reseted");
+ ok(!element.validity.customError, "The element should not suffer anymore from a custom error");
+ ok(element.validity.valid, "The element should now be valid");
+
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ isBarred && element.tagName != "FIELDSET" ? "rgb(0, 0, 0)" : "rgb(0, 255, 0)",
+ ":valid pseudo-classs should apply");
+}
+
+function checkCheckValidity(element)
+{
+ element.setCustomValidity("message");
+ ok(!element.checkValidity(), "checkValidity() should return false when the element is not valid");
+
+ ok(gInvalid, "Invalid event should have been handled");
+
+ gInvalid = false;
+ element.setCustomValidity("");
+
+ ok(element.checkValidity(), "Element should be valid");
+ ok(!gInvalid, "Invalid event should not have been handled");
+}
+
+function checkValidityStateObjectAliveWithoutElement(element)
+{
+ // We are creating a temporary element and getting it's ValidityState object.
+ // Then, we make sure it is removed by the garbage collector and we check the
+ // ValidityState default values (it should not crash).
+
+ var v = document.createElement(element).validity;
+ SpecialPowers.gc();
+
+ ok(!v.valueMissing,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.typeMismatch,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.badInput,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.patternMismatch,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.tooLong,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.rangeUnderflow,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.rangeOverflow,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.stepMismatch,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(!v.customError,
+ "When the element is not alive, it shouldn't suffer from constraint validation");
+ ok(v.valid, "When the element is not alive, it should be valid");
+}
+
+checkConstraintValidationAPIExist(document.getElementById('f'));
+checkConstraintValidationAPIExist(document.getElementById('i'));
+checkConstraintValidationAPIExist(document.getElementById('b'));
+checkConstraintValidationAPIExist(document.getElementById('s'));
+checkConstraintValidationAPIExist(document.getElementById('t'));
+checkConstraintValidationAPIExist(document.getElementById('o'));
+checkConstraintValidationAPIExist(document.getElementById('obj'));
+
+checkConstraintValidationAPIDefaultValues(document.getElementById('f'));
+checkConstraintValidationAPIDefaultValues(document.getElementById('i'));
+checkConstraintValidationAPIDefaultValues(document.getElementById('b'));
+checkConstraintValidationAPIDefaultValues(document.getElementById('s'));
+checkConstraintValidationAPIDefaultValues(document.getElementById('t'));
+checkConstraintValidationAPIDefaultValues(document.getElementById('o'));
+checkConstraintValidationAPIDefaultValues(document.getElementById('obj'));
+
+checkDefaultPseudoClass();
+
+checkSpecificWillValidate();
+
+// Not checking button, fieldset, output and object
+// because they are always barred from constraint validation.
+checkCommonWillValidate(document.getElementById('i'));
+checkCommonWillValidate(document.getElementById('s'));
+checkCommonWillValidate(document.getElementById('t'));
+
+checkCustomError(document.getElementById('i'), false);
+checkCustomError(document.getElementById('s'), false);
+checkCustomError(document.getElementById('t'), false);
+checkCustomError(document.getElementById('o'), true);
+checkCustomError(document.getElementById('b'), false);
+checkCustomError(document.getElementById('f'), true);
+checkCustomError(document.getElementById('obj'), true);
+
+// Not checking button, fieldset, output and object
+// because they are always barred from constraint validation.
+checkCheckValidity(document.getElementById('i'));
+checkCheckValidity(document.getElementById('s'));
+checkCheckValidity(document.getElementById('t'));
+
+checkValidityStateObjectAliveWithoutElement("fieldset");
+checkValidityStateObjectAliveWithoutElement("input");
+checkValidityStateObjectAliveWithoutElement("button");
+checkValidityStateObjectAliveWithoutElement("select");
+checkValidityStateObjectAliveWithoutElement("textarea");
+checkValidityStateObjectAliveWithoutElement("output");
+checkValidityStateObjectAliveWithoutElement("object");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_validation_not_in_doc.html b/dom/html/test/forms/test_validation_not_in_doc.html
new file mode 100644
index 0000000000..1500c60869
--- /dev/null
+++ b/dom/html/test/forms/test_validation_not_in_doc.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for constraint validation of form controls not in documents</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var input = document.createElement('input');
+ input.required = true;
+ assert_false(input.checkValidity());
+}, "Should validate input not in document");
+
+test(function() {
+ var textarea = document.createElement('textarea');
+ textarea.required = true;
+ assert_false(textarea.checkValidity());
+}, "Should validate textarea not in document");
+</script>
diff --git a/dom/html/test/forms/test_valueasdate_attribute.html b/dom/html/test/forms/test_valueasdate_attribute.html
new file mode 100644
index 0000000000..9055879a85
--- /dev/null
+++ b/dom/html/test/forms/test_valueasdate_attribute.html
@@ -0,0 +1,751 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=769370
+-->
+<head>
+ <title>Test for input.valueAsDate</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=769370">Mozilla Bug 769370</a>
+<iframe name="testFrame" style="display: none"></iframe>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 769370**/
+
+/**
+ * This test is checking .valueAsDate.
+ */
+
+var element = document.createElement("input");
+
+var validTypes =
+[
+ ["text", false],
+ ["password", false],
+ ["search", false],
+ ["tel", false],
+ ["email", false],
+ ["url", false],
+ ["hidden", false],
+ ["checkbox", false],
+ ["radio", false],
+ ["file", false],
+ ["submit", false],
+ ["image", false],
+ ["reset", false],
+ ["button", false],
+ ["number", false],
+ ["range", false],
+ ["date", true],
+ ["time", true],
+ ["color", false],
+ ["month", true],
+ ["week", true],
+ ["datetime-local", true],
+];
+
+function checkAvailability()
+{
+ for (let data of validTypes) {
+ var exceptionCatched = false;
+ element.type = data[0];
+ try {
+ element.valueAsDate;
+ } catch (e) {
+ exceptionCatched = true;
+ }
+ is(exceptionCatched, false,
+ "valueAsDate shouldn't throw exception on getting");
+
+ exceptionCatched = false;
+ try {
+ element.valueAsDate = new Date();
+ } catch (e) {
+ exceptionCatched = true;
+ }
+ is(exceptionCatched, !data[1], "valueAsDate for " + data[0] +
+ " availability is not correct");
+ }
+}
+
+function checkGarbageValues()
+{
+ for (let type of validTypes) {
+ if (!type[1]) {
+ continue;
+ }
+ type = type[0];
+
+ var inputElement = document.createElement('input');
+ inputElement.type = type;
+
+ inputElement.value = "test";
+ inputElement.valueAsDate = null;
+ is(inputElement.value, "", "valueAsDate should set the value to the empty string");
+
+ inputElement.value = "test";
+ inputElement.valueAsDate = undefined;
+ is(inputElement.value, "", "valueAsDate should set the value to the empty string");
+
+ inputElement.value = "test";
+ inputElement.valueAsDate = new Date(NaN);
+ is(inputElement.value, "", "valueAsDate should set the value to the empty string");
+
+ var illegalValues = [
+ "foobar", 42, {}, function() { return 42; }, function() { return Date(); }
+ ];
+
+ for (let value of illegalValues) {
+ try {
+ var caught = false;
+ inputElement.valueAsDate = value;
+ } catch(e) {
+ is(e.name, "TypeError", "Exception should be 'TypeError'.");
+ caught = true;
+ }
+ ok(caught, "Assigning " + value + " to .valueAsDate should throw");
+ }
+ }
+}
+
+function checkDateGet()
+{
+ var validData =
+ [
+ [ "2012-07-12", 1342051200000 ],
+ [ "1970-01-01", 0 ],
+ [ "1970-01-02", 86400000 ],
+ [ "1969-12-31", -86400000 ],
+ [ "0311-01-31", -52350451200000 ],
+ [ "275760-09-13", 8640000000000000 ],
+ [ "0001-01-01", -62135596800000 ],
+ [ "2012-02-29", 1330473600000 ],
+ [ "2011-02-28", 1298851200000 ],
+ ];
+
+ var invalidData =
+ [
+ [ "invaliddate" ],
+ [ "-001-12-31" ],
+ [ "901-12-31" ],
+ [ "1901-13-31" ],
+ [ "1901-12-32" ],
+ [ "1901-00-12" ],
+ [ "1901-01-00" ],
+ [ "1900-02-29" ],
+ [ "0000-01-01" ],
+ [ "" ],
+ // This date is valid for the input element, but is out of
+ // the date object range. In this case, on getting valueAsDate,
+ // a Date object will be created, but it will have a NaN internal value,
+ // and will return the string "Invalid Date".
+ [ "275760-09-14", true ],
+ ];
+
+ element.type = "date";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsDate.valueOf(), data[1],
+ "valueAsDate should return the " +
+ "valid date object representing this date");
+ }
+
+ for (let data of invalidData) {
+ element.value = data[0];
+ if (data[1]) {
+ is(String(element.valueAsDate), "Invalid Date",
+ "valueAsDate should return an invalid Date object " +
+ "when the element value is not a valid date");
+ } else {
+ is(element.valueAsDate, null,
+ "valueAsDate should return null " +
+ "when the element value is not a valid date");
+ }
+ }
+}
+
+function checkDateSet()
+{
+ var testData =
+ [
+ [ 1342051200000, "2012-07-12" ],
+ [ 0, "1970-01-01" ],
+ // Maximum valid date (limited by the ecma date object range).
+ [ 8640000000000000, "275760-09-13" ],
+ // Minimum valid date (limited by the input element minimum valid value).
+ [ -62135596800000 , "0001-01-01" ],
+ [ 1330473600000, "2012-02-29" ],
+ [ 1298851200000, "2011-02-28" ],
+ // "Values must be truncated to valid dates"
+ [ 42.1234, "1970-01-01" ],
+ [ 123.123456789123, "1970-01-01" ],
+ [ 1e-1, "1970-01-01" ],
+ [ 1298851200010, "2011-02-28" ],
+ [ -1, "1969-12-31" ],
+ [ -86400000, "1969-12-31" ],
+ [ 86400000, "1970-01-02" ],
+ // Negative years, this is out of range for the input element,
+ // the corresponding date string is the empty string
+ [ -62135596800001, "" ],
+ // Invalid dates.
+ ];
+
+ element.type = "date";
+ for (let data of testData) {
+ element.valueAsDate = new Date(data[0]);
+ is(element.value, data[1], "valueAsDate should set the value to "
+ + data[1]);
+ element.valueAsDate = new testFrame.Date(data[0]);
+ is(element.value, data[1], "valueAsDate with other-global date should " +
+ "set the value to " + data[1]);
+ }
+}
+
+function checkTimeGet()
+{
+ var tests = [
+ // Some invalid values to begin.
+ { value: "", result: null },
+ { value: "foobar", result: null },
+ { value: "00:", result: null },
+ { value: "24:00", result: null },
+ { value: "00:99", result: null },
+ { value: "00:00:", result: null },
+ { value: "00:00:99", result: null },
+ { value: "00:00:00:", result: null },
+ { value: "00:00:00.", result: null },
+ { value: "00:00:00.0000", result: null },
+ // Some simple valid values.
+ { value: "00:00", result: { time: 0, hours: 0, minutes: 0, seconds: 0, ms: 0 } },
+ { value: "00:01", result: { time: 60000, hours: 0, minutes: 1, seconds: 0, ms: 0 } },
+ { value: "01:00", result: { time: 3600000, hours: 1, minutes: 0, seconds: 0, ms: 0 } },
+ { value: "01:01", result: { time: 3660000, hours: 1, minutes: 1, seconds: 0, ms: 0 } },
+ { value: "13:37", result: { time: 49020000, hours: 13, minutes: 37, seconds: 0, ms: 0 } },
+ // Valid values including seconds.
+ { value: "00:00:01", result: { time: 1000, hours: 0, minutes: 0, seconds: 1, ms: 0 } },
+ { value: "13:37:42", result: { time: 49062000, hours: 13, minutes: 37, seconds: 42, ms: 0 } },
+ // Valid values including seconds fractions.
+ { value: "00:00:00.001", result: { time: 1, hours: 0, minutes: 0, seconds: 0, ms: 1 } },
+ { value: "00:00:00.123", result: { time: 123, hours: 0, minutes: 0, seconds: 0, ms: 123 } },
+ { value: "00:00:00.100", result: { time: 100, hours: 0, minutes: 0, seconds: 0, ms: 100 } },
+ { value: "00:00:00.000", result: { time: 0, hours: 0, minutes: 0, seconds: 0, ms: 0 } },
+ { value: "20:17:31.142", result: { time: 73051142, hours: 20, minutes: 17, seconds: 31, ms: 142 } },
+ // Highest possible value.
+ { value: "23:59:59.999", result: { time: 86399999, hours: 23, minutes: 59, seconds: 59, ms: 999 } },
+ // Some values with one or two digits for the fraction of seconds.
+ { value: "00:00:00.1", result: { time: 100, hours: 0, minutes: 0, seconds: 0, ms: 100 } },
+ { value: "00:00:00.14", result: { time: 140, hours: 0, minutes: 0, seconds: 0, ms: 140 } },
+ { value: "13:37:42.7", result: { time: 49062700, hours: 13, minutes: 37, seconds: 42, ms: 700 } },
+ { value: "23:31:12.23", result: { time: 84672230, hours: 23, minutes: 31, seconds: 12, ms: 230 } },
+ ];
+
+ var inputElement = document.createElement('input');
+ inputElement.type = 'time';
+
+ for (let test of tests) {
+ inputElement.value = test.value;
+ if (test.result === null) {
+ is(inputElement.valueAsDate, null, "element.valueAsDate should return null");
+ } else {
+ var date = inputElement.valueAsDate;
+ isnot(date, null, "element.valueAsDate should not be null");
+
+ is(date.getTime(), test.result.time);
+ is(date.getUTCHours(), test.result.hours);
+ is(date.getUTCMinutes(), test.result.minutes);
+ is(date.getUTCSeconds(), test.result.seconds);
+ is(date.getUTCMilliseconds(), test.result.ms);
+ }
+ }
+}
+
+function checkTimeSet()
+{
+ var tests = [
+ // Simple tests.
+ { value: 0, result: "00:00" },
+ { value: 1, result: "00:00:00.001" },
+ { value: 100, result: "00:00:00.100" },
+ { value: 1000, result: "00:00:01" },
+ { value: 60000, result: "00:01" },
+ { value: 3600000, result: "01:00" },
+ { value: 83622234, result: "23:13:42.234" },
+ // Some edge cases.
+ { value: 86400000, result: "00:00" },
+ { value: 86400001, result: "00:00:00.001" },
+ { value: 170022234, result: "23:13:42.234" },
+ { value: 432000000, result: "00:00" },
+ { value: -1, result: "23:59:59.999" },
+ { value: -86400000, result: "00:00" },
+ { value: -86400001, result: "23:59:59.999" },
+ { value: -56789, result: "23:59:03.211" },
+ { value: 0.9, result: "00:00" },
+ ];
+
+ var inputElement = document.createElement('input');
+ inputElement.type = 'time';
+
+ for (let test of tests) {
+ inputElement.valueAsDate = new Date(test.value);
+ is(inputElement.value, test.result,
+ "element.value should have been changed by setting valueAsDate");
+ }
+}
+
+function checkWithBustedPrototype()
+{
+ for (let type of validTypes) {
+ if (!type[1]) {
+ continue;
+ }
+
+ type = type[0];
+
+ var inputElement = document.createElement('input');
+ inputElement.type = type;
+
+ var backupPrototype = {};
+ backupPrototype.getUTCFullYear = Date.prototype.getUTCFullYear;
+ backupPrototype.getUTCMonth = Date.prototype.getUTCMonth;
+ backupPrototype.getUTCDate = Date.prototype.getUTCDate;
+ backupPrototype.getTime = Date.prototype.getTime;
+ backupPrototype.setUTCFullYear = Date.prototype.setUTCFullYear;
+
+ Date.prototype.getUTCFullYear = function() { return {}; };
+ Date.prototype.getUTCMonth = function() { return {}; };
+ Date.prototype.getUTCDate = function() { return {}; };
+ Date.prototype.getTime = function() { return {}; };
+ Date.prototype.setUTCFullYear = function(y,m,d) { };
+
+ inputElement.valueAsDate = new Date();
+
+ isnot(inputElement.valueAsDate, null, ".valueAsDate should not return null");
+ // The object returned by element.valueAsDate should return a Date object
+ // with the same prototype:
+ is(inputElement.valueAsDate.getUTCFullYear, Date.prototype.getUTCFullYear,
+ "prototype is the same");
+ is(inputElement.valueAsDate.getUTCMonth, Date.prototype.getUTCMonth,
+ "prototype is the same");
+ is(inputElement.valueAsDate.getUTCDate, Date.prototype.getUTCDate,
+ "prototype is the same");
+ is(inputElement.valueAsDate.getTime, Date.prototype.getTime,
+ "prototype is the same");
+ is(inputElement.valueAsDate.setUTCFullYear, Date.prototype.setUTCFullYear,
+ "prototype is the same");
+
+ // However the Date should have the correct information.
+ // Skip type=month for now, since .valueAsNumber returns number of months
+ // and not milliseconds.
+ if (type != "month") {
+ var witnessDate = new Date(inputElement.valueAsNumber);
+ is(inputElement.valueAsDate.valueOf(), witnessDate.valueOf(), "correct Date");
+ }
+
+ // Same test as above but using NaN instead of {}.
+
+ Date.prototype.getUTCFullYear = function() { return NaN; };
+ Date.prototype.getUTCMonth = function() { return NaN; };
+ Date.prototype.getUTCDate = function() { return NaN; };
+ Date.prototype.getTime = function() { return NaN; };
+ Date.prototype.setUTCFullYear = function(y,m,d) { };
+
+ inputElement.valueAsDate = new Date();
+
+ isnot(inputElement.valueAsDate, null, ".valueAsDate should not return null");
+ // The object returned by element.valueAsDate should return a Date object
+ // with the same prototype:
+ is(inputElement.valueAsDate.getUTCFullYear, Date.prototype.getUTCFullYear,
+ "prototype is the same");
+ is(inputElement.valueAsDate.getUTCMonth, Date.prototype.getUTCMonth,
+ "prototype is the same");
+ is(inputElement.valueAsDate.getUTCDate, Date.prototype.getUTCDate,
+ "prototype is the same");
+ is(inputElement.valueAsDate.getTime, Date.prototype.getTime,
+ "prototype is the same");
+ is(inputElement.valueAsDate.setUTCFullYear, Date.prototype.setUTCFullYear,
+ "prototype is the same");
+
+ // However the Date should have the correct information.
+ // Skip type=month for now, since .valueAsNumber returns number of months
+ // and not milliseconds.
+ if (type != "month") {
+ var witnessDate = new Date(inputElement.valueAsNumber);
+ is(inputElement.valueAsDate.valueOf(), witnessDate.valueOf(), "correct Date");
+ }
+
+ Date.prototype.getUTCFullYear = backupPrototype.getUTCFullYear;
+ Date.prototype.getUTCMonth = backupPrototype.getUTCMonth;
+ Date.prototype.getUTCDate = backupPrototype.getUTCDate;
+ Date.prototype.getTime = backupPrototype.getTime;
+ Date.prototype.setUTCFullYear = backupPrototype.setUTCFullYear;
+ }
+}
+
+function checkMonthGet()
+{
+ var validData =
+ [
+ [ "2016-07", 1467331200000 ],
+ [ "1970-01", 0 ],
+ [ "1970-02", 2678400000 ],
+ [ "1969-12", -2678400000 ],
+ [ "0001-01", -62135596800000 ],
+ [ "275760-09", 8639998963200000 ],
+ ];
+
+ var invalidData =
+ [
+ [ "invalidmonth" ],
+ [ "0000-01" ],
+ [ "2016-00" ],
+ [ "123-01" ],
+ [ "2017-13" ],
+ [ "" ],
+ // This month is valid for the input element, but is out of
+ // the date object range. In this case, on getting valueAsDate,
+ // a Date object will be created, but it will have a NaN internal value,
+ // and will return the string "Invalid Date".
+ [ "275760-10", true ],
+ ];
+
+ element.type = "month";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsDate.valueOf(), data[1],
+ "valueAsDate should return the " +
+ "valid date object representing this month");
+ }
+
+ for (let data of invalidData) {
+ element.value = data[0];
+ if (data[1]) {
+ is(String(element.valueAsDate), "Invalid Date",
+ "valueAsDate should return an invalid Date object " +
+ "when the element value is not a valid month");
+ } else {
+ is(element.valueAsDate, null,
+ "valueAsDate should return null " +
+ "when the element value is not a valid month");
+ }
+ }
+}
+
+function checkMonthSet()
+{
+ var testData =
+ [
+ [ 1342051200000, "2012-07" ],
+ [ 0, "1970-01" ],
+ // Maximum valid month (limited by the ecma date object range).
+ [ 8640000000000000, "275760-09" ],
+ // Minimum valid month (limited by the input element minimum valid value).
+ [ -62135596800000 , "0001-01" ],
+ [ 1330473600000, "2012-02" ],
+ [ 1298851200000, "2011-02" ],
+ // "Values must be truncated to valid months"
+ [ 42.1234, "1970-01" ],
+ [ 123.123456789123, "1970-01" ],
+ [ 1e-1, "1970-01" ],
+ [ 1298851200010, "2011-02" ],
+ [ -1, "1969-12" ],
+ [ -86400000, "1969-12" ],
+ [ 86400000, "1970-01" ],
+ // Negative years, this is out of range for the input element,
+ // the corresponding month string is the empty string
+ [ -62135596800001, "" ],
+ ];
+
+ element.type = "month";
+ for (let data of testData) {
+ element.valueAsDate = new Date(data[0]);
+ is(element.value, data[1], "valueAsDate should set the value to "
+ + data[1]);
+ element.valueAsDate = new testFrame.Date(data[0]);
+ is(element.value, data[1], "valueAsDate with other-global date should " +
+ "set the value to " + data[1]);
+ }
+}
+
+function checkWeekGet()
+{
+ var validData =
+ [
+ // Common years starting on different days of week.
+ [ "2007-W01", Date.UTC(2007, 0, 1) ], // Mon
+ [ "2013-W01", Date.UTC(2012, 11, 31) ], // Tue
+ [ "2014-W01", Date.UTC(2013, 11, 30) ], // Wed
+ [ "2015-W01", Date.UTC(2014, 11, 29) ], // Thu
+ [ "2010-W01", Date.UTC(2010, 0, 4) ], // Fri
+ [ "2011-W01", Date.UTC(2011, 0, 3) ], // Sat
+ [ "2017-W01", Date.UTC(2017, 0, 2) ], // Sun
+ // Common years ending on different days of week.
+ [ "2007-W52", Date.UTC(2007, 11, 24) ], // Mon
+ [ "2013-W52", Date.UTC(2013, 11, 23) ], // Tue
+ [ "2014-W52", Date.UTC(2014, 11, 22) ], // Wed
+ [ "2015-W53", Date.UTC(2015, 11, 28) ], // Thu
+ [ "2010-W52", Date.UTC(2010, 11, 27) ], // Fri
+ [ "2011-W52", Date.UTC(2011, 11, 26) ], // Sat
+ [ "2017-W52", Date.UTC(2017, 11, 25) ], // Sun
+ // Leap years starting on different days of week.
+ [ "1996-W01", Date.UTC(1996, 0, 1) ], // Mon
+ [ "2008-W01", Date.UTC(2007, 11, 31) ], // Tue
+ [ "2020-W01", Date.UTC(2019, 11, 30) ], // Wed
+ [ "2004-W01", Date.UTC(2003, 11, 29) ], // Thu
+ [ "2016-W01", Date.UTC(2016, 0, 4) ], // Fri
+ [ "2000-W01", Date.UTC(2000, 0, 3) ], // Sat
+ [ "2012-W01", Date.UTC(2012, 0, 2) ], // Sun
+ // Leap years ending on different days of week.
+ [ "2012-W52", Date.UTC(2012, 11, 24) ], // Mon
+ [ "2024-W52", Date.UTC(2024, 11, 23) ], // Tue
+ [ "1980-W52", Date.UTC(1980, 11, 22) ], // Wed
+ [ "1992-W53", Date.UTC(1992, 11, 28) ], // Thu
+ [ "2004-W53", Date.UTC(2004, 11, 27) ], // Fri
+ [ "1988-W52", Date.UTC(1988, 11, 26) ], // Sat
+ [ "2000-W52", Date.UTC(2000, 11, 25) ], // Sun
+ // Other normal cases.
+ [ "2016-W36", 1473033600000 ],
+ [ "1969-W52", -864000000 ],
+ [ "1970-W01", -259200000 ],
+ [ "275760-W37", 8639999568000000 ],
+ ];
+
+ var invalidData =
+ [
+ [ "invalidweek" ],
+ [ "0000-W01" ],
+ [ "2016-W00" ],
+ [ "123-W01" ],
+ [ "2016-W53" ],
+ [ "" ],
+ // This week is valid for the input element, but is out of
+ // the date object range. In this case, on getting valueAsDate,
+ // a Date object will be created, but it will have a NaN internal value,
+ // and will return the string "Invalid Date".
+ [ "275760-W38", true ],
+ ];
+
+ element.type = "week";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsDate.valueOf(), data[1],
+ "valueAsDate should return the " +
+ "valid date object representing this week");
+ }
+
+ for (let data of invalidData) {
+ element.value = data[0];
+ if (data[1]) {
+ is(String(element.valueAsDate), "Invalid Date",
+ "valueAsDate should return an invalid Date object " +
+ "when the element value is not a valid week");
+ } else {
+ is(element.valueAsDate, null,
+ "valueAsDate should return null " +
+ "when the element value is not a valid week");
+ }
+ }
+}
+
+function checkWeekSet()
+{
+ var testData =
+ [
+ // Common years starting on different days of week.
+ [ Date.UTC(2007, 0, 1), "2007-W01" ], // Mon
+ [ Date.UTC(2013, 0, 1), "2013-W01" ], // Tue
+ [ Date.UTC(2014, 0, 1), "2014-W01" ], // Wed
+ [ Date.UTC(2015, 0, 1), "2015-W01" ], // Thu
+ [ Date.UTC(2010, 0, 1), "2009-W53" ], // Fri
+ [ Date.UTC(2011, 0, 1), "2010-W52" ], // Sat
+ [ Date.UTC(2017, 0, 1), "2016-W52" ], // Sun
+ // Common years ending on different days of week.
+ [ Date.UTC(2007, 11, 31), "2008-W01" ], // Mon
+ [ Date.UTC(2013, 11, 31), "2014-W01" ], // Tue
+ [ Date.UTC(2014, 11, 31), "2015-W01" ], // Wed
+ [ Date.UTC(2015, 11, 31), "2015-W53" ], // Thu
+ [ Date.UTC(2010, 11, 31), "2010-W52" ], // Fri
+ [ Date.UTC(2011, 11, 31), "2011-W52" ], // Sat
+ [ Date.UTC(2017, 11, 31), "2017-W52" ], // Sun
+ // Leap years starting on different days of week.
+ [ Date.UTC(1996, 0, 1), "1996-W01" ], // Mon
+ [ Date.UTC(2008, 0, 1), "2008-W01" ], // Tue
+ [ Date.UTC(2020, 0, 1), "2020-W01" ], // Wed
+ [ Date.UTC(2004, 0, 1), "2004-W01" ], // Thu
+ [ Date.UTC(2016, 0, 1), "2015-W53" ], // Fri
+ [ Date.UTC(2000, 0, 1), "1999-W52" ], // Sat
+ [ Date.UTC(2012, 0, 1), "2011-W52" ], // Sun
+ // Leap years ending on different days of week.
+ [ Date.UTC(2012, 11, 31), "2013-W01" ], // Mon
+ [ Date.UTC(2024, 11, 31), "2025-W01" ], // Tue
+ [ Date.UTC(1980, 11, 31), "1981-W01" ], // Wed
+ [ Date.UTC(1992, 11, 31), "1992-W53" ], // Thu
+ [ Date.UTC(2004, 11, 31), "2004-W53" ], // Fri
+ [ Date.UTC(1988, 11, 31), "1988-W52" ], // Sat
+ [ Date.UTC(2000, 11, 31), "2000-W52" ], // Sun
+ // Other normal cases.
+ [ Date.UTC(2016, 8, 9), "2016-W36" ],
+ [ Date.UTC(2010, 0, 3), "2009-W53" ],
+ [ Date.UTC(2010, 0, 4), "2010-W01" ],
+ [ Date.UTC(2010, 0, 10), "2010-W01" ],
+ [ Date.UTC(2010, 0, 11), "2010-W02" ],
+ [ 0, "1970-W01" ],
+ // Maximum valid month (limited by the ecma date object range).
+ [ 8640000000000000, "275760-W37" ],
+ // Minimum valid month (limited by the input element minimum valid value).
+ [ -62135596800000 , "0001-W01" ],
+ // "Values must be truncated to valid week"
+ [ 42.1234, "1970-W01" ],
+ [ 123.123456789123, "1970-W01" ],
+ [ 1e-1, "1970-W01" ],
+ [ -1.1, "1970-W01" ],
+ [ -345600000, "1969-W52" ],
+ // Negative years, this is out of range for the input element,
+ // the corresponding week string is the empty string
+ [ -62135596800001, "" ],
+ ];
+
+ element.type = "week";
+ for (let data of testData) {
+ element.valueAsDate = new Date(data[0]);
+ is(element.value, data[1], "valueAsDate should set the value to "
+ + data[1]);
+ element.valueAsDate = new testFrame.Date(data[0]);
+ is(element.value, data[1], "valueAsDate with other-global date should " +
+ "set the value to " + data[1]);
+ }
+}
+
+function checkDatetimeLocalGet()
+{
+ var validData =
+ [
+ // Simple cases.
+ [ "2016-12-27T10:30", Date.UTC(2016, 11, 27, 10, 30, 0) ],
+ [ "2016-12-27T10:30:40", Date.UTC(2016, 11, 27, 10, 30, 40) ],
+ [ "2016-12-27T10:30:40.567", Date.UTC(2016, 11, 27, 10, 30, 40, 567) ],
+ [ "1969-12-31T12:00:00", Date.UTC(1969, 11, 31, 12, 0, 0) ],
+ [ "1970-01-01T00:00", 0 ],
+ // Leap years.
+ [ "1804-02-29 12:34", Date.UTC(1804, 1, 29, 12, 34, 0) ],
+ [ "2016-02-29T12:34", Date.UTC(2016, 1, 29, 12, 34, 0) ],
+ [ "2016-12-31T12:34:56", Date.UTC(2016, 11, 31, 12, 34, 56) ],
+ [ "2016-01-01T12:34:56.789", Date.UTC(2016, 0, 1, 12, 34, 56, 789) ],
+ [ "2017-01-01 12:34:56.789", Date.UTC(2017, 0, 1, 12, 34, 56, 789) ],
+ // Maximum valid datetime-local (limited by the ecma date object range).
+ [ "275760-09-13T00:00", 8640000000000000 ],
+ // Minimum valid datetime-local (limited by the input element minimum valid value).
+ [ "0001-01-01T00:00", -62135596800000 ],
+ ];
+
+ var invalidData =
+ [
+ [ "invaliddateime-local" ],
+ [ "0000-01-01T00:00" ],
+ [ "2016-12-25T00:00Z" ],
+ [ "2015-02-29T12:34" ],
+ [ "1-1-1T12:00" ],
+ [ "" ],
+ // This datetime-local is valid for the input element, but is out of the
+ // date object range. In this case, on getting valueAsDate, a Date object
+ // will be created, but it will have a NaN internal value, and will return
+ // the string "Invalid Date".
+ [ "275760-09-13T12:00", true ],
+ ];
+
+ element.type = "datetime-local";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsDate.valueOf(), data[1],
+ "valueAsDate should return the " +
+ "valid date object representing this datetime-local");
+ }
+
+ for (let data of invalidData) {
+ element.value = data[0];
+ if (data[1]) {
+ is(String(element.valueAsDate), "Invalid Date",
+ "valueAsDate should return an invalid Date object " +
+ "when the element value is not a valid datetime-local");
+ } else {
+ is(element.valueAsDate, null,
+ "valueAsDate should return null " +
+ "when the element value is not a valid datetime-local");
+ }
+ }
+}
+
+function checkDatetimeLocalSet()
+{
+ var testData =
+ [
+ // Simple cases.
+ [ Date.UTC(2016, 11, 27, 10, 30, 0), "2016-12-27T10:30" ],
+ [ Date.UTC(2016, 11, 27, 10, 30, 30), "2016-12-27T10:30:30" ],
+ [ Date.UTC(1999, 11, 31, 23, 59, 59), "1999-12-31T23:59:59" ],
+ [ Date.UTC(1999, 11, 31, 23, 59, 59, 999), "1999-12-31T23:59:59.999" ],
+ [ Date.UTC(123456, 7, 8, 9, 10), "123456-08-08T09:10" ],
+ [ 0, "1970-01-01T00:00" ],
+ // Maximum valid datetime-local (limited by the ecma date object range).
+ [ 8640000000000000, "275760-09-13T00:00" ],
+ // Minimum valid datetime-local (limited by the input element minimum valid value).
+ [ -62135596800000, "0001-01-01T00:00" ],
+ // Leap years.
+ [ Date.UTC(1804, 1, 29, 12, 34, 0), "1804-02-29T12:34" ],
+ [ Date.UTC(2016, 1, 29, 12, 34, 0), "2016-02-29T12:34" ],
+ [ Date.UTC(2016, 11, 31, 12, 34, 56), "2016-12-31T12:34:56" ],
+ [ Date.UTC(2016, 0, 1, 12, 34, 56, 789), "2016-01-01T12:34:56.789" ],
+ [ Date.UTC(2017, 0, 1, 12, 34, 56, 789), "2017-01-01T12:34:56.789" ],
+ // "Values must be truncated to valid datetime-local"
+ [ 123.123456789123, "1970-01-01T00:00:00.123" ],
+ [ 1e-1, "1970-01-01T00:00" ],
+ [ -1.1, "1969-12-31T23:59:59.999" ],
+ [ -345600000, "1969-12-28T00:00" ],
+ // Negative years, this is out of range for the input element,
+ // the corresponding datetime-local string is the empty string
+ [ -62135596800001, "" ],
+ ];
+
+ element.type = "datetime-local";
+ for (let data of testData) {
+ element.valueAsDate = new Date(data[0]);
+ is(element.value, data[1], "valueAsDate should set the value to " +
+ data[1]);
+ element.valueAsDate = new testFrame.Date(data[0]);
+ is(element.value, data[1], "valueAsDate with other-global date should " +
+ "set the value to " + data[1]);
+ }
+}
+
+checkAvailability();
+checkGarbageValues();
+checkWithBustedPrototype();
+
+// Test <input type='date'>.
+checkDateGet();
+checkDateSet();
+
+// Test <input type='time'>.
+checkTimeGet();
+checkTimeSet();
+
+// Test <input type='month'>.
+checkMonthGet();
+checkMonthSet();
+
+// Test <input type='week'>.
+checkWeekGet();
+checkWeekSet();
+
+// Test <input type='datetime-local'>.
+checkDatetimeLocalGet();
+checkDatetimeLocalSet();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/test_valueasnumber_attribute.html b/dom/html/test/forms/test_valueasnumber_attribute.html
new file mode 100644
index 0000000000..5f7537f7a8
--- /dev/null
+++ b/dom/html/test/forms/test_valueasnumber_attribute.html
@@ -0,0 +1,858 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=636737
+-->
+<head>
+ <title>Test for Bug input.valueAsNumber</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=636737">Mozilla Bug 636737</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 636737 **/
+
+/**
+ * This test is checking .valueAsNumber.
+ */
+
+function checkAvailability()
+{
+ var testData =
+ [
+ ["text", false],
+ ["password", false],
+ ["search", false],
+ ["tel", false],
+ ["email", false],
+ ["url", false],
+ ["hidden", false],
+ ["checkbox", false],
+ ["radio", false],
+ ["file", false],
+ ["submit", false],
+ ["image", false],
+ ["reset", false],
+ ["button", false],
+ ["number", true],
+ ["range", true],
+ ["date", true],
+ ["time", true],
+ ["color", false],
+ ["month", true],
+ ["week", true],
+ ["datetime-local", true],
+ ];
+
+ var element = document.createElement('input');
+
+ for (let data of testData) {
+ var exceptionCatched = false;
+ element.type = data[0];
+ try {
+ element.valueAsNumber;
+ } catch (e) {
+ exceptionCatched = true;
+ }
+ is(exceptionCatched, false,
+ "valueAsNumber shouldn't throw exception on getting");
+
+ exceptionCatched = false;
+ try {
+ element.valueAsNumber = 42;
+ } catch (e) {
+ exceptionCatched = true;
+ }
+ is(exceptionCatched, !data[1], "valueAsNumber for " + data[0] +
+ " availability is not correct");
+ }
+}
+
+function checkNumberGet()
+{
+ var testData =
+ [
+ ["42", 42],
+ ["-42", -42], // should work for negative values
+ ["42.1234", 42.1234],
+ ["123.123456789123", 123.123456789123], // double precision
+ ["1e2", 100], // e should be usable
+ ["2e1", 20],
+ ["1e-1", 0.1], // value after e can be negative
+ ["1E2", 100], // E can be used instead of e
+ ["e", null],
+ ["e2", null],
+ ["1e0.1", null],
+ ["", null], // the empty string is not a number
+ ["foo", null],
+ ["42,13", null], // comma can't be used as a decimal separator
+ ];
+
+ var element = document.createElement('input');
+ element.type = "number";
+ for (let data of testData) {
+ element.value = data[0];
+
+ // Given that NaN != NaN, we have to use null when the expected value is NaN.
+ if (data[1] != null) {
+ is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+ "floating point representation of the value");
+ } else {
+ ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
+ "when the element value is not a number");
+ }
+ }
+}
+
+function checkNumberSet()
+{
+ var testData =
+ [
+ [42, "42"],
+ [-42, "-42"], // should work for negative values
+ [42.1234, "42.1234"],
+ [123.123456789123, "123.123456789123"], // double precision
+ [1e2, "100"], // e should be usable
+ [2e1, "20"],
+ [1e-1, "0.1"], // value after e can be negative
+ [1E2, "100"], // E can be used instead of e
+ // Setting a string will set NaN.
+ ["foo", ""],
+ // "" is converted to 0.
+ ["", "0"],
+ [42, "42"], // Keep this here, it is used by the next test.
+ // Setting Infinity should throw and not change the current value.
+ [Infinity, "42", true],
+ [-Infinity, "42", true],
+ // Setting NaN should change the value to the empty string.
+ [NaN, ""],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "number";
+ for (let data of testData) {
+ var caught = false;
+ try {
+ element.valueAsNumber = data[0];
+ is(element.value, data[1],
+ "valueAsNumber should be able to set the value");
+ } catch (e) {
+ caught = true;
+ }
+
+ if (data[2]) {
+ ok(caught, "valueAsNumber should have thrown");
+ is(element.value, data[1], "value should not have changed");
+ } else {
+ ok(!caught, "valueAsNumber should not have thrown");
+ }
+ }
+}
+
+function checkRangeGet()
+{
+ // For type=range we should never get NaN since the user agent is required
+ // to fix up the input's value to be something sensible.
+
+ var min = -200;
+ var max = 200;
+ var defaultValue = min + (max - min)/2;
+
+ var testData =
+ [
+ ["42", 42],
+ ["-42", -42], // should work for negative values
+ ["42.1234", 42.1234],
+ ["123.123456789123", 123.123456789123], // double precision
+ ["1e2", 100], // e should be usable
+ ["2e1", 20],
+ ["1e-1", 0.1], // value after e can be negative
+ ["1E2", 100], // E can be used instead of e
+ ["e", defaultValue],
+ ["e2", defaultValue],
+ ["1e0.1", defaultValue],
+ ["", defaultValue],
+ ["foo", defaultValue],
+ ["42,13", defaultValue],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "range";
+ element.setAttribute("min", min); // avoids out of range sanitization
+ element.setAttribute("max", max);
+ element.setAttribute("step", "any"); // avoids step mismatch sanitization
+ for (let data of testData) {
+ element.value = data[0];
+
+ // Given that NaN != NaN, we have to use null when the expected value is NaN.
+ is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+ "floating point representation of the value");
+ }
+}
+
+function checkRangeSet()
+{
+ var min = -200;
+ var max = 200;
+ var defaultValue = String(min + (max - min)/2);
+
+ var testData =
+ [
+ [42, "42"],
+ [-42, "-42"], // should work for negative values
+ [42.1234, "42.1234"],
+ [123.123456789123, "123.123456789123"], // double precision
+ [1e2, "100"], // e should be usable
+ [2e1, "20"],
+ [1e-1, "0.1"], // value after e can be negative
+ [1E2, "100"], // E can be used instead of e
+ ["foo", defaultValue],
+ ["", defaultValue],
+ [42, "42"], // Keep this here, it is used by the next test.
+ // Setting Infinity should throw and not change the current value.
+ [Infinity, "42", true],
+ [-Infinity, "42", true],
+ // Setting NaN should change the value to the empty string.
+ [NaN, defaultValue],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "range";
+ element.setAttribute("min", min); // avoids out of range sanitization
+ element.setAttribute("max", max);
+ element.setAttribute("step", "any"); // avoids step mismatch sanitization
+ for (let data of testData) {
+ var caught = false;
+ try {
+ element.valueAsNumber = data[0];
+ is(element.value, data[1],
+ "valueAsNumber should be able to set the value");
+ } catch (e) {
+ caught = true;
+ }
+
+ if (data[2]) {
+ ok(caught, "valueAsNumber should have thrown");
+ is(element.value, data[1], "value should not have changed");
+ } else {
+ ok(!caught, "valueAsNumber should not have thrown");
+ }
+ }
+}
+
+function checkDateGet()
+{
+ var validData =
+ [
+ [ "2012-07-12", 1342051200000 ],
+ [ "1970-01-01", 0 ],
+ // We are supposed to support at least until this date.
+ // (corresponding to the date object maximal value)
+ [ "275760-09-13", 8640000000000000 ],
+ // Minimum valid date (limited by the input element minimum valid value)
+ [ "0001-01-01", -62135596800000 ],
+ [ "2012-02-29", 1330473600000 ],
+ [ "2011-02-28", 1298851200000 ],
+ ];
+
+ var invalidData =
+ [
+ "invaliddate",
+ "",
+ "275760-09-14",
+ "999-12-31",
+ "-001-12-31",
+ "0000-01-01",
+ "2011-02-29",
+ "1901-13-31",
+ "1901-12-32",
+ "1901-00-12",
+ "1901-01-00",
+ "1900-02-29",
+ ];
+
+ var element = document.createElement('input');
+ element.type = "date";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+ "timestamp representing this date");
+ }
+
+ for (let data of invalidData) {
+ element.value = data;
+ ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
+ "when the element value is not a valid date");
+ }
+}
+
+function checkDateSet()
+{
+ var testData =
+ [
+ [ 1342051200000, "2012-07-12" ],
+ [ 0, "1970-01-01" ],
+ // Maximum valid date (limited by the ecma date object range).
+ [ 8640000000000000, "275760-09-13" ],
+ // Minimum valid date (limited by the input element minimum valid value)
+ [ -62135596800000, "0001-01-01" ],
+ [ 1330473600000, "2012-02-29" ],
+ [ 1298851200000, "2011-02-28" ],
+ // "Values must be truncated to valid dates"
+ [ 42.1234, "1970-01-01" ],
+ [ 123.123456789123, "1970-01-01" ],
+ [ 1e2, "1970-01-01" ],
+ [ 1E9, "1970-01-12" ],
+ [ 1e-1, "1970-01-01" ],
+ [ 2e10, "1970-08-20" ],
+ [ 1298851200010, "2011-02-28" ],
+ [ -1, "1969-12-31" ],
+ [ -86400000, "1969-12-31" ],
+ [ 86400000, "1970-01-02" ],
+ // Invalid numbers.
+ // Those are implicitly converted to numbers
+ [ "", "1970-01-01" ],
+ [ true, "1970-01-01" ],
+ [ false, "1970-01-01" ],
+ [ null, "1970-01-01" ],
+ // Those are converted to NaN, the corresponding date string is the empty string
+ [ "invaliddatenumber", "" ],
+ [ NaN, "" ],
+ [ undefined, "" ],
+ // Out of range, the corresponding date string is the empty string
+ [ -62135596800001, "" ],
+ // Infinity will keep the current value and throw (so we need to set a current value).
+ [ 1298851200010, "2011-02-28" ],
+ [ Infinity, "2011-02-28", true ],
+ [ -Infinity, "2011-02-28", true ],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "date";
+ for (let data of testData) {
+ var caught = false;
+
+ try {
+ element.valueAsNumber = data[0];
+ is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
+ } catch(e) {
+ caught = true;
+ }
+
+ if (data[2]) {
+ ok(caught, "valueAsNumber should have thrown");
+ is(element.value, data[1], "the value should not have changed");
+ } else {
+ ok(!caught, "valueAsNumber should not have thrown");
+ }
+ }
+
+}
+
+function checkTimeGet()
+{
+ var tests = [
+ // Some invalid values to begin.
+ { value: "", result: NaN },
+ { value: "foobar", result: NaN },
+ { value: "00:", result: NaN },
+ { value: "24:00", result: NaN },
+ { value: "00:99", result: NaN },
+ { value: "00:00:", result: NaN },
+ { value: "00:00:99", result: NaN },
+ { value: "00:00:00:", result: NaN },
+ { value: "00:00:00.", result: NaN },
+ { value: "00:00:00.0000", result: NaN },
+ // Some simple valid values.
+ { value: "00:00", result: 0 },
+ { value: "00:01", result: 60000 },
+ { value: "01:00", result: 3600000 },
+ { value: "01:01", result: 3660000 },
+ { value: "13:37", result: 49020000 },
+ // Valid values including seconds.
+ { value: "00:00:01", result: 1000 },
+ { value: "13:37:42", result: 49062000 },
+ // Valid values including seconds fractions.
+ { value: "00:00:00.001", result: 1 },
+ { value: "00:00:00.123", result: 123 },
+ { value: "00:00:00.100", result: 100 },
+ { value: "00:00:00.000", result: 0 },
+ { value: "20:17:31.142", result: 73051142 },
+ // Highest possible value.
+ { value: "23:59:59.999", result: 86399999 },
+ // Some values with one or two digits for the fraction of seconds.
+ { value: "00:00:00.1", result: 100 },
+ { value: "00:00:00.14", result: 140 },
+ { value: "13:37:42.7", result: 49062700 },
+ { value: "23:31:12.23", result: 84672230 },
+ ];
+
+ var element = document.createElement('input');
+ element.type = 'time';
+
+ for (let test of tests) {
+ element.value = test.value;
+ if (isNaN(test.result)) {
+ ok(isNaN(element.valueAsNumber),
+ "invalid value should have .valueAsNumber return NaN");
+ } else {
+ is(element.valueAsNumber, test.result,
+ ".valueAsNumber should return " + test.result);
+ }
+ }
+}
+
+function checkTimeSet()
+{
+ var tests = [
+ // Some NaN values (should set to empty string).
+ { value: NaN, result: "" },
+ { value: "foobar", result: "" },
+ { value() {}, result: "" },
+ // Inifinity (should throw).
+ { value: Infinity, throw: true },
+ { value: -Infinity, throw: true },
+ // "" converts to 0... JS is fun :)
+ { value: "", result: "00:00" },
+ // Simple tests.
+ { value: 0, result: "00:00" },
+ { value: 1, result: "00:00:00.001" },
+ { value: 100, result: "00:00:00.100" },
+ { value: 1000, result: "00:00:01" },
+ { value: 60000, result: "00:01" },
+ { value: 3600000, result: "01:00" },
+ { value: 83622234, result: "23:13:42.234" },
+ // Some edge cases.
+ { value: 86400000, result: "00:00" },
+ { value: 86400001, result: "00:00:00.001" },
+ { value: 170022234, result: "23:13:42.234" },
+ { value: 432000000, result: "00:00" },
+ { value: -1, result: "23:59:59.999" },
+ { value: -86400000, result: "00:00" },
+ { value: -86400001, result: "23:59:59.999" },
+ { value: -56789, result: "23:59:03.211" },
+ { value: 0.9, result: "00:00" },
+ ];
+
+ var element = document.createElement('input');
+ element.type = 'time';
+
+ for (let test of tests) {
+ try {
+ var caught = false;
+ element.valueAsNumber = test.value;
+ is(element.value, test.result, "value should return " + test.result);
+ } catch(e) {
+ caught = true;
+ }
+
+ if (!test.throw) {
+ test.throw = false;
+ }
+
+ is(caught, test.throw, "the test throwing status should be " + test.throw);
+ }
+}
+
+function checkMonthGet()
+{
+ var validData =
+ [
+ [ "2016-07", 558 ],
+ [ "1970-01", 0 ],
+ [ "1969-12", -1 ],
+ [ "0001-01", -23628 ],
+ [ "10000-12", 96371 ],
+ [ "275760-09", 3285488 ],
+ ];
+
+ var invalidData =
+ [
+ "invalidmonth",
+ "0000-01",
+ "2000-00",
+ "2012-13",
+ // Out of range.
+ "275760-10",
+ ];
+
+ var element = document.createElement('input');
+ element.type = "month";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+ "integer value representing this month");
+ }
+
+ for (let data of invalidData) {
+ element.value = data;
+ ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
+ "when the element value is not a valid month");
+ }
+}
+
+function checkMonthSet()
+{
+ var testData =
+ [
+ [ 558, "2016-07" ],
+ [ 0, "1970-01" ],
+ [ -1, "1969-12" ],
+ [ 96371, "10000-12" ],
+ [ 12, "1971-01" ],
+ [ -12, "1969-01" ],
+ // Maximum valid month (limited by the ecma date object range)
+ [ 3285488, "275760-09" ],
+ // Minimum valid month (limited by the input element minimum valid value)
+ [ -23628, "0001-01" ],
+ // "Values must be truncated to valid months"
+ [ 0.3, "1970-01" ],
+ [ -1.1, "1969-11" ],
+ [ 1e2, "1978-05" ],
+ [ 1e-1, "1970-01" ],
+ // Invalid numbers.
+ // Those are implicitly converted to numbers
+ [ "", "1970-01" ],
+ [ true, "1970-02" ],
+ [ false, "1970-01" ],
+ [ null, "1970-01" ],
+ // Those are converted to NaN, the corresponding month string is the empty string
+ [ "invalidmonth", "" ],
+ [ NaN, "" ],
+ [ undefined, "" ],
+ // Out of range, the corresponding month string is the empty string
+ [ -23629, "" ],
+ [ 3285489, "" ],
+ // Infinity will keep the current value and throw (so we need to set a current value)
+ [ 558, "2016-07" ],
+ [ Infinity, "2016-07", true ],
+ [ -Infinity, "2016-07", true ],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "month";
+ for (let data of testData) {
+ var caught = false;
+
+ try {
+ element.valueAsNumber = data[0];
+ is(element.value, data[1], "valueAsNumber should set the value to " + data[1]);
+ } catch(e) {
+ caught = true;
+ }
+
+ if (data[2]) {
+ ok(caught, "valueAsNumber should have thrown");
+ is(element.value, data[1], "the value should not have changed");
+ } else {
+ ok(!caught, "valueAsNumber should not have thrown");
+ }
+ }
+}
+
+function checkWeekGet()
+{
+ var validData =
+ [
+ // Common years starting on different days of week.
+ [ "2007-W01", Date.UTC(2007, 0, 1) ], // Mon
+ [ "2013-W01", Date.UTC(2012, 11, 31) ], // Tue
+ [ "2014-W01", Date.UTC(2013, 11, 30) ], // Wed
+ [ "2015-W01", Date.UTC(2014, 11, 29) ], // Thu
+ [ "2010-W01", Date.UTC(2010, 0, 4) ], // Fri
+ [ "2011-W01", Date.UTC(2011, 0, 3) ], // Sat
+ [ "2017-W01", Date.UTC(2017, 0, 2) ], // Sun
+ // Common years ending on different days of week.
+ [ "2007-W52", Date.UTC(2007, 11, 24) ], // Mon
+ [ "2013-W52", Date.UTC(2013, 11, 23) ], // Tue
+ [ "2014-W52", Date.UTC(2014, 11, 22) ], // Wed
+ [ "2015-W53", Date.UTC(2015, 11, 28) ], // Thu
+ [ "2010-W52", Date.UTC(2010, 11, 27) ], // Fri
+ [ "2011-W52", Date.UTC(2011, 11, 26) ], // Sat
+ [ "2017-W52", Date.UTC(2017, 11, 25) ], // Sun
+ // Leap years starting on different days of week.
+ [ "1996-W01", Date.UTC(1996, 0, 1) ], // Mon
+ [ "2008-W01", Date.UTC(2007, 11, 31) ], // Tue
+ [ "2020-W01", Date.UTC(2019, 11, 30) ], // Wed
+ [ "2004-W01", Date.UTC(2003, 11, 29) ], // Thu
+ [ "2016-W01", Date.UTC(2016, 0, 4) ], // Fri
+ [ "2000-W01", Date.UTC(2000, 0, 3) ], // Sat
+ [ "2012-W01", Date.UTC(2012, 0, 2) ], // Sun
+ // Leap years ending on different days of week.
+ [ "2012-W52", Date.UTC(2012, 11, 24) ], // Mon
+ [ "2024-W52", Date.UTC(2024, 11, 23) ], // Tue
+ [ "1980-W52", Date.UTC(1980, 11, 22) ], // Wed
+ [ "1992-W53", Date.UTC(1992, 11, 28) ], // Thu
+ [ "2004-W53", Date.UTC(2004, 11, 27) ], // Fri
+ [ "1988-W52", Date.UTC(1988, 11, 26) ], // Sat
+ [ "2000-W52", Date.UTC(2000, 11, 25) ], // Sun
+ // Other normal cases.
+ [ "2015-W53", Date.UTC(2015, 11, 28) ],
+ [ "2016-W36", Date.UTC(2016, 8, 5) ],
+ [ "1970-W01", Date.UTC(1969, 11, 29) ],
+ [ "275760-W37", Date.UTC(275760, 8, 8) ],
+ ];
+
+ var invalidData =
+ [
+ "invalidweek",
+ "0000-W01",
+ "2016-W00",
+ "2016-W53",
+ // Out of range.
+ "275760-W38",
+ ];
+
+ var element = document.createElement('input');
+ element.type = "week";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+ "integer value representing this week");
+ }
+
+ for (let data of invalidData) {
+ element.value = data;
+ ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
+ "when the element value is not a valid week");
+ }
+}
+
+function checkWeekSet()
+{
+ var testData =
+ [
+ // Common years starting on different days of week.
+ [ Date.UTC(2007, 0, 1), "2007-W01" ], // Mon
+ [ Date.UTC(2013, 0, 1), "2013-W01" ], // Tue
+ [ Date.UTC(2014, 0, 1), "2014-W01" ], // Wed
+ [ Date.UTC(2015, 0, 1), "2015-W01" ], // Thu
+ [ Date.UTC(2010, 0, 1), "2009-W53" ], // Fri
+ [ Date.UTC(2011, 0, 1), "2010-W52" ], // Sat
+ [ Date.UTC(2017, 0, 1), "2016-W52" ], // Sun
+ // Common years ending on different days of week.
+ [ Date.UTC(2007, 11, 31), "2008-W01" ], // Mon
+ [ Date.UTC(2013, 11, 31), "2014-W01" ], // Tue
+ [ Date.UTC(2014, 11, 31), "2015-W01" ], // Wed
+ [ Date.UTC(2015, 11, 31), "2015-W53" ], // Thu
+ [ Date.UTC(2010, 11, 31), "2010-W52" ], // Fri
+ [ Date.UTC(2011, 11, 31), "2011-W52" ], // Sat
+ [ Date.UTC(2017, 11, 31), "2017-W52" ], // Sun
+ // Leap years starting on different days of week.
+ [ Date.UTC(1996, 0, 1), "1996-W01" ], // Mon
+ [ Date.UTC(2008, 0, 1), "2008-W01" ], // Tue
+ [ Date.UTC(2020, 0, 1), "2020-W01" ], // Wed
+ [ Date.UTC(2004, 0, 1), "2004-W01" ], // Thu
+ [ Date.UTC(2016, 0, 1), "2015-W53" ], // Fri
+ [ Date.UTC(2000, 0, 1), "1999-W52" ], // Sat
+ [ Date.UTC(2012, 0, 1), "2011-W52" ], // Sun
+ // Leap years ending on different days of week.
+ [ Date.UTC(2012, 11, 31), "2013-W01" ], // Mon
+ [ Date.UTC(2024, 11, 31), "2025-W01" ], // Tue
+ [ Date.UTC(1980, 11, 31), "1981-W01" ], // Wed
+ [ Date.UTC(1992, 11, 31), "1992-W53" ], // Thu
+ [ Date.UTC(2004, 11, 31), "2004-W53" ], // Fri
+ [ Date.UTC(1988, 11, 31), "1988-W52" ], // Sat
+ [ Date.UTC(2000, 11, 31), "2000-W52" ], // Sun
+ // Other normal cases.
+ [ Date.UTC(2008, 8, 26), "2008-W39" ],
+ [ Date.UTC(2016, 0, 4), "2016-W01" ],
+ [ Date.UTC(2016, 0, 10), "2016-W01" ],
+ [ Date.UTC(2016, 0, 11), "2016-W02" ],
+ // Maximum valid week (limited by the ecma date object range).
+ [ 8640000000000000, "275760-W37" ],
+ // Minimum valid week (limited by the input element minimum valid value)
+ [ -62135596800000, "0001-W01" ],
+ // "Values must be truncated to valid weeks"
+ [ 0.3, "1970-W01" ],
+ [ 1e-1, "1970-W01" ],
+ [ -1.1, "1970-W01" ],
+ [ -345600000, "1969-W52" ],
+ // Invalid numbers.
+ // Those are implicitly converted to numbers
+ [ "", "1970-W01" ],
+ [ true, "1970-W01" ],
+ [ false, "1970-W01" ],
+ [ null, "1970-W01" ],
+ // Those are converted to NaN, the corresponding week string is the empty string
+ [ "invalidweek", "" ],
+ [ NaN, "" ],
+ [ undefined, "" ],
+ // Infinity will keep the current value and throw (so we need to set a current value).
+ [ Date.UTC(2016, 8, 8), "2016-W36" ],
+ [ Infinity, "2016-W36", true ],
+ [ -Infinity, "2016-W36", true ],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "week";
+ for (let data of testData) {
+ var caught = false;
+
+ try {
+ element.valueAsNumber = data[0];
+ is(element.value, data[1], "valueAsNumber should set the value to " +
+ data[1]);
+ } catch(e) {
+ caught = true;
+ }
+
+ if (data[2]) {
+ ok(caught, "valueAsNumber should have thrown");
+ is(element.value, data[1], "the value should not have changed");
+ } else {
+ ok(!caught, "valueAsNumber should not have thrown");
+ }
+ }
+}
+
+function checkDatetimeLocalGet() {
+ var validData =
+ [
+ // Simple cases.
+ [ "2016-12-20T09:58", Date.UTC(2016, 11, 20, 9, 58) ],
+ [ "2016-12-20T09:58:30", Date.UTC(2016, 11, 20, 9, 58, 30) ],
+ [ "2016-12-20T09:58:30.123", Date.UTC(2016, 11, 20, 9, 58, 30, 123) ],
+ [ "2017-01-01T10:00", Date.UTC(2017, 0, 1, 10, 0, 0) ],
+ [ "1969-12-31T12:00:00", Date.UTC(1969, 11, 31, 12, 0, 0) ],
+ [ "1970-01-01T00:00", 0 ],
+ // Leap years.
+ [ "1804-02-29 12:34", Date.UTC(1804, 1, 29, 12, 34, 0) ],
+ [ "2016-02-29T12:34", Date.UTC(2016, 1, 29, 12, 34, 0) ],
+ [ "2016-12-31T12:34:56", Date.UTC(2016, 11, 31, 12, 34, 56) ],
+ [ "2016-01-01T12:34:56.789", Date.UTC(2016, 0, 1, 12, 34, 56, 789) ],
+ [ "2017-01-01 12:34:56.789", Date.UTC(2017, 0, 1, 12, 34, 56, 789) ],
+ // Maximum valid datetime-local (limited by the ecma date object range).
+ [ "275760-09-13T00:00", 8640000000000000 ],
+ // Minimum valid datetime-local (limited by the input element minimum valid value).
+ [ "0001-01-01T00:00", -62135596800000 ],
+ ];
+
+ var invalidData =
+ [
+ "invaliddatetime-local",
+ "0000-01-01T00:00",
+ "2016-12-25T00:00Z",
+ "2015-02-29T12:34",
+ "1-1-1T12:00",
+ // Out of range.
+ "275760-09-13T12:00",
+ ];
+
+ var element = document.createElement('input');
+ element.type = "datetime-local";
+ for (let data of validData) {
+ element.value = data[0];
+ is(element.valueAsNumber, data[1], "valueAsNumber should return the " +
+ "integer value representing this datetime-local");
+ }
+
+ for (let data of invalidData) {
+ element.value = data;
+ ok(isNaN(element.valueAsNumber), "valueAsNumber should return NaN " +
+ "when the element value is not a valid datetime-local");
+ }
+}
+
+function checkDatetimeLocalSet()
+{
+ var testData =
+ [
+ // Simple cases.
+ [ Date.UTC(2016, 11, 20, 9, 58, 0), "2016-12-20T09:58", ],
+ [ Date.UTC(2016, 11, 20, 9, 58, 30), "2016-12-20T09:58:30" ],
+ [ Date.UTC(2016, 11, 20, 9, 58, 30, 123), "2016-12-20T09:58:30.123" ],
+ [ Date.UTC(2017, 0, 1, 10, 0, 0), "2017-01-01T10:00" ],
+ [ Date.UTC(1969, 11, 31, 12, 0, 0), "1969-12-31T12:00" ],
+ [ 0, "1970-01-01T00:00" ],
+ // Maximum valid week (limited by the ecma date object range).
+ [ 8640000000000000, "275760-09-13T00:00" ],
+ // Minimum valid datetime-local (limited by the input element minimum valid value).
+ [ -62135596800000, "0001-01-01T00:00" ],
+ // Leap years.
+ [ Date.UTC(1804, 1, 29, 12, 34, 0), "1804-02-29T12:34" ],
+ [ Date.UTC(2016, 1, 29, 12, 34, 0), "2016-02-29T12:34" ],
+ [ Date.UTC(2016, 11, 31, 12, 34, 56), "2016-12-31T12:34:56" ],
+ [ Date.UTC(2016, 0, 1, 12, 34, 56, 789), "2016-01-01T12:34:56.789" ],
+ [ Date.UTC(2017, 0, 1, 12, 34, 56, 789), "2017-01-01T12:34:56.789" ],
+ // "Values must be truncated to valid datetime-local"
+ [ 0.3, "1970-01-01T00:00" ],
+ [ 1e-1, "1970-01-01T00:00" ],
+ [ -1 , "1969-12-31T23:59:59.999" ],
+ [ -345600000, "1969-12-28T00:00" ],
+ // Invalid numbers.
+ // Those are implicitly converted to numbers
+ [ "", "1970-01-01T00:00" ],
+ [ true, "1970-01-01T00:00:00.001" ],
+ [ false, "1970-01-01T00:00" ],
+ [ null, "1970-01-01T00:00" ],
+ // Those are converted to NaN, the corresponding week string is the empty string
+ [ "invaliddatetime-local", "" ],
+ [ NaN, "" ],
+ [ undefined, "" ],
+ // Infinity will keep the current value and throw (so we need to set a current value).
+ [ Date.UTC(2016, 11, 27, 15, 10, 0), "2016-12-27T15:10" ],
+ [ Infinity, "2016-12-27T15:10", true ],
+ [ -Infinity, "2016-12-27T15:10", true ],
+ ];
+
+ var element = document.createElement('input');
+ element.type = "datetime-local";
+ for (let data of testData) {
+ var caught = false;
+
+ try {
+ element.valueAsNumber = data[0];
+ is(element.value, data[1], "valueAsNumber should set the value to " +
+ data[1]);
+ } catch(e) {
+ caught = true;
+ }
+
+ if (data[2]) {
+ ok(caught, "valueAsNumber should have thrown");
+ is(element.value, data[1], "the value should not have changed");
+ } else {
+ ok(!caught, "valueAsNumber should not have thrown");
+ }
+ }
+}
+
+checkAvailability();
+
+// <input type='number'> test
+checkNumberGet();
+checkNumberSet();
+
+// <input type='range'> test
+checkRangeGet();
+checkRangeSet();
+
+// <input type='date'> test
+checkDateGet();
+checkDateSet();
+
+// <input type='time'> test
+checkTimeGet();
+checkTimeSet();
+
+// <input type='month'> test
+checkMonthGet();
+checkMonthSet();
+
+// <input type='week'> test
+checkWeekGet();
+checkWeekSet();
+
+// <input type='datetime-local'> test
+checkDatetimeLocalGet();
+checkDatetimeLocalSet();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/forms/without_selectionchange/mochitest.toml b/dom/html/test/forms/without_selectionchange/mochitest.toml
new file mode 100644
index 0000000000..8f019d8d80
--- /dev/null
+++ b/dom/html/test/forms/without_selectionchange/mochitest.toml
@@ -0,0 +1,5 @@
+[DEFAULT]
+prefs = ["dom.select_events.textcontrols.enabled=false"]
+
+["test_select.html"]
+
diff --git a/dom/html/test/forms/without_selectionchange/test_select.html b/dom/html/test/forms/without_selectionchange/test_select.html
new file mode 100644
index 0000000000..3d11611b1b
--- /dev/null
+++ b/dom/html/test/forms/without_selectionchange/test_select.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Test for Bug 1717435</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+
+<textarea id="textarea">foo</textarea>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ textarea.addEventListener("select", ev => {
+ ok(true, "A select event must fire regardless of dom.select_events.textcontrols.enabled");
+ SimpleTest.finish();
+ });
+
+ textarea.focus();
+ textarea.select();
+ is(textarea.selectionStart, 0, "selectionStart")
+ is(textarea.selectionEnd, 3, "selectionEnd")
+</script>
diff --git a/dom/html/test/head.js b/dom/html/test/head.js
new file mode 100644
index 0000000000..1e3b435d0c
--- /dev/null
+++ b/dom/html/test/head.js
@@ -0,0 +1,65 @@
+function pushPrefs(...aPrefs) {
+ return SpecialPowers.pushPrefEnv({ set: aPrefs });
+}
+
+function promiseWaitForEvent(
+ object,
+ eventName,
+ capturing = false,
+ chrome = false
+) {
+ return new Promise(resolve => {
+ function listener(event) {
+ info("Saw " + eventName);
+ object.removeEventListener(eventName, listener, capturing, chrome);
+ resolve(event);
+ }
+
+ info("Waiting for " + eventName);
+ object.addEventListener(eventName, listener, capturing, chrome);
+ });
+}
+
+/**
+ * Waits for the next load to complete in any browser or the given browser.
+ * If a <tabbrowser> is given it waits for a load in any of its browsers.
+ *
+ * @return promise
+ */
+function waitForDocLoadComplete(aBrowser = gBrowser) {
+ return new Promise(resolve => {
+ let listener = {
+ onStateChange(webProgress, req, flags, status) {
+ let docStop =
+ Ci.nsIWebProgressListener.STATE_IS_NETWORK |
+ Ci.nsIWebProgressListener.STATE_STOP;
+ info(
+ "Saw state " +
+ flags.toString(16) +
+ " and status " +
+ status.toString(16)
+ );
+ // When a load needs to be retargetted to a new process it is cancelled
+ // with NS_BINDING_ABORTED so ignore that case
+ if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
+ aBrowser.removeProgressListener(this);
+ waitForDocLoadComplete.listeners.delete(this);
+ let chan = req.QueryInterface(Ci.nsIChannel);
+ info("Browser loaded " + chan.originalURI.spec);
+ resolve();
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ };
+ aBrowser.addProgressListener(listener);
+ waitForDocLoadComplete.listeners.add(listener);
+ info("Waiting for browser load");
+ });
+}
+// Keep a set of progress listeners for waitForDocLoadComplete() to make sure
+// they're not GC'ed before we saw the page load.
+waitForDocLoadComplete.listeners = new Set();
+registerCleanupFunction(() => waitForDocLoadComplete.listeners.clear());
diff --git a/dom/html/test/image-allow-credentials.png b/dom/html/test/image-allow-credentials.png
new file mode 100644
index 0000000000..df24ac6d34
--- /dev/null
+++ b/dom/html/test/image-allow-credentials.png
Binary files differ
diff --git a/dom/html/test/image-allow-credentials.png^headers^ b/dom/html/test/image-allow-credentials.png^headers^
new file mode 100644
index 0000000000..a03f99a9c0
--- /dev/null
+++ b/dom/html/test/image-allow-credentials.png^headers^
@@ -0,0 +1,2 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
+Access-Control-Allow-Credentials: true
diff --git a/dom/html/test/image.png b/dom/html/test/image.png
new file mode 100644
index 0000000000..d26878c9f2
--- /dev/null
+++ b/dom/html/test/image.png
Binary files differ
diff --git a/dom/html/test/image_yellow.png b/dom/html/test/image_yellow.png
new file mode 100644
index 0000000000..51e8aaf38c
--- /dev/null
+++ b/dom/html/test/image_yellow.png
Binary files differ
diff --git a/dom/html/test/mochitest.toml b/dom/html/test/mochitest.toml
new file mode 100644
index 0000000000..d1dd78705b
--- /dev/null
+++ b/dom/html/test/mochitest.toml
@@ -0,0 +1,990 @@
+[DEFAULT]
+prefs = ["gfx.font_loader.delay=0"]
+support-files = [
+ "347174transform.xsl",
+ "347174transformable.xml",
+ "allowMedia.sjs",
+ "bug100533_iframe.html",
+ "bug100533_load.html",
+ "bug196523-subframe.html",
+ "bug199692-nested-d2.html",
+ "bug199692-nested.html",
+ "bug199692-popup.html",
+ "bug199692-scrolled.html",
+ "bug242709_iframe.html",
+ "bug242709_load.html",
+ "bug277724_iframe1.html",
+ "bug277724_iframe2.xhtml",
+ "bug277890_iframe.html",
+ "bug277890_load.html",
+ "bug340800_iframe.txt",
+ "bug369370-popup.png",
+ "bug372098-link-target.html",
+ "bug441930_iframe.html",
+ "bug445004-inner.html",
+ "bug445004-inner.js",
+ "bug445004-outer-abs.html",
+ "bug445004-outer-rel.html",
+ "bug445004-outer-write.html",
+ "bug446483-iframe.html",
+ "bug448564-echo.sjs",
+ "bug448564-iframe-1.html",
+ "bug448564-iframe-2.html",
+ "bug448564-iframe-3.html",
+ "bug448564-submit.js",
+ "bug499092.html",
+ "bug499092.xml",
+ "bug514856_iframe.html",
+ "bug1260704_iframe.html",
+ "bug1260704_iframe_empty.html",
+ "bug1292522_iframe.html",
+ "bug1292522_page.html",
+ "bug1315146-iframe.html",
+ "bug1315146-main.html",
+ "dummy_page.html",
+ "test_non-ascii-cookie.html^headers^",
+ "file_bug209275_1.html",
+ "file_bug209275_2.html",
+ "file_bug209275_3.html",
+ "file_bug297761.html",
+ "file_bug417760.png",
+ "file_bug893537.html",
+ "file_bug1260704.png",
+ "file_formSubmission_img.jpg",
+ "file_formSubmission_text.txt",
+ "file_iframe_sandbox_a_if1.html",
+ "file_iframe_sandbox_a_if10.html",
+ "file_iframe_sandbox_a_if11.html",
+ "file_iframe_sandbox_a_if12.html",
+ "file_iframe_sandbox_a_if13.html",
+ "file_iframe_sandbox_a_if14.html",
+ "file_iframe_sandbox_a_if15.html",
+ "file_iframe_sandbox_a_if16.html",
+ "file_iframe_sandbox_a_if17.html",
+ "file_iframe_sandbox_a_if18.html",
+ "file_iframe_sandbox_a_if19.html",
+ "file_iframe_sandbox_a_if2.html",
+ "file_iframe_sandbox_a_if3.html",
+ "file_iframe_sandbox_a_if4.html",
+ "file_iframe_sandbox_a_if5.html",
+ "file_iframe_sandbox_a_if6.html",
+ "file_iframe_sandbox_a_if7.html",
+ "file_iframe_sandbox_a_if8.html",
+ "file_iframe_sandbox_a_if9.html",
+ "file_iframe_sandbox_b_if1.html",
+ "file_iframe_sandbox_b_if2.html",
+ "file_iframe_sandbox_b_if3.html",
+ "file_iframe_sandbox_c_if1.html",
+ "file_iframe_sandbox_c_if2.html",
+ "file_iframe_sandbox_c_if3.html",
+ "file_iframe_sandbox_c_if4.html",
+ "file_iframe_sandbox_c_if5.html",
+ "file_iframe_sandbox_c_if6.html",
+ "file_iframe_sandbox_c_if7.html",
+ "file_iframe_sandbox_c_if8.html",
+ "file_iframe_sandbox_c_if9.html",
+ "file_iframe_sandbox_close.html",
+ "file_iframe_sandbox_d_if1.html",
+ "file_iframe_sandbox_d_if10.html",
+ "file_iframe_sandbox_d_if11.html",
+ "file_iframe_sandbox_d_if12.html",
+ "file_iframe_sandbox_d_if13.html",
+ "file_iframe_sandbox_d_if14.html",
+ "file_iframe_sandbox_d_if15.html",
+ "file_iframe_sandbox_d_if16.html",
+ "file_iframe_sandbox_d_if17.html",
+ "file_iframe_sandbox_d_if18.html",
+ "file_iframe_sandbox_d_if19.html",
+ "file_iframe_sandbox_d_if2.html",
+ "file_iframe_sandbox_d_if20.html",
+ "file_iframe_sandbox_d_if21.html",
+ "file_iframe_sandbox_d_if22.html",
+ "file_iframe_sandbox_d_if23.html",
+ "file_iframe_sandbox_d_if3.html",
+ "file_iframe_sandbox_d_if4.html",
+ "file_iframe_sandbox_d_if5.html",
+ "file_iframe_sandbox_d_if6.html",
+ "file_iframe_sandbox_d_if7.html",
+ "file_iframe_sandbox_d_if8.html",
+ "file_iframe_sandbox_d_if9.html",
+ "file_iframe_sandbox_e_if1.html",
+ "file_iframe_sandbox_e_if10.html",
+ "file_iframe_sandbox_e_if11.html",
+ "file_iframe_sandbox_e_if12.html",
+ "file_iframe_sandbox_e_if13.html",
+ "file_iframe_sandbox_e_if14.html",
+ "file_iframe_sandbox_e_if15.html",
+ "file_iframe_sandbox_e_if16.html",
+ "file_iframe_sandbox_e_if2.html",
+ "file_iframe_sandbox_e_if3.html",
+ "file_iframe_sandbox_e_if4.html",
+ "file_iframe_sandbox_e_if5.html",
+ "file_iframe_sandbox_e_if6.html",
+ "file_iframe_sandbox_e_if7.html",
+ "file_iframe_sandbox_e_if8.html",
+ "file_iframe_sandbox_e_if9.html",
+ "file_iframe_sandbox_fail.js",
+ "file_iframe_sandbox_form_fail.html",
+ "file_iframe_sandbox_form_pass.html",
+ "file_iframe_sandbox_g_if1.html",
+ "file_iframe_sandbox_h_if1.html",
+ "file_iframe_sandbox_k_if1.html",
+ "file_iframe_sandbox_k_if2.html",
+ "file_iframe_sandbox_k_if3.html",
+ "file_iframe_sandbox_k_if4.html",
+ "file_iframe_sandbox_k_if5.html",
+ "file_iframe_sandbox_k_if6.html",
+ "file_iframe_sandbox_k_if7.html",
+ "file_iframe_sandbox_k_if8.html",
+ "file_iframe_sandbox_k_if9.html",
+ "file_iframe_sandbox_navigation_fail.html",
+ "file_iframe_sandbox_navigation_pass.html",
+ "file_iframe_sandbox_navigation_start.html",
+ "file_iframe_sandbox_open_window_fail.html",
+ "file_iframe_sandbox_open_window_pass.html",
+ "file_iframe_sandbox_pass.js",
+ "file_iframe_sandbox_redirect.html",
+ "file_iframe_sandbox_redirect.html^headers^",
+ "file_iframe_sandbox_redirect_target.html",
+ "file_iframe_sandbox_refresh.html",
+ "file_iframe_sandbox_refresh.html^headers^",
+ "file_iframe_sandbox_srcdoc_allow_scripts.html",
+ "file_iframe_sandbox_srcdoc_no_allow_scripts.html",
+ "file_iframe_sandbox_top_navigation_fail.html",
+ "file_iframe_sandbox_top_navigation_pass.html",
+ "file_iframe_sandbox_window_form_fail.html",
+ "file_iframe_sandbox_window_form_pass.html",
+ "file_iframe_sandbox_window_navigation_fail.html",
+ "file_iframe_sandbox_window_navigation_pass.html",
+ "file_iframe_sandbox_window_top_navigation_pass.html",
+ "file_iframe_sandbox_window_top_navigation_fail.html",
+ "file_iframe_sandbox_worker.js",
+ "file_srcdoc-2.html",
+ "file_srcdoc.html",
+ "file_srcdoc_iframe3.html",
+ "file_window_open_close_outer.html",
+ "file_window_open_close_inner.html",
+ "formSubmission_chrome.js",
+ "form_submit_server.sjs",
+ "formData_worker.js",
+ "formData_test.js",
+ "image.png",
+ "image-allow-credentials.png",
+ "image-allow-credentials.png^headers^",
+ "nnc_lockup.gif",
+ "reflect.js",
+ "simpleFileOpener.js",
+ "file_bug1166138_1x.png",
+ "file_bug1166138_2x.png",
+ "file_bug1166138_def.png",
+ "script_fakepath.js",
+ "sw_formSubmission.js",
+ "object_bug287465_o1.html",
+ "object_bug287465_o2.html",
+ "object_bug556645.html",
+ "file.webm",
+ "!/gfx/layers/apz/test/mochitest/apz_test_utils.js",
+]
+
+["test_a_text.html"]
+
+["test_allowMedia.html"]
+skip-if = [
+ "verify && (os == 'linux' || os == 'win')",
+ "!debug && os == 'mac' && bits == 64",
+ "debug && os == 'win'",
+ "debug && os == 'linux' && os_version == '18.04'", #Bug 1434744
+]
+
+["test_anchor_href_cache_invalidation.html"]
+
+["test_base_attributes_reflection.html"]
+
+["test_bug589.html"]
+
+["test_bug691.html"]
+
+["test_bug694.html"]
+
+["test_bug696.html"]
+
+["test_bug1297.html"]
+
+["test_bug1366.html"]
+
+["test_bug1400.html"]
+
+["test_bug1682.html"]
+
+["test_bug1823.html"]
+
+["test_bug2082.html"]
+
+["test_bug3348.html"]
+
+["test_bug6296.html"]
+
+["test_bug24958.html"]
+
+["test_bug57600.html"]
+
+["test_bug95530.html"]
+
+["test_bug100533.html"]
+
+["test_bug109445.html"]
+
+["test_bug109445.xhtml"]
+
+["test_bug143220.html"]
+
+["test_bug182279.html"]
+
+["test_bug196523.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug199692.html"]
+
+["test_bug209275.xhtml"]
+skip-if = ["os == 'android'"] #TIMED_OUT
+
+["test_bug237071.html"]
+
+["test_bug242709.html"]
+
+["test_bug255820.html"]
+
+["test_bug259332.html"]
+
+["test_bug274626.html"]
+
+["test_bug277724.html"]
+
+["test_bug277890.html"]
+
+["test_bug287465.html"]
+
+["test_bug295561.html"]
+
+["test_bug297761.html"]
+
+["test_bug300691-1.html"]
+
+["test_bug300691-2.html"]
+
+["test_bug300691-3.xhtml"]
+
+["test_bug311681.html"]
+
+["test_bug311681.xhtml"]
+
+["test_bug324378.html"]
+
+["test_bug330705-1.html"]
+
+["test_bug332246.html"]
+
+["test_bug332848.xhtml"]
+
+["test_bug332893-1.html"]
+
+["test_bug332893-2.html"]
+
+["test_bug332893-3.html"]
+
+["test_bug332893-4.html"]
+
+["test_bug332893-5.html"]
+
+["test_bug332893-6.html"]
+
+["test_bug332893-7.html"]
+
+["test_bug340017.xhtml"]
+
+["test_bug340800.html"]
+
+["test_bug347174.html"]
+
+["test_bug347174_write.html"]
+
+["test_bug347174_xsl.html"]
+
+["test_bug347174_xslp.html"]
+
+["test_bug353415-1.html"]
+
+["test_bug353415-2.html"]
+
+["test_bug359657.html"]
+
+["test_bug369370.html"]
+skip-if = [
+ "os == 'android'",
+ "os == 'linux'", # disabled on linux bug 1258103
+]
+
+["test_bug371375.html"]
+
+["test_bug372098.html"]
+
+["test_bug373589.html"]
+
+["test_bug375003-1.html"]
+
+["test_bug375003-2.html"]
+
+["test_bug377624.html"]
+
+["test_bug380383.html"]
+
+["test_bug383383.html"]
+
+["test_bug383383_2.xhtml"]
+
+["test_bug384419.html"]
+
+["test_bug386496.html"]
+
+["test_bug386728.html"]
+
+["test_bug386996.html"]
+
+["test_bug388558.html"]
+
+["test_bug388746.html"]
+
+["test_bug388794.html"]
+
+["test_bug389797.html"]
+
+["test_bug390975.html"]
+
+["test_bug391994.html"]
+
+["test_bug394700.html"]
+
+["test_bug395107.html"]
+
+["test_bug401160.xhtml"]
+
+["test_bug402680.html"]
+
+["test_bug403868.html"]
+
+["test_bug403868.xhtml"]
+
+["test_bug405242.html"]
+
+["test_bug406596.html"]
+
+["test_bug417760.html"]
+
+["test_bug421640.html"]
+
+["test_bug424698.html"]
+
+["test_bug428135.xhtml"]
+
+["test_bug430351.html"]
+skip-if = ["os == 'android'"] # Bug 1525959
+
+["test_bug435128.html"]
+skip-if = ["true"] # Disabled for timeouts.
+
+["test_bug441930.html"]
+
+["test_bug442801.html"]
+
+["test_bug445004.html"]
+skip-if = ["true"] # Disabled permanently (bug 559932).
+
+["test_bug446483.html"]
+
+["test_bug448166.html"]
+
+["test_bug448564.html"]
+
+["test_bug456229.html"]
+
+["test_bug458037.xhtml"]
+allow_xul_xbl = true
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug460568.html"]
+
+["test_bug463104.html"]
+
+["test_bug478251.html"]
+
+["test_bug481335.xhtml"]
+skip-if = ["os == 'android'"] #TIMED_OUT
+
+["test_bug481440.html"]
+
+["test_bug481647.html"]
+
+["test_bug482659.html"]
+
+["test_bug486741.html"]
+
+["test_bug489532.html"]
+
+["test_bug497242.xhtml"]
+
+["test_bug499092.html"]
+
+["test_bug500885.html"]
+
+["test_bug512367.html"]
+
+["test_bug514856.html"]
+
+["test_bug518122.html"]
+
+["test_bug519987.html"]
+
+["test_bug523771.html"]
+
+["test_bug529819.html"]
+
+["test_bug529859.html"]
+
+["test_bug535043.html"]
+
+["test_bug536891.html"]
+
+["test_bug536895.html"]
+
+["test_bug546995.html"]
+
+["test_bug547850.html"]
+
+["test_bug551846.html"]
+
+["test_bug555567.html"]
+
+["test_bug556645.html"]
+
+["test_bug557087-1.html"]
+
+["test_bug557087-2.html"]
+
+["test_bug557087-3.html"]
+
+["test_bug557087-4.html"]
+
+["test_bug557087-5.html"]
+
+["test_bug557087-6.html"]
+
+["test_bug557620.html"]
+
+["test_bug558788-1.html"]
+
+["test_bug558788-2.html"]
+
+["test_bug560112.html"]
+
+["test_bug561634.html"]
+
+["test_bug561636.html"]
+
+["test_bug561640.html"]
+
+["test_bug564001.html"]
+
+["test_bug566046.html"]
+
+["test_bug567938-1.html"]
+
+["test_bug567938-2.html"]
+
+["test_bug567938-3.html"]
+
+["test_bug567938-4.html"]
+
+["test_bug569955.html"]
+
+["test_bug573969.html"]
+
+["test_bug579079.html"]
+
+["test_bug582412-1.html"]
+
+["test_bug582412-2.html"]
+
+["test_bug583514.html"]
+
+["test_bug583533.html"]
+
+["test_bug586763.html"]
+
+["test_bug586786.html"]
+
+["test_bug587469.html"]
+
+["test_bug590353-1.html"]
+
+["test_bug590353-2.html"]
+
+["test_bug590363.html"]
+
+["test_bug592802.html"]
+
+["test_bug593689.html"]
+
+["test_bug595429.html"]
+
+["test_bug595447.html"]
+
+["test_bug595449.html"]
+
+["test_bug596350.html"]
+
+["test_bug596511.html"]
+
+["test_bug598643.html"]
+
+["test_bug598833-1.html"]
+
+["test_bug600155.html"]
+
+["test_bug601030.html"]
+
+["test_bug605124-1.html"]
+
+["test_bug605124-2.html"]
+
+["test_bug605125-1.html"]
+
+["test_bug605125-2.html"]
+
+["test_bug606817.html"]
+
+["test_bug607145.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug610212.html"]
+
+["test_bug610687.html"]
+
+["test_bug611189.html"]
+
+["test_bug612730.html"]
+skip-if = ["os == 'android'"] # form control not selected/checked with synthesizeMouse
+
+["test_bug613019.html"]
+
+["test_bug613113.html"]
+
+["test_bug613722.html"]
+
+["test_bug613979.html"]
+
+["test_bug615595.html"]
+fail-if = ["xorigin"]
+
+["test_bug615833.html"]
+skip-if = [
+ "os == 'android'",
+ "os == 'mac'", #TIMED_OUT # form control not selected/checked with synthesizeMouse, osx(bug 1275664)
+]
+
+["test_bug618948.html"]
+
+["test_bug619278.html"]
+
+["test_bug622597.html"]
+
+["test_bug623291.html"]
+
+["test_bug629801.html"]
+
+["test_bug633058.html"]
+
+["test_bug636336.html"]
+
+["test_bug641219.html"]
+
+["test_bug643051.html"]
+
+["test_bug646157.html"]
+
+["test_bug649134.html"]
+# This extra subdirectory is needed due to the nature of this test.
+# With the bug, the test loads the base URL of the bug649134/file_*.sjs
+# files, and the mochitest server responds with the contents of index.html if
+# it exists in that case, which we use to detect failure.
+# We cannot have index.html in this directory because it would prevent
+# running the tests here.
+support-files = [
+ "bug649134/file_bug649134-1.sjs",
+ "bug649134/file_bug649134-2.sjs",
+ "bug649134/index.html",
+]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug651956.html"]
+
+["test_bug658746.html"]
+
+["test_bug659596.html"]
+
+["test_bug659743.xml"]
+
+["test_bug660663.html"]
+
+["test_bug660959-1.html"]
+
+["test_bug660959-2.html"]
+
+["test_bug660959-3.html"]
+
+["test_bug666200.html"]
+
+["test_bug666666.html"]
+
+["test_bug669012.html"]
+
+["test_bug674558.html"]
+
+["test_bug674927.html"]
+
+["test_bug677495-1.html"]
+
+["test_bug677495.html"]
+
+["test_bug677658.html"]
+
+["test_bug682886.html"]
+
+["test_bug694503.html"]
+skip-if = ["os == 'android'"] # Bug 1525959
+
+["test_bug717819.html"]
+
+["test_bug741266.html"]
+skip-if = [
+ "os == 'android'", # Android: needs control of popup window size
+ "display == 'wayland' && os_version == '22.04' && debug", # Bug 1856975
+]
+
+["test_bug742030.html"]
+
+["test_bug742549.html"]
+
+["test_bug745685.html"]
+
+["test_bug763626.html"]
+
+["test_bug765780.html"]
+
+["test_bug780993.html"]
+
+["test_bug787134.html"]
+
+["test_bug797113.html"]
+
+["test_bug803677.html"]
+
+["test_bug821307.html"]
+
+["test_bug827126.html"]
+
+["test_bug838582.html"]
+
+["test_bug839371.html"]
+
+["test_bug839913.html"]
+
+["test_bug841466.html"]
+
+["test_bug845057.html"]
+
+["test_bug869040.html"]
+
+["test_bug870787.html"]
+
+["test_bug871161.html"]
+support-files = [
+ "file_bug871161-1.html",
+ "file_bug871161-2.html",
+]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug874758.html"]
+
+["test_bug879319.html"]
+
+["test_bug885024.html"]
+
+["test_bug893537.html"]
+
+["test_bug969346.html"]
+
+["test_bug982039.html"]
+
+["test_bug1003539.html"]
+
+["test_bug1013316.html"]
+
+["test_bug1045270.html"]
+
+["test_bug1089326.html"]
+
+["test_bug1146116.html"]
+
+["test_bug1166138.html"]
+
+["test_bug1203668.html"]
+
+["test_bug1230665.html"]
+
+["test_bug1250401.html"]
+
+["test_bug1260664.html"]
+
+["test_bug1260704.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug1261673.html"]
+skip-if = [
+ "os == 'android'",
+ "os == 'mac'",
+]
+
+["test_bug1261674-1.html"]
+skip-if = [
+ "os == 'android'",
+ "os == 'mac'",
+]
+
+["test_bug1261674-2.html"]
+skip-if = ["os == 'mac'"]
+
+["test_bug1264157.html"]
+
+["test_bug1279218.html"]
+
+["test_bug1287321.html"]
+
+["test_bug1292522_same_domain_with_different_port_number.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug1295719_event_sequence_for_arrow_keys.html"]
+skip-if = ["os == 'android'"] # up/down arrow keys not supported on android
+
+["test_bug1295719_event_sequence_for_number_keys.html"]
+
+["test_bug1310865.html"]
+
+["test_bug1315146.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug1322678.html"]
+skip-if = ["os == 'android'"]
+
+["test_bug1323815.html"]
+
+["test_bug1472426.html"]
+
+["test_bug1785739.html"]
+
+["test_change_crossorigin.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_checked.html"]
+
+["test_dir_attributes_reflection.html"]
+
+["test_dl_attributes_reflection.html"]
+
+["test_document-element-inserted.html"]
+
+["test_documentAll.html"]
+
+["test_element_prototype.html"]
+
+["test_embed_attributes_reflection.html"]
+
+["test_fakepath.html"]
+
+["test_filepicker_default_directory.html"]
+
+["test_focusshift_button.html"]
+
+["test_form-parsing.html"]
+
+["test_formData.html"]
+
+["test_formSubmission.html"]
+skip-if = ["os == 'android'"] #TIMED_OUT
+
+["test_formSubmission2.html"]
+skip-if = ["os == 'android'"]
+
+["test_formelements.html"]
+
+["test_fragment_form_pointer.html"]
+
+["test_frame_count_with_synthetic_doc.html"]
+
+["test_getElementsByName_after_mutation.html"]
+
+["test_hidden.html"]
+
+["test_html_attributes_reflection.html"]
+
+["test_htmlcollection.html"]
+
+["test_iframe_sandbox_general.html"]
+tags = "openwindow"
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_iframe_sandbox_inheritance.html"]
+tags = "openwindow"
+
+["test_iframe_sandbox_navigation.html"]
+tags = "openwindow"
+
+["test_iframe_sandbox_navigation2.html"]
+tags = "openwindow"
+
+["test_iframe_sandbox_popups.html"]
+tags = "openwindow"
+
+["test_iframe_sandbox_popups_inheritance.html"]
+tags = "openwindow"
+
+["test_iframe_sandbox_redirect.html"]
+
+["test_iframe_sandbox_refresh.html"]
+
+["test_iframe_sandbox_same_origin.html"]
+
+["test_iframe_sandbox_workers.html"]
+
+["test_imageSrcSet.html"]
+
+["test_image_clone_load.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_img_attributes_reflection.html"]
+
+["test_input_file_cancel_event.html"]
+
+["test_input_files_not_nsIFile.html"]
+
+["test_input_lastInteractiveValue.html"]
+
+["test_inputmode.html"]
+
+["test_li_attributes_reflection.html"]
+
+["test_link_attributes_reflection.html"]
+
+["test_link_sizes.html"]
+
+["test_map_attributes_reflection.html"]
+
+["test_meta_attributes_reflection.html"]
+
+["test_mod_attributes_reflection.html"]
+
+["test_multipleFilePicker.html"]
+
+["test_named_options.html"]
+
+["test_nested_invalid_fieldsets.html"]
+
+["test_nestediframe.html"]
+
+["test_non-ascii-cookie.html"]
+support-files = ["file_cookiemanager.js"]
+skip-if = [
+ "xorigin",
+ "http3",
+ "http2",
+]
+
+["test_object_attributes_reflection.html"]
+
+["test_ol_attributes_reflection.html"]
+
+["test_option_defaultSelected.html"]
+
+["test_option_selected_state.html"]
+
+["test_param_attributes_reflection.html"]
+
+["test_q_attributes_reflection.html"]
+
+["test_restore_from_parser_fragment.html"]
+
+["test_rowscollection.html"]
+
+["test_script_module.html"]
+support-files = ["file_script_module.html"]
+
+["test_set_input_files.html"]
+
+["test_srcdoc-2.html"]
+
+["test_srcdoc.html"]
+
+["test_style_attributes_reflection.html"]
+
+["test_track.html"]
+
+["test_ul_attributes_reflection.html"]
+
+["test_viewport_resize.html"]
+
+["test_window_open_close.html"]
+tags = "openwindow"
+skip-if = [
+ "os == 'android' && debug",
+ "os == 'linux'",
+ "os == 'win' && debug && bits == 64", # Bug 1533759
+]
+
+["test_window_open_from_closing.html"]
+skip-if = ["os == 'android'"] # test does not function on android due to aggressive background tab freezing
+support-files = [
+ "file_window_close_and_open.html",
+ "file_broadcast_load.html",
+]
diff --git a/dom/html/test/nnc_lockup.gif b/dom/html/test/nnc_lockup.gif
new file mode 100644
index 0000000000..f746bb71d9
--- /dev/null
+++ b/dom/html/test/nnc_lockup.gif
Binary files differ
diff --git a/dom/html/test/object_bug287465_o1.html b/dom/html/test/object_bug287465_o1.html
new file mode 100644
index 0000000000..0a65a7f9e1
--- /dev/null
+++ b/dom/html/test/object_bug287465_o1.html
@@ -0,0 +1 @@
+<svg xmlns='http://www.w3.org/2000/svg'></svg>
diff --git a/dom/html/test/object_bug287465_o2.html b/dom/html/test/object_bug287465_o2.html
new file mode 100644
index 0000000000..18ecdcb795
--- /dev/null
+++ b/dom/html/test/object_bug287465_o2.html
@@ -0,0 +1 @@
+<html></html>
diff --git a/dom/html/test/object_bug556645.html b/dom/html/test/object_bug556645.html
new file mode 100644
index 0000000000..773837502a
--- /dev/null
+++ b/dom/html/test/object_bug556645.html
@@ -0,0 +1 @@
+<body><button>Child</button></body>
diff --git a/dom/html/test/post_action_page.html b/dom/html/test/post_action_page.html
new file mode 100644
index 0000000000..ba6ae514f2
--- /dev/null
+++ b/dom/html/test/post_action_page.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Submission Flush Test Post Action Page</title>
+ </head>
+ <body>
+ <h1>Post Action Page</h1>
+ </body>
+</html>
diff --git a/dom/html/test/reflect.js b/dom/html/test/reflect.js
new file mode 100644
index 0000000000..44f73ae4a2
--- /dev/null
+++ b/dom/html/test/reflect.js
@@ -0,0 +1,1078 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * reflect.js is a collection of methods to test HTML attribute reflection.
+ * Each of attribute is reflected differently, depending on various parameters,
+ * see:
+ * http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
+ *
+ * Do not forget to add these line at the beginning of each new reflect* method:
+ * ok(attr in element, attr + " should be an IDL attribute of this element");
+ * is(typeof element[attr], <type>, attr + " IDL attribute should be a <type>");
+ */
+
+/**
+ * Checks that a given attribute is correctly reflected as a string.
+ *
+ * @param aParameters Object object containing the parameters, which are:
+ * - element Element node to test
+ * - attribute String name of the attribute
+ * OR
+ * attribute Object object containing two attributes, 'content' and 'idl'
+ * - otherValues Array [optional] other values to test in addition of the default ones
+ * - extendedAttributes Object object which can have 'TreatNullAs': "EmptyString"
+ */
+function reflectString(aParameters) {
+ var element = aParameters.element;
+ var contentAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.content;
+ var idlAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.idl;
+ var otherValues =
+ aParameters.otherValues !== undefined ? aParameters.otherValues : [];
+ var treatNullAs = aParameters.extendedAttributes
+ ? aParameters.extendedAttributes.TreatNullAs
+ : null;
+
+ ok(
+ idlAttr in element,
+ idlAttr + " should be an IDL attribute of this element"
+ );
+ is(
+ typeof element[idlAttr],
+ "string",
+ "'" + idlAttr + "' IDL attribute should be a string"
+ );
+
+ // Tests when the attribute isn't set.
+ is(
+ element.getAttribute(contentAttr),
+ null,
+ "When not set, the content attribute should be null."
+ );
+ is(
+ element[idlAttr],
+ "",
+ "When not set, the IDL attribute should return the empty string"
+ );
+
+ /**
+ * TODO: as long as null stringification doesn't follow the WebIDL
+ * specifications, don't add it to the loop below and keep it here.
+ */
+ element.setAttribute(contentAttr, null);
+ is(
+ element.getAttribute(contentAttr),
+ "null",
+ "null should have been stringified to 'null' for '" + contentAttr + "'"
+ );
+ is(
+ element[idlAttr],
+ "null",
+ "null should have been stringified to 'null' for '" + idlAttr + "'"
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = null;
+ if (treatNullAs == "EmptyString") {
+ is(
+ element.getAttribute(contentAttr),
+ "",
+ "null should have been stringified to '' for '" + contentAttr + "'"
+ );
+ is(
+ element[idlAttr],
+ "",
+ "null should have been stringified to '' for '" + idlAttr + "'"
+ );
+ } else {
+ is(
+ element.getAttribute(contentAttr),
+ "null",
+ "null should have been stringified to 'null' for '" + contentAttr + "'"
+ );
+ is(
+ element[idlAttr],
+ "null",
+ "null should have been stringified to 'null' for '" + contentAttr + "'"
+ );
+ }
+ element.removeAttribute(contentAttr);
+
+ // Tests various strings.
+ var stringsToTest = [
+ // [ test value, expected result ]
+ ["", ""],
+ ["null", "null"],
+ ["undefined", "undefined"],
+ ["foo", "foo"],
+ [contentAttr, contentAttr],
+ [idlAttr, idlAttr],
+ // TODO: uncomment this when null stringification will follow the specs.
+ // [ null, "null" ],
+ [undefined, "undefined"],
+ [true, "true"],
+ [false, "false"],
+ [42, "42"],
+ // ES5, verse 8.12.8.
+ [
+ {
+ toString() {
+ return "foo";
+ },
+ },
+ "foo",
+ ],
+ [
+ {
+ valueOf() {
+ return "foo";
+ },
+ },
+ "[object Object]",
+ ],
+ [
+ {
+ valueOf() {
+ return "quux";
+ },
+ toString: undefined,
+ },
+ "quux",
+ ],
+ [
+ {
+ valueOf() {
+ return "foo";
+ },
+ toString() {
+ return "bar";
+ },
+ },
+ "bar",
+ ],
+ ];
+
+ otherValues.forEach(function (v) {
+ stringsToTest.push([v, v]);
+ });
+
+ stringsToTest.forEach(function ([v, r]) {
+ element.setAttribute(contentAttr, v);
+ is(
+ element[idlAttr],
+ r,
+ "IDL attribute '" +
+ idlAttr +
+ "' should return the value it has been set to."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ r,
+ "Content attribute '" +
+ contentAttr +
+ "'should return the value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v;
+ is(
+ element[idlAttr],
+ r,
+ "IDL attribute '" +
+ idlAttr +
+ "' should return the value it has been set to."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ r,
+ "Content attribute '" +
+ contentAttr +
+ "' should return the value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+ });
+
+ // Tests after removeAttribute() is called. Should be equivalent with not set.
+ is(
+ element.getAttribute(contentAttr),
+ null,
+ "When not set, the content attribute should be null."
+ );
+ is(
+ element[idlAttr],
+ "",
+ "When not set, the IDL attribute should return the empty string"
+ );
+}
+
+/**
+ * Checks that a given attribute name for a given element is correctly reflected
+ * as an unsigned int.
+ *
+ * @param aParameters Object object containing the parameters, which are:
+ * - element Element node to test on
+ * - attribute String name of the attribute
+ * - nonZero Boolean whether the attribute should be non-null
+ * - defaultValue Integer [optional] default value, if different from the default one
+ */
+function reflectUnsignedInt(aParameters) {
+ var element = aParameters.element;
+ var attr = aParameters.attribute;
+ var nonZero = aParameters.nonZero;
+ var defaultValue = aParameters.defaultValue;
+ var fallback = aParameters.fallback;
+
+ if (defaultValue === undefined) {
+ if (nonZero) {
+ defaultValue = 1;
+ } else {
+ defaultValue = 0;
+ }
+ }
+
+ if (fallback === undefined) {
+ fallback = false;
+ }
+
+ ok(attr in element, attr + " should be an IDL attribute of this element");
+ is(
+ typeof element[attr],
+ "number",
+ attr + " IDL attribute should be a number"
+ );
+
+ // Check default value.
+ is(element[attr], defaultValue, "default value should be " + defaultValue);
+ ok(!element.hasAttribute(attr), attr + " shouldn't be present");
+
+ var values = [1, 3, 42, 2147483647];
+
+ for (var value of values) {
+ element[attr] = value;
+ is(element[attr], value, "." + attr + " should be equals " + value);
+ is(
+ element.getAttribute(attr),
+ String(value),
+ "@" + attr + " should be equals " + value
+ );
+
+ element.setAttribute(attr, value);
+ is(element[attr], value, "." + attr + " should be equals " + value);
+ is(
+ element.getAttribute(attr),
+ String(value),
+ "@" + attr + " should be equals " + value
+ );
+ }
+
+ // -3000000000 is equivalent to 1294967296 when using the IDL attribute.
+ element[attr] = -3000000000;
+ is(element[attr], 1294967296, "." + attr + " should be equals to 1294967296");
+ is(
+ element.getAttribute(attr),
+ "1294967296",
+ "@" + attr + " should be equals to 1294967296"
+ );
+
+ // When setting the content attribute, it's a string so it will be invalid.
+ element.setAttribute(attr, -3000000000);
+ is(
+ element.getAttribute(attr),
+ "-3000000000",
+ "@" + attr + " should be equals to " + -3000000000
+ );
+ is(
+ element[attr],
+ defaultValue,
+ "." + attr + " should be equals to " + defaultValue
+ );
+
+ // When interpreted as unsigned 32-bit integers, all of these fall between
+ // 2^31 and 2^32 - 1, so per spec they return the default value.
+ var nonValidValues = [-2147483648, -1, 3147483647];
+
+ for (var value of nonValidValues) {
+ element[attr] = value;
+ is(
+ element.getAttribute(attr),
+ String(defaultValue),
+ "@" + attr + " should be equals to " + defaultValue
+ );
+ is(
+ element[attr],
+ defaultValue,
+ "." + attr + " should be equals to " + defaultValue
+ );
+ }
+
+ for (var values of nonValidValues) {
+ element.setAttribute(attr, values[0]);
+ is(
+ element.getAttribute(attr),
+ String(values[0]),
+ "@" + attr + " should be equals to " + values[0]
+ );
+ is(
+ element[attr],
+ defaultValue,
+ "." + attr + " should be equals to " + defaultValue
+ );
+ }
+
+ // Setting to 0 should throw an error if nonZero is true.
+ var caught = false;
+ try {
+ element[attr] = 0;
+ } catch (e) {
+ caught = true;
+ is(e.name, "IndexSizeError", "exception should be IndexSizeError");
+ is(
+ e.code,
+ DOMException.INDEX_SIZE_ERR,
+ "exception code should be INDEX_SIZE_ERR"
+ );
+ }
+
+ if (nonZero && !fallback) {
+ ok(caught, "an exception should have been caught");
+ } else {
+ ok(!caught, "no exception should have been caught");
+ }
+
+ // If 0 is set in @attr, it will be ignored when calling .attr.
+ element.setAttribute(attr, "0");
+ is(element.getAttribute(attr), "0", "@" + attr + " should be equals to 0");
+ if (nonZero) {
+ is(
+ element[attr],
+ defaultValue,
+ "." + attr + " should be equals to " + defaultValue
+ );
+ } else {
+ is(element[attr], 0, "." + attr + " should be equals to 0");
+ }
+}
+
+/**
+ * Checks that a given attribute is correctly reflected as limited to known
+ * values enumerated attribute.
+ *
+ * @param aParameters Object object containing the parameters, which are:
+ * - element Element node to test on
+ * - attribute String name of the attribute
+ * OR
+ * attribute Object object containing two attributes, 'content' and 'idl'
+ * - validValues Array valid values we support
+ * - invalidValues Array invalid values
+ * - defaultValue String [optional] default value when no valid value is set
+ * OR
+ * defaultValue Object [optional] object containing two attributes, 'invalid' and 'missing'
+ * - unsupportedValues Array [optional] valid values we do not support
+ * - nullable boolean [optional] whether the attribute is nullable
+ */
+function reflectLimitedEnumerated(aParameters) {
+ var element = aParameters.element;
+ var contentAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.content;
+ var idlAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.idl;
+ var validValues = aParameters.validValues;
+ var invalidValues = aParameters.invalidValues;
+ var defaultValueInvalid =
+ aParameters.defaultValue === undefined
+ ? ""
+ : typeof aParameters.defaultValue === "string"
+ ? aParameters.defaultValue
+ : aParameters.defaultValue.invalid;
+ var defaultValueMissing =
+ aParameters.defaultValue === undefined
+ ? ""
+ : typeof aParameters.defaultValue === "string"
+ ? aParameters.defaultValue
+ : aParameters.defaultValue.missing;
+ var unsupportedValues =
+ aParameters.unsupportedValues !== undefined
+ ? aParameters.unsupportedValues
+ : [];
+ var nullable = aParameters.nullable;
+
+ ok(
+ idlAttr in element,
+ idlAttr + " should be an IDL attribute of this element"
+ );
+ if (nullable) {
+ // The missing value default is null, which is typeof == "object"
+ is(
+ typeof element[idlAttr],
+ "object",
+ "'" +
+ idlAttr +
+ "' IDL attribute should be null, which has typeof == object"
+ );
+ is(
+ element[idlAttr],
+ null,
+ "'" + idlAttr + "' IDL attribute should be null"
+ );
+ } else {
+ is(
+ typeof element[idlAttr],
+ "string",
+ "'" + idlAttr + "' IDL attribute should be a string"
+ );
+ }
+
+ if (nullable) {
+ element.setAttribute(contentAttr, "something");
+ // Now it will be a string
+ is(
+ typeof element[idlAttr],
+ "string",
+ "'" + idlAttr + "' IDL attribute should be a string"
+ );
+ }
+
+ // Explicitly check the default value.
+ element.removeAttribute(contentAttr);
+ is(
+ element[idlAttr],
+ defaultValueMissing,
+ "When no attribute is set, the value should be the default value."
+ );
+
+ // Check valid values.
+ validValues.forEach(function (v) {
+ element.setAttribute(contentAttr, v);
+ is(
+ element[idlAttr],
+ v,
+ "'" + v + "' should be accepted as a valid value for " + idlAttr
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v,
+ "Content attribute should return the value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+
+ element.setAttribute(contentAttr, v.toUpperCase());
+ is(
+ element[idlAttr],
+ v,
+ "Enumerated attributes should be case-insensitive."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v.toUpperCase(),
+ "Content attribute should not be lower-cased."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v;
+ is(
+ element[idlAttr],
+ v,
+ "'" + v + "' should be accepted as a valid value for " + idlAttr
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v,
+ "Content attribute should return the value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v.toUpperCase();
+ is(
+ element[idlAttr],
+ v,
+ "Enumerated attributes should be case-insensitive."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v.toUpperCase(),
+ "Content attribute should not be lower-cased."
+ );
+ element.removeAttribute(contentAttr);
+ });
+
+ // Check invalid values.
+ invalidValues.forEach(function (v) {
+ element.setAttribute(contentAttr, v);
+ is(
+ element[idlAttr],
+ defaultValueInvalid,
+ "When the content attribute is set to an invalid value, the default value should be returned."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v,
+ "Content attribute should not have been changed."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v;
+ is(
+ element[idlAttr],
+ defaultValueInvalid,
+ "When the value is set to an invalid value, the default value should be returned."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v,
+ "Content attribute should not have been changed."
+ );
+ element.removeAttribute(contentAttr);
+ });
+
+ // Check valid values we currently do not support.
+ // Basically, it's like the checks for the valid values but with some todo's.
+ unsupportedValues.forEach(function (v) {
+ element.setAttribute(contentAttr, v);
+ todo_is(
+ element[idlAttr],
+ v,
+ "'" + v + "' should be accepted as a valid value for " + idlAttr
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v,
+ "Content attribute should return the value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+
+ element.setAttribute(contentAttr, v.toUpperCase());
+ todo_is(
+ element[idlAttr],
+ v,
+ "Enumerated attributes should be case-insensitive."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v.toUpperCase(),
+ "Content attribute should not be lower-cased."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v;
+ todo_is(
+ element[idlAttr],
+ v,
+ "'" + v + "' should be accepted as a valid value for " + idlAttr
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v,
+ "Content attribute should return the value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v.toUpperCase();
+ todo_is(
+ element[idlAttr],
+ v,
+ "Enumerated attributes should be case-insensitive."
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v.toUpperCase(),
+ "Content attribute should not be lower-cased."
+ );
+ element.removeAttribute(contentAttr);
+ });
+
+ if (nullable) {
+ is(
+ defaultValueMissing,
+ null,
+ "Missing default value should be null for nullable attributes"
+ );
+ ok(validValues.length, "We better have at least one valid value");
+ element.setAttribute(contentAttr, validValues[0]);
+ ok(
+ element.hasAttribute(contentAttr),
+ "Should have content attribute: we just set it"
+ );
+ element[idlAttr] = null;
+ ok(
+ !element.hasAttribute(contentAttr),
+ "Should have removed content attribute"
+ );
+ }
+}
+
+/**
+ * Checks that a given attribute is correctly reflected as a boolean.
+ *
+ * @param aParameters Object object containing the parameters, which are:
+ * - element Element node to test on
+ * - attribute String name of the attribute
+ * OR
+ * attribute Object object containing two attributes, 'content' and 'idl'
+ */
+function reflectBoolean(aParameters) {
+ var element = aParameters.element;
+ var contentAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.content;
+ var idlAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.idl;
+
+ ok(
+ idlAttr in element,
+ idlAttr + " should be an IDL attribute of this element"
+ );
+ is(
+ typeof element[idlAttr],
+ "boolean",
+ idlAttr + " IDL attribute should be a boolean"
+ );
+
+ // Tests when the attribute isn't set.
+ is(
+ element.getAttribute(contentAttr),
+ null,
+ "When not set, the content attribute should be null."
+ );
+ is(
+ element[idlAttr],
+ false,
+ "When not set, the IDL attribute should return false"
+ );
+
+ /**
+ * Test various values.
+ * Each value to test is actually an object containing a 'value' property
+ * containing the value to actually test, a 'stringified' property containing
+ * the stringified value and a 'result' property containing the expected
+ * result when the value is set to the IDL attribute.
+ */
+ var valuesToTest = [
+ { value: true, stringified: "true", result: true },
+ { value: false, stringified: "false", result: false },
+ { value: "true", stringified: "true", result: true },
+ { value: "false", stringified: "false", result: true },
+ { value: "foo", stringified: "foo", result: true },
+ { value: idlAttr, stringified: idlAttr, result: true },
+ { value: contentAttr, stringified: contentAttr, result: true },
+ { value: "null", stringified: "null", result: true },
+ { value: "undefined", stringified: "undefined", result: true },
+ { value: "", stringified: "", result: false },
+ { value: undefined, stringified: "undefined", result: false },
+ { value: null, stringified: "null", result: false },
+ { value: +0, stringified: "0", result: false },
+ { value: -0, stringified: "0", result: false },
+ { value: NaN, stringified: "NaN", result: false },
+ { value: 42, stringified: "42", result: true },
+ { value: Infinity, stringified: "Infinity", result: true },
+ { value: -Infinity, stringified: "-Infinity", result: true },
+ // ES5, verse 9.2.
+ {
+ value: {
+ toString() {
+ return "foo";
+ },
+ },
+ stringified: "foo",
+ result: true,
+ },
+ {
+ value: {
+ valueOf() {
+ return "foo";
+ },
+ },
+ stringified: "[object Object]",
+ result: true,
+ },
+ {
+ value: {
+ valueOf() {
+ return "quux";
+ },
+ toString: undefined,
+ },
+ stringified: "quux",
+ result: true,
+ },
+ {
+ value: {
+ valueOf() {
+ return "foo";
+ },
+ toString() {
+ return "bar";
+ },
+ },
+ stringified: "bar",
+ result: true,
+ },
+ {
+ value: {
+ valueOf() {
+ return false;
+ },
+ },
+ stringified: "[object Object]",
+ result: true,
+ },
+ {
+ value: { foo: false, bar: false },
+ stringified: "[object Object]",
+ result: true,
+ },
+ { value: {}, stringified: "[object Object]", result: true },
+ ];
+
+ valuesToTest.forEach(function (v) {
+ element.setAttribute(contentAttr, v.value);
+ is(
+ element[idlAttr],
+ true,
+ "IDL attribute should return always return 'true' if the content attribute has been set"
+ );
+ is(
+ element.getAttribute(contentAttr),
+ v.stringified,
+ "Content attribute should return the stringified value it has been set to."
+ );
+ element.removeAttribute(contentAttr);
+
+ element[idlAttr] = v.value;
+ is(element[idlAttr], v.result, "IDL attribute should return " + v.result);
+ is(
+ element.getAttribute(contentAttr),
+ v.result ? "" : null,
+ v.result
+ ? "Content attribute should return the empty string."
+ : "Content attribute should return null."
+ );
+ is(
+ element.hasAttribute(contentAttr),
+ v.result,
+ v.result
+ ? contentAttr + " should not be present"
+ : contentAttr + " should be present"
+ );
+ element.removeAttribute(contentAttr);
+ });
+
+ // Tests after removeAttribute() is called. Should be equivalent with not set.
+ is(
+ element.getAttribute(contentAttr),
+ null,
+ "When not set, the content attribute should be null."
+ );
+ is(
+ element[contentAttr],
+ false,
+ "When not set, the IDL attribute should return false"
+ );
+}
+
+/**
+ * Checks that a given attribute name for a given element is correctly reflected
+ * as an signed integer.
+ *
+ * @param aParameters Object object containing the parameters, which are:
+ * - element Element node to test on
+ * - attribute String name of the attribute
+ * - nonNegative Boolean true if the attribute is limited to 'non-negative numbers', false otherwise
+ * - defaultValue Integer [optional] default value, if one exists
+ */
+function reflectInt(aParameters) {
+ // Expected value returned by .getAttribute() when |value| has been previously passed to .setAttribute().
+ function expectedGetAttributeResult(value) {
+ return String(value);
+ }
+
+ function stringToInteger(value, nonNegative, defaultValue) {
+ // Parse: Ignore leading whitespace, find [+/-][numbers]
+ var result = /^[ \t\n\f\r]*([\+\-]?[0-9]+)/.exec(value);
+ if (result) {
+ var resultInt = parseInt(result[1], 10);
+ if (
+ (nonNegative ? 0 : -0x80000000) <= resultInt &&
+ resultInt <= 0x7fffffff
+ ) {
+ // If the value is within allowed value range for signed/unsigned
+ // integer, return it -- but add 0 to it to convert a possible -0 into
+ // +0, the only zero present in the signed integer range.
+ return resultInt + 0;
+ }
+ }
+ return defaultValue;
+ }
+
+ // Expected value returned by .getAttribute(attr) or .attr if |value| has been set via the IDL attribute.
+ function expectedIdlAttributeResult(value) {
+ // This returns the result of calling the ES ToInt32 algorithm on value.
+ return value << 0;
+ }
+
+ var element = aParameters.element;
+ var attr = aParameters.attribute;
+ var nonNegative = aParameters.nonNegative;
+
+ var defaultValue =
+ aParameters.defaultValue !== undefined
+ ? aParameters.defaultValue
+ : nonNegative
+ ? -1
+ : 0;
+
+ ok(attr in element, attr + " should be an IDL attribute of this element");
+ is(
+ typeof element[attr],
+ "number",
+ attr + " IDL attribute should be a number"
+ );
+
+ // Check default value.
+ is(element[attr], defaultValue, "default value should be " + defaultValue);
+ ok(!element.hasAttribute(attr), attr + " shouldn't be present");
+
+ /**
+ * Test various values.
+ * value: The test value that will be set using both setAttribute(value) and
+ * element[attr] = value
+ */
+ var valuesToTest = [
+ // Test numeric inputs up to max signed integer
+ 0,
+ 1,
+ 55555,
+ 2147483647,
+ +42,
+ // Test string inputs up to max signed integer
+ "0",
+ "1",
+ "777777",
+ "2147483647",
+ "+42",
+ // Test negative numeric inputs up to min signed integer
+ -0,
+ -1,
+ -3333,
+ -2147483648,
+ // Test negative string inputs up to min signed integer
+ "-0",
+ "-1",
+ "-222",
+ "-2147483647",
+ "-2147483648",
+ // Test numeric inputs that are outside legal 32 bit signed values
+ -2147483649,
+ -3000000000,
+ -4294967296,
+ 2147483649,
+ 4000000000,
+ -4294967297,
+ // Test string inputs with extra padding
+ " 1111111",
+ " 23456 ",
+ // Test non-numeric string inputs
+ "",
+ " ",
+ "+",
+ "-",
+ "foo",
+ "+foo",
+ "-foo",
+ "+ foo",
+ "- foo",
+ "+-2",
+ "-+2",
+ "++2",
+ "--2",
+ "hello1234",
+ "1234hello",
+ "444 world 555",
+ "why 567 what",
+ "-3 nots",
+ "2e5",
+ "300e2",
+ "42+-$",
+ "+42foo",
+ "-514not",
+ "\vblah",
+ "0x10FFFF",
+ "-0xABCDEF",
+ // Test decimal numbers
+ 1.2345,
+ 42.0,
+ 3456789.1,
+ -2.3456,
+ -6789.12345,
+ -2147483649.1234,
+ // Test decimal strings
+ "1.2345",
+ "42.0",
+ "3456789.1",
+ "-2.3456",
+ "-6789.12345",
+ "-2147483649.1234",
+ // Test special values
+ undefined,
+ null,
+ NaN,
+ Infinity,
+ -Infinity,
+ ];
+
+ valuesToTest.forEach(function (v) {
+ var intValue = stringToInteger(v, nonNegative, defaultValue);
+
+ element.setAttribute(attr, v);
+
+ is(
+ element.getAttribute(attr),
+ expectedGetAttributeResult(v),
+ element.localName +
+ ".setAttribute(" +
+ attr +
+ ", " +
+ v +
+ "), " +
+ element.localName +
+ ".getAttribute(" +
+ attr +
+ ") "
+ );
+
+ is(
+ element[attr],
+ intValue,
+ element.localName +
+ ".setAttribute(" +
+ attr +
+ ", " +
+ v +
+ "), " +
+ element.localName +
+ "[" +
+ attr +
+ "] "
+ );
+ element.removeAttribute(attr);
+
+ if (nonNegative && expectedIdlAttributeResult(v) < 0) {
+ try {
+ element[attr] = v;
+ ok(
+ false,
+ element.localName +
+ "[" +
+ attr +
+ "] = " +
+ v +
+ " should throw IndexSizeError"
+ );
+ } catch (e) {
+ is(
+ e.name,
+ "IndexSizeError",
+ element.localName +
+ "[" +
+ attr +
+ "] = " +
+ v +
+ " should throw IndexSizeError"
+ );
+ is(
+ e.code,
+ DOMException.INDEX_SIZE_ERR,
+ element.localName +
+ "[" +
+ attr +
+ "] = " +
+ v +
+ " should throw INDEX_SIZE_ERR"
+ );
+ }
+ } else {
+ element[attr] = v;
+ is(
+ element[attr],
+ expectedIdlAttributeResult(v),
+ element.localName +
+ "[" +
+ attr +
+ "] = " +
+ v +
+ ", " +
+ element.localName +
+ "[" +
+ attr +
+ "] "
+ );
+ is(
+ element.getAttribute(attr),
+ String(expectedIdlAttributeResult(v)),
+ element.localName +
+ "[" +
+ attr +
+ "] = " +
+ v +
+ ", " +
+ element.localName +
+ ".getAttribute(" +
+ attr +
+ ") "
+ );
+ }
+ element.removeAttribute(attr);
+ });
+
+ // Tests after removeAttribute() is called. Should be equivalent with not set.
+ is(
+ element.getAttribute(attr),
+ null,
+ "When not set, the content attribute should be null."
+ );
+ is(
+ element[attr],
+ defaultValue,
+ "When not set, the IDL attribute should return default value."
+ );
+}
+
+/**
+ * Checks that a given attribute is correctly reflected as a url.
+ *
+ * @param aParameters Object object containing the parameters, which are:
+ * - element Element node to test
+ * - attribute String name of the attribute
+ * OR
+ * attribute Object object containing two attributes, 'content' and 'idl'
+ */
+function reflectURL(aParameters) {
+ var element = aParameters.element;
+ var contentAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.content;
+ var idlAttr =
+ typeof aParameters.attribute === "string"
+ ? aParameters.attribute
+ : aParameters.attribute.idl;
+
+ element[idlAttr] = "";
+ is(
+ element[idlAttr],
+ document.URL,
+ "Empty string should resolve to document URL"
+ );
+}
diff --git a/dom/html/test/script_fakepath.js b/dom/html/test/script_fakepath.js
new file mode 100644
index 0000000000..f95ac493d2
--- /dev/null
+++ b/dom/html/test/script_fakepath.js
@@ -0,0 +1,16 @@
+/* eslint-env mozilla/chrome-script */
+
+// eslint-disable-next-line mozilla/reject-importGlobalProperties
+Cu.importGlobalProperties(["File"]);
+
+addMessageListener("file.open", function (e) {
+ var tmpFile = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIDirectoryService)
+ .QueryInterface(Ci.nsIProperties)
+ .get("ProfD", Ci.nsIFile);
+ tmpFile.append("prefs.js");
+
+ File.createFromNsIFile(tmpFile).then(file => {
+ sendAsyncMessage("file.opened", { data: [file] });
+ });
+});
diff --git a/dom/html/test/simpleFileOpener.js b/dom/html/test/simpleFileOpener.js
new file mode 100644
index 0000000000..cb98f0c64e
--- /dev/null
+++ b/dom/html/test/simpleFileOpener.js
@@ -0,0 +1,38 @@
+/* eslint-env mozilla/chrome-script */
+
+// eslint-disable-next-line mozilla/reject-importGlobalProperties
+Cu.importGlobalProperties(["File"]);
+
+var file;
+
+addMessageListener("file.open", function (stem) {
+ try {
+ if (!file) {
+ file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+ file.append(stem);
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+ }
+
+ File.createFromNsIFile(file).then(function (domFile) {
+ sendAsyncMessage("file.opened", {
+ fullPath: file.path,
+ leafName: file.leafName,
+ domFile,
+ });
+ });
+ } catch (e) {
+ sendAsyncMessage("fail", e.toString());
+ }
+});
+
+addMessageListener("file.remove", function () {
+ try {
+ file.remove(/* recursive: */ false);
+ file = undefined;
+ sendAsyncMessage("file.removed", null);
+ } catch (e) {
+ sendAsyncMessage("fail", e.toString());
+ }
+});
diff --git a/dom/html/test/submission_flush.html b/dom/html/test/submission_flush.html
new file mode 100644
index 0000000000..f70884c66a
--- /dev/null
+++ b/dom/html/test/submission_flush.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <title>Submission Flush Test</title>
+ </head>
+ <body>
+ <form id="test_form" action="post_action_page.html" target="form_target" method="POST" onsubmit="return false;">
+ <button type="submit" id="submit_button">Submit</button>
+ </form>
+ <iframe name="form_target" id="test_frame"></iframe>
+ </body>
+</html>
diff --git a/dom/html/test/sw_formSubmission.js b/dom/html/test/sw_formSubmission.js
new file mode 100644
index 0000000000..2e102ac74c
--- /dev/null
+++ b/dom/html/test/sw_formSubmission.js
@@ -0,0 +1,36 @@
+/**
+ * We are used by test_formSubmission.html to immediately activate and start
+ * controlling its page. We operate in 3 modes, conveyed via ?MODE appended to
+ * our URL.
+ *
+ * - "no-fetch": Don't register a fetch listener so that the optimized fetch
+ * event bypass happens.
+ * - "reset-fetch": Do register a fetch listener, reset every interception.
+ * - "proxy-fetch": Do register a fetch listener, resolve every interception
+ * with fetch(event.request).
+ */
+
+const mode = location.search.slice(1);
+
+// Fetch handling.
+if (mode !== "no-fetch") {
+ addEventListener("fetch", function (event) {
+ if (mode === "reset-fetch") {
+ // Don't invoke respondWith, resetting the interception.
+ return;
+ }
+ if (mode === "proxy-fetch") {
+ // Per the spec, there's an automatic waitUntil() on this too.
+ event.respondWith(fetch(event.request));
+ }
+ });
+}
+
+// Go straight to activation, bypassing waiting.
+addEventListener("install", function (event) {
+ event.waitUntil(skipWaiting());
+});
+// Control the test document ASAP.
+addEventListener("activate", function (event) {
+ event.waitUntil(clients.claim());
+});
diff --git a/dom/html/test/test_a_text.html b/dom/html/test/test_a_text.html
new file mode 100644
index 0000000000..5ffc1995f8
--- /dev/null
+++ b/dom/html/test/test_a_text.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for a.text</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <link rel="help" href="http://www.whatwg.org/html/#dom-a-text"/>
+</head>
+<body>
+<div id="content">
+<a href="a">a b c</a>
+<a href="b">a <!--b--> c</a>
+<a href="c">a <b>b</b> c</a>
+</div>
+<pre id="test">
+<script>
+var d = document.getElementById("content")
+ .appendChild(document.createElement("a"));
+d.href = "d";
+d.appendChild(document.createTextNode("a "));
+d.appendChild(document.createTextNode("b "));
+d.appendChild(document.createTextNode("c "));
+var expected = ["a b c", "a c", "a b c", "a b c "];
+var list = document.getElementById("content").getElementsByTagName("a");
+for (var i = 0, il = list.length; i < il; ++i) {
+ is(list[i].text, list[i].textContent);
+ is(list[i].text, expected[i]);
+
+ list[i].text = "x";
+ is(list[i].text, "x");
+ is(list[i].textContent, "x");
+ is(list[i].firstChild.data, "x");
+ is(list[i].childNodes.length, 1);
+
+ list[i].textContent = "y";
+ is(list[i].text, "y");
+ is(list[i].textContent, "y");
+ is(list[i].firstChild.data, "y");
+ is(list[i].childNodes.length, 1);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_allowMedia.html b/dom/html/test/test_allowMedia.html
new file mode 100644
index 0000000000..46a692283a
--- /dev/null
+++ b/dom/html/test/test_allowMedia.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=759964
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 759964</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 759964 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runNextTest);
+
+var SJS = `${location.origin}/tests/dom/html/test/allowMedia.sjs`;
+var TEST_PAGE = "data:text/html,<audio src='" + SJS + "?audio'></audio>";
+
+function runNextTest() {
+ var test = tests.shift();
+ if (!test) {
+ SimpleTest.finish();
+ return;
+ }
+ test();
+}
+
+var tests = [
+
+ // Set allowMedia = false, load a page with <audio>, verify the <audio>
+ // doesn't load its source.
+ function basic() {
+ var iframe = insertIframe();
+ SpecialPowers.allowMedia(iframe.contentWindow, false);
+ loadIframe(iframe, TEST_PAGE, function () {
+ verifyPass();
+ iframe.remove();
+ runNextTest();
+ });
+ },
+
+ // Set allowMedia = false on parent docshell, load a page with <audio> in a
+ // child iframe, verify the <audio> doesn't load its source.
+ function inherit() {
+ SpecialPowers.allowMedia(window, false);
+
+ var iframe = insertIframe();
+ loadIframe(iframe, TEST_PAGE, function () {
+ verifyPass();
+ iframe.remove();
+ SpecialPowers.allowMedia(window, true);
+ runNextTest();
+ });
+ },
+
+ // In a display:none iframe, set allowMedia = false, load a page with <audio>,
+ // verify the <audio> doesn't load its source.
+ function displayNone() {
+ var iframe = insertIframe();
+ iframe.style.display = "none";
+ SpecialPowers.allowMedia(iframe.contentWindow, false);
+ loadIframe(iframe, TEST_PAGE, function () {
+ verifyPass();
+ iframe.remove();
+ runNextTest();
+ });
+ },
+];
+
+function insertIframe() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ return iframe;
+}
+
+function loadIframe(iframe, url, onDone) {
+ iframe.setAttribute("src", url);
+ iframe.addEventListener("load", onDone);
+}
+
+function verifyPass() {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", SJS, false);
+ xhr.send();
+ is(xhr.responseText, "PASS", "<audio> source should not have been loaded.");
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=759964">Mozilla Bug 759964</a>
+<p id="display">
+</p>
+</body>
+</html>
diff --git a/dom/html/test/test_anchor_href_cache_invalidation.html b/dom/html/test/test_anchor_href_cache_invalidation.html
new file mode 100644
index 0000000000..c1a8327e62
--- /dev/null
+++ b/dom/html/test/test_anchor_href_cache_invalidation.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for anchor cache invalidation</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <a id="x" href="http://example.com"></a>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+is($("x").href, "http://example.com/");
+is($("x").host, "example.com");
+
+$("x").href = "http://www.example.com";
+
+is($("x").href, "http://www.example.com/");
+is($("x").host, "www.example.com");
+
+$("x").setAttribute("href", "http://www.example.net/");
+is($("x").host, "www.example.net");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_anchor_ping.html b/dom/html/test/test_anchor_ping.html
new file mode 100644
index 0000000000..513e5ee05f
--- /dev/null
+++ b/dom/html/test/test_anchor_ping.html
@@ -0,0 +1,304 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=786347
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 786347</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 786347 **/
+
+SimpleTest.waitForExplicitFinish();
+
+const {NetUtil} = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+const {HttpServer} = ChromeUtils.importESModule(
+ "resource://testing-common/httpd.sys.mjs"
+);
+
+addLoadEvent(function () {
+ (async function run_tests() {
+ while (tests.length) {
+ let test = tests.shift();
+ info("-- running " + test.name);
+ await test();
+ }
+
+ SimpleTest.finish();
+ })();
+});
+
+let tests = [
+
+ // Ensure that sending pings is enabled.
+ function setup() {
+ Services.prefs.setBoolPref("browser.send_pings", true);
+ Services.prefs.setIntPref("browser.send_pings.max_per_link", -1);
+
+ SimpleTest.registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.send_pings");
+ Services.prefs.clearUserPref("browser.send_pings.max_per_link");
+ });
+ },
+
+ // If both the address of the document containing the hyperlink being audited
+ // and ping URL have the same origin then the request must include a Ping-From
+ // HTTP header with, as its value, the address of the document containing the
+ // hyperlink, and a Ping-To HTTP header with, as its value, the target URL.
+ // The request must not include a Referer (sic) HTTP header.
+ async function same_origin() {
+ let from = "/ping-from/" + Math.random();
+ let to = "/ping-to/" + Math.random();
+ let ping = "/ping/" + Math.random();
+
+ let base;
+ let server = new HttpServer();
+
+ // The page that contains the link.
+ createFromPathHandler(server, from, to, () => ping);
+
+ // The page that the link's href points to.
+ let promiseHref = createToPathHandler(server, to);
+
+ // The ping we want to receive.
+ let promisePing = createPingPathHandler(server, ping, () => {
+ return {from: base + from, to: base + to};
+ });
+
+ // Start the server, get its base URL and run the test.
+ server.start(-1);
+ base = "http://localhost:" + server.identity.primaryPort;
+ navigate(base + from);
+
+ // Wait until the target and ping url have loaded.
+ await Promise.all([promiseHref, promisePing]);
+
+ // Cleanup.
+ await stopServer(server);
+ },
+
+ // If the origins are different, but the document containing the hyperlink
+ // being audited was not retrieved over an encrypted connection then the
+ // request must include a Referer (sic) HTTP header with, as its value, the
+ // address of the document containing the hyperlink, a Ping-From HTTP header
+ // with the same value, and a Ping-To HTTP header with, as its value, target
+ // URL.
+ async function diff_origin() {
+ let from = "/ping-from/" + Math.random();
+ let to = "/ping-to/" + Math.random();
+ let ping = "/ping/" + Math.random();
+
+ // We will use two servers to simulate two different origins.
+ let base, base2;
+ let server = new HttpServer();
+ let server2 = new HttpServer();
+
+ // The page that contains the link.
+ createFromPathHandler(server, from, to, () => base2 + ping);
+
+ // The page that the link's href points to.
+ let promiseHref = createToPathHandler(server, to);
+
+ // Start the first server and get its base URL.
+ server.start(-1);
+ base = "http://localhost:" + server.identity.primaryPort;
+
+ // The ping we want to receive.
+ let promisePing = createPingPathHandler(server2, ping, () => {
+ return {referrer: base + from, from: base + from, to: base + to};
+ });
+
+ // Start the second server, get its base URL and run the test.
+ server2.start(-1);
+ base2 = "http://localhost:" + server2.identity.primaryPort;
+ navigate(base + from);
+
+ // Wait until the target and ping url have loaded.
+ await Promise.all([promiseHref, promisePing]);
+
+ // Cleanup.
+ await stopServer(server);
+ await stopServer(server2);
+ },
+
+ // If the origins are different and the document containing the hyperlink
+ // being audited was retrieved over an encrypted connection then the request
+ // must include a Ping-To HTTP header with, as its value, target URL. The
+ // request must neither include a Referer (sic) HTTP header nor include a
+ // Ping-From HTTP header.
+ async function diff_origin_secure_referrer() {
+ let ping = "/ping/" + Math.random();
+ let server = new HttpServer();
+
+ // The ping we want to receive.
+ let promisePing = createPingPathHandler(server, ping, () => {
+ return {to: "https://example.com/"};
+ });
+
+ // Start the server and run the test.
+ server.start(-1);
+
+ // The referrer will be loaded using a secure channel.
+ navigate("https://example.com/chrome/dom/html/test/" +
+ "file_anchor_ping.html?" + "http://127.0.0.1:" +
+ server.identity.primaryPort + ping);
+
+ // Wait until the ping has been sent.
+ await promisePing;
+
+ // Cleanup.
+ await stopServer(server);
+ },
+
+ // Test that the <a ping> attribute is properly tokenized using ASCII white
+ // space characters as separators.
+ async function tokenize_white_space() {
+ let from = "/ping-from/" + Math.random();
+ let to = "/ping-to/" + Math.random();
+
+ let base;
+ let server = new HttpServer();
+
+ let pings = [
+ "/ping1/" + Math.random(),
+ "/ping2/" + Math.random(),
+ "/ping3/" + Math.random(),
+ "/ping4/" + Math.random()
+ ];
+
+ // The page that contains the link.
+ createFromPathHandler(server, from, to, () => {
+ return " " + pings[0] + " \r " + pings[1] + " \t " +
+ pings[2] + " \n " + pings[3] + " ";
+ });
+
+ // The page that the link's href points to.
+ let promiseHref = createToPathHandler(server, to);
+
+ // The pings we want to receive.
+ let pingPathHandlers = createPingPathHandlers(server, pings, () => {
+ return {from: base + from, to: base + to};
+ });
+
+ // Start the server, get its base URL and run the test.
+ server.start(-1);
+ base = "http://localhost:" + server.identity.primaryPort;
+ navigate(base + from);
+
+ // Wait until the target and ping url have loaded.
+ await Promise.all([promiseHref, ...pingPathHandlers]);
+
+ // Cleanup.
+ await stopServer(server);
+ }
+];
+
+// Navigate the iframe used for testing to a new URL.
+function navigate(uri) {
+ document.getElementById("frame").src = uri;
+}
+
+// Registers a path handler for the given server that will serve a page
+// containing an <a ping> element. The page will automatically simulate
+// clicking the link after it has loaded.
+function createFromPathHandler(server, path, href, lazyPing) {
+ server.registerPathHandler(path, function (request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+
+ let body = '<body onload="document.body.firstChild.click()">' +
+ '<a href="' + href + '" ping="' + lazyPing() + '"></a></body>';
+ response.write(body);
+ });
+}
+
+// Registers a path handler for the given server that will serve a simple empty
+// page we can use as the href attribute for links. It returns a promise that
+// will be resolved once the page has been requested.
+function createToPathHandler(server, path) {
+ return new Promise(resolve => {
+
+ server.registerPathHandler(path, function (request, response) {
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write("OK");
+
+ resolve();
+ });
+
+ });
+}
+
+// Register multiple path handlers for the given server that will receive
+// pings as sent when an <a ping> element is clicked. This method uses
+// createPingPathHandler() defined below to ensure all headers are sent
+// and received as expected.
+function createPingPathHandlers(server, paths, lazyHeaders) {
+ return Array.from(paths, (path) => createPingPathHandler(server, path, lazyHeaders));
+}
+
+// Registers a path handler for the given server that will receive pings as
+// sent when an <a ping> element has been clicked. It will check that the
+// correct http method has been used, the post data is correct and all headers
+// are given as expected. It returns a promise that will be resolved once the
+// ping has been received.
+function createPingPathHandler(server, path, lazyHeaders) {
+ return new Promise(resolve => {
+
+ server.registerPathHandler(path, function (request, response) {
+ let headers = lazyHeaders();
+
+ is(request.method, "POST", "correct http method used");
+ is(request.getHeader("Ping-To"), headers.to, "valid ping-to header");
+
+ if ("from" in headers) {
+ is(request.getHeader("Ping-From"), headers.from, "valid ping-from header");
+ } else {
+ ok(!request.hasHeader("Ping-From"), "no ping-from header");
+ }
+
+ if ("referrer" in headers) {
+ let expectedReferrer = headers.referrer.match(/https?:\/\/[^\/]+\/?/i)[0];
+ is(request.getHeader("Referer"), expectedReferrer, "valid referer header");
+ } else {
+ ok(!request.hasHeader("Referer"), "no referer header");
+ }
+
+ let bs = request.bodyInputStream;
+ let body = NetUtil.readInputStreamToString(bs, bs.available());
+ is(body, "PING", "correct body sent");
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.write("OK");
+
+ resolve();
+ });
+
+ });
+}
+
+// Returns a promise that is resolved when the given http server instance has
+// been stopped.
+function stopServer(server) {
+ return new Promise(resolve => {
+ server.stop(resolve);
+ });
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786347">Mozilla Bug 786347</a>
+<p id="display"></p>
+<iframe id="frame" />
+</body>
+</html>
diff --git a/dom/html/test/test_base_attributes_reflection.html b/dom/html/test/test_base_attributes_reflection.html
new file mode 100644
index 0000000000..cbb8955d5c
--- /dev/null
+++ b/dom/html/test/test_base_attributes_reflection.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLBaseElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLBaseElement attributes reflection **/
+
+// .href is sort of like a URL reflection, but with some special rules. Watch
+// out for that!
+reflectURL({
+ element: document.createElement("base"),
+ attribute: "href"
+});
+
+// .target
+reflectString({
+ element: document.createElement("base"),
+ attribute: "target"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1003539.html b/dom/html/test/test_bug1003539.html
new file mode 100644
index 0000000000..cbdc1e9fbe
--- /dev/null
+++ b/dom/html/test/test_bug1003539.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1003539
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1003539</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1003539 **/
+// Refering to this specification: http://www.whatwg.org/specs/web-apps/current-work/multipage/tabular-data.html#dom-table-insertrow
+var tab;
+tab = document.createElement("table");
+tab.createTHead();
+tab.insertRow();
+is(tab.innerHTML, '<thead></thead><tbody><tr></tr></tbody>', "Row should be inserted in the tbody.");
+
+tab = document.createElement("table");
+tab.createTBody();
+tab.createTBody();
+tab.insertRow();
+is(tab.innerHTML, '<tbody></tbody><tbody><tr></tr></tbody>', "Row should be inserted in the last tbody.");
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1003539">Mozilla Bug 1003539</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug100533.html b/dom/html/test/test_bug100533.html
new file mode 100644
index 0000000000..29c52f4f0a
--- /dev/null
+++ b/dom/html/test/test_bug100533.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=100533
+-->
+<head>
+ <title>Test for Bug 100533</title>
+ <script type="text/javascript" src="/MochiKit/Base.js"></script>
+ <script type="text/javascript" src="/MochiKit/DOM.js"></script>
+ <script type="text/javascript" src="/MochiKit/Style.js"></script>
+ <script type="text/javascript" src="/MochiKit/Signal.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=100533">Mozilla Bug 100533</a>
+<p id="display"></p>
+<div id="content" >
+
+<button id="thebutton">Test</button>
+<iframe style='display: none;' src='bug100533_iframe.html' id='a'></iframe>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/** Test for Bug 100533 **/
+var submitIframeForm = function() {
+ $('a').contentDocument.getElementById('b').submit();
+}
+
+submitted = function() {
+ ok(true, "Finished. Form submits when located in iframe set to display:none;");
+ SimpleTest.finish();
+};
+
+addLoadEvent(function() {
+ connect("thebutton", "click", submitIframeForm);
+ signal("thebutton", "click");
+});
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug1013316.html b/dom/html/test/test_bug1013316.html
new file mode 100644
index 0000000000..fdb9e5363d
--- /dev/null
+++ b/dom/html/test/test_bug1013316.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013316
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1013316</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1013316 **/
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ is(Object.keys(document.all).length, 15, "We have 15 indexed props");
+ var props = Object.getOwnPropertyNames(document.all);
+ is(props.length, 20, "Should have five names");
+ is(props[15], "display", "display first");
+ is(props[16], "content", "content second");
+ is(props[17], "bar", "bar third");
+ is(props[18], "foo", "foo fourth");
+ is(props[19], "test", "test fifth");
+
+ is(Object.keys(document.images).length, 2, "We have 2 indexed props");
+ props = Object.getOwnPropertyNames(document.images);
+ is(props.length, 5, "Should have 3 names");
+ is(props[2], "display", "display first on document.images");
+ is(props[3], "bar", "bar second on document.images");
+ is(props[4], "foo", "foo third on document.images");
+ SimpleTest.finish();
+ })
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013316">Mozilla Bug 1013316</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <img id="display">
+ <img name="foo" id="bar">
+ <div name="baz">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1045270.html b/dom/html/test/test_bug1045270.html
new file mode 100644
index 0000000000..b0c81daf61
--- /dev/null
+++ b/dom/html/test/test_bug1045270.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1045270
+-->
+ <head>
+ <title>Test for Bug 583514</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045270">Mozilla Bug 1045270</a>
+ <p id="display"></p>
+ <div id="content">
+ <input type=number>
+ </div>
+ <pre id="test">
+ <script type="application/javascript">
+
+ /** Test for Bug 1045270 **/
+
+ var input = document.querySelector("input");
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(function() {
+ input.focus();
+ input.addEventListener("input", function() {
+ // reframe
+ document.body.style.display = "none";
+ document.body.style.display = "";
+ document.body.offsetLeft; // flush
+ });
+ sendString("1");
+ SimpleTest.executeSoon(function() {
+ sendString("2");
+ SimpleTest.executeSoon(function() {
+ is(input.value, "12", "Reframe should restore focus and selection properly");
+ SimpleTest.finish();
+ });
+ });
+ });
+
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/html/test/test_bug1089326.html b/dom/html/test/test_bug1089326.html
new file mode 100644
index 0000000000..fed0a467cd
--- /dev/null
+++ b/dom/html/test/test_bug1089326.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1089326
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1089326</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1089326 **/
+ function test() {
+ var b = document.getElementById("button");
+ var b_rect = b.getBoundingClientRect();
+ var a = document.getElementById("anchor");
+ var a_rect = a.getBoundingClientRect();
+
+ is(document.elementFromPoint(b_rect.x + 1, b_rect.y + 1), b,
+ "Should find button when doing hit test on top of it.");
+ is(document.elementFromPoint(a_rect.x + 1, a_rect.y + 1), a,
+ "Should find anchor when doing hit test on top of it.");
+
+ var expectedTarget;
+ var clickCount = 0;
+ var container = document.getElementById("interactiveContentContainer");
+ container.addEventListener("click", function(event) {
+ is(event.target, expectedTarget, "Got expected click event target.");
+ ++clickCount;
+ }, true);
+ var i1 = document.getElementById("interactiveContent1");
+ var s11 = document.getElementById("s11");
+ var s12 = document.getElementById("s12");
+
+ var i2 = document.getElementById("interactiveContent2");
+ var s21 = document.getElementById("s21");
+
+ expectedTarget = i1;
+ synthesizeMouseAtCenter(s11, { type: "mousedown" });
+ synthesizeMouseAtCenter(s12, { type: "mouseup" });
+ is(clickCount, 1, "Should have got a click event.");
+
+ expectedTarget = container;
+ synthesizeMouseAtCenter(s11, { type: "mousedown" });
+ synthesizeMouseAtCenter(s21, { type: "mouseup" });
+ is(clickCount, 2, "Should not have got a click event.");
+
+ expectedTarget = container;
+ synthesizeMouseAtCenter(s21, { type: "mousedown" });
+ synthesizeMouseAtCenter(s11, { type: "mouseup" });
+ is(clickCount, 3, "Should not have got a click event.");
+
+ var span1 = document.getElementById("span1");
+ var span2 = document.getElementById("span2");
+ expectedTarget = container;
+ synthesizeMouseAtCenter(span1, { type: "mousedown" });
+ synthesizeMouseAtCenter(span2, { type: "mouseup" });
+ is(clickCount, 4, "Should not have got a click event.");
+
+ button.addEventListener("click", function(event) {
+ is(event.target, expectedTarget, "Got expected click event target.");
+ ++clickCount;
+ }, true);
+
+ expectedTarget = a;
+ synthesizeMouseAtCenter(a, { type: "mousedown" });
+ synthesizeMouseAtCenter(a, { type: "mouseup" });
+ is(clickCount, 5, "Should have got a click event.");
+
+ expectedTarget = a;
+ synthesizeMouseAtCenter(b, { type: "mousedown" });
+ synthesizeMouseAtCenter(b, { type: "mouseup" });
+ is(clickCount, 6, "Should have got a click event.");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(test);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1089326">Mozilla Bug 1089326</a>
+<p id="display"></p>
+<button id="button">button <a id="anchor" href="#">anchor</a>button</button>
+
+<div id="interactiveContentContainer">
+ <a id="interactiveContent1" href="#">foo <span id="s11">s11</span><span id="s12">s12</span> bar</a>
+ <a id="interactiveContent2" href="#">foo <span id="s21">s21</span><span id="s22">s22</span> bar</a>
+
+ <div>
+ <span>
+ <span id="span1">span1</span>
+ </span>
+ </div>
+
+ <div>
+ <span>
+ <span id="span2">span2</span>
+ </span>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug109445.html b/dom/html/test/test_bug109445.html
new file mode 100644
index 0000000000..27ffe22948
--- /dev/null
+++ b/dom/html/test/test_bug109445.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=109445
+-->
+<head>
+ <title>Test for Bug 109445</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=109445">Mozilla Bug 109445</a>
+<p id="display">
+<map name=a>
+<area shape=rect coords=25,25,75,75 href=#x>
+</map>
+<map id=b>
+<area shape=rect coords=25,25,75,75 href=#y>
+</map>
+<map name=a>
+<area shape=rect coords=25,25,75,75 href=#FAIL>
+</map>
+<map id=b>
+<area shape=rect coords=25,25,75,75 href=#FAIL>
+</map>
+
+<img usemap=#a src=image.png>
+<img usemap=#b src=image.png>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 109445 **/
+SimpleTest.waitForExplicitFinish();
+var images = document.getElementsByTagName("img");
+var second = false;
+onhashchange = function() {
+ if (!second) {
+ second = true;
+ is(location.hash, "#x", "First map");
+ SimpleTest.waitForFocus(() => synthesizeMouse(images[1], 50, 50, {}));
+ } else {
+ is(location.hash, "#y", "Second map");
+ SimpleTest.finish();
+ }
+};
+SimpleTest.waitForFocus(() => synthesizeMouse(images[0], 50, 50, {}));
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug109445.xhtml b/dom/html/test/test_bug109445.xhtml
new file mode 100644
index 0000000000..b1524c8ead
--- /dev/null
+++ b/dom/html/test/test_bug109445.xhtml
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=109445
+-->
+<head>
+ <title>Test for Bug 109445</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=109445">Mozilla Bug 109445</a>
+<p id="display">
+<map name="a">
+<area shape="rect" coords="25,25,75,75" href="#x"/>
+</map>
+<map id="b">
+<area shape="rect" coords="25,25,75,75" href="#y"/>
+</map>
+<map name="a">
+<area shape="rect" coords="25,25,75,75" href="#FAIL"/>
+</map>
+<map id="b">
+<area shape="rect" coords="25,25,75,75" href="#FAIL"/>
+</map>
+
+<img usemap="#a" src="image.png"/>
+<img usemap="#b" src="image.png"/>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 109445 **/
+SimpleTest.waitForExplicitFinish();
+var images = document.getElementsByTagName("img");
+var second = false;
+onhashchange = function() {
+ if (!second) {
+ second = true;
+ is(location.hash, "#x", "First map");
+ SimpleTest.waitForFocus(() => synthesizeMouse(images[1], 50, 50, {}));
+ } else {
+ is(location.hash, "#y", "Second map");
+ SimpleTest.finish();
+ }
+};
+SimpleTest.waitForFocus(() => synthesizeMouse(images[0], 50, 50, {}));
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1146116.html b/dom/html/test/test_bug1146116.html
new file mode 100644
index 0000000000..95d52af9eb
--- /dev/null
+++ b/dom/html/test/test_bug1146116.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1146116
+-->
+<head>
+ <title>Test for Bug 1146116</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1146116">Mozilla Bug 1146116</a>
+<p id="display">
+ <input type="file" id="file">
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for bug 1146116 **/
+
+SimpleTest.waitForExplicitFinish();
+
+const helperURL = SimpleTest.getTestFileURL("simpleFileOpener.js");
+const helper = SpecialPowers.loadChromeScript(helperURL);
+helper.addMessageListener("fail", function onFail(message) {
+ is(message, null, "chrome script failed");
+ SimpleTest.finish();
+});
+helper.addMessageListener("file.opened", onFileOpened);
+helper.sendAsyncMessage("file.open", "test_bug1146116.txt");
+
+function getGlobal(thing) {
+ return SpecialPowers.unwrap(SpecialPowers.Cu.getGlobalForObject(thing));
+}
+
+function onFileOpened(message) {
+ const file = message.domFile;
+ const elem = document.getElementById("file");
+ is(getGlobal(elem), window,
+ "getGlobal() works as expected");
+ is(getGlobal(file), window,
+ "File from MessageManager is not wrapped");
+ SpecialPowers.wrap(elem).mozSetFileArray([file]);
+ is(getGlobal(elem.files[0]), window,
+ "File read back from input element is not wrapped");
+ helper.addMessageListener("file.removed", onFileRemoved);
+ helper.sendAsyncMessage("file.remove", null);
+}
+
+function onFileRemoved() {
+ helper.destroy();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1166138.html b/dom/html/test/test_bug1166138.html
new file mode 100644
index 0000000000..5b65db6c04
--- /dev/null
+++ b/dom/html/test/test_bug1166138.html
@@ -0,0 +1,130 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1166138
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1166138</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1166138">Mozilla Bug 1166138</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+
+ <script type="application/javascript">
+ var img1x = `${location.origin}/tests/dom/html/test/file_bug1166138_1x.png`;
+ var img2x = `${location.origin}/tests/dom/html/test/file_bug1166138_2x.png`;
+ var imgdef = `${location.origin}/tests/dom/html/test/file_bug1166138_def.png`;
+ var onLoadCallback = null;
+ var done = false;
+
+ var startPromise = new Promise((a) => {
+ onLoadCallback = () => {
+ var image = document.querySelector('img');
+ // If we aren't starting at 2x scale, resize to 2x scale, and wait for a load
+ if (image.currentSrc != img2x) {
+ onLoadCallback = a;
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+ } else {
+ a();
+ }
+ };
+ });
+
+ // if aLoad is true, waits for a load event. Otherwise, spins the event loop twice to
+ // ensure that no events were queued to be fired.
+ function spin(aLoad) {
+ if (aLoad) {
+ return new Promise((a) => {
+ ok(!onLoadCallback, "Shouldn't be an existing callback");
+ onLoadCallback = a;
+ });
+ } else {
+ return new Promise((a) => SimpleTest.executeSoon(() => SimpleTest.executeSoon(a)));
+ }
+ }
+
+ function onLoad() {
+ if (done) return;
+ ok(onLoadCallback, "Expected a load event");
+ if (onLoadCallback) {
+ var cb = onLoadCallback;
+ onLoadCallback = null;
+ cb();
+ }
+ }
+
+ add_task(async function() {
+ await startPromise;
+ var image = document.querySelector('img');
+ is(image.currentSrc, img2x, "initial scale must be 2x");
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+ await spin(true);
+ is(image.currentSrc, img1x, "pre-existing img tag to 1x");
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+ await spin(true);
+ is(image.currentSrc, img2x, "pre-existing img tag to 2x");
+
+ // Try removing & re-adding the image
+ document.body.removeChild(image);
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+ await spin(false); // No load should occur because the element is unbound
+
+ document.body.appendChild(image);
+ await spin(true);
+ is(image.currentSrc, img1x, "remove and re-add tag after changing to 1x");
+
+ document.body.removeChild(image);
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 2]]});
+ await spin(false); // No load should occur because the element is unbound
+
+ document.body.appendChild(image);
+ await spin(true);
+ is(image.currentSrc, img2x, "remove and re-add tag after changing to 2x");
+
+ // get rid of the srcset attribute! It should become the default
+ image.removeAttribute('srcset');
+ await spin(true);
+ is(image.currentSrc, imgdef, "remove srcset attribute");
+
+ // Setting srcset again should return it to the correct value
+ image.setAttribute('srcset', "file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x");
+ await spin(true);
+ is(image.currentSrc, img2x, "restore srcset attribute");
+
+ // Create a new image
+ var newImage = document.createElement('img');
+ // Switch load listening over to newImage
+ newImage.addEventListener('load', onLoad);
+ image.removeEventListener('load', onLoad);
+
+ document.body.appendChild(newImage);
+ await spin(false); // no load event should fire - as the image has no attributes
+ is(newImage.currentSrc, "", "New element with no attributes");
+ newImage.setAttribute('srcset', "file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x");
+ await spin(true);
+ is(newImage.currentSrc, img2x, "Adding srcset attribute");
+
+ SpecialPowers.pushPrefEnv({'set': [['layout.css.devPixelsPerPx', 1]]});
+ await spin(true);
+ is(newImage.currentSrc, img1x, "new image after switching to 1x");
+ is(image.currentSrc, img1x, "old image after switching to 1x");
+
+ // Clear the listener
+ done = true;
+ });
+ </script>
+
+ <img srcset="file_bug1166138_1x.png 1x, file_bug1166138_2x.png 2x"
+ src="file_bug1166138_def.png"
+ onload="onLoad()">
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug1203668.html b/dom/html/test/test_bug1203668.html
new file mode 100644
index 0000000000..41249d90ab
--- /dev/null
+++ b/dom/html/test/test_bug1203668.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1203668
+-->
+<head>
+ <title>Test for Bug 1203668</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203668">Mozilla Bug 1203668</a>
+<p id="display"></p>
+<div id="content">
+ <select class="select" multiple>
+ <option value="foo" selected>foo</option>
+ <option value="bar" selected>bar</option>
+ </select>
+ <select class="select" multiple>
+ <option value="foo">foo</option>
+ <option value="bar" selected>bar</option>
+ </select>
+ <select class="select" multiple>
+ <option value="foo">foo</option>
+ <option value="bar">bar</option>
+ </select>
+ <select class="select" size=1>
+ <option value="foo">foo</option>
+ <option value="bar" selected>bar</option>
+ </select>
+ <select class="select" size=1>
+ <option value="foo">foo</option>
+ <option value="bar">bar</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1203668 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest()
+{
+ var selects = document.querySelectorAll('.select');
+ for (i=0; i < selects.length; i++) {
+ var select = selects[i];
+ select.value = "bogus"
+ is(select.selectedIndex, -1, "no option is selected");
+ is(select.children[0].selected, false, "first option is not selected");
+ is(select.children[1].selected, false, "second option is not selected");
+ }
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1230665.html b/dom/html/test/test_bug1230665.html
new file mode 100644
index 0000000000..cbe9c91d30
--- /dev/null
+++ b/dom/html/test/test_bug1230665.html
@@ -0,0 +1,46 @@
+<html>
+<head>
+ <title>Test for Bug 1230665</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ document.getElementById("flexbutton1").focus();
+ synthesizeKey("KEY_Tab");
+ var e = document.getElementById("flexbutton2");
+ is(document.activeElement, e, "focus in flexbutton2 after TAB");
+
+ document.getElementById("gridbutton1").focus();
+ synthesizeKey("KEY_Tab");
+ e = document.getElementById("gridbutton2");
+ is(document.activeElement, e, "focus in gridbutton2 after TAB");
+
+ SimpleTest.finish();
+});
+
+</script>
+
+<div tabindex="0" style="display:flex">
+ <button id="flexbutton1"></button>
+ text <!-- this text will force a :-moz-anonymous-flex-item frame -->
+ <div style="">
+ <button id="flexbutton2"></button>
+ </div>
+</div>
+
+
+<div tabindex="0" style="display:grid">
+ <button id="gridbutton1"></button>
+ text <!-- this text will force a :-moz-anonymous-grid-item frame -->
+ <div style="">
+ <button id="gridbutton2"></button>
+ </div>
+</div>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug1250401.html b/dom/html/test/test_bug1250401.html
new file mode 100644
index 0000000000..d4a1073856
--- /dev/null
+++ b/dom/html/test/test_bug1250401.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1250401
+-->
+<head>
+ <title>Test for Bug 1250401</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1250401">Bug 1250401</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1250401 **/
+function test_add() {
+ var select = document.createElement("select");
+
+ var g1 = document.createElement("optgroup");
+ var o1 = document.createElement("option");
+ g1.appendChild(o1);
+ select.appendChild(g1);
+
+ var g2 = document.createElement("optgroup");
+ var o2 = document.createElement("option");
+ g2.appendChild(o2);
+ select.add(g2, 0);
+
+ is(select.children.length, 1, "Select has 1 item");
+ is(select.firstChild, g1, "First item is g1");
+ is(select.firstChild.children.length, 2, "g2 has 2 children");
+ is(select.firstChild.children[0], g2, "g1 has 2 children: g2");
+ is(select.firstChild.children[1], o1, "g1 has 2 children: o1");
+ is(o1.index, 0, "o1.index should be 0");
+ is(o2.index, 0, "o2.index should be 0");
+}
+
+function test_append() {
+ var select = document.createElement("select");
+
+ var g1 = document.createElement("optgroup");
+ var o1 = document.createElement("option");
+ g1.appendChild(o1);
+ select.appendChild(g1);
+
+ var g2 = document.createElement("optgroup");
+ var o2 = document.createElement("option");
+ g2.appendChild(o2);
+ g1.appendChild(g2);
+
+ is(select.children.length, 1, "Select has 1 item");
+ is(select.firstChild, g1, "First item is g1");
+ is(select.firstChild.children.length, 2, "g2 has 2 children");
+ is(select.firstChild.children[0], o1, "g1 has 2 children: o1");
+ is(select.firstChild.children[1], g2, "g1 has 2 children: g1");
+ is(o1.index, 0, "o1.index should be 0");
+ is(o2.index, 0, "o2.index should be 0");
+}
+
+function test_no_select() {
+ var g1 = document.createElement("optgroup");
+ var o1 = document.createElement("option");
+ g1.appendChild(o1);
+
+ var g2 = document.createElement("optgroup");
+ var o2 = document.createElement("option");
+ g2.appendChild(o2);
+ g1.appendChild(g2);
+
+ is(g1.children.length, 2, "g2 has 2 children");
+ is(g1.children[0], o1, "g1 has 2 children: o1");
+ is(g1.children[1], g2, "g1 has 2 children: g1");
+ is(o1.index, 0, "o1.index should be 0");
+ is(o2.index, 0, "o2.index should be 0");
+}
+
+function test_no_parent() {
+ var o1 = document.createElement("option");
+ var o2 = document.createElement("option");
+
+ is(o1.index, 0, "o1.index should be 0");
+ is(o2.index, 0, "o2.index should be 0");
+}
+
+test_add();
+test_append();
+test_no_select();
+test_no_parent();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1260664.html b/dom/html/test/test_bug1260664.html
new file mode 100644
index 0000000000..99878a46b6
--- /dev/null
+++ b/dom/html/test/test_bug1260664.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1260664
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1260664</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1260664">Mozilla Bug 1260664</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1260664 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests() {
+ var elements = [ "iframe", "img", "a", "area", "link", "script"];
+
+ for (var i = 0; i < elements.length; ++i) {
+ reflectLimitedEnumerated({
+ element: document.createElement(elements[i]),
+ attribute: { content: "referrerpolicy", idl: "referrerPolicy" },
+ validValues: [ "no-referrer",
+ "origin",
+ /** These 2 below values are still invalid, please see
+ Bug 1178337 - Valid referrer attribute values **/
+ /** "no-referrer-when-downgrade",
+ "origin-when-cross-origin", **/
+ "unsafe-url" ],
+ invalidValues: [
+ "", " orIgin ", " unsafe-uRl ", " No-RefeRRer ", " fOoBaR "
+ ],
+ defaultValue: "",
+ });
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1260704.html b/dom/html/test/test_bug1260704.html
new file mode 100644
index 0000000000..ca576051b0
--- /dev/null
+++ b/dom/html/test/test_bug1260704.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1260704
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1260704</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ /** Test for Bug 1260704 **/
+
+function runTests() {
+ let testIdx = -1;
+ let testUrls = [
+ "bug1260704_iframe.html?noDefault=true&isMap=true",
+ "bug1260704_iframe.html?noDefault=true&isMap=false",
+ "bug1260704_iframe.html?noDefault=false&isMap=true",
+ "bug1260704_iframe.html?noDefault=false&isMap=false"
+ ];
+
+ let runningTest = false;
+ let iframe = document.getElementById("testFrame");
+ let iframeWin = iframe.contentWindow;
+ let rect;
+ let x;
+ let y;
+
+ window.addEventListener("message", event => {
+ if (event.data == "started") {
+ ok(!runningTest, "Start to test " + testIdx);
+ runningTest = true;
+ rect = iframeWin.document.getElementById("testImage").getBoundingClientRect();
+ x = rect.width / 2;
+ y = rect.height / 2;
+ synthesizeMouseAtPoint(rect.left + x, rect.top + y, { type: 'mousedown' }, iframeWin);
+ synthesizeMouseAtPoint(rect.left + x, rect.top + y, { type: 'mouseup' }, iframeWin);
+ }
+ else if (runningTest && event.data == "empty_frame_loaded") {
+ ok(testUrls[testIdx].includes("noDefault=false"), "Page unload");
+ let search = iframeWin.location.search;
+ if (testUrls[testIdx].includes("isMap=true")) {
+ // url trigger by image with ismap attribute should contains coordinates
+ // try to parse coordinates and check them with small tolerance
+ let coorStr = search.split("?");
+ let coordinates = coorStr[1].split(",");
+ ok(Math.abs(coordinates[0] - x) <= 1, "expect X=" + x + " got " + coordinates[0]);
+ ok(Math.abs(coordinates[1] - y) <= 1, "expect Y=" + y + " got " + coordinates[1]);
+ } else {
+ ok(search == "", "expect empty search string got:" + search);
+ }
+ nextTest();
+ }
+ else if (runningTest && event.data == "finished") {
+ ok(testUrls[testIdx].includes("noDefault=true"), "Page should not leave");
+ nextTest();
+ }
+ });
+
+ function nextTest() {
+ testIdx++;
+ runningTest = false;
+ if (testIdx >= testUrls.length) {
+ SimpleTest.finish();
+ } else {
+ ok(true, "Test " + testIdx + " - Set url to " + testUrls[testIdx]);
+ iframeWin.location.href = testUrls[testIdx];
+ }
+ }
+ nextTest();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+ </script>
+</head>
+<body>
+
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe id="testFrame" src="about:blank" width="400" height="400">
+</iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1261673.html b/dom/html/test/test_bug1261673.html
new file mode 100644
index 0000000000..c574967dd7
--- /dev/null
+++ b/dom/html/test/test_bug1261673.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1261673
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1261673</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1261673">Mozilla Bug 1261673</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<input id="test_number" type="number" value=5>
+<script type="text/javascript">
+
+/** Test for Bug 1261673 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests() {
+ let input = window.document.getElementById("test_number");
+
+ // focus: whether the target input element is focused
+ // deltaY: deltaY of WheelEvent
+ // deltaMode: deltaMode of WheelEvent
+ // valueChanged: expected value changes after input element handled the wheel event
+ let params = [
+ {focus: true, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: -1},
+ {focus: true, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: 1},
+ {focus: true, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_PAGE, valueChanged: -1},
+ {focus: true, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_PAGE, valueChanged: 1},
+ {focus: true, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL, valueChanged: 0},
+ {focus: true, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL, valueChanged: 0},
+ {focus: false, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: 0},
+ {focus: false, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: 0}
+ ];
+
+ let testIdx = 0;
+ let result = parseInt(input.value);
+ let numberChange = 0;
+ let expectChange = 0;
+
+ input.addEventListener("change", () => {
+ ++numberChange;
+ });
+
+ function runNext() {
+ let p = params[testIdx];
+ (p.focus) ? input.focus() : input.blur();
+ expectChange = p.valueChanged == 0 ? expectChange : expectChange + 1;
+ result += parseInt(p.valueChanged);
+ sendWheelAndPaint(input, 1, 1, { deltaY: p.deltaY, deltaMode: p.deltaMode }, () => {
+ ok(input.value == result,
+ "Handle wheel in number input test-" + testIdx + " expect " + result +
+ " get " + input.value);
+ ok(numberChange == expectChange,
+ "UA should fire change event when input's value changed, expect " + expectChange + " get " + numberChange);
+ (++testIdx >= params.length) ? SimpleTest.finish() : runNext();
+ });
+ }
+ runNext();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1261674-1.html b/dom/html/test/test_bug1261674-1.html
new file mode 100644
index 0000000000..a9042be733
--- /dev/null
+++ b/dom/html/test/test_bug1261674-1.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1261674
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1261674</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1261674">Mozilla Bug 1261674</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<input id="test_input" type="range" value=5 max=10 min=0>
+<script type="text/javascript">
+
+/** Test for Bug 1261674 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests() {
+ let input = window.document.getElementById("test_input");
+
+ // focus: whether the target input element is focused
+ // deltaY: deltaY of WheelEvent
+ // deltaMode: deltaMode of WheelEvent
+ // valueChanged: expected value changes after input element handled the wheel event
+ let params = [
+ {focus: true, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: -1},
+ {focus: true, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: 1},
+ {focus: true, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_PAGE, valueChanged: -1},
+ {focus: true, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_PAGE, valueChanged: 1},
+ {focus: true, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL, valueChanged: 0},
+ {focus: true, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL, valueChanged: 0},
+ {focus: false, deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: 0},
+ {focus: false, deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_LINE, valueChanged: 0}
+ ];
+
+ let testIdx = 0;
+ let result = parseInt(input.value);
+ let rangeChange = 0;
+ let expectChange = 0;
+
+ input.addEventListener("change", () => {
+ ++rangeChange;
+ });
+
+ function runNext() {
+ let p = params[testIdx];
+ (p.focus) ? input.focus() : input.blur();
+ expectChange = p.valueChanged == 0 ? expectChange : expectChange + 1;
+ result += parseInt(p.valueChanged);
+ sendWheelAndPaint(input, 1, 1, { deltaY: p.deltaY, deltaMode: p.deltaMode }, () => {
+ ok(input.value == result,
+ "Handle wheel in range input test-" + testIdx + " expect " + result + " get " + input.value);
+ ok(rangeChange == expectChange,
+ "UA should fire change event when input's value changed, expect " + expectChange + " get " + rangeChange);
+ (++testIdx >= params.length) ? SimpleTest.finish() : runNext();
+ });
+ }
+
+ input.addEventListener("input", () => {
+ ok(input.value == result,
+ "Test-" + testIdx + " receive input event, expect " + result + " get " + input.value);
+ });
+
+ runNext();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1261674-2.html b/dom/html/test/test_bug1261674-2.html
new file mode 100644
index 0000000000..cfda243749
--- /dev/null
+++ b/dom/html/test/test_bug1261674-2.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1261674
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1261674</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1261674">Mozilla Bug 1261674</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<input id="test_input" type="range" max=0 min=10>
+<script type="text/javascript">
+
+/** Test for Bug 1261674 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests() {
+ let input = window.document.getElementById("test_input");
+
+ // deltaY: deltaY of WheelEvent
+ // deltaMode: deltaMode of WheelEvent
+ let params = [
+ {deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_LINE},
+ {deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_LINE},
+ {deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_PAGE},
+ {deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_PAGE},
+ {deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL},
+ {deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_PIXEL},
+ {deltaY: 1.0, deltaMode: WheelEvent.DOM_DELTA_LINE},
+ {deltaY: -1.0, deltaMode: WheelEvent.DOM_DELTA_LINE}
+ ];
+
+ let testIdx = 0;
+ let result = parseInt(input.value);
+ let rangeChange = 0;
+
+ input.addEventListener("change", () => {
+ ++rangeChange;
+ });
+
+ function runNext() {
+ let p = params[testIdx];
+ (p.focus) ? input.focus() : input.blur();
+ sendWheelAndPaint(input, 1, 1, { deltaY: p.deltaY, deltaMode: p.deltaMode }, () => {
+ ok(input.value == result,
+ "Handle wheel in range input test-" + testIdx + " expect " + result + " get " + input.value);
+ ok(rangeChange == 0, "Wheel event should not trigger change event when max < min");
+ testIdx++;
+ (testIdx >= params.length) ? SimpleTest.finish() : runNext();
+ });
+ }
+
+ input.addEventListener("input", () => {
+ ok(false, "Wheel event should be no effect to range input element with max < min");
+ });
+
+ runNext();
+}
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1264157.html b/dom/html/test/test_bug1264157.html
new file mode 100644
index 0000000000..1bede807da
--- /dev/null
+++ b/dom/html/test/test_bug1264157.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=535043
+-->
+<head>
+ <title>Test for Bug 535043</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input {
+ outline: 2px solid lime;
+ }
+ input:in-range {
+ outline: 2px solid red;
+ }
+ input:out-of-range {
+ outline: 2px solid orange;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=535043">Mozilla Bug 535043</a>
+<p id="display"></p>
+<div id="content">
+
+</head>
+<body>
+ <input type="number" value=0 min=0 max=10> Active in-range
+ <br><br>
+ <input type="number" value=0 min=0 max=10 disabled> Disabled in-range
+ <br><br>
+ <input type="number" value=0 min=0 max=10 readonly> Read-only in-range
+ <br><br>
+ <input type="number" value=11 min=0 max=10> Active out-of-range
+ <br><br>
+ <input type="number" value=11 min=0 max=10 disabled> Disabled out-of-range
+ <br><br>
+ <input type="number" value=11 min=0 max=10 readonly> Read-only out-of-range
+</div>
+<pre id="test">
+<script>
+
+/** Test for Bug 1264157 **/
+SimpleTest.waitForFocus(function() {
+ // Check the initial values.
+ let active = [].slice.call(document.querySelectorAll("input:not(:disabled):not(:read-only)"));
+ let disabled = [].slice.call(document.querySelectorAll("input:disabled"));
+ let readonly = [].slice.call(document.querySelectorAll("input:read-only:not(:disabled)"));
+ is(active.length, 2, "Test is messed up: missing non-disabled/non-readonly inputs");
+ is(disabled.length, 2, "Test is messed up: missing disabled inputs");
+ is(readonly.length, 2, "Test is messed up: missing readonly inputs");
+
+ is(document.querySelectorAll("input:in-range").length, 1,
+ "Wrong number of in-range elements selected.");
+ is(document.querySelectorAll("input:out-of-range").length, 1,
+ "Wrong number of out-of-range elements selected.");
+
+ // Dynamically change the values to see if that works too.
+ active[0].value = -1;
+ is(document.querySelectorAll("input:in-range").length, 0,
+ "Wrong number of in-range elements selected after value changed.");
+ is(document.querySelectorAll("input:out-of-range").length, 2,
+ "Wrong number of out-of-range elements selected after value changed.");
+ active[0].value = 0;
+ is(document.querySelectorAll("input:in-range").length, 1,
+ "Wrong number of in-range elements selected after value changed back.");
+ is(document.querySelectorAll("input:out-of-range").length, 1,
+ "Wrong number of out-of-range elements selected after value changed back.");
+
+ // Dynamically change the attributes to see if that works too.
+ disabled.forEach(function(e) { e.removeAttribute("disabled"); });
+ readonly.forEach(function(e) { e.removeAttribute("readonly"); });
+ active.forEach(function(e) { e.setAttribute("readonly", true); });
+
+ is(document.querySelectorAll("input:in-range").length, 2,
+ "Wrong number of in-range elements selected after attribute changed.");
+ is(document.querySelectorAll("input:out-of-range").length, 2,
+ "Wrong number of out-of-range elements selected after attribute changed.");
+
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1279218.html b/dom/html/test/test_bug1279218.html
new file mode 100644
index 0000000000..0d8386280d
--- /dev/null
+++ b/dom/html/test/test_bug1279218.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for Bug 1279218</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript">
+ function load() {
+ let applets = document.applets;
+ is(applets.length, 0, "Applet list length should be 0, even with applet tag in body");
+ SimpleTest.finish();
+ }
+
+ window.onload=load;
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1279218">Mozilla Bug 1279218</a>
+ <applet id="applet-test"></applet>
+ </body>
+</html>
diff --git a/dom/html/test/test_bug1287321.html b/dom/html/test/test_bug1287321.html
new file mode 100644
index 0000000000..142b06d104
--- /dev/null
+++ b/dom/html/test/test_bug1287321.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1287321
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1287321</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1287321 **/
+
+ function test() {
+ var r = document.getElementById("range");
+ var rect = r.getBoundingClientRect();
+ var y = parseInt((rect.height / 2));
+ var movement = parseInt(rect.width / 10);
+ var x = movement;
+ synthesizeMouse(r, x, y, { type: "mousedown" });
+ x += movement;
+ var eventCount = 0;
+ r.oninput = function() {
+ ++eventCount;
+ }
+ synthesizeMouse(r, x, y, { type: "mousemove" });
+ is(eventCount, 1, "Got the expected input event");
+
+ x += movement;
+ synthesizeMouse(r, x, y, { type: "mousemove" });
+ is(eventCount, 2, "Got the expected input event");
+
+ synthesizeMouse(r, x, y, { type: "mousemove" });
+ is(eventCount, 2, "Got the expected input event");
+
+ x += movement;
+ synthesizeMouse(r, x, y, { type: "mousemove" });
+ is(eventCount, 3, "Got the expected input event");
+
+ synthesizeMouse(r, x, y, { type: "mouseup" });
+ is(eventCount, 3, "Got the expected input event");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(test);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1287321">Mozilla Bug 1287321</a>
+<input type="range" id="range">
+</body>
+</html>
diff --git a/dom/html/test/test_bug1292522_same_domain_with_different_port_number.html b/dom/html/test/test_bug1292522_same_domain_with_different_port_number.html
new file mode 100644
index 0000000000..b7a443f6a7
--- /dev/null
+++ b/dom/html/test/test_bug1292522_same_domain_with_different_port_number.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1292522
+If we set domain using document.domain = "...", a page and iframe must be
+treated as the same domain if they differ in port number,
+e.g. test1.example.org:8000 and test2.example.org:80 are the same domain if
+document.domain = "example.org".
+-->
+<head>
+ <title>Test for Bug 1292522</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1292522">Mozilla Bug 1292522</a>
+ <p id="display"></p>
+
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+
+ if (navigator.platform.startsWith("Linux")) {
+ SimpleTest.expectAssertions(0, 1);
+ }
+ SimpleTest.waitForExplicitFinish();
+ window.addEventListener("message", onMessageReceived);
+
+ var page;
+
+ function onMessageReceived(event)
+ {
+ is(event.data, "testiframe", "Must be able to access the variable," +
+ " because page and iframe are the " +
+ "same domain.");
+ page.close();
+ SimpleTest.finish();
+ }
+
+ page = window.open("http://test1.example.org:8000/tests/dom/html/test/bug1292522_page.html");
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/html/test/test_bug1295719_event_sequence_for_arrow_keys.html b/dom/html/test/test_bug1295719_event_sequence_for_arrow_keys.html
new file mode 100644
index 0000000000..4e622391e8
--- /dev/null
+++ b/dom/html/test/test_bug1295719_event_sequence_for_arrow_keys.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1295719
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1295719</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1295719">Mozilla Bug 1295719</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<input id="test_number" type="number" value=50>
+<input id="test_range" type="range" value=50 max=100 min=0>
+<script type="text/javascript">
+
+/** Test for Bug 1295719 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests() {
+ let number = window.document.getElementById("test_number");
+ let range = window.document.getElementById("test_range");
+ let waiting_event_sequence = ["keydown", "input", "change"];
+ let waiting_event_idx = 0;
+ waiting_event_sequence.forEach((eventType) => {
+ number.addEventListener(eventType, (event) => {
+ let waiting_event = waiting_event_sequence[waiting_event_idx];
+ is(waiting_event, eventType, "Waiting " + waiting_event + " get " + eventType);
+ // Input element will fire input and change events when handling keypress
+ // with keycode=arrows. When user press and hold the keyboard, we expect
+ // that input element repeatedly fires "keydown"(, "keypress"), "input", and
+ // "change" events until user release the keyboard. Using
+ // waiting_event_sequence as a circular buffer and reset waiting_event_idx
+ // when it point to the end of buffer.
+ waiting_event_idx = waiting_event_idx == waiting_event_sequence.length -1 ? 0 : waiting_event_idx + 1;
+ });
+ range.addEventListener(eventType, (event) => {
+ let waiting_event = waiting_event_sequence[waiting_event_idx];
+ is(waiting_event, eventType, "Waiting " + waiting_event + " get " + eventType);
+ waiting_event_idx = waiting_event_idx == waiting_event_sequence.length - 1 ? 0 : waiting_event_idx + 1;
+ });
+ });
+
+ number.focus();
+ synthesizeKey("KEY_ArrowDown", {type: "keydown"});
+ synthesizeKey("KEY_ArrowDown", {type: "keydown"});
+ synthesizeKey("KEY_ArrowDown", {type: "keyup"});
+ number.blur();
+ range.focus();
+ waiting_event_idx = 0;
+ synthesizeKey("KEY_ArrowDown", {type: "keydown"});
+ synthesizeKey("KEY_ArrowDown", {type: "keydown"});
+ synthesizeKey("KEY_ArrowDown", {type: "keyup"});
+
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1295719_event_sequence_for_number_keys.html b/dom/html/test/test_bug1295719_event_sequence_for_number_keys.html
new file mode 100644
index 0000000000..f8f0537ddb
--- /dev/null
+++ b/dom/html/test/test_bug1295719_event_sequence_for_number_keys.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1295719
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1295719</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1295719">Mozilla Bug 1295719</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<input id="test_number" type="number" value=50>
+<script type="text/javascript">
+
+/** Test for Bug 1295719 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests() {
+ let number = window.document.getElementById("test_number");
+ let waiting_event_sequence = ["keydown", "keypress", "input"];
+ let change_event_of_number = 0;
+ let keyup_event_of_number = 0;
+ let waiting_event_idx = 0;
+ waiting_event_sequence.forEach((eventType) => {
+ number.addEventListener(eventType, (event) => {
+ let waiting_event = waiting_event_sequence[waiting_event_idx];
+ is(eventType, waiting_event, "Waiting " + waiting_event + " get " + eventType);
+ // Input element will fire input event when handling keypress with
+ // keycode=numbers. When user press and hold the keyboard, we expect that
+ // input element repeatedly fires "keydown", "keypress", and "input" until
+ // user release the keyboard. Input element will fire change event when
+ // it's blurred. Using waiting_event_sequence as a circular buffer and
+ // reset waiting_event_idx when it point to the end of buffer.
+ waiting_event_idx = waiting_event_idx == waiting_event_sequence.length - 1 ? 0 : waiting_event_idx + 1;
+ });
+ });
+ number.addEventListener("change", (event) => {
+ is(keyup_event_of_number, 1, "change event should be fired after blurred");
+ ++change_event_of_number;
+ });
+ number.addEventListener("keyup", (event) => {
+ is(keyup_event_of_number, 0, "keyup event should be fired once");
+ is(change_event_of_number, 0, "keyup event should be fired before change event");
+ ++keyup_event_of_number;
+ });
+ number.focus();
+ synthesizeKey("5", {type: "keydown"});
+ synthesizeKey("5", {type: "keydown"});
+ synthesizeKey("5", {type: "keyup"});
+ is(change_event_of_number, 0, "change event shouldn't be fired when input element is focused");
+ number.blur();
+ is(change_event_of_number, 1, "change event should be fired when input element is blurred");
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1297.html b/dom/html/test/test_bug1297.html
new file mode 100644
index 0000000000..d0c96c87d4
--- /dev/null
+++ b/dom/html/test/test_bug1297.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1297
+-->
+<head>
+ <title>Test for Bug 1297</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1297">Mozilla Bug 1297</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<table border=1>
+<tr>
+<td id="td1" onmousedown="alert(this.cellIndex)">cellIndex=0</td>
+<td id="td2" onmousedown="alert(this.cellIndex)">cellIndex=1</td>
+<td id="td3" onmousedown="alert(this.cellIndex)">cellIndex=2</td>
+<tr id="tr1"
+onmousedown="alert(this.rowIndex)"><td>rowIndex=1<td>rowIndex=1<td>rowIndex=1</t
+r>
+<tr id="tr2"
+onmousedown="alert(this.rowIndex)"><td>rowIndex=2<td>rowIndex=2<td>rowIndex=2</t
+r>
+</tr>
+</table>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1297 **/
+is($('td1').cellIndex, 0, "cellIndex / rowIndex working td1");
+is($('td2').cellIndex, 1, "cellIndex / rowIndex working td2");
+is($('td3').cellIndex, 2, "cellIndex / rowIndex working td3");
+is($('tr1').rowIndex, 1, "cellIndex / rowIndex working tr1");
+is($('tr2').rowIndex, 2, "cellIndex / rowIndex working tr2");
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug1310865.html b/dom/html/test/test_bug1310865.html
new file mode 100644
index 0000000000..4dcccbfa0d
--- /dev/null
+++ b/dom/html/test/test_bug1310865.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Test for Bug 1310865</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<input value="a
+b" type="hidden">
+<input type="hidden" value="a
+b">
+<script>
+var input1 = document.querySelector("input");
+var input2 = document.querySelector("input + input");
+var clone1 = input1.cloneNode(false);
+var clone2 = input2.cloneNode(false);
+// Newlines must not be stripped
+is(clone1.value, "a\nb");
+is(clone2.value, "a\nb");
+</script>
diff --git a/dom/html/test/test_bug1315146.html b/dom/html/test/test_bug1315146.html
new file mode 100644
index 0000000000..0cf25b36bf
--- /dev/null
+++ b/dom/html/test/test_bug1315146.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1315146
+-->
+<head>
+ <title>Test for Bug 1315146</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1315146">Mozilla Bug 1315146</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1315146 **/
+
+SimpleTest.waitForExplicitFinish();
+onmessage = function(e) {
+ win.close();
+ is(e.data.start, 2, "Correct start offset expected");
+ is(e.data.end, 2, "Correct end offset expected");
+ SimpleTest.finish();
+};
+let win = window.open("http://test1.example.org/tests/dom/html/test/bug1315146-main.html", "_blank");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1322678.html b/dom/html/test/test_bug1322678.html
new file mode 100644
index 0000000000..57b43f039c
--- /dev/null
+++ b/dom/html/test/test_bug1322678.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1322678
+-->
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>Test for Bug 1322678</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript">
+
+const CUSTOM_TITLE = "Custom Title";
+
+async function openNewWindowForTest() {
+ let win = window.open("bug369370-popup.png", "bug1322678",
+ "width=400,height=300,scrollbars=no");
+ ok(win, "opened child window");
+
+ await new Promise(resolve => {
+ win.onload = function() {
+ ok(true, "window loaded");
+ resolve();
+ };
+ });
+
+ return win;
+}
+
+async function testCustomTitle(aWin, aTitle) {
+ let doc = aWin.document;
+ let elements = doc.getElementsByTagName("img");
+ is(elements.length, 1, "looking for img in ImageDocument");
+ let img = elements[0];
+
+ // Click to zoom in
+ synthesizeMouse(img, 25, 25, { }, aWin);
+ is(doc.title, aTitle, "Checking title");
+
+ // Click there again to zoom out
+ synthesizeMouse(img, 25, 25, { }, aWin);
+ is(doc.title, aTitle, "Checking title");
+
+ // Now try resizing the window so the image fits vertically and horizontally.
+ await new Promise(resolve => {
+ aWin.addEventListener("resize", function() {
+ // Give the image document time to respond
+ SimpleTest.executeSoon(function() {
+ is(doc.title, aTitle, "Checking title");
+ resolve();
+ });
+ }, {once: true});
+
+ let decorationSize = aWin.outerHeight - aWin.innerHeight;
+ aWin.resizeTo(800 + 50 + decorationSize, 600 + 50 + decorationSize);
+ });
+
+ // Now try resizing the window so the image no longer fits.
+ await new Promise(resolve => {
+ aWin.addEventListener("resize", function() {
+ // Give the image document time to respond
+ SimpleTest.executeSoon(function() {
+ is(doc.title, aTitle, "Checking title");
+ resolve();
+ });
+ }, {once: true});
+
+ aWin.resizeTo(400, 300);
+ });
+}
+
+// eslint-disable-next-line mozilla/no-addtask-setup
+add_task(async function setup() {
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["browser.enable_automatic_image_resizing", true],
+ ]});
+});
+
+add_task(async function testUpdateDocumentTitle() {
+ let win = await openNewWindowForTest();
+ // Set custom title.
+ win.document.title = CUSTOM_TITLE;
+ await testCustomTitle(win, CUSTOM_TITLE);
+ win.close();
+});
+
+add_task(async function testUpdateTitleElement() {
+ let win = await openNewWindowForTest();
+ // Set custom title.
+ let title = win.document.getElementsByTagName("title")[0];
+ title.text = CUSTOM_TITLE;
+ await testCustomTitle(win, CUSTOM_TITLE);
+ win.close();
+});
+
+add_task(async function testAppendNewTitleElement() {
+ let win = await openNewWindowForTest();
+ // Set custom title.
+ let doc = win.document;
+ doc.getElementsByTagName("title")[0].remove();
+ let title = doc.createElement("title");
+ title.text = CUSTOM_TITLE;
+ doc.head.appendChild(title);
+ await testCustomTitle(win, CUSTOM_TITLE);
+ win.close();
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1323815.html b/dom/html/test/test_bug1323815.html
new file mode 100644
index 0000000000..47e223aa7b
--- /dev/null
+++ b/dom/html/test/test_bug1323815.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1323815
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1323815</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1323815 **/
+
+SimpleTest.waitForExplicitFinish();
+function test() {
+ var n = document.getElementById("number");
+ var t = document.getElementById("text");
+ t.focus();
+ var gotBlur = false;
+ t.onblur = function(e) {
+ try {
+ is(e.relatedTarget.localName, "input");
+ } catch(ex) {
+ ok(false, "Accessing properties on the relatedTarget shouldn't throw! " + ex);
+ }
+ gotBlur = true;
+ }
+
+ n.focus();
+ ok(gotBlur);
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(test);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1323815">Mozilla Bug 1323815</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<input type="number" id="number"><input type="text" id="text">
+</body>
+</html>
diff --git a/dom/html/test/test_bug1366.html b/dom/html/test/test_bug1366.html
new file mode 100644
index 0000000000..f29179509f
--- /dev/null
+++ b/dom/html/test/test_bug1366.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1366
+-->
+<head>
+ <title>Test for Bug 1366</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1366">Mozilla Bug 1366</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<table id="testtable" width=150 border>
+ <tbody id="testbody">
+ <tr>
+ <td>cell content</td>
+ </tr>
+ </tbody>
+</table>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1366 **/
+$('testtable').removeChild($('testbody'));
+$('display').innerHTML = "SCRIPT: deleted first ROWGROUP\n";
+is($('testbody'), null, "deleting tbody works");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug1400.html b/dom/html/test/test_bug1400.html
new file mode 100644
index 0000000000..38e87a56da
--- /dev/null
+++ b/dom/html/test/test_bug1400.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1400
+-->
+<head>
+ <title>Test for Bug 1400</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1400">Mozilla Bug 1400</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1400 **/
+
+table = document.createElement("TABLE");
+thead = table.createTHead();
+thead2 = table.createTHead();
+
+table.appendChild(thead);
+table.appendChild(thead);
+table.appendChild(thead);
+table.appendChild(thead2);
+table.appendChild(thead2);
+table.appendChild(thead2);
+table.appendChild(thead);
+table.appendChild(thead2);
+
+is(table.childNodes.length, 1,
+ "adding multiple theads results in one thead child");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug1414077.html b/dom/html/test/test_bug1414077.html
new file mode 100644
index 0000000000..aa430c2737
--- /dev/null
+++ b/dom/html/test/test_bug1414077.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1414077
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 1414077</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+<script type="application/javascript">
+
+/** Test for Bug 1414077 **/
+
+add_task(async function() {
+ await SpecialPowers.pushPrefEnv({"set": [["browser.enable_automatic_image_resizing", true]]});
+
+ return new Promise(resolve => {
+ var testWin = document.querySelector("iframe");
+ testWin.src = "image.png";
+ testWin.onload = function() {
+ var testDoc = testWin.contentDocument;
+
+ // testDoc should be a image document.
+ ok(testDoc.imageIsOverflowing, "image is overflowing");
+ ok(testDoc.imageIsResized, "image is resized to fit visible area by default");
+
+ // Restore image to original size.
+ testDoc.restoreImage();
+ ok(testDoc.imageIsOverflowing, "image is overflowing");
+ ok(!testDoc.imageIsResized, "image is restored to original size");
+
+ // Resize the image to fit visible area
+ testDoc.shrinkToFit();
+ ok(testDoc.imageIsOverflowing, "image is overflowing");
+ ok(testDoc.imageIsResized, "image is resized to fit visible area");
+
+ resolve();
+ };
+ })
+});
+
+</script>
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1414077">Mozilla Bug 1414077</a>
+<iframe width="0" height="0"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/test_bug143220.html b/dom/html/test/test_bug143220.html
new file mode 100644
index 0000000000..f94ec5571e
--- /dev/null
+++ b/dom/html/test/test_bug143220.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=143220
+-->
+<head>
+ <title>Test for Bug 143220</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=143220">Mozilla Bug 143220</a>
+<p id="display">
+ <input type="file" id="i1">
+ <input type="file" id="i2">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 143220 **/
+SimpleTest.waitForExplicitFinish();
+const helperURL = SimpleTest.getTestFileURL("simpleFileOpener.js");
+const helper = SpecialPowers.loadChromeScript(helperURL);
+helper.addMessageListener("fail", function onFail(message) {
+ is(message, null, "chrome script failed");
+ SimpleTest.finish();
+});
+helper.addMessageListener("file.opened", onFileOpened);
+helper.sendAsyncMessage("file.open", "test_bug143220.txt");
+
+function onFileOpened(message) {
+ const { leafName, fullPath, domFile } = message;
+
+ function initControl1() {
+ SpecialPowers.wrap($("i1")).mozSetFileArray([domFile]);
+ }
+
+ function initControl2() {
+ SpecialPowers.wrap($("i2")).mozSetFileArray([domFile]);
+ }
+
+ // Check that we can't just set the value
+ try {
+ $("i1").value = fullPath;
+ is(0, 1, "Should have thrown exception on set!");
+ } catch(e) {
+ is($("i1").value, "", "Shouldn't have value here");
+ }
+
+ initControl1();
+ initControl2();
+
+ is($("i1").value, 'C:\\fakepath\\' + leafName, "Leaking full value?");
+ is($("i2").value, 'C:\\fakepath\\' + leafName, "Leaking full value?");
+
+ helper.addMessageListener("file.removed", onFileRemoved);
+ helper.sendAsyncMessage("file.remove", null);
+}
+
+function onFileRemoved() {
+ helper.destroy();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug1472426.html b/dom/html/test/test_bug1472426.html
new file mode 100644
index 0000000000..6f891184b8
--- /dev/null
+++ b/dom/html/test/test_bug1472426.html
@@ -0,0 +1,120 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1472426
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1472426</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1472426 **/
+
+ var shadowIframe;
+ var targetIframe;
+ var form;
+ var sr;
+
+ function checkMPSubmission(sub, expected, test) {
+ function getPropCount(o) {
+ var x, l = 0;
+ for (x in o) ++l;
+ return l;
+ }
+ function mpquote(s) {
+ return s.replace(/\r\n/g, " ")
+ .replace(/\r/g, " ")
+ .replace(/\n/g, " ")
+ .replace(/\"/g, "\\\"");
+ }
+
+ is(sub.length, expected.length,
+ "Correct number of multipart items in " + test);
+
+ if (sub.length != expected.length) {
+ alert(JSON.stringify(sub));
+ }
+
+ var i;
+ for (i = 0; i < expected.length; ++i) {
+ if (!("fileName" in expected[i])) {
+ is(sub[i].headers["Content-Disposition"],
+ "form-data; name=\"" + mpquote(expected[i].name) + "\"",
+ "Correct name in " + test);
+ is (getPropCount(sub[i].headers), 1,
+ "Wrong number of headers in " + test);
+ is(sub[i].body,
+ expected[i].value.replace(/\r\n|\r|\n/, "\r\n"),
+ "Correct value in " + test);
+ }
+ else {
+ is(sub[i].headers["Content-Disposition"],
+ "form-data; name=\"" + mpquote(expected[i].name) + "\"; filename=\"" +
+ mpquote(expected[i].fileName) + "\"",
+ "Correct name in " + test);
+ is(sub[i].headers["Content-Type"],
+ expected[i].contentType,
+ "Correct content type in " + test);
+ is (getPropCount(sub[i].headers), 2,
+ "Wrong number of headers in " + test);
+ is(sub[i].body,
+ expected[i].value,
+ "Correct value in " + test);
+ }
+ }
+ }
+
+ function testFormSubmissionInShadowDOM() {
+ targetIframe = document.getElementById("target_iframe");
+ shadowIframe = document.createElement("iframe");
+ shadowIframe.src = "about:blank";
+ shadowIframe.onload = shadowFrameCreated;
+ document.body.appendChild(shadowIframe);
+ }
+
+ function shadowFrameCreated() {
+ var doc = shadowIframe.contentDocument;
+ var body = doc.body;
+ var host = doc.createElement("div");
+ body.appendChild(host);
+ sr = host.attachShadow({ mode: "open" });
+ sr.appendChild(document.getElementById('template').content.cloneNode(true));
+ targetIframe.onload = checkSubmitValues;
+ sr.getElementById("form").submit();
+ }
+
+ function checkSubmitValues() {
+ submission = JSON.parse(targetIframe.contentDocument.documentElement.textContent);
+ var expected = [
+ { name: "text", value: "textvalue" },
+ { name: "hidden", value: "hiddenvalue" },
+ { name: "select", value: "selectvalue" },
+ { name: "textarea", value: "textareavalue" }
+ ];
+ checkMPSubmission(submission, expected, "form submission inside shadow DOM");
+ SimpleTest.finish();
+ }
+
+ window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ testFormSubmissionInShadowDOM();
+ }
+
+ </script>
+ <template id="template">
+ <form action="form_submit_server.sjs" target="target_iframe" id="form"
+ method="POST" enctype="multipart/form-data">
+ <input name="text" value="textvalue">
+ <input name="hidden" value="hiddenvalue" type="hidden">
+ <select name="select"><option selected>selectvalue</option></select>
+ <textarea name="textarea">textareavalue</textarea>
+ </form>
+ </template>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1472426">Mozilla Bug 1472426</a>
+<iframe name="target_iframe" id="target_iframe"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1682.html b/dom/html/test/test_bug1682.html
new file mode 100644
index 0000000000..8a0b7abf19
--- /dev/null
+++ b/dom/html/test/test_bug1682.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1682
+-->
+<head>
+ <title>Test for Bug 1682</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1682">Mozilla Bug 1682</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1682 **/
+var count = 1;
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function () {
+ is(count, 1, "onload executes once");
+ ++count;
+});
+addLoadEvent(SimpleTest.finish);
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug1785739.html b/dom/html/test/test_bug1785739.html
new file mode 100644
index 0000000000..2c87c57bd0
--- /dev/null
+++ b/dom/html/test/test_bug1785739.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>nsFind::Find() should initialize the editor</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<input value="1abc1
+ 2abc2
+ 3abc3
+ 4abc4
+ 5abc5
+ 6abc6
+ 7abc7
+ 8abc8
+ 9abc9" id="input">
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ // The current window.find() impl does not support text controls, so import the internal component
+ const finder =
+ SpecialPowers
+ .Cc["@mozilla.org/typeaheadfind;1"]
+ .getService(SpecialPowers.Ci.nsITypeAheadFind);
+
+ finder.init(SpecialPowers.wrap(window).docShell);
+
+ function find() {
+ return finder.find(
+ "abc",
+ false,
+ SpecialPowers.Ci.nsITypeAheadFind.FIND_NEXT,
+ true);
+ }
+
+ async function runTests() {
+ finder.find("abc", false, SpecialPowers.Ci.nsITypeAheadFind.FIND_FIRST, true);
+ // Wait until layout flush as the bug repro needs it
+ await new Promise(requestAnimationFrame);
+
+ for (let i = 0; i < 9; i++) {
+ find();
+ await new Promise(requestAnimationFrame);
+ is(input.selectionStart, (i * 19) + 1);
+ }
+
+ SimpleTest.finish();
+ }
+ window.addEventListener("load", runTests);
+</script>
diff --git a/dom/html/test/test_bug182279.html b/dom/html/test/test_bug182279.html
new file mode 100644
index 0000000000..1421c86ee0
--- /dev/null
+++ b/dom/html/test/test_bug182279.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=182279
+-->
+<head>
+ <title>Test for Bug 182279</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=182279">Moozilla Bug 182279</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 182279 **/
+var sel = document.createElement("select");
+var opt1 = new Option();
+var opt2 = new Option();
+var opt3 = new Option();
+opt1.value = 1;
+opt2.value = 2;
+opt3.value = 3;
+sel.add(opt1, null);
+sel.add(opt2, opt1);
+sel.add(opt3);
+is(sel[0], opt2, "1st item should be 2");
+is(sel[1], opt1, "2nd item should be 1");
+is(sel[2], opt3, "3rd item should be 3");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug1823.html b/dom/html/test/test_bug1823.html
new file mode 100644
index 0000000000..0f42b49980
--- /dev/null
+++ b/dom/html/test/test_bug1823.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1823
+-->
+<head>
+ <title>Test for Bug 1823</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1823">Mozilla Bug 1823</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1823 **/
+ok(!(document.location + "").includes("["), "location object has a toString()");
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug196523.html b/dom/html/test/test_bug196523.html
new file mode 100644
index 0000000000..edd71247a7
--- /dev/null
+++ b/dom/html/test/test_bug196523.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=196523
+-->
+<head>
+ <title>Test for Bug 196523</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=196523">Mozilla Bug 196523</a>
+<script>
+ var expectedMessages = 2;
+ SimpleTest.waitForExplicitFinish();
+ window.addEventListener("message", function(e) {
+ --expectedMessages;
+ var str = e.data;
+ var idx = str.indexOf(';');
+ var val = str.substring(0, idx);
+ var msg = str.substring(idx+1);
+ ok(val == "true", msg);
+ if (!expectedMessages) { SimpleTest.finish(); }
+ });
+</script>
+<p id="display">
+ <iframe src="http://test1.example.org/tests/dom/html/test/bug196523-subframe.html"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 196523 **/
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug199692.html b/dom/html/test/test_bug199692.html
new file mode 100644
index 0000000000..0be6d7ed47
--- /dev/null
+++ b/dom/html/test/test_bug199692.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=199692
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Test for Bug 199692</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ // The popup calls MochiTest methods in this window through window.opener
+ window.open("bug199692-popup.html", "bug199692", "width=600,height=600");
+ </script>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug2082.html b/dom/html/test/test_bug2082.html
new file mode 100644
index 0000000000..5c1ec8f8ec
--- /dev/null
+++ b/dom/html/test/test_bug2082.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=2082
+-->
+<head>
+ <title>Test for Bug 2082</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=2082">Mozilla Bug 2082</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<FORM name="gui" id="gui">
+<INPUT TYPE="text" NAME="field" VALUE="some value">
+</FORM>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 2082 **/
+var guiform = document.getElementById("gui");
+ok(document.getElementById("gui").hasChildNodes(), "form elements should be treated as form's children");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug209275.xhtml b/dom/html/test/test_bug209275.xhtml
new file mode 100644
index 0000000000..0cdb64ea00
--- /dev/null
+++ b/dom/html/test/test_bug209275.xhtml
@@ -0,0 +1,258 @@
+<!DOCTYPE html [
+<!ATTLIST foo:base
+ id ID #IMPLIED
+>
+]>
+<html xmlns:foo="http://foo.com" xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=209275
+-->
+<head>
+ <title>Test for Bug 209275</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style>
+ @namespace svg url("http://www.w3.org/2000/svg");
+ svg|a { fill:blue; }
+ svg|a:visited { fill:purple; }
+ </style>
+
+ <!--
+ base0 should be ignored because it's not in the XHTML namespace
+ -->
+ <foo:base id="base0" href="http://www.foo.com" />
+
+ <!--
+ baseEmpty should be ignored because it has no href and never gets one.
+ -->
+ <base id="baseEmpty" />
+
+ <!--
+ baseWrongAttrNS should be ignored because its href attribute isn't in the empty
+ namespace.
+ -->
+ <base id="baseWrongAttrNS" foo:href="http://foo.com" />
+
+ <base id="base1" />
+ <base id="base2" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=209275">Mozilla Bug 209275</a>
+<p id="display">
+</p>
+<div id="content">
+ <a href="/" id="link1">link1</a>
+ <div style="display:none">
+ <a href="/" id="link2">link2</a>
+ </div>
+ <a href="/" id="link3" style="display:none">link3</a>
+ <a href="#" id="link4">link4</a>
+ <a href="" id="colorlink">colorlink</a>
+ <a href="#" id="link5">link5</a>
+ <iframe id="iframe"></iframe>
+
+ <svg width="5cm" height="3cm" viewBox="0 0 5 3" version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+ <a xlink:href="" id="ellipselink">
+ <ellipse cx="2.5" cy="1.5" rx="2" ry="1" id="ellipse" />
+ </a>
+ </svg>
+
+</div>
+<pre id="test">
+<script type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 209275 **/
+SimpleTest.waitForExplicitFinish();
+
+function link123HrefIs(href, testNum) {
+ is($('link1').href, href, "link1 test " + testNum);
+ is($('link2').href, href, "link2 test " + testNum);
+ is($('link3').href, href, "link3 test " + testNum);
+}
+
+var gGen;
+
+function visitedDependentComputedStyle(win, elem, property) {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ return utils.getVisitedDependentComputedStyle(elem, "", property);
+}
+
+function getColor(elem) {
+ return visitedDependentComputedStyle(document.defaultView, elem, "color");
+}
+
+function getFill(elem) {
+ return visitedDependentComputedStyle(document.defaultView, elem, "fill");
+}
+
+function setXlinkHref(elem, href) {
+ elem.setAttributeNS("http://www.w3.org/1999/xlink", "href", href);
+}
+
+function continueTest() {
+ gGen.next();
+}
+
+function* run() {
+ var iframe = document.getElementById("iframe");
+ var iframeCw = iframe.contentWindow;
+
+ // First, set the visited/unvisited link/ellipse colors.
+ const unvisitedColor = "rgb(0, 0, 238)";
+ const visitedColor = "rgb(85, 26, 139)";
+ const unvisitedFill = "rgb(0, 0, 255)";
+ const visitedFill = "rgb(128, 0, 128)";
+
+ const rand = Date.now() + "-" + Math.random();
+
+ // Now we can start the tests in earnest.
+
+ var loc = location;
+ // everything from the location up to and including the final forward slash
+ var path = /(.*\/)[^\/]*/.exec(location)[1];
+
+ // Set colorlink's href so we can check that it changes colors after we
+ // change the base href.
+ $('colorlink').href = "http://example.com/" + rand;
+ setXlinkHref($("ellipselink"), "http://example.com/" + rand);
+
+ // Load http://example.com/${rand} into a new window so we can test that
+ // changing the document's base changes the visitedness of our links.
+ //
+ // cross-origin window.open'd windows don't fire load / error events, so we
+ // wait to close it until we observed the visited color.
+ let win = window.open("http://example.com/" + rand, "_blank");
+
+ // Make sure things are what as we expect them at the beginning.
+ link123HrefIs(`${location.origin}/`, 1);
+ is($('link4').href, loc + "#", "link 4 test 1");
+ is($('link5').href, loc + "#", "link 5 test 1");
+
+ // Remove link5 from the document. We're going to test that its href changes
+ // properly when we change our base.
+ var link5 = $('link5');
+ link5.remove();
+
+ $('base1').href = "http://example.com";
+
+ // Were the links' hrefs updated after the base change?
+ link123HrefIs("http://example.com/", 2);
+ is($('link4').href, "http://example.com/#", "link 4 test 2");
+ is(link5.href, "http://example.com/#", "link 5 test 2");
+
+ // Were colorlink's color and ellipse's fill updated appropriately?
+ // Because link coloring is asynchronous, we wait until it is updated (or we
+ // timeout and fail anyway).
+ while (getColor($('colorlink')) != visitedColor) {
+ requestIdleCallback(continueTest);
+ yield undefined;
+ }
+ is(getColor($('colorlink')), visitedColor,
+ "Wrong link color after base change.");
+ while (getFill($('ellipselink')) != visitedFill) {
+ requestIdleCallback(continueTest);
+ yield undefined;
+ }
+ is(getFill($('ellipselink')), visitedFill,
+ "Wrong ellipse fill after base change.");
+
+ win.close();
+
+ $('base1').href = "foo/";
+ // Should be interpreted relative to current URI (not the current base), so
+ // base should now be http://mochi.test:8888/foo/
+
+ link123HrefIs(`${location.origin}/`, 3);
+ is($('link4').href, path + "foo/#", "link 4 test 3");
+
+ // Changing base2 shouldn't affect anything, because it's not the first base
+ // tag.
+ $('base2').href = "http://example.org/bar/";
+ link123HrefIs(`${location.origin}/`, 4);
+ is($('link4').href, path + "foo/#", "link 4 test 4");
+
+ // If we unset base1's href attribute, the document's base should come from
+ // base2, whose href is http://example.org/bar/.
+ $('base1').removeAttribute("href");
+ link123HrefIs("http://example.org/", 5);
+ is($('link4').href, "http://example.org/bar/#", "link 4 test 5");
+
+ // If we remove base1, base2 should become the first base tag, and the hrefs
+ // of all the links should change accordingly.
+ $('base1').remove();
+ link123HrefIs("http://example.org/", 6);
+ is($('link4').href, "http://example.org/bar/#", "link 4 test 6");
+
+ // If we add a new base after base2, nothing should change.
+ var base3 = document.createElement("base");
+ base3.href = "http://base3.example.org/";
+ $('base2').parentNode.insertBefore(base3, $('base2').nextSibling);
+ link123HrefIs("http://example.org/", 7);
+ is($('link4').href, "http://example.org/bar/#", "link 4 test 7");
+
+ // But now if we add a new base before base 2, it should become the primary
+ // base.
+ var base4 = document.createElement("base");
+ base4.href = "http://base4.example.org/";
+ $('base2').parentNode.insertBefore(base4, $('base2'));
+ link123HrefIs("http://base4.example.org/", 8);
+ is($('link4').href, "http://base4.example.org/#", "link 4 test 8");
+
+ // Now if we remove all the base tags, the base should become the page's URI
+ // again.
+ $('base2').remove();
+ base3.remove();
+ base4.remove();
+
+ link123HrefIs(`${location.origin}/`, 9);
+ is($('link4').href, loc + "#", "link 4 test 9");
+
+ // Setting the href of base0 shouldn't do anything because it's not in the
+ // XHTML namespace.
+ $('base0').href = "http://bar.com";
+ link123HrefIs(`${location.origin}/`, 10);
+ is($('link4').href, loc + "#", "link 4 test 10");
+
+ // We load into an iframe a document with a <base href="...">, then remove
+ // the document element. Then we add an <html>, <body>, and <a>, and make
+ // sure that the <a> is resolved relative to the page's location, not its
+ // original base. We do this twice, rebuilding the document in a different
+ // way each time.
+
+ iframeCw.location = "file_bug209275_1.html";
+ yield undefined; // wait for our child to call us back.
+ is(iframeCw.document.getElementById("link").href,
+ path + "file_bug209275_1.html#",
+ "Wrong href after nuking document.");
+
+ iframeCw.location = "file_bug209275_2.html";
+ yield undefined; // wait for callback from child
+ is(iframeCw.document.getElementById("link").href,
+ `${location.origin}/`,
+ "Wrong href after nuking document second time.");
+
+ // Make sure that document.open() makes the document forget about any <base>
+ // tags it has.
+ iframeCw.location = "file_bug209275_3.html";
+ yield undefined; // wait for callback from child
+ is(iframeCw.document.getElementById("link").href,
+ "http://mochi.test:8888/",
+ "Wrong href after document.open().");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", function() {
+ gGen = run();
+ gGen.next();
+});
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug237071.html b/dom/html/test/test_bug237071.html
new file mode 100644
index 0000000000..8360c1eb86
--- /dev/null
+++ b/dom/html/test/test_bug237071.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=237071
+-->
+<head>
+ <title>Test for Bug 237071</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=237071">Mozilla Bug 237071</a>
+<p id="display"></p>
+<div id="content" >
+ <ol id="theOL" start="22">
+ <li id="foo" >should be 22</li>
+ <li id="foo23">should be 23</li>
+ </ol>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 237071 **/
+is($('theOL').start, 22, "OL start attribute mapped to .start, not just text attribute");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug242709.html b/dom/html/test/test_bug242709.html
new file mode 100644
index 0000000000..7dde04713d
--- /dev/null
+++ b/dom/html/test/test_bug242709.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=242709
+-->
+<head>
+ <title>Test for Bug 242709</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=242709">Mozilla Bug 242709</a>
+<p id="display"></p>
+<div id="content">
+<iframe src="bug242709_iframe.html" id="a"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 242709 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var submitted = function() {
+ ok(true, "Disabling button after form submission doesn't prevent submitting");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug24958.html b/dom/html/test/test_bug24958.html
new file mode 100644
index 0000000000..a6a077aefe
--- /dev/null
+++ b/dom/html/test/test_bug24958.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=24958
+-->
+<head>
+ <title>Test for Bug 24958</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <SCRIPT id="foo" TYPE="text/javascript">/*This space intentionally left blank*/</SCRIPT>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=24958">Mozilla Bug 24958</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 24958 **/
+is($("foo").text, "\/*This space intentionally left blank*\/", "HTMLScriptElement.text should return text")
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug255820.html b/dom/html/test/test_bug255820.html
new file mode 100644
index 0000000000..5de2fca7c0
--- /dev/null
+++ b/dom/html/test/test_bug255820.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=255820
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Test for Bug 255820</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=255820">Mozilla Bug 255820</a>
+<p id="display">
+ <iframe id="f1"></iframe>
+ <iframe id="f2"></iframe>
+ <iframe id="f3"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 255820 **/
+SimpleTest.waitForExplicitFinish();
+
+is(document.characterSet, "UTF-8",
+ "Unexpected character set for our document");
+
+var testsLeft = 3;
+
+function testFinished() {
+ --testsLeft;
+ if (testsLeft == 0) {
+ SimpleTest.finish();
+ }
+}
+
+function charsetTestFinished(id, doc, charsetTarget) {
+ is(doc.characterSet, charsetTarget, "Unexpected charset for subframe " + id);
+ testFinished();
+}
+
+function f3Continue() {
+ var doc = $("f3").contentDocument;
+ is(doc.defaultView.getComputedStyle(doc.body).color, "rgb(0, 180, 0)",
+ "Wrong color");
+ charsetTestFinished('f3', doc, "UTF-8");
+}
+
+function runTest() {
+ var doc = $("f1").contentDocument;
+ is(doc.characterSet, "UTF-8",
+ "Unexpected initial character set for first frame");
+ doc.open();
+ doc.write('<html></html>');
+ doc.close();
+ charsetTestFinished("f1", doc, "UTF-8");
+
+ doc = $("f2").contentDocument;
+ is(doc.characterSet, "UTF-8",
+ "Unexpected initial character set for second frame");
+ doc.open();
+ var str = '<html><head>';
+ str += '<script src="data:application/javascript,"><'+'/script>';
+ str += '<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">';
+ str += '</head><body>';
+ str += '</body></html>';
+ doc.write(str);
+ doc.close();
+ is(doc.characterSet, "UTF-8",
+ "Unexpected character set for second frame after write");
+ $("f2").
+ setAttribute("onload",
+ "charsetTestFinished('f2', this.contentDocument, 'UTF-8');");
+
+ doc = $("f3").contentDocument;
+ is(doc.characterSet, "UTF-8",
+ "Unexpected initial character set for third frame");
+ doc.open();
+ var str = '<html><head>';
+ str += '<style>body { color: rgb(255, 0, 0) }</style>';
+ str += '<link type="text/css" rel="stylesheet" href="data:text/css, body { color: rgb(0, 180, 0) }">';
+ str += '</head><body>';
+ str += '</body></html>';
+ doc.write(str);
+ doc.close();
+ is(doc.characterSet, "UTF-8",
+ "Unexpected character set for third frame after write");
+ $("f3").setAttribute("onload", "f3Continue()");
+}
+
+addLoadEvent(runTest);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug259332.html b/dom/html/test/test_bug259332.html
new file mode 100644
index 0000000000..f41f88930c
--- /dev/null
+++ b/dom/html/test/test_bug259332.html
@@ -0,0 +1,64 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=259332
+-->
+<head>
+ <title>Test for Bug 259332</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=259332">Mozilla Bug 259332</a>
+<p id="display"></p>
+<div id="content">
+ <div id="a">a
+ <div id="a">a</div>
+ <input name="a" value="a">
+ <div id="b">b</div>
+ <input name="b" value="b">
+ <div id="c">c</div>
+ </div>
+ <input name="write">
+ <input name="write">
+ <input id="write">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 259332 **/
+
+list = document.all.a;
+ok(list.length == 3, "initial a length");
+
+blist = document.all.b;
+ok(document.all.b.length == 2, "initial b length");
+document.getElementById('b').id = 'a';
+ok(document.all.b.nodeName == "INPUT", "just one b");
+
+ok(blist.length == 1, "just one b");
+ok(list.length == 4, "one more a");
+
+newDiv = document.createElement('div');
+newDiv.id = 'a';
+newDiv.innerHTML = 'a';
+list[0].appendChild(newDiv);
+ok(list.length == 5, "two more a");
+
+ok(document.all.c.textContent == 'c', "one c");
+document.all.c.id = 'a';
+ok(!document.all.c, "no c");
+ok(list.length == 6, "three more a");
+
+ok(document.all.write.length == 3, "name is write");
+
+newDiv = document.createElement('div');
+newDiv.id = 'd';
+newDiv.innerHTML = 'd';
+list[0].appendChild(newDiv);
+ok(document.all.d.textContent == 'd', "new d");
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug274626.html b/dom/html/test/test_bug274626.html
new file mode 100644
index 0000000000..6003722bef
--- /dev/null
+++ b/dom/html/test/test_bug274626.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=274626
+-->
+<head>
+ <title>Test for Bug 274626</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=274626">Mozilla Bug 274626</a>
+<br>
+
+<input id='textbox_enabled' title='hello' value='hello' />
+<input id='textbox_disabled' title='hello' value='hello' disabled/>
+
+<br>
+<input id='input_button_enabled' title='hello' value='hello' type='button' />
+<input id='input_button_disabled' title='hello' value='hello' type='button' disabled />
+
+<br>
+<input id='checkbox_enabled' title='hello' type='checkbox'>hello</input>
+<input id='checkbox_disabled' title='hello' type='checkbox' disabled >hello</input>
+
+<br>
+<button id='button_enabled' title='hello' value='hello' type='button'>test</button>
+<button id='button_disabled' title='hello' value='hello' type='button' disabled>test</button>
+
+<br>
+<textarea id='textarea_enabled' title='hello' value='hello' onclick="alert('click event');"> </textarea>
+<textarea id='textarea_disabled' title='hello' value='hello' onclick="alert('click event');" disabled></textarea>
+
+
+<br>
+<select id='select_enabled' title='hello' onclick="alert('click event');">
+ <option value='item1'>item1</option>
+ <option value='item2'>item2</option>
+</select>
+<select id='select_disabled' title='hello' onclick="alert('click event');" disabled>
+ <option value='item1'>item1</option>
+ <option value='item2'>item2</option>
+</select>
+
+<br>
+<form>
+ <fieldset id='fieldset_enabled' title='hello' onclick="alert('click event');">
+ <legend>Enabled fieldset:</legend>
+ Name: <input type='text' size='30' /><br />
+ Email: <input type='text' size='30' /><br />
+ Date of birth: <input type='text' size='10' />
+ </fieldset>
+</form>
+<form>
+ <fieldset id='fieldset_disabled' title='hello' onclick="alert('click event');" disabled>
+ <legend>Disabled fieldset:</legend>
+ Name: <input type='text' size='30' /><br />
+ Email: <input type='text' size='30' /><br />
+ Date of birth: <input type='text' size='10' />
+ </fieldset>
+</form>
+
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 274626 **/
+
+ function HandlesMouseMove(evt) {
+ evt.target.handlesMouseMove = true;
+ }
+
+ var controls=["textbox_enabled","textbox_disabled",
+ "input_button_enabled", "input_button_disabled", "checkbox_enabled",
+ "checkbox_disabled", "button_enabled", "button_disabled",
+ "textarea_enabled", "textarea_disabled", "select_enabled",
+ "select_disabled", "fieldset_enabled", "fieldset_disabled"];
+
+ for (id of controls) {
+ var ctrl = document.getElementById(id);
+ ctrl.addEventListener('mousemove', HandlesMouseMove);
+ ctrl.handlesMouseMove = false;
+ var evt = document.createEvent("MouseEvents");
+ evt.initMouseEvent("mousemove", true, true, window,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ ctrl.dispatchEvent(evt);
+
+ // Mouse move events are what causes tooltips to show up.
+ // Before this fix we would not allow mouse move events to go through
+ // which in turn did not allow tooltips to be displayed.
+ // This test will ensure that all HTML elements handle mouse move events
+ // so that tooltips can be displayed
+ ok(ctrl.handlesMouseMove, "Disabled element need mouse move for tooltips");
+ }
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug277724.html b/dom/html/test/test_bug277724.html
new file mode 100644
index 0000000000..0732a4cf9a
--- /dev/null
+++ b/dom/html/test/test_bug277724.html
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=277724
+-->
+<head>
+ <title>Test for Bug 277724</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=277724">Mozilla Bug 277724</a>
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 277724 **/
+
+var childUnloaded = false;
+
+var nodes = [
+ [ "select", HTMLSelectElement ],
+ [ "textarea", HTMLTextAreaElement ],
+ [ "text", HTMLInputElement ],
+ [ "password", HTMLInputElement ],
+ [ "checkbox", HTMLInputElement ],
+ [ "radio", HTMLInputElement ],
+ [ "image", HTMLInputElement ],
+ [ "submit", HTMLInputElement ],
+ [ "reset", HTMLInputElement ],
+ [ "button input", HTMLInputElement ],
+ [ "hidden", HTMLInputElement ],
+ [ "file", HTMLInputElement ],
+ [ "submit button", HTMLButtonElement ],
+ [ "reset button", HTMLButtonElement ],
+ [ "button", HTMLButtonElement ]
+];
+
+function soon(f) {
+ return function() { setTimeout(f, 0); }
+}
+
+function startTest(frameid) {
+ is(childUnloaded, false, "Child not unloaded yet");
+
+ var doc = $(frameid).contentDocument;
+ var win = $(frameid).contentWindow;
+ ok(doc instanceof win.Document, "doc should be a document");
+
+ for (var i = 0; i < nodes.length; ++i) {
+ var id = nodes[i][0];
+ var node = doc.getElementById(id);
+ ok(node instanceof win[nodes[i][1].name], id + " should be a " + nodes[i][1]);
+ is(node.disabled, false, "check for " + id + " state");
+ node.disabled = true;
+ is(node.disabled, true, "check for " + id + " state change");
+ }
+
+ $(frameid).onload = soon(function() { continueTest(frameid) });
+
+ // Do this off a timeout so it's not treated like a replace load.
+ function loadBlank() {
+ $(frameid).contentWindow.location = "about:blank";
+ }
+ setTimeout(loadBlank, 0);
+}
+
+function continueTest(frameid) {
+ is(childUnloaded, true, "Unload handler should have fired");
+ var doc = $(frameid).contentDocument;
+ var win = $(frameid).contentWindow;
+ ok(doc instanceof win.Document, "doc should be a document");
+
+ for (var i = 0; i < nodes.length; ++i) {
+ var id = nodes[i][0];
+ var node = doc.getElementById(id);
+ ok(node === null, id + " should be null");
+ }
+
+ $(frameid).onload = soon(function() { finishTest(frameid); });
+
+ // Do this off a timeout too. Why, I'm not sure. Something in session
+ // history creates another history state if we don't. :(
+ function goBack() {
+ $(frameid).contentWindow.history.back();
+ }
+ setTimeout(goBack, 0);
+}
+
+// XXXbz this is a nasty hack to work around the XML content sink not being
+// incremental, so that the _first_ control we test is ok but others are not.
+var testIs = is;
+var once = false;
+function flipper(a, b, c) {
+ if (once) {
+ todo(a == b, c);
+ } else {
+ once = true;
+ is(a, b, c);
+ }
+}
+
+function finishTest(frameid) {
+ var doc = $(frameid).contentDocument;
+ var win = $(frameid).contentWindow;
+ ok(doc instanceof win.Document, "doc should be a document");
+
+ for (var i = 0; i < nodes.length; ++i) {
+ var id = nodes[i][0];
+ var node = doc.getElementById(id);
+ ok(node instanceof win[nodes[i][1].name], id + " should be a " + nodes[i][1]);
+ //testIs(node.disabled, true, "check for " + id + " state restore");
+ }
+
+ if (frameid == "frame2") {
+ SimpleTest.finish();
+ } else {
+ childUnloaded = false;
+
+ // XXXbz this is a nasty hack to deal with the content sink. See above.
+ testIs = flipper;
+
+ $("frame2").onload = soon(function() { startTest("frame2"); });
+ $("frame2").src = "bug277724_iframe2.xhtml";
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+
+<!-- Don't use display:none, since we don't support framestate restoration
+ without a frame tree -->
+<div id="content" style="visibility: hidden">
+ <iframe src="bug277724_iframe1.html" id="frame1"
+ onload="setTimeout(function() { startTest('frame1') }, 0)"></iframe>
+ <iframe src="" id="frame2"></iframe>
+</div>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug277890.html b/dom/html/test/test_bug277890.html
new file mode 100644
index 0000000000..69bc820880
--- /dev/null
+++ b/dom/html/test/test_bug277890.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=277890
+-->
+<head>
+ <title>Test for Bug 277890</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=277890">Mozilla Bug 277890</a>
+<p id="display"></p>
+<div id="content">
+<iframe src="bug277890_iframe.html" id="a"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 277890 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var submitted = function() {
+ ok(true, "Disabling button after form submission doesn't prevent submitting");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug287465.html b/dom/html/test/test_bug287465.html
new file mode 100644
index 0000000000..bc6307423e
--- /dev/null
+++ b/dom/html/test/test_bug287465.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=287465
+-->
+<head>
+ <title>Test for Bug 287465</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=287465">Mozilla Bug 287465</a>
+<p id="display"></p>
+<div id="content" style="display:none">
+
+<iframe id="i1" srcdoc="<svg xmlns='http://www.w3.org/2000/svg'></svg>"></iframe>
+<object id="o1" data="object_bug287465_o1.html"></object>
+<iframe id="i2" srcdoc="<html></html>"></iframe>
+<object id="o2" data="object_bug287465_o2.html"></object>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+function doTest() {
+ function checkSVGDocument(id) {
+ var e = document.getElementById(id);
+ ok(e.contentDocument != null, "check nonnull contentDocument '" + id + "'");
+ is(e.contentDocument, e.getSVGDocument(), "check documents match '" + id + "'");
+ }
+
+ checkSVGDocument("o1");
+ checkSVGDocument("i1");
+ checkSVGDocument("o2");
+ checkSVGDocument("i2");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug295561.html b/dom/html/test/test_bug295561.html
new file mode 100644
index 0000000000..456985f731
--- /dev/null
+++ b/dom/html/test/test_bug295561.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=295561
+-->
+<head>
+ <title>Test for Bug 295561</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=295561">Mozilla Bug 295561</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<table id="testTable">
+<thead>
+<tr id="headRow"><td></td></tr>
+</thead>
+<tfoot>
+<tr id="footRow"><td></td></tr>
+</tfoot>
+<tbody id="tBody" name="namedTBody">
+<tr id="trow" name="namedTRow">
+<td id="tcell" name="namedTCell"></td>
+<th id="tcellh" name="namedTH"></th>
+</tr>
+<tr><td></td></tr>
+</tbody>
+<tbody id="tBody2" name="namedTBody2">
+<tr id="trow2" name="namedTRow2">
+<td id="tcell2" name="namedTCell2"></td>
+<th id="tcellh2" name="namedTH2"></th>
+</tr>
+</table>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function testItById(id, collection, collectionName) {
+ is(collection[id], $(id),
+ "Should be able to get by id '" + id + "' from " + collectionName +
+ " collection using square brackets.")
+ is(collection.namedItem(id), $(id),
+ "Should be able to get by id '" + id + "' from " + collectionName +
+ " collection using namedItem.")
+}
+
+function testItByName(name, id, collection, collectionName) {
+ is(collection[name], $(id),
+ "Should be able to get by name '" + name + "' from " + collectionName +
+ " collection using square brackets.")
+ is(collection.namedItem(name), $(id),
+ "Should be able to get by name '" + name + "' from " + collectionName +
+ " collection using namedItem.")
+}
+
+function testIt(name, id, collection, collectionName) {
+ testItByName(name, id, collection, collectionName);
+ testItById(id, collection, collectionName);
+}
+
+var table = $("testTable")
+testIt("namedTBody", "tBody", table.tBodies, "tBodies")
+testIt("namedTRow", "trow", table.rows, "table rows")
+testIt("namedTRow", "trow", $("tBody").rows, "tbody rows")
+testIt("namedTCell", "tcell", $("trow").cells, "cells")
+testIt("namedTH", "tcellh", $("trow").cells, "cells")
+testIt("namedTBody2", "tBody2", table.tBodies, "tBodies")
+testIt("namedTRow2", "trow2", table.rows, "table rows")
+testIt("namedTRow2", "trow2", $("tBody2").rows, "tbody rows")
+testIt("namedTCell2", "tcell2", $("trow2").cells, "cells")
+testIt("namedTH2", "tcellh2", $("trow2").cells, "cells")
+is(table.tBodies.length, 2, "Incorrect tBodies length");
+is(table.rows.length, 5, "Incorrect rows length");
+is(table.rows[0], $("headRow"), "THead row in wrong spot");
+is(table.rows[1], $("trow"), "First tbody row in wrong spot");
+is(table.rows[3], $("trow2"), "Second tbody row in wrong spot");
+is(table.rows[4], $("footRow"), "TFoot row in wrong spot");
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug297761.html b/dom/html/test/test_bug297761.html
new file mode 100644
index 0000000000..0d4532827d
--- /dev/null
+++ b/dom/html/test/test_bug297761.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=297761
+-->
+<head>
+ <title>Test for Bug 297761</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=297761">Mozilla Bug 297761</a>
+<p id="display"></p>
+<div id="content">
+ <iframe src="file_bug297761.html"></iframe>
+ <iframe src="file_bug297761.html"></iframe>
+ <iframe src="file_bug297761.html"></iframe>
+ <iframe src="file_bug297761.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 297761 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var nbTests = 4;
+var curTest = 0;
+
+function nextTest()
+{
+ if (curTest == 3) {
+ frames[curTest].document.forms[0].submit();
+ } else {
+ var el = null;
+ if (curTest == 2) {
+ el = frames[curTest].document.getElementById('i');
+ } else {
+ el = frames[curTest].document.forms[0].elements[curTest];
+ }
+
+ el.focus();
+ el.click();
+ }
+}
+
+function frameLoaded(aFrame)
+{
+ var documentLocation = location.href.replace(/\.html.*/, "\.html");
+ is(aFrame.contentWindow.location.href.replace(/\?x=0&y=0/, "?"),
+ documentLocation.replace(/test_bug/, "file_bug") + "?",
+ "form should have been submitted to the document location");
+
+ if (++curTest == nbTests) {
+ SimpleTest.finish();
+ } else {
+ nextTest();
+ }
+}
+
+function runTest()
+{
+ // Initialize event handlers.
+ var frames = document.getElementsByTagName('iframe');
+ for (var i=0; i<nbTests; ++i) {
+ frames[i].setAttribute('onload', "frameLoaded(this);");
+ }
+
+ nextTest();
+}
+
+addLoadEvent(runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug300691-1.html b/dom/html/test/test_bug300691-1.html
new file mode 100644
index 0000000000..44418e8f3a
--- /dev/null
+++ b/dom/html/test/test_bug300691-1.html
@@ -0,0 +1,126 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=300691
+-->
+<head>
+ <title>Test for Bug 300691</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=300691">Mozilla Bug 300691</a>
+<p id="display">
+ <textarea id="target"></textarea>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var t = $("target");
+
+// FIXME(bug 1838346): This shouldn't need to be async probably?
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+
+/** Test for Bug 300691 **/
+function valueIs(arg, reason) {
+ is(t.value, arg, reason);
+}
+
+function defValueIs(arg, reason) {
+ is(t.defaultValue, arg, reason);
+}
+
+valueIs("", "Nothing in the textarea");
+defValueIs("", "Nothing in the textarea 2");
+
+t.appendChild(document.createTextNode("ab"));
+valueIs("ab", "Appended textnode");
+defValueIs("ab", "Appended textnode 2");
+
+t.firstChild.data = "abcd";
+valueIs("abcd", "Modified textnode text");
+defValueIs("abcd", "Modified textnode text 2");
+
+t.appendChild(document.createTextNode("efgh"));
+valueIs("abcdefgh", "Appended another textnode");
+defValueIs("abcdefgh", "Appended another textnode 2");
+
+t.removeChild(t.lastChild);
+valueIs("abcd", "Removed textnode");
+defValueIs("abcd", "Removed textnode 2");
+
+t.appendChild(document.createTextNode("efgh"));
+valueIs("abcdefgh", "Appended yet another textnode");
+defValueIs("abcdefgh", "Appended yet another textnode 2");
+
+t.normalize();
+valueIs("abcdefgh", "Normalization changes nothing for the value");
+defValueIs("abcdefgh", "Normalization changes nothing for the value 2");
+
+t.defaultValue = "abc";
+valueIs("abc", "Just set the default value on non-edited textarea");
+defValueIs("abc", "Just set the default value on non-edited textarea 2");
+
+t.appendChild(document.createTextNode("defgh"));
+valueIs("abcdefgh", "Appended another textnode again");
+defValueIs("abcdefgh", "Appended another textnode again 2");
+
+t.focus(); // This puts the caret at the end of the textarea, and doing
+ // something like "home" in a cross-platform way is kinda hard.
+sendKey("left");
+sendKey("left");
+sendKey("left");
+sendString("Test");
+
+valueIs("abcdeTestfgh", "Typed 'Test' after three left-arrows starting from end");
+defValueIs("abcdefgh", "Typing 'Test' shouldn't affect default value");
+
+sendKey("right");
+sendKey("right");
+sendKey("back_space");
+sendKey("back_space");
+
+valueIs("abcdeTesth",
+ "Backspaced twice after two right-arrows starting from end of typing");
+defValueIs("abcdefgh", "Deleting shouldn't affect default value");
+
+t.appendChild(document.createTextNode("ijk"));
+valueIs("abcdeTesth",
+ "Appending textnode shouldn't affect value in edited textarea");
+defValueIs("abcdefghijk", "Appended textnode 3");
+
+t.lastChild.data = "lmno";
+valueIs("abcdeTesth",
+ "Modifying textnode text shouldn't affect value in edited textarea");
+defValueIs("abcdefghlmno", "Modified textnode text 3");
+
+t.firstChild.remove();
+valueIs("abcdeTesth",
+ "Removing child textnode shouldn't affect value in edited textarea");
+defValueIs("defghlmno", "Removed textnode 3");
+
+t.insertBefore(document.createTextNode("abc"), t.firstChild);
+valueIs("abcdeTesth",
+ "Inserting child textnode shouldn't affect value in edited textarea");
+defValueIs("abcdefghlmno", "Inserted a text node");
+
+t.normalize();
+valueIs("abcdeTesth", "Normalization changes nothing for the value 3");
+defValueIs("abcdefghlmno", "Normalization changes nothing for the value 4");
+
+t.defaultValue = "abc";
+valueIs("abcdeTesth", "Setting default value shouldn't affect edited textarea");
+defValueIs("abc", "Just set the default value textarea");
+SimpleTest.finish();
+
+};
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug300691-2.html b/dom/html/test/test_bug300691-2.html
new file mode 100644
index 0000000000..0dbc5be79a
--- /dev/null
+++ b/dom/html/test/test_bug300691-2.html
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=300691
+-->
+<head>
+ <title>Test for Bug 300691</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=300691">Mozilla Bug 300691</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript">
+ // First, setup. We'll be toggling these variables as we go.
+ var test1Ran = false;
+ var test2Ran = false;
+ var test3Ran = false;
+ var test4Ran = false;
+ var test5Ran = false;
+ var test6Ran = false;
+ var test7Ran = false;
+ var test8Ran = false;
+ var test9Ran = false;
+ var test10Ran = false;
+ var test11Ran = false;
+ var test12Ran = false;
+ var test13Ran = false;
+ var test14aRan = false;
+ var test14bRan = false;
+ var test15aRan = false;
+ var test15bRan = false;
+</script>
+<script id="test1" type="text/javascript">test1Ran = true;</script>
+<script id="test2" type="text/javascript"></script>
+<script id="test3" type="text/javascript">;</script>
+<script id="test4" type="text/javascript"> </script>
+<script id="test5" type="text/javascript"></script>
+<script id="test6" type="text/javascript"></script>
+<script id="test7" type="text/javascript"></script>
+<script id="test8" type="text/javascript"></script>
+<script id="test9" type="text/javascript"></script>
+<script id="test10" type="text/javascript" src="data:text/javascript,">
+ test10Ran = true;
+</script>
+<script id="test11" type="text/javascript"
+ src="data:text/javascript,test11Ran = true">
+ test11Ran = false;
+</script>
+<script id="test12" type="text/javascript"></script>
+<script id="test13" type="text/javascript"></script>
+<script id="test14" type="text/javascript"></script>
+<script id="test15" type="text/javascript"></script>
+<script class="testbody" type="text/javascript">
+ /** Test for Bug 300691 **/
+ $("test2").appendChild(document.createTextNode("test2Ran = true"));
+ is(test2Ran, true, "Should be 2!");
+
+ $("test3").appendChild(document.createTextNode("test3Ran = true"));
+ is(test3Ran, false, "Should have run already 3!");
+
+ $("test4").appendChild(document.createTextNode("test4Ran = true"));
+ is(test4Ran, false, "Should have run already 4!");
+
+ $("test5").appendChild(document.createTextNode(" "));
+ $("test5").appendChild(document.createTextNode("test5Ran = true"));
+ is(test5Ran, false, "Should have run already 5!");
+
+ $("test6").appendChild(document.createTextNode(" "));
+
+ $("test7").appendChild(document.createTextNode(""));
+
+ $("test8").appendChild(document.createTextNode(""));
+
+ $("test9").appendChild(document.createTextNode(""));
+
+ $("test12").src = "data:text/javascript,test12Ran = true;";
+ is(test12Ran, false, "Not yet 12!");
+
+ $("test13").setAttribute("src", "data:text/javascript,test13Ran = true;");
+ is(test13Ran, false, "Not yet 13!");
+
+ $("test14").src = "data:text/javascript,test14aRan = true;";
+ $("test14").appendChild(document.createTextNode("test14bRan = true"));
+ is(test14aRan, false, "Not yet 14a!");
+ is(test14bRan, false, "Not yet 14b!");
+
+ $("test15").src = "data:text/javascript,test15aRan = true;";
+ $("test15").appendChild(document.createTextNode("test15bRan = true"));
+ $("test15").removeAttribute("src");
+ is(test15aRan, false, "Not yet 15a!");
+ is(test15bRan, false, "Not yet 15b!");
+</script>
+<script type="text/javascript">
+ // Follow up on some of those
+ $("test6").appendChild(document.createTextNode("test6Ran = true"));
+ is(test6Ran, false, "Should have run already 6!");
+
+ $("test7").appendChild(document.createTextNode("test7Ran = true"));
+ is(test7Ran, true, "Should be 7!");
+
+ $("test8").insertBefore(document.createTextNode("test8Ran = true"),
+ $("test8").firstChild);
+ is(test8Ran, true, "Should be 8!");
+
+ $("test9").firstChild.data = "test9Ran = true";
+ is(test9Ran, true, "Should be 9!");
+</script>
+<script type="text/javascript">
+function done() {
+ is(test1Ran, true, "Should have run!");
+ is(test3Ran, false, "Already executed test3 script once");
+ is(test4Ran, false,
+ "Should have executed whitespace-only script already");
+ is(test5Ran, false,
+ "Should have executed once the whitespace node was added");
+ is(test6Ran, false,
+ "Should have executed once the whitespace node was added 2");
+ is(test10Ran, false, "Has an src; inline part shouldn't run");
+ is(test11Ran, true, "Script with src should have run");
+ is(test12Ran, true, "Setting src should execute script");
+ is(test13Ran, true, "Setting src attribute should execute script");
+ is(test14aRan, true, "src attribute takes precedence over inline content");
+ is(test14bRan, false, "src attribute takes precedence over inline content 2");
+ is(test15aRan, true,
+ "src attribute load should have started before the attribute got removed");
+ is(test15bRan, false,
+ "src attribute still got executed, so this shouldn't have been");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(done);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug300691-3.xhtml b/dom/html/test/test_bug300691-3.xhtml
new file mode 100644
index 0000000000..788ca2160d
--- /dev/null
+++ b/dom/html/test/test_bug300691-3.xhtml
@@ -0,0 +1,48 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=300691
+-->
+<head>
+ <title>Test for Bug 300691</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=300691">Mozilla Bug 300691</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="text/javascript">
+ // First, setup. We'll be toggling these variables as we go.
+ // Note that scripts don't execute immediately when you put text in them --
+ // they wait for the currently-running script to finish.
+ var test1Ran = false;
+ var test2Ran = false;
+ var test3Ran = false;
+</script>
+<script id="test1" type="text/javascript">test1Ran = true;</script>
+<script id="test2" type="text/javascript"></script>
+<script id="test3" type="text/javascript"></script>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+ /** Test for Bug 300691 **/
+ $("test2").appendChild(document.createCDATASection("test2Ran = true"));
+ is(test2Ran, true, "Should be 2!");
+
+ $("test3").appendChild(document.createCDATASection(""));
+]]>
+</script>
+<script type="text/javascript">
+ // Follow up on some of those
+ $("test3").firstChild.data = "test3Ran = true";
+ is(test3Ran, true, "Should be 3!");
+</script>
+<script type="text/javascript">
+ is(test1Ran, true, "Should have run!");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug311681.html b/dom/html/test/test_bug311681.html
new file mode 100644
index 0000000000..7c74f7664b
--- /dev/null
+++ b/dom/html/test/test_bug311681.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=311681
+-->
+<head>
+ <title>Test for Bug 311681</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=311681">Mozilla Bug 311681</a>
+<script class="testbody" type="text/javascript">
+ // Setup script
+ SimpleTest.waitForExplicitFinish();
+
+ // Make sure to trigger the hashtable case by asking for enough elements
+ // by ID.
+ for (var i = 0; i < 256; ++i) {
+ var x = document.getElementById(i);
+ }
+
+ // save off the document.getElementById function, since getting it as a
+ // property off the document it causes a content flush.
+ var fun = document.getElementById;
+
+ // Slot for our initial element with id "content"
+ var testNode;
+
+ function getCont() {
+ return fun.call(document, "content");
+ }
+
+ function testClone() {
+ // Test to make sure that if we have multiple nodes with the same ID in
+ // a document we don't forget about one of them when the other is
+ // removed.
+ var newParent = $("display");
+ var node = testNode.cloneNode(true);
+ isnot(node, testNode, "Clone should be a different node");
+
+ newParent.appendChild(node);
+
+ // Check what getElementById returns, no flushing
+ is(getCont(), node, "Should be getting new node pre-flush 1");
+
+ // Trigger a layout flush, just in case.
+ var itemHeight = newParent.offsetHeight/10;
+
+ // Check what getElementById returns now.
+ is(getCont(), node, "Should be getting new node post-flush 1");
+
+ clear(newParent);
+
+ // Check what getElementById returns, no flushing
+ is(getCont(), testNode, "Should be getting orig node pre-flush 2");
+
+ // Trigger a layout flush, just in case.
+ var itemHeight = newParent.offsetHeight/10;
+
+ // Check what getElementById returns now.
+ is(getCont(), testNode, "Should be getting orig node post-flush 2");
+
+ node = testNode.cloneNode(true);
+ newParent.appendChild(node);
+ testNode.remove();
+
+ // Check what getElementById returns, no flushing
+ is(getCont(), node, "Should be getting clone pre-flush");
+
+ // Trigger a layout flush, just in case.
+ var itemHeight = newParent.offsetHeight/10;
+
+ // Check what getElementById returns now.
+ is(getCont(), node, "Should be getting clone post-flush");
+
+ }
+
+ function clear(node) {
+ while (node.hasChildNodes()) {
+ node.firstChild.remove();
+ }
+ }
+
+ addLoadEvent(testClone);
+ addLoadEvent(SimpleTest.finish);
+</script>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <script class="testbody" type="text/javascript">
+ testNode = fun.call(document, "content");
+ isnot(testNode, null, "Should have node here");
+ </script>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug311681.xhtml b/dom/html/test/test_bug311681.xhtml
new file mode 100644
index 0000000000..15019fa644
--- /dev/null
+++ b/dom/html/test/test_bug311681.xhtml
@@ -0,0 +1,102 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=311681
+-->
+<head>
+ <title>Test for Bug 311681</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=311681">Mozilla Bug 311681</a>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+ // Setup script
+ SimpleTest.waitForExplicitFinish();
+
+ // Make sure to trigger the hashtable case by asking for enough elements
+ // by ID.
+ for (var i = 0; i < 256; ++i) {
+ var x = document.getElementById(i);
+ }
+
+ // save off the document.getElementById function, since getting it as a
+ // property off the document it causes a content flush.
+ var fun = document.getElementById;
+
+ // Slot for our initial element with id "content"
+ var testNode;
+
+ function getCont() {
+ return fun.call(document, "content");
+ }
+
+ function testClone() {
+ // Test to make sure that if we have multiple nodes with the same ID in
+ // a document we don't forget about one of them when the other is
+ // removed.
+ var newParent = $("display");
+ var node = testNode.cloneNode(true);
+ isnot(node, testNode, "Clone should be a different node");
+
+ newParent.appendChild(node);
+
+ // Check what getElementById returns, no flushing
+ is(getCont(), node, "Should be getting new node pre-flush 1");
+
+ // Trigger a layout flush, just in case.
+ var itemHeight = newParent.offsetHeight/10;
+
+ // Check what getElementById returns now.
+ is(getCont(), node, "Should be getting new node post-flush 1");
+
+ clear(newParent);
+
+ // Check what getElementById returns, no flushing
+ is(getCont(), testNode, "Should be getting orig node pre-flush 2");
+
+ // Trigger a layout flush, just in case.
+ var itemHeight = newParent.offsetHeight/10;
+
+ // Check what getElementById returns now.
+ is(getCont(), testNode, "Should be getting orig node post-flush 2");
+
+ node = testNode.cloneNode(true);
+ newParent.appendChild(node);
+ testNode.remove();
+
+ // Check what getElementById returns, no flushing
+ is(getCont(), node, "Should be getting clone pre-flush");
+
+ // Trigger a layout flush, just in case.
+ var itemHeight = newParent.offsetHeight/10;
+
+ // Check what getElementById returns now.
+ is(getCont(), node, "Should be getting clone post-flush");
+
+ }
+
+ function clear(node) {
+ while (node.hasChildNodes()) {
+ node.firstChild.remove();
+ }
+ }
+
+ addLoadEvent(testClone);
+ addLoadEvent(SimpleTest.finish);
+]]>
+</script>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <script class="testbody" type="text/javascript">
+ <![CDATA[
+ testNode = fun.call(document, "content");
+ ok(testNode != null, "Should have node here");
+ ]]>
+ </script>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug324378.html b/dom/html/test/test_bug324378.html
new file mode 100644
index 0000000000..8bab3feaf4
--- /dev/null
+++ b/dom/html/test/test_bug324378.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html id="a" id="b">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=324378
+-->
+<head id="c" id="d">
+ <head id="j" foo="k" foo="l">
+ <title>Test for Bug 324378</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body id="e" id="f">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=324378">Mozilla Bug 324378</a>
+<script>
+ var html = document.documentElement;
+ is(document.getElementsByTagName("html").length, 1,
+ "Unexpected number of htmls");
+ is(document.getElementsByTagName("html")[0], html,
+ "Unexpected <html> element");
+ is(document.getElementsByTagName("head").length, 1,
+ "Unexpected number of heads");
+ is(html.getElementsByTagName("head").length, 1,
+ "Unexpected number of heads in <html>");
+ is(document.getElementsByTagName("body").length, 1,
+ "Unexpected number of bodies");
+ is(html.getElementsByTagName("body").length, 1,
+ "Unexpected number of bodies in <html>");
+ var head = document.getElementsByTagName("head")[0];
+ var body = document.getElementsByTagName("body")[0];
+</script>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <html id="g" foo="h" foo="i">
+ <body id="m" foo="n" foo="o">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 324378 **/
+ is(document.getElementsByTagName("html").length, 1,
+ "Unexpected number of htmls after additions");
+ is(document.getElementsByTagName("html")[0], html,
+ "Unexpected <html> element");
+ is(document.documentElement, html,
+ "Unexpected root node");
+ is(document.getElementsByTagName("head").length, 1,
+ "Unexpected number of heads after additions");
+ is(document.getElementsByTagName("head")[0], head,
+ "Unexpected <head> element");
+ is(document.getElementsByTagName("body").length, 1,
+ "Unexpected number of bodies after additions");
+ is(document.getElementsByTagName("body")[0], body,
+ "Unexpected <body> element");
+
+ is(html.id, "a", "Unexpected <html> id");
+ is(head.id, "c", "Unexpected <head> id");
+ is(body.id, "e", "Unexpected <body> id");
+ is($("a"), html, "Unexpected node with id=a");
+ is($("b"), null, "Unexpected node with id=b");
+ is($("c"), head, "Unexpected node with id=c");
+ is($("d"), null, "Unexpected node with id=d");
+ is($("e"), body, "Unexpected node with id=e");
+ is($("f"), null, "Unexpected node with id=f");
+ is($("g"), null, "Unexpected node with id=g");
+ is($("j"), null, "Unexpected node with id=j");
+ is($("m"), null, "Unexpected node with id=m");
+
+ is(html.getAttribute("foo"), "h", "Unexpected 'foo' value on <html>");
+ is(head.getAttribute("foo"), null, "Unexpected 'foo' value on <head>");
+ is(body.getAttribute("foo"), "n", "Unexpected 'foo' value on <body>");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug330705-1.html b/dom/html/test/test_bug330705-1.html
new file mode 100644
index 0000000000..64b6e89b29
--- /dev/null
+++ b/dom/html/test/test_bug330705-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=330705
+-->
+<head>
+ <title>Test for Bug 330705</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script>
+ /* variable is true if the element is focused, false otherwise */
+ var inputFocused = false;
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=330705">Mozilla Bug 330705</a>
+<p id="display">
+ <input onfocus="inputFocused = true" onblur="inputFocused = false" type="text">
+ <button></button>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 330705 **/
+ SimpleTest.waitForExplicitFinish();
+ var isFocused = false;
+
+ function onLoad() {
+ document.getElementsByTagName('input')[0].focus();
+ document.getElementsByTagName('button')[0].blur();
+ ok(inputFocused == true, "the input element is still focused after blur() has been called on the unfocused element");
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(onLoad);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug332246.html b/dom/html/test/test_bug332246.html
new file mode 100644
index 0000000000..e4fbd20bec
--- /dev/null
+++ b/dom/html/test/test_bug332246.html
@@ -0,0 +1,75 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=332246
+-->
+<head>
+ <title>Test for Bug 332246 - scrollIntoView(false) doesn't work correctly for inline elements that wrap at multiple lines</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=332246">Mozilla Bug 332246</a>
+<p id="display"></p>
+<div id="content">
+
+<div id="a1" style="height: 100px; width: 100px; overflow: hidden; outline:1px dotted black;">
+<div style="height: 100px"></div>
+<a id="a2" href="#" style="display:block; background:yellow; height:200px;">Top</a>
+<div style="height: 100px"></div>
+</div>
+
+<div id="b1" style="height: 100px; width: 100px; overflow: hidden; outline:1px dotted black;">
+<div style="height: 100px"></div>
+<div id="b2" href="#" style="border:10px solid black; background:yellow; height:200px;"></div>
+<div style="height: 100px"></div>
+</div>
+
+<br>
+
+<div id="c1" style="height: 100px; width: 100px; overflow: hidden; position: relative; outline:1px dotted black;">
+<div id="c2" style="border: 10px solid black; height: 200px; width: 50px; position: absolute; top: 100px;"></div>
+<div style="height: 100px"></div>
+</div>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 332246 **/
+
+function isWithFuzz(itIs, itShouldBe, fuzz, description) {
+ ok(Math.abs(itIs - itShouldBe) <= fuzz, `${description} - expected a value between ${itShouldBe - fuzz} and ${itShouldBe + fuzz}, got ${itIs}`);
+}
+
+var a1 = document.getElementById('a1');
+var a2 = document.getElementById('a2');
+isWithFuzz(a1.scrollHeight, 400, 1, "Wrong a1.scrollHeight");
+is(a1.offsetHeight, 100, "Wrong a1.offsetHeight");
+a2.scrollIntoView(true);
+is(a1.scrollTop, 100, "Wrong scrollTop value after a2.scrollIntoView(true)");
+a2.scrollIntoView(false);
+is(a1.scrollTop, 200, "Wrong scrollTop value after a2.scrollIntoView(false)");
+
+var b1 = document.getElementById('b1');
+var b2 = document.getElementById('b2');
+isWithFuzz(b1.scrollHeight, 420, 1, "Wrong b1.scrollHeight");
+is(b1.offsetHeight, 100, "Wrong b1.offsetHeight");
+b2.scrollIntoView(true);
+is(b1.scrollTop, 100, "Wrong scrollTop value after b2.scrollIntoView(true)");
+b2.scrollIntoView(false);
+is(b1.scrollTop, 220, "Wrong scrollTop value after b2.scrollIntoView(false)");
+
+var c1 = document.getElementById('c1');
+var c2 = document.getElementById('c2');
+isWithFuzz(c1.scrollHeight, 320, 1, "Wrong c1.scrollHeight");
+is(c1.offsetHeight, 100, "Wrong c1.offsetHeight");
+c2.scrollIntoView(true);
+is(c1.scrollTop, 100, "Wrong scrollTop value after c2.scrollIntoView(true)");
+c2.scrollIntoView(false);
+isWithFuzz(c1.scrollTop, 220, 1, "Wrong scrollTop value after c2.scrollIntoView(false)");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug332848.xhtml b/dom/html/test/test_bug332848.xhtml
new file mode 100644
index 0000000000..a7a1950125
--- /dev/null
+++ b/dom/html/test/test_bug332848.xhtml
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=332848
+-->
+<head>
+ <title>Test for Bug 332848</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=332848">Mozilla Bug 332848</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 332848 **/
+
+// parseChecker will become true if we keep parsing after calling close().
+var parseChecker = false;
+
+function test() {
+ try {
+ document.open();
+ is(0, 1, "document.open succeeded");
+ } catch (e) {
+ is (e.name, "InvalidStateError",
+ "Wrong exception from document.open");
+ is (e.code, DOMException.INVALID_STATE_ERR,
+ "Wrong exception from document.open");
+ }
+
+ try {
+ document.write("aaa");
+ is(0, 1, "document.write succeeded");
+ } catch (e) {
+ is (e.name, "InvalidStateError",
+ "Wrong exception from document.write");
+ is (e.code, DOMException.INVALID_STATE_ERR,
+ "Wrong exception from document.write");
+ }
+
+ try {
+ document.writeln("aaa");
+ is(0, 1, "document.write succeeded");
+ } catch (e) {
+ is (e.name, "InvalidStateError",
+ "Wrong exception from document.write");
+ is (e.code, DOMException.INVALID_STATE_ERR,
+ "Wrong exception from document.write");
+ }
+
+ try {
+ document.close();
+ is(0, 1, "document.close succeeded");
+ } catch (e) {
+ is (e.name, "InvalidStateError",
+ "Wrong exception from document.close");
+ is (e.code, DOMException.INVALID_STATE_ERR,
+ "Wrong exception from document.close");
+ }
+}
+
+function loadTest() {
+ is(parseChecker, true, "Parsing stopped");
+ test();
+ SimpleTest.finish();
+}
+
+window.onload = loadTest;
+
+SimpleTest.waitForExplicitFinish();
+
+test();
+]]>
+</script>
+<script>
+ parseChecker = true;
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug332893-1.html b/dom/html/test/test_bug332893-1.html
new file mode 100644
index 0000000000..552da95c28
--- /dev/null
+++ b/dom/html/test/test_bug332893-1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+<form id="form1">
+ <input id="F1I1" type="input" value="11"/>
+ <input id="F1I2" type="input" value="12"/>
+</form>
+<form id="form2">
+ <input id="F2I1" type="input" value="21"/>
+ <input id="F2I2" type="input" value="22"/>
+</form>
+<script>
+<!-- Create a new input, add it to the first form, move it to the 2nd form, then move it back to the first -->
+ var form1 = document.getElementById("form1");
+ var form2 = document.getElementById("form2");
+ var newInput = document.createElement("input");
+ newInput.value = "13";
+ form1.insertBefore(newInput, form1.firstChild);
+ var F2I2 = document.getElementById("F2I2");
+ form2.insertBefore(newInput, F2I2);
+ form1.insertBefore(newInput, form1.firstChild);
+
+ is(form1.elements.length, 3, "Form 1 has the correct length");
+ is(form1.elements[0].value, "13", "Form 1 element 1 is correct");
+ is(form1.elements[1].value, "11", "Form 1 element 2 is correct");
+ is(form1.elements[2].value, "12", "Form 1 element 3 is correct");
+
+ is(form2.elements.length, 2, "Form 2 has the correct length");
+ is(form2.elements[0].value, "21", "Form 2 element 1 is correct");
+ is(form2.elements[1].value, "22", "Form 2 element 2 is correct");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug332893-2.html b/dom/html/test/test_bug332893-2.html
new file mode 100644
index 0000000000..d24b746566
--- /dev/null
+++ b/dom/html/test/test_bug332893-2.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+
+
+<form id="form1">
+ <table>
+ <tbody id="table1">
+ <tr id="F1I0"><td><input form='form1' type="input" value="10"/></td></tr>
+ <tr id="F1I1"><td><input type="input" value="11"/></td></tr>
+ <tr id="F1I2"><td><input type="input" value="12"/></td></tr>
+ </tbody>
+ </table>
+</form>
+<form id="form2">
+ <table>
+ <tbody id="table2">
+ <tr id="F2I1"><td><input type="input" value="21"/></td></tr>
+ <tr id="F2I2"><td><input type="input" value="22"/></td></tr>
+ </tbody>
+ </table>
+</form>
+
+<script>
+ var table1 = document.getElementById("table1");
+ var F1I0 = table1.getElementsByTagName("tr")[0];
+ var F1I1 = table1.getElementsByTagName("tr")[1];
+ table1.removeChild(F1I0);
+ table1.removeChild(F1I1);
+
+ var table2 = document.getElementById("table2");
+ table2.insertBefore(F1I0, table2.firstChild);
+ table2.insertBefore(F1I1, table2.firstChild);
+
+ var form1 = document.getElementById("form1");
+ var form2 = document.getElementById("form2");
+
+ is(form1.elements.length, 2, "Form 1 length is correct");
+ is(form1.elements[0].value, "12", "Form 1 first element is correct");
+ is(form1.elements[1].value, "10", "Form 2 second element is correct");
+ is(form2.elements.length, 3, "Form 2 length is correct");
+ is(form2.elements[0].value, "11", "Form 2 element 1 is correct");
+ is(form2.elements[1].value, "21", "Form 2 element 2 is correct");
+ is(form2.elements[2].value, "22", "Form 2 element 3 is correct");
+
+</script>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug332893-3.html b/dom/html/test/test_bug332893-3.html
new file mode 100644
index 0000000000..247607dcb2
--- /dev/null
+++ b/dom/html/test/test_bug332893-3.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<form id="form1">
+ <table>
+ <tbody>
+ <tr>
+ <td>
+ <table>
+ <tbody id="table1">
+ <tr id="F1I0"><td><input form='form1' type="input" value="10"/></td></tr>
+ <tr id="F1I1"><td><input type="input" value="11"/></td></tr>
+ <tr id="F1I2"><td><input type="input" value="12"/></td></tr>
+ </tbody>
+ </table>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</form>
+<form id="form2">
+ <table>
+ <tbody id="table2">
+ <tr id="F2I1"><td><input type="input" value="21"/></td></tr>
+ <tr id="F2I2"><td><input type="input" value="22"/></td></tr>
+ </tbody>
+ </table>
+</form>
+
+<script>
+ var table1 = document.getElementById("table1");
+ var F1I0 = table1.getElementsByTagName("tr")[0];
+ var F1I1 = table1.getElementsByTagName("tr")[1];
+ table1.removeChild(F1I0);
+ table1.removeChild(F1I1);
+
+ var table2 = document.getElementById("table2");
+ table2.insertBefore(F1I0, table2.firstChild);
+ table2.insertBefore(F1I1, table2.firstChild);
+
+ var form1 = document.getElementById("form1");
+ var form2 = document.getElementById("form2");
+
+ is(form1.elements.length, 2, "Form 1 has the correct length");
+ is(form1.elements[0].value, "12", "Form 1 element 1 is correct");
+ is(form1.elements[1].value, "10", "Form 1 element 2 is correct");
+
+ is(form2.elements.length, 3, "Form 2 has the correct length");
+ is(form2.elements[0].value, "11", "Form 2 element 1 is correct");
+ is(form2.elements[1].value, "21", "Form 2 element 2 is correct");
+ is(form2.elements[2].value, "22", "Form 2 element 2 is correct");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug332893-4.html b/dom/html/test/test_bug332893-4.html
new file mode 100644
index 0000000000..72a0239a5d
--- /dev/null
+++ b/dom/html/test/test_bug332893-4.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<form id="form1">
+ <input id="input1" type="input" name="input" value="1"/>
+ <input id="input2" type="input" name="input" value="2"/>
+ <input id="input3" type="input" name="input" value="3"/>
+</form>
+<script>
+ var input1 = document.getElementById("input1");
+ var input2 = document.getElementById("input2");
+ var form1 = document.getElementById("form1");
+ form1.insertBefore(input2, input1);
+
+ is(form1.elements.input.length, 3, "Form 1 'input' has the correct length");
+ is(form1.elements.input[0].value, "2", "Form 1 element 1 is correct");
+ is(form1.elements.input[1].value, "1", "Form 1 element 2 is correct");
+ is(form1.elements.input[2].value, "3", "Form 1 element 3 is correct");
+
+ is(form1.elements.input[0].id, "input2", "Form 1 element 1 id is correct");
+ is(form1.elements.input[1].id, "input1", "Form 1 element 2 id is correct");
+ is(form1.elements.input[2].id, "input3", "Form 1 element 3 id is correct");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug332893-5.html b/dom/html/test/test_bug332893-5.html
new file mode 100644
index 0000000000..e5fb9b94d6
--- /dev/null
+++ b/dom/html/test/test_bug332893-5.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<form id="form1">
+ <input id="input1" type="input" name="input" value="1"/>
+ <input id="input" type="input" name="input_other" value="2"/>
+ <input id="input3" type="input" name="input" value="3"/>
+</form>
+<script>
+ var input1 = document.getElementById("input1");
+ var input2 = document.getElementById("input");
+ var form1 = document.getElementById("form1");
+ form1.insertBefore(input2, input1);
+
+ is(form1.elements.input.length, 3, "Form 1 'input' has the correct length");
+ is(form1.elements.input[0].value, "2", "Form 1 element 1 is correct");
+ is(form1.elements.input[1].value, "1", "Form 1 element 2 is correct");
+ is(form1.elements.input[2].value, "3", "Form 1 element 3 is correct");
+
+ is(form1.elements.input[0].id, "input", "Form 1 element 1 id is correct");
+ is(form1.elements.input[1].id, "input1", "Form 1 element 2 id is correct");
+ is(form1.elements.input[2].id, "input3", "Form 1 element 3 id is correct");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug332893-6.html b/dom/html/test/test_bug332893-6.html
new file mode 100644
index 0000000000..b12e7a0c3a
--- /dev/null
+++ b/dom/html/test/test_bug332893-6.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<form id="form1">
+ <input id="input1" type="input" name="input" value="1"/>
+ <input id="input" type="input" name="input_other" value="2"/>
+ <input id="input3" type="input" name="input" value="3"/>
+</form>
+<script>
+ var input1 = document.getElementById("input1");
+ var input2 = document.getElementById("input");
+ var form1 = document.getElementById("form1");
+ form1.insertBefore(input2, input1);
+
+ is(form1.elements.input.length, 3, "Form 1 'input' has the correct length");
+ is(form1.elements.input[0].value, "2", "Form 1 element 1 is correct");
+ is(form1.elements.input[1].value, "1", "Form 1 element 2 is correct");
+
+ is(form1.elements.input[0].id, "input", "Form 1 element 1 id is correct");
+ is(form1.elements.input[1].id, "input1", "Form 1 element 2 id is correct");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug332893-7.html b/dom/html/test/test_bug332893-7.html
new file mode 100644
index 0000000000..15672d2d20
--- /dev/null
+++ b/dom/html/test/test_bug332893-7.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<form id="form1">
+ <input id="input1" type="input" name="input" value="1"/>
+ <input id="input2" type="input" name="input" value="2"/>
+ <input id="input3" type="input" name="input" value="3"/>
+ <input id="input4" type="input" name="input" value="4"/>
+ <input id="input5" type="input" name="input" value="5"/>
+ <input id="input6" type="input" name="input" value="6"/>
+ <input id="input7" type="input" name="input" value="7"/>
+ <input id="input8" type="input" name="input" value="8"/>
+ <input id="input9" type="input" name="input" value="9"/>
+ <input id="input10" type="input" name="input" value="10"/>
+
+
+
+
+</form>
+<script>
+ var input1 = document.getElementById("input1");
+ var input2 = document.getElementById("input2");
+ var input3 = document.getElementById("input3");
+ var input4 = document.getElementById("input4");
+ var input5 = document.getElementById("input5");
+ var input6 = document.getElementById("input6");
+ var input7 = document.getElementById("input7");
+ var input8 = document.getElementById("input8");
+ var input9 = document.getElementById("input9");
+ var input10 = document.getElementById("input10");
+
+
+ var form1 = document.getElementById("form1");
+
+ form1.insertBefore(input2, input1);
+ form1.insertBefore(input10, input6);
+ form1.insertBefore(input8, input4);
+ form1.insertBefore(input9, input2);
+
+ is(form1.elements.input.length, 10, "Form 1 'input' has the correct length");
+ is(form1.elements.input[0].value, "9", "Form 1 element 1 is correct");
+ is(form1.elements.input[1].value, "2", "Form 1 element 2 is correct");
+ is(form1.elements.input[2].value, "1", "Form 1 element 3 is correct");
+ is(form1.elements.input[3].value, "3", "Form 1 element 4 is correct");
+ is(form1.elements.input[4].value, "8", "Form 1 element 5 is correct");
+ is(form1.elements.input[5].value, "4", "Form 1 element 6 is correct");
+ is(form1.elements.input[6].value, "5", "Form 1 element 7 is correct");
+ is(form1.elements.input[7].value, "10", "Form 1 element 8 is correct");
+ is(form1.elements.input[8].value, "6", "Form 1 element 9 is correct");
+ is(form1.elements.input[9].value, "7", "Form 1 element 10 is correct");
+
+ is(form1.elements.input[0].id, "input9", "Form 1 element 1 id is correct");
+ is(form1.elements.input[1].id, "input2", "Form 1 element 2 id is correct");
+ is(form1.elements.input[2].id, "input1", "Form 1 element 3 id is correct");
+ is(form1.elements.input[3].id, "input3", "Form 1 element 4 id is correct");
+ is(form1.elements.input[4].id, "input8", "Form 1 element 5 id is correct");
+ is(form1.elements.input[5].id, "input4", "Form 1 element 6 id is correct");
+ is(form1.elements.input[6].id, "input5", "Form 1 element 7 id is correct");
+ is(form1.elements.input[7].id, "input10", "Form 1 element 8 id is correct");
+ is(form1.elements.input[8].id, "input6", "Form 1 element 9 id is correct");
+ is(form1.elements.input[9].id, "input7", "Form 1 element 10 id is correct");
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug3348.html b/dom/html/test/test_bug3348.html
new file mode 100644
index 0000000000..a0f99d9c43
--- /dev/null
+++ b/dom/html/test/test_bug3348.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=3348
+-->
+<head>
+ <title>Test for Bug 3348</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=3348">Mozilla Bug 3348</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<form id="form1">
+<input type="button" value="click here" onclick="buttonClick();">
+</form>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 3348 **/
+
+var oForm = document.getElementById("form1");
+is(oForm.tagName, "FORM", "tagName of HTML element gives tag in upper case");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug340017.xhtml b/dom/html/test/test_bug340017.xhtml
new file mode 100644
index 0000000000..69de021e41
--- /dev/null
+++ b/dom/html/test/test_bug340017.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=340017
+-->
+<head>
+ <title>Test for Bug 340017</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=340017">Mozilla Bug 340017</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form id="frmfoo" name="foo" action="" />
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 340017 **/
+is(document.foo, document.getElementById("frmfoo"),
+ "The form with name 'foo' should be a document accessible property");
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug340800.html b/dom/html/test/test_bug340800.html
new file mode 100644
index 0000000000..bcb5b2de08
--- /dev/null
+++ b/dom/html/test/test_bug340800.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=340800
+-->
+<head>
+ <title>Test for Bug 340800</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=340800">Mozilla Bug 340800</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <h1>iframe text/plain as DOM test</h1>
+
+ <div>
+
+ <iframe name="iframe1" width="100%" height="200"
+ src="bug340800_iframe.txt"></iframe>
+ </div>
+
+ <div>
+ <h2>textarea with iframe content</h2>
+ <textarea rows="10" cols="80" id="textarea1"></textarea>
+ </div>
+
+ <div>
+ <h2>div with white-space: pre and iframe content</h2>
+ <div id="div1"></div>
+ </div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 340800 **/
+function populateIframes () {
+ var iframe, iframeBody;
+ if ((iframe = window.frames.iframe1) && (iframeBody = iframe.document.body)) {
+ $('div1').innerHTML = iframeBody.innerHTML;
+ $('textarea1').value = iframeBody.innerHTML;
+ }
+ is($('div1').firstChild.tagName, "PRE", "innerHTML from txt iframe works with div");
+ ok($('textarea1').value.indexOf("<pre>") > -1, "innerHTML from txt iframe works with textarea.value");
+ SimpleTest.finish();
+}
+
+addLoadEvent(populateIframes);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug347174.html b/dom/html/test/test_bug347174.html
new file mode 100644
index 0000000000..a453627f06
--- /dev/null
+++ b/dom/html/test/test_bug347174.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=347174
+-->
+<head>
+ <title>Test for Bug 347174</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=347174">Mozilla Bug 347174</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 347174 **/
+// simple test of readyState during loading, DOMContentLoaded, and complete
+// this test passes in IE7
+window.readyStateText = [];
+window.readyStateText.push("script tag: " + document.readyState);
+is(document.readyState, "loading", "document.readyState should be 'loading' when scripts runs initially");
+
+function attachCustomEventListener(element, eventName, command) {
+ if (window.addEventListener && !window.opera)
+ element.addEventListener(eventName, command, true);
+ else if (window.attachEvent)
+ element.attachEvent("on" + eventName, command);
+}
+
+function showMessage(msg) {
+ window.readyStateText.push(msg);
+ document.getElementById("display").innerHTML = readyStateText.join("<br>");
+}
+
+function load() {
+ is(document.readyState, "complete", "document.readyState should be 'complete' on load");
+ showMessage("load: " + document.readyState);
+ SimpleTest.finish();
+}
+
+function readyStateChange() {
+ showMessage("readyStateChange: " + document.readyState);
+}
+
+function DOMContentLoaded() {
+ is(document.readyState, "interactive", "document.readyState should be 'interactive' on DOMContentLoaded");
+ showMessage("DOMContentLoaded: " + document.readyState);
+}
+
+window.onload=load;
+
+attachCustomEventListener(document, "readystatechange", readyStateChange);
+attachCustomEventListener(document, "DOMContentLoaded", DOMContentLoaded);
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug347174_write.html b/dom/html/test/test_bug347174_write.html
new file mode 100644
index 0000000000..75912b59a9
--- /dev/null
+++ b/dom/html/test/test_bug347174_write.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=347174
+-->
+<head>
+ <title>Test for Bug 347174</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=347174">Mozilla Bug 347174</a>
+<p id="display"></p>
+
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 347174 **/
+// simple test of readyState during loading, DOMContentLoaded, and complete
+// this test passes in IE7
+window.readyStateText = [];
+window.loaded = false;
+function attachCustomEventListener(element, eventName, command) {
+ if (window.addEventListener && !window.opera)
+ element.addEventListener(eventName, command, true);
+ else if (window.attachEvent)
+ element.attachEvent("on" + eventName, command);
+}
+
+function showMessage(msg) {
+ window.readyStateText.push(msg);
+ document.getElementById("display").innerHTML = readyStateText.join("<br>");
+}
+
+function frameLoad() {
+ var doc = $('iframe').contentWindow.document;
+ is(doc.readyState, "complete", "frame document.readyState should be 'complete' on load");
+ showMessage("frame load: " + doc.readyState);
+ if (window.loaded) SimpleTest.finish();
+}
+
+function load() {
+ window.loaded = true;
+
+ var imgsrc = "<img onload ='window.parent.imgLoad()' src='image.png?noCache="
+ + (new Date().getTime()) + "'>\n";
+ var doc = $('iframe').contentWindow.document;
+ doc.writeln(imgsrc);
+ doc.close();
+ showMessage("frame after document.write: " + doc.readyState);
+ isnot(doc.readyState, "complete", "frame document.readyState should not be 'complete' after document.write");
+}
+
+function imgLoad() {
+ var doc = $('iframe').contentWindow.document;
+ showMessage("frame after imgLoad: " + doc.readyState);
+ is(doc.readyState, "interactive", "frame document.readyState should still be 'interactive' after img loads");
+}
+
+window.onload=load;
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe src="404doesnotexist" id="iframe" onload="frameLoad();"></iframe>
+</body>
+</html>
diff --git a/dom/html/test/test_bug347174_xsl.html b/dom/html/test/test_bug347174_xsl.html
new file mode 100644
index 0000000000..e396e8b721
--- /dev/null
+++ b/dom/html/test/test_bug347174_xsl.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=347174
+-->
+<head>
+ <title>Test for Bug 347174</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=347174">Mozilla Bug 347174</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe src="347174transformable.xml" id="iframe"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 347174 **/
+// Test of readyState of XML document transformed via XSLT to HTML
+// this test passes in IE7
+window.readyStateText = [];
+
+function showMessage(msg) {
+ window.readyStateText.push(msg);
+ document.getElementById("display").innerHTML = readyStateText.join("<br>");
+}
+
+function frameScriptTag(readyState) {
+ isnot(readyState, "complete", "document.readyState should not be 'complete' when scripts run initially");
+ showMessage("script tag: " + readyState);
+}
+
+function frameLoad(readyState) {
+ is(readyState, "complete", "document.readyState should be 'complete' on load");
+ showMessage("load: " + readyState);
+ SimpleTest.finish();
+}
+
+function frameReadyStateChange(readyState) {
+ showMessage("readyStateChange: " + readyState);
+}
+
+function frameDOMContentLoaded(readyState) {
+ is(readyState, "interactive", "document.readyState should be 'interactive' on DOMContentLoaded");
+ showMessage("DOMContentLoaded: " + readyState);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug347174_xslp.html b/dom/html/test/test_bug347174_xslp.html
new file mode 100644
index 0000000000..313e96d1d0
--- /dev/null
+++ b/dom/html/test/test_bug347174_xslp.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=347174
+-->
+<head>
+ <title>Test for Bug 347174</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=347174">Mozilla Bug 347174</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 347174 **/
+// verifies that documents created with createDocument are born in "complete" state
+// (so we don't accidentally leave them in "interactive" state)
+window.readyStateText = [];
+
+function runTest() {
+ var xhr = new XMLHttpRequest();
+ xhr.responseType = "document";
+ xhr.open("GET", "347174transform.xsl");
+ xhr.send();
+ xhr.onload = function() {
+ var xslDoc = xhr.responseXML.documentElement;
+
+ var processor = new XSLTProcessor();
+ processor.importStylesheet(xslDoc);
+
+ window.transformedDoc = processor.transformToDocument(xmlDoc);
+
+ showMessage("loaded: " + xmlDoc.readyState);
+ is(xmlDoc.readyState, "complete", "XML document.readyState should be 'complete' after transform");
+ SimpleTest.finish();
+ };
+}
+
+var xmlDoc = document.implementation.createDocument("", "test", null);
+showMessage("createDocument: " + xmlDoc.readyState);
+is(xmlDoc.readyState, "complete", "created document readyState should be 'complete' before being associated with a parser");
+
+runTest();
+
+function showMessage(msg) {
+ window.readyStateText.push(msg);
+ $("display").innerHTML = readyStateText.join("<br>");
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug353415-1.html b/dom/html/test/test_bug353415-1.html
new file mode 100644
index 0000000000..5cb93e0577
--- /dev/null
+++ b/dom/html/test/test_bug353415-1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<iframe name="submit_frame"></iframe>
+<form method="get" id="form1" target="submit_frame" action="../../../../../blah">
+<input type="text" name="field1" value="teststring"><br>
+<input type="radio" name="field2" value="0" checked> 0
+<input type="radio" name="field3" value="1"> 1<br>
+<input type="checkbox" name="field4" value="1" checked> 1
+<input type="checkbox" name="field5" value="2"> 2
+<input type="checkbox" name="field6" value="3" checked> 3
+<select name="field7">
+<option value="1">1</option>
+<option value="2" selected>2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+</select>
+<input name="field8" value="8">
+<input name="field9" value="9">
+<input type="image" name="field10">
+<label name="field11">
+<input name="field12">
+<input type="button" name="field13" value="button">
+</form>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ document.getElementsByName('submit_frame')[0].onload = function() {
+ is(frames.submit_frame.location.href, `${location.origin}/blah?field1=teststring&field2=0&field4=1&field6=3&field7=2&field8=8&field9=9&field12=`, "Submit string was correct.");
+ SimpleTest.finish();
+ };
+
+ document.forms[0].submit();
+ });
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug353415-2.html b/dom/html/test/test_bug353415-2.html
new file mode 100644
index 0000000000..d27480dff4
--- /dev/null
+++ b/dom/html/test/test_bug353415-2.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<iframe name="submit_frame"></iframe>
+<form method="get" id="form1" target="submit_frame" action="../../../../../blah">
+<table>
+<tr><td>
+<input type="text" name="field1" value="teststring"><br>
+<input type="radio" name="field2" value="0" checked> 0
+<input type="radio" name="field3" value="1"> 1<br>
+<input type="checkbox" name="field4" value="1" checked> 1
+<input type="checkbox" name="field5" value="2"> 2
+<input type="checkbox" name="field6" value="3" checked> 3
+<select name="field7">
+<option value="1">1</option>
+<option value="2" selected>2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+</select>
+<input name="field8" value="8">
+<input name="field9" value="9">
+<input type="image" name="field10">
+<label name="field11"></label>
+<input name="field12">
+<input type="button" name="field13" value="button">
+<input type="hidden" name="field14" value="14">
+</td>
+<input type="text" name="field1-2" value="teststring"><br>
+<input type="radio" name="field2-2" value="0" checked> 0
+<input type="radio" name="field3-2" value="1"> 1<br>
+<input type="checkbox" name="field4-2" value="1" checked> 1
+<input type="checkbox" name="field5-2" value="2"> 2
+<input type="checkbox" name="field6-2" value="3" checked> 3
+<select name="field7-2">
+<option value="1">1</option>
+<option value="2" selected>2</option>
+<option value="3">3</option>
+<option value="4">4</option>
+</select>
+<input name="field8-2" value="8">
+<input name="field9-2" value="9">
+<input type="image" name="field10-2">
+<label name="field11-2"></label>
+<input name="field12-2">
+<input type="button" name="field13-2" value="button">
+<input type="hidden" name="field14-2" value="14">
+</tr>
+</table>
+</form>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ document.getElementsByName('submit_frame')[0].onload = function() {
+ is(frames.submit_frame.location.href, `${location.origin}/blah?field1-2=teststring&field2-2=0&field4-2=1&field6-2=3&field7-2=2&field8-2=8&field9-2=9&field12-2=&field1=teststring&field2=0&field4=1&field6=3&field7=2&field8=8&field9=9&field12=&field14=14&field14-2=14`, "Submit string was correct.");
+ SimpleTest.finish();
+ };
+
+ document.forms[0].submit();
+ });
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug359657.html b/dom/html/test/test_bug359657.html
new file mode 100644
index 0000000000..fe24eace2e
--- /dev/null
+++ b/dom/html/test/test_bug359657.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=359657
+-->
+<head>
+ <title>Test for Bug 359657</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=359657">Mozilla Bug 359657</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/** Test for Bug 359657 **/
+function runTest() {
+ var span = document.createElement("span");
+ $("test").insertBefore(span, $("test").firstChild);
+ ok(true, "Reachability, we should get here without crashing");
+ is($("test").firstChild, span, "First child is correct");
+ SimpleTest.finish();
+}
+</script>
+<div>
+ <iframe src=""></iframe>
+ <!-- Important: This test needs to run async at this point. The actual test
+ is not crashing while running this test! -->
+ <script type="text/javascript" src="data:text/javascript,runTest()"></script>
+</div>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug369370.html b/dom/html/test/test_bug369370.html
new file mode 100644
index 0000000000..c39e6e3243
--- /dev/null
+++ b/dom/html/test/test_bug369370.html
@@ -0,0 +1,153 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=369370
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Test for Bug 369370</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <script type="text/javascript">
+ /*
+ * Test strategy:
+ */
+ function makeClickFor(x, y) {
+ var event = kidDoc.createEvent("mouseevent");
+ event.initMouseEvent("click",
+ true, true, kidWin, 1, // bubbles, cancelable, view, single-click
+ x, y, x, y, // screen X/Y, client X/Y
+ false, false, false, false, // no key modifiers
+ 0, null); // left click, not relatedTarget
+ return event;
+ }
+
+ function childLoaded() {
+ kidDoc = kidWin.document;
+ ok(true, "Child window loaded");
+
+ var elements = kidDoc.getElementsByTagName("img");
+ is(elements.length, 1, "looking for imagedoc img");
+ var img = elements[0];
+
+ // Need to use innerWidth/innerHeight of the window
+ // since the containing image is absolutely positioned,
+ // causing clientHeight to be zero.
+ is(kidWin.innerWidth, 400, "Checking doc width");
+ is(kidWin.innerHeight, 300, "Checking doc height");
+
+ // Image just loaded and is scaled to window size.
+ is(img.width, 400, "image width");
+ is(img.height, 300, "image height");
+ is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
+ is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
+
+ // ========== test 1 ==========
+ // Click in the upper left to zoom in
+ var event = makeClickFor(25,25);
+ img.dispatchEvent(event);
+ ok(true, "----- click 1 -----");
+
+ is(img.width, 800, "image width");
+ is(img.height, 600, "image height");
+ is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
+ is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
+
+ // ========== test 2 ==========
+ // Click there again to zoom out
+ event = makeClickFor(25,25);
+ img.dispatchEvent(event);
+ ok(true, "----- click 2 -----");
+
+ is(img.width, 400, "image width");
+ is(img.height, 300, "image height");
+ is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
+ is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
+
+ // ========== test 3 ==========
+ // Click in the lower right to zoom in
+ event = makeClickFor(350, 250);
+ img.dispatchEvent(event);
+ ok(true, "----- click 3 -----");
+
+ is(img.width, 800, "image width");
+ is(img.height, 600, "image height");
+ is(kidDoc.body.scrollLeft,
+ kidDoc.body.scrollLeftMax, "Checking scrollLeft");
+ is(kidDoc.body.scrollTop,
+ kidDoc.body.scrollTopMax, "Checking scrollTop");
+
+ // ========== test 4 ==========
+ // Click there again to zoom out
+ event = makeClickFor(350, 250);
+ img.dispatchEvent(event);
+ ok(true, "----- click 4 -----");
+
+ is(img.width, 400, "image width");
+ is(img.height, 300, "image height");
+ is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
+ is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
+
+ // ========== test 5 ==========
+ // Click in the upper left to zoom in again
+ event = makeClickFor(25, 25);
+ img.dispatchEvent(event);
+ ok(true, "----- click 5 -----");
+ is(img.width, 800, "image width");
+ is(img.height, 600, "image height");
+ is(kidDoc.body.scrollLeft, 0, "Checking scrollLeft");
+ is(kidDoc.body.scrollTop, 0, "Checking scrollTop");
+ is(img.getBoundingClientRect().top, 0, "Image is in view vertically");
+
+ // ========== test 6 ==========
+ // Now try resizing the window so the image fits vertically.
+ function test6() {
+ kidWin.addEventListener("resize", function() {
+ // Give the image document time to respond
+ SimpleTest.executeSoon(function() {
+ is(img.height, 600, "image height");
+ var bodyHeight = kidDoc.documentElement.scrollHeight;
+ var imgRect = img.getBoundingClientRect();
+ is(imgRect.top, bodyHeight - imgRect.bottom, "Image is vertically centered");
+ test7();
+ });
+ }, {once: true});
+
+ var decorationSize = kidWin.outerHeight - kidWin.innerHeight;
+ kidWin.resizeTo(400, 600 + 50 + decorationSize);
+ }
+
+ // ========== test 7 ==========
+ // Now try resizing the window so the image no longer fits vertically.
+ function test7() {
+ kidWin.addEventListener("resize", function() {
+ // Give the image document time to respond
+ SimpleTest.executeSoon(function() {
+ is(img.height, 600, "image height");
+ is(img.getBoundingClientRect().top, 0, "Image is at top again");
+ kidWin.close();
+ SimpleTest.finish();
+ });
+ }, {once: true});
+
+ var decorationSize = kidWin.outerHeight - kidWin.innerHeight;
+ kidWin.resizeTo(400, 300 + decorationSize);
+ }
+
+ test6();
+ }
+ var kidWin;
+ var kidDoc;
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set":[["browser.enable_automatic_image_resizing", true]]}, function() {
+ kidWin = window.open("bug369370-popup.png", "bug369370", "width=400,height=300");
+ // will init onload
+ ok(kidWin, "opened child window");
+ kidWin.onload = childLoaded;
+ });
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug371375.html b/dom/html/test/test_bug371375.html
new file mode 100644
index 0000000000..1cd0865293
--- /dev/null
+++ b/dom/html/test/test_bug371375.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=371375
+-->
+<head>
+ <title>Test for Bug 371375</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=371375">Mozilla Bug 371375</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Test for Bug 371375 **/
+ var load1Called = false;
+ var error1Called = false;
+ var s = document.createElement('script');
+ s.type = 'text/javascript';
+ s.onload = function() { load1Called = true; };
+ s.onerror = function(event) { error1Called = true; event.stopPropagation(); };
+ s.src = 'about:cache-entry?client=image&sb=0&key=http://www.google.com';
+ document.body.appendChild(s);
+
+ var load2Called = false;
+ var error2Called = false;
+ var s2 = document.createElement('script');
+ s2.type = 'text/javascript';
+ s2.onload = function() { load2Called = true; };
+ s2.onerror = function(event) { error2Called = true; event.stopPropagation(); };
+ s2.src = 'data:text/plain, var x = 1;'
+ document.body.appendChild(s2);
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ is(load1Called, false, "Load handler should not be called");
+ is(error1Called, true, "Error handler should be called");
+ is(load2Called, true, "Load handler for valid script should be called");
+ is(error2Called, false,
+ "Error handler for valid script should not be called");
+ SimpleTest.finish();
+ });
+</script>
+</body>
+</html>
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug372098.html b/dom/html/test/test_bug372098.html
new file mode 100644
index 0000000000..900b20100d
--- /dev/null
+++ b/dom/html/test/test_bug372098.html
@@ -0,0 +1,68 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=372098
+-->
+<head>
+ <title>Test for Bug 372098</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <base target="bug372098"></base>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=372098">Mozilla Bug 372098</a>
+ <p id="display"></p>
+ <div id="content" style="display:none;">
+ <iframe name="bug372098"></iframe>
+ <a id="a" href="bug372098-link-target.html?a" target="">link</a>
+ <map>
+ <area id="area" shape="default" href="bug372098-link-target.html?area" target=""/>
+ </map>
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+
+var a_passed = false;
+var area_passed = false;
+
+/* Start the test */
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(handle_load);
+
+function handle_load()
+{
+ sendMouseEvent({type:'click'}, 'a');
+}
+
+/* Finish the test */
+
+function finish_test()
+{
+ ok(a_passed, "The 'a' element used the correct target.");
+ ok(area_passed, "The 'area' element used the correct target.");
+ SimpleTest.finish();
+}
+
+/* Callback function used by the linked document */
+
+function callback(tag)
+{
+ switch (tag) {
+ case 'a':
+ a_passed = true;
+ sendMouseEvent({type:'click'}, 'area');
+ return;
+ case 'area':
+ area_passed = true;
+ finish_test();
+ return;
+ }
+ throw new Error("Eh??? We only test the 'a', 'link' and 'area' elements.");
+}
+
+ </script>
+ </pre>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug373589.html b/dom/html/test/test_bug373589.html
new file mode 100644
index 0000000000..f494370f3d
--- /dev/null
+++ b/dom/html/test/test_bug373589.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=373589
+-->
+<head>
+ <title>Test for Bug 373589</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=373589">Mozilla Bug 373589</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 373589 **/
+ var docElem = document.documentElement;
+ var body = document.body;
+ var numChildren = docElem.childNodes.length;
+ docElem.removeChild(body);
+ ok(numChildren > docElem.childNodes.length, "body was removed");
+ body.link;
+ ok(true, "didn't crash");
+ docElem.appendChild(body);
+ is(numChildren, docElem.childNodes.length, "body re-added");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug375003-1.html b/dom/html/test/test_bug375003-1.html
new file mode 100644
index 0000000000..2a5e6b911e
--- /dev/null
+++ b/dom/html/test/test_bug375003-1.html
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html id="html">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=375003
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Test 1 for bug 375003</title>
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+
+ html,body {
+ color:black; background-color:white; font-size:16px; padding:0; margin:0;
+ }
+
+ .s { display:block; width:20px; height:20px; background-color:lime; }
+ table { background:pink; }
+ #td5,#td6 { border:7px solid blue;}
+ </style>
+
+<script>
+var x = [ 'Left','Top','Width','Height' ];
+function test(id,s,expected) {
+ var el = document.getElementById(id);
+ for(var i = 0; i < x.length; ++i) {
+ // eslint-disable-next-line no-eval
+ var actual = eval('el.'+s+x[i]);
+ if (expected[i] != -1 && s+x[i]!='scrollHeight')
+ is(actual, expected[i], id+"."+s+x[i]);
+ }
+}
+function t3(id,c,o,s,pid) {
+ test(id,'client',c);
+ test(id,'offset',o);
+ test(id,'scroll',s);
+ var p = document.getElementById(id).offsetParent;
+ is(p.id, pid, id+".offsetParent");
+}
+
+function run_test() {
+ t3('span1',[0,0,20,20],[12,12,20,20],[0,0,20,20],'td1');
+ t3('td1' ,[1,1,69,44],[16,16,71,46],[0,0,69,46],'table1');
+ t3('tr1' ,[0,0,71,46],[16,16,71,46],[0,0,71,44],'table1');
+ t3('span2',[10,0,20,20],[27,12,30,20],[0,0,20,20],'td2');
+ t3('table1',[0,0,103,131],[10,10,103,131],[0,0,103,131],'body');
+ t3('div1',[10,10,-1,131],[0,0,-1,151],[0,0,-1,85],'body');
+
+ t3('span2b',[10,0,20,20],[25,-1,30,20],[0,0,20,20],'body');
+ // XXX not sure how to make reliable cross-platform tests for replaced-inline, inline
+ // t3('span2c',[10,2,18,2],[25,-1,30,6],[0,0,30,20],'body');
+ // t3('span2d',[0,0,0,0],[25,-1,10,19],[0,0,10,20],'body');
+
+ t3('span3' ,[0,0,20,20],[15,0,20,20],[0,0,20,20],'td3');
+ t3('td3' ,[0,0,35,20],[0,0,35,20],[0,0,35,20],'table3');
+ t3('tr3' ,[0,0,35,20],[0,0,35,20],[0,0,35,22],'table3');
+ t3('span4' ,[0,0,20,20],[0,0,20,20],[0,0,20,20],'td4');
+ t3('table3',[0,0,35,40],[0,0,35,40],[0,0,35,40],'div3');
+ t3('div3',[10,10,-1,40],[0,151,-1,60],[0,0,-1,70],'body');
+
+ t3('span5' ,[0,0,20,20],[1,1,20,20],[0,0,20,20],'td5');
+ t3('td5' ,[7,7,22,22],[2,2,36,36],[0,0,22,36],'table5');
+ t3('tr5' ,[0,0,36,36],[2,2,36,36],[0,0,36,22],'table5');
+ t3('span6' ,[0,0,20,20],[20,58,20,20],[0,0,20,20],'div5');
+ t3('table5',[0,0,40,78],[0,0,40,78],[0,0,40,78],'div5');
+ t3('div5',[10,10,-1,78],[0,211,-1,98],[0,0,-1,70],'body');
+
+ t3('span7' ,[0,0,20,20],[1,1,20,20],[0,0,20,20],'td7');
+ t3('td7' ,[1,1,37,22],[2,2,39,24],[0,0,37,22],'table7');
+ t3('tr7' ,[0,0,39,24],[2,2,39,24],[0,0,39,22],'table7');
+ t3('span8' ,[0,0,20,20],[19,30,20,20],[0,0,20,20],'table7');
+ t3('table7',[0,0,57,68],[10,319,57,68],[0,0,57,68],'body');
+ t3('div7',[10,10,-1,68],[0,309,-1,88],[0,0,-1,70],'body');
+
+ t3('span9' ,[0,0,20,20],[1,1,20,20],[0,0,20,20],'td9');
+ t3('td9' ,[1,1,22,22],[2,2,24,24],[0,0,22,24],'table9');
+ t3('tr9' ,[0,0,24,24],[2,2,24,24],[0,0,24,22],'table9');
+ t3('span10' ,[0,0,20,20],[17,43,20,20],[0,0,20,20],'table9');
+ t3('table9',[0,0,54,60],[10,407,54,60],[0,0,54,60],'body');
+ t3('div9',[10,10,-1,0],[0,397,-1,20],[0,0,-1,70],'body');
+
+ t3('span11' ,[0,0,20,20],[1,1,20,20],[0,0,20,20],'td11');
+ t3('td11' ,[0,0,22,22],[2,2,22,22],[0,0,22,22],'table11');
+ t3('tr11' ,[0,0,22,22],[2,2,22,22],[0,0,22,22],'table11');
+ t3('span12' ,[0,0,20,20],[28,454,20,20],[0,0,20,20],'body');
+ t3('table11',[0,0,26,30],[10,427,26,30],[0,0,26,30],'body');
+ t3('div11',[10,10,-1,30],[0,417,-1,50],[0,0,-1,70],'body');
+}
+</script>
+</head>
+<body id="body">
+
+<div id="content">
+<div id="div1" style="border:10px solid black">
+<table id="table1" cellspacing="7" cellpadding="12" border="9">
+ <tbody id="tbody1"><tr id="tr1"><td id="td1"><div class="s" id="span1"></div></td></tr></tbody>
+ <tbody id="tbody2"><tr id="tr2"><td id="td2"><div class="s" id="span2" style="margin-left:15px; border-left:10px solid blue;"></div></td></tr></tbody>
+</table>
+</div>
+
+<div id="div3" style="border:10px solid black; position:relative">
+<table id="table3" cellpadding="0" cellspacing="0" border="0">
+ <tbody id="tbody3"><tr id="tr3"><td id="td3"><div class="s" id="span3" style="margin-left:15px"></div></td></tr></tbody>
+ <tbody id="tbody4"><tr id="tr4"><td id="td4"><div class="s" id="span4"></div></td></tr></tbody>
+</table>
+</div>
+
+<div id="div5" style="border:10px solid black; position:relative">
+<table id="table5">
+ <tbody id="tbody5"><tr id="tr5"><td id="td5"><div class="s" id="span5"></div></td></tr></tbody>
+ <tbody id="tbody6"><tr id="tr6"><td id="td6"><div class="s" id="span6" style="left:10px; top:10px; position:relative"></div></td></tr></tbody>
+</table>
+</div>
+
+<div id="div7" style="border:10px solid black;">
+<table id="table7" style="position:relative" border=7>
+ <tbody id="tbody7"><tr id="tr7"><td id="td7"><div class="s" id="span7"></div></td></tr></tbody>
+ <tbody id="tbody8"><tr id="tr8"><td id="td8"><div class="s" id="span8" style="position:relative; margin-left:15px"></div></td></tr></tbody>
+</table>
+</div>
+
+<div id="div9" style="border:10px solid black;">
+<table id="table9" style="position:absolute" border="13">
+ <tbody id="tbody9"><tr id="tr9"><td id="td9"><div class="s" id="span9"></div></td></tr></tbody>
+ <tbody id="tbody10"><tr id="tr10"><td id="td10"><div class="s" id="span10" style="position:absolute"></div></td></tr></tbody>
+</table>
+</div>
+
+<div id="div11" style="border:10px solid black; ">
+<table id="table11">
+ <tbody id="tbody11"><tr id="tr11"><td id="td11"><div class="s" id="span11"></div></td></tr></tbody>
+ <tbody id="tbody12"><tr id="tr12"><td id="td12"><div class="s" id="span12" style="position:absolute;margin-left:15px"></div></td></tr></tbody>
+</table>
+</div>
+
+<div style="border:10px solid black">
+<div class="s" id="span2b" style="margin-left:15px; border-left:10px solid blue;"></div></div>
+
+<div style="border:10px solid black">
+<button id="span2c" style="margin-left:15px; border-left:10px solid blue;"></button></div>
+
+<div style="border:10px solid black">
+<span id="span2d" style="margin-left:15px; border-left:10px solid blue;"></span></div>
+</div>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=375003">Mozilla Bug 375003</a>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+run_test();
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug375003-2.html b/dom/html/test/test_bug375003-2.html
new file mode 100644
index 0000000000..b8b985846c
--- /dev/null
+++ b/dom/html/test/test_bug375003-2.html
@@ -0,0 +1,110 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=375003
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Test 2 for bug 375003</title>
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+
+ html {
+ padding:0; margin:0;
+ }
+ body {
+ color:black; background-color:white; font-size:12px; padding:10px; margin:0;
+ }
+
+ #div1,#abs1,#table1 {
+ border: 20px solid lime;
+ padding: 30px;
+ width: 100px;
+ height: 60px;
+ overflow:scroll;
+ }
+ #abs1,#table2parent {
+ position:absolute;
+ left:500px;
+ }
+ #table3parent {
+ position:fixed;
+ left:300px;
+ top:100px;
+ }
+ .content {
+ display:block;
+ width:200px;
+ height:200px;
+ background:yellow;
+ border: 0px dotted black;
+ }
+</style>
+
+
+<script type="text/javascript">
+var x = [ 'Left','Top','Width','Height' ];
+function test(id,s,expected) {
+ var el = document.getElementById(id);
+ for(var i = 0; i < x.length; ++i) {
+ // eslint-disable-next-line no-eval
+ var actual = eval('el.'+s+x[i]);
+ if (expected[i] != -1 && s+x[i]!='scrollHeight')
+ is(actual, expected[i], id+"."+s+x[i]);
+ }
+}
+function t3(id,c,o,s,pid) {
+ test(id,'client',c);
+ test(id,'offset',o);
+ test(id,'scroll',s);
+ var p = document.getElementById(id).offsetParent;
+ is(p.id, pid, id+".offsetParent");
+}
+
+function run_test() {
+ // XXX how test clientWidth/clientHeight (the -1 below) in cross-platform manner
+ // without hard-coding the scrollbar width?
+ t3('div1',[20,20,-1,-1],[10,10,200,160],[0,0,230,20],'body');
+ t3('abs1',[20,20,-1,-1],[500,170,200,160],[0,0,230,20],'body');
+ t3('table1',[0,0,306,306],[10,170,306,306],[0,0,306,306],'body');
+ t3('table2',[0,0,206,206],[0,0,206,206],[0,0,206,20],'table2parent');
+ t3('table3',[0,0,228,228],[0,0,228,228],[0,0,228,228],'table3parent');
+ t3('table3parent',[0,0,228,228],[300,100,228,228],[0,0,228,228],'body');
+}
+</script>
+
+</head>
+<body id="body">
+<div id="content">
+<div id="div1parent">
+ <div id="div1"><span class="content">DIV</span></div>
+</div>
+
+<div id="abs1parent">
+ <div id="abs1"><span class="content">abs.pos.DIV</span></div>
+</div>
+
+<div id="table1parent">
+ <table id="table1"><tbody><tr><td id="td1"><span class="content">TABLE</span></td></tr></tbody></table>
+</div>
+
+<div id="table2parent">
+ <table id="table2"><tbody><tr><td id="td2"><span class="content">TABLE in abs</span></td></tr></tbody></table>
+</div>
+
+<div id="table3parent">
+ <table id="table3" border="10"><tbody><tr><td id="td3"><span class="content">TABLE in fixed</span></td></tr></tbody></table>
+</div>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+run_test();
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug377624.html b/dom/html/test/test_bug377624.html
new file mode 100644
index 0000000000..d385708c5e
--- /dev/null
+++ b/dom/html/test/test_bug377624.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=377624
+-->
+<head>
+ <title>Test for Bug 377624</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=377624">Mozilla Bug 377624</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 377624 **/
+
+var input = document.createElement('input');
+ok("accept" in input, "'accept' is a valid input property");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug380383.html b/dom/html/test/test_bug380383.html
new file mode 100644
index 0000000000..4ec632c0d6
--- /dev/null
+++ b/dom/html/test/test_bug380383.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=380383
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <title>Test for Bug 380383</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=380383">Mozilla Bug 380383</a>
+<p id="display">
+ <iframe id="f1" name="f1"></iframe>
+ <iframe id="f2" name="f2"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ /** Test for Bug 380383 **/
+ is($("f1").contentDocument.characterSet, "UTF-8",
+ "Unexpected charset for f1");
+
+ function runTest() {
+ is($("f2").contentDocument.characterSet, "UTF-8",
+ "Unexpected charset for f2");
+ }
+
+ addLoadEvent(runTest);
+ addLoadEvent(SimpleTest.finish);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug383383.html b/dom/html/test/test_bug383383.html
new file mode 100644
index 0000000000..a518f426cf
--- /dev/null
+++ b/dom/html/test/test_bug383383.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=383383
+-->
+<head>
+ <title>Test for Bug 383383</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=383383">Mozilla Bug 383383</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript" for=" window " event=" onload() ">
+
+var foo = "bar";
+
+</script>
+
+<script class="testbody" type="text/javascript" for="object" event="handler">
+
+// This script should fail to run
+foo = "baz";
+
+isnot(foo, "baz", "test failed");
+
+</script>
+
+<script class="testbody" type="text/javascript">
+
+ok(foo == "bar", "test passed");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug383383_2.xhtml b/dom/html/test/test_bug383383_2.xhtml
new file mode 100644
index 0000000000..4dccd381c4
--- /dev/null
+++ b/dom/html/test/test_bug383383_2.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test for bug 383383</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <script>
+ SimpleTest.waitForExplicitFinish()
+ </script>
+ <script for="window" event="bar">
+ // This script should not run, but should not cause a parse error either.
+ ok(false, "Script was unexpectedly run")
+ </script>
+ <script>
+ ok(true, "Script was run as it should")
+ SimpleTest.finish()
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug384419.html b/dom/html/test/test_bug384419.html
new file mode 100644
index 0000000000..72ed15ef87
--- /dev/null
+++ b/dom/html/test/test_bug384419.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=384419
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Test for bug 384419</title>
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font-size:16px; padding:0; margin:0;
+ }
+ body { margin: 10px; }
+ table { border:15px solid black; margin-left:100px; }
+</style>
+
+
+<script type="text/javascript">
+function t3(id,expected,pid) {
+ var el = document.getElementById(id);
+ var actual = el.offsetLeft;
+ is(actual, expected, id+".offsetLeft");
+
+ var p = document.getElementById(id).offsetParent;
+ is(p.id, pid, id+".offsetParent");
+}
+
+function run_test() {
+ t3('rel384419',135,'body');
+ t3('abs384419',135,'body');
+ t3('fix384419',135,'body');
+}
+</script>
+
+</head>
+<body id="body">
+<!-- It's important for the test that the tables below are directly inside body -->
+<table cellpadding="7" cellspacing="3"><tr><td width="100"><div id="rel384419" style="position:relative;border:1px solid blue">X</div> relative</table>
+<table cellpadding="7" cellspacing="3"><tr><td width="100"><div id="abs384419" style="position:absolute;border:1px solid blue">X</div> absolute</table>
+<table cellpadding="7" cellspacing="3"><tr><td width="100"><div id="fix384419" style="position:fixed;border:1px solid blue">X</div> fixed</table>
+
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+run_test();
+</script>
+</pre>
+
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=384419">bug 384419</a>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug386496.html b/dom/html/test/test_bug386496.html
new file mode 100644
index 0000000000..47c48592e8
--- /dev/null
+++ b/dom/html/test/test_bug386496.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=386496
+-->
+<head>
+ <title>Test for Bug 386496</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <SCRIPT Type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=386496">Mozilla Bug 386496</a>
+<p id="display"></p>
+<div id="content">
+ <iframe style='display: block;' id="testIframe"
+ srcdoc="<div><a id='a' href='http://a.invalid/'>Link</a></div>">
+ </iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 386496 **/
+
+var frame = document.getElementById("testIframe");
+
+function testDesignMode() {
+ var unloadRequested = false;
+
+ frame.contentDocument.designMode = "on";
+
+ frame.contentWindow.addEventListener("beforeunload", function() {
+ unloadRequested = true;
+ });
+
+ synthesizeMouseAtCenter(frame.contentDocument.getElementById("a"), {},
+ frame.contentWindow);
+
+ // The click has been sent. If 'beforeunload' event has been caught when we go
+ // back from the event loop that means the link has been activated.
+ setTimeout(function() {
+ ok(!unloadRequested, "The link should not be activated in designMode");
+ SimpleTest.finish();
+ }, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(testDesignMode);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug386728.html b/dom/html/test/test_bug386728.html
new file mode 100644
index 0000000000..5562ce6712
--- /dev/null
+++ b/dom/html/test/test_bug386728.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=386728
+-->
+<head>
+ <title>Test for Bug 386728</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=386728">Mozilla Bug 386728</a>
+<p id="display"></p>
+<div id="content">
+ <div id="frameContent">
+ <div id="edit">This text is editable</div>
+ <button id="button_on" onclick="document.getElementById('edit').setAttribute('contenteditable', 'true')"></button>
+ </div>
+ <iframe id="testIframe"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 386728 **/
+
+var frame = document.getElementById("testIframe");
+
+function testContentEditable() {
+ frame.style.display = 'block';
+ var frameContent = frame.contentDocument.adoptNode(document.getElementById("frameContent"));
+ frame.contentDocument.body.appendChild(frameContent);
+ frame.contentDocument.getElementById("edit").contentEditable = "true";
+ frame.contentDocument.getElementById("edit").contentEditable = "false";
+ frame.contentDocument.getElementById("button_on").click();
+ is(frame.contentDocument.getElementById("edit").contentEditable, "true");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testContentEditable);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug386996.html b/dom/html/test/test_bug386996.html
new file mode 100644
index 0000000000..a3068c2c2e
--- /dev/null
+++ b/dom/html/test/test_bug386996.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=386996
+-->
+<head>
+ <title>Test for Bug 386996</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=386996">Mozilla Bug 386996</a>
+<p id="display"></p>
+<div id="content">
+ <input id="input1"><input disabled><input id="input2">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 386996 **/
+
+var frame = document.getElementById("testIframe");
+
+function testContentEditable() {
+ var focusedElement;
+ document.getElementById("input1").onfocus = function() { focusedElement = this };
+ document.getElementById("input2").onfocus = function() { focusedElement = this };
+
+ document.getElementById("input1").focus();
+ synthesizeKey("KEY_Tab");
+
+ is(focusedElement.id, "input2");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testContentEditable);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug388558.html b/dom/html/test/test_bug388558.html
new file mode 100644
index 0000000000..a86bab8d1a
--- /dev/null
+++ b/dom/html/test/test_bug388558.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=388558
+-->
+<head>
+ <title>Test for Bug 388558</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=388558">Mozilla Bug 388558</a>
+<p id="display"></p>
+<div id="content">
+ <input type="text" id="input" onchange="++inputChange;">
+ <textarea id="textarea" onchange="++textareaChange;"></textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 388558 **/
+var inputChange = 0;
+var textareaChange = 0;
+
+function testUserInput() {
+ var input = document.getElementById("input");
+ var textarea = SpecialPowers.wrap(document.getElementById("textarea"));
+
+ input.focus();
+ SpecialPowers.wrap(input).setUserInput("foo");
+ input.blur();
+ is(inputChange, 1, "Input element should have got one change event.");
+
+ input.focus();
+ input.value = "bar";
+ input.blur();
+ is(inputChange, 1,
+ "Change event dispatched when setting the value of the input element");
+
+ input.value = "";
+ is(inputChange, 1,
+ "Change event dispatched when setting the value of the input element (2).");
+
+ SpecialPowers.wrap(input).setUserInput("foo");
+ is(inputChange, 2,
+ "Change event dispatched when input element doesn't have focus.");
+
+ textarea.focus();
+ textarea.setUserInput("foo");
+ textarea.blur();
+ is(textareaChange, 1, "Textarea element should have got one change event.");
+
+ textarea.focus();
+ textarea.value = "bar";
+ textarea.blur();
+ is(textareaChange, 1,
+ "Change event dispatched when setting the value of the textarea element.");
+
+ textarea.value = "";
+ is(textareaChange, 1,
+ "Change event dispatched when setting the value of the textarea element (2).");
+
+ textarea.setUserInput("foo");
+ is(textareaChange, 1,
+ "Change event dispatched when textarea element doesn't have focus.");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testUserInput);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug388746.html b/dom/html/test/test_bug388746.html
new file mode 100644
index 0000000000..38c73ee0b5
--- /dev/null
+++ b/dom/html/test/test_bug388746.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=388746
+-->
+<head>
+ <title>Test for Bug 388746</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=388746">Mozilla Bug 388746</a>
+<p id="display"></p>
+<div id="content">
+ <input>
+ <textarea></textarea>
+ <select>
+ <option>option1</option>
+ <optgroup label="optgroup">
+ <option>option2</option>
+ </optgroup>
+ </select>
+ <button>Button</button>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 388746 **/
+
+var previousEventTarget = "";
+
+function handler(evt) {
+ if (evt.eventPhase == 2) {
+ previousEventTarget = evt.target.localName.toLowerCase();
+ }
+}
+
+function testElementType(type) {
+ var el = document.getElementsByTagName(type)[0];
+ el.addEventListener("DOMAttrModified", handler, true);
+ el.setAttribute("foo", "bar");
+ ok(previousEventTarget == type,
+ type + " element should have got DOMAttrModified event.");
+}
+
+function test() {
+ testElementType("input");
+ testElementType("textarea");
+ testElementType("select");
+ testElementType("option");
+ testElementType("optgroup");
+ testElementType("button");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+addLoadEvent(SimpleTest.finish);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug388794.html b/dom/html/test/test_bug388794.html
new file mode 100644
index 0000000000..06388547d7
--- /dev/null
+++ b/dom/html/test/test_bug388794.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=388794
+-->
+<head>
+ <title>Test for Bug 388794</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input { padding: 0; margin: 0; border: none; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=388794">Mozilla Bug 388794</a>
+<p id="display">
+ <form action="dummy_page.html" target="test1" method="GET">
+ <input id="test1image" type="image" name="testImage">
+ </form>
+ <form action="dummy_page.html" target="test2" method="GET">
+ <input id="test2image" type="image">
+ </form>
+ <form action="dummy_page.html" target="test3" method="GET">
+ <input id="test3image" type="image" src="nnc_lockup.gif" name="testImage">
+ </form>
+ <form action="dummy_page.html" target="test4" method="GET">
+ <input id="test4image" type="image" src="nnc_lockup.gif">
+ </form>
+ <form action="dummy_page.html" target="test5" method="GET">
+ <input id="test5image" type="image" src="nnc_lockup.gif" name="testImage">
+ </form>
+ <form action="dummy_page.html" target="test6" method="GET">
+ <input id="test6image" type="image" src="nnc_lockup.gif">
+ </form>
+ <iframe name="test1" id="test1"></iframe>
+ <iframe name="test2" id="test2"></iframe>
+ <iframe name="test3" id="test3"></iframe>
+ <iframe name="test4" id="test4"></iframe>
+ <iframe name="test5" id="test5"></iframe>
+ <iframe name="test6" id="test6"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 388794 **/
+SimpleTest.waitForExplicitFinish();
+
+var pendingLoads = 0;
+/* Use regex due to rounding error in Fennec with C++APZ enabled */
+var hrefs = {
+ test1: /\?testImage\.x=0&testImage\.y=0/,
+ test2: /\?x=0&y=0/,
+ test3: /\?testImage\.x=0&testImage\.y=0/,
+ test4: /\?x=0&y=0/,
+ test5: /\?testImage\.x=[4-6]&testImage\.y=[4-6]/,
+ test6: /\?x=[4-6]&y=[4-6]/,
+};
+
+function submitForm(idNum) {
+ $("test"+idNum).setAttribute("onload", "frameLoaded(this)");
+ $("test" + idNum + "image").focus();
+ sendKey("return");
+}
+
+function submitFormMouse(idNum) {
+ $("test"+idNum).setAttribute("onload", "frameLoaded(this)");
+ // Use 4.99 instead of 5 to guard against the possibility that the
+ // image's 'top' is exactly N + 0.5 pixels from the root. In that case
+ // we'd round up the widget mouse coordinate to N + 6, which relative
+ // to the image would be 5.5, which would get rounded up to 6 when
+ // submitting the form. Instead we round the widget mouse coordinate to
+ // N + 5, which relative to the image would be 4.5 which gets rounded up
+ // to 5.
+ synthesizeMouse($("test" + idNum + "image"), 4.99, 4.99, {});
+}
+
+addLoadEvent(function() {
+ // Need the timeout so painting has a chance to be unsuppressed.
+ setTimeout(function() {
+ submitForm(++pendingLoads);
+ submitForm(++pendingLoads);
+ submitForm(++pendingLoads);
+ submitForm(++pendingLoads);
+ submitFormMouse(++pendingLoads);
+ submitFormMouse(++pendingLoads);
+ }, 0);
+});
+
+function frameLoaded(frame) {
+ ok(
+ hrefs[frame.name].test(frame.contentWindow.location.href),
+ "Unexpected href for frame " + frame.name + " - " +
+ "expected to match: " + hrefs[frame.name].toString() + " got: " + frame.contentWindow.location.href
+ );
+ if (--pendingLoads == 0) {
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug389797.html b/dom/html/test/test_bug389797.html
new file mode 100644
index 0000000000..701d6e65c9
--- /dev/null
+++ b/dom/html/test/test_bug389797.html
@@ -0,0 +1,243 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=389797
+-->
+<head>
+ <title>Test for Bug 389797</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=389797">Mozilla Bug 389797</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 389797 **/
+var allTags = [];
+var classInfos = {};
+var interfaces = {};
+var interfacesNonClassinfo = {};
+
+function getClassName(tag) {
+ return "HTML" + classInfos[tag] + "Element";
+}
+
+function HTML_TAG(aTagName, aImplClass) {
+ allTags.push(aTagName);
+ classInfos[aTagName] = aImplClass;
+ interfaces[aTagName] = [];
+
+ // Some interfaces don't appear in classinfo because other interfaces that
+ // inherit from them do.
+ interfacesNonClassinfo[aTagName] = [ ];
+
+ if (arguments.length > 2) {
+ for (var i = 0; i < arguments[2].length; ++i) {
+ interfaces[aTagName].push(arguments[2][i]);
+ }
+ }
+
+ if (arguments.length > 3) {
+ for (i = 0; i < arguments[3].length; ++i) {
+ interfacesNonClassinfo[aTagName].push(arguments[3][i]);
+ }
+ }
+}
+
+const objectIfaces = [
+ "nsIRequestObserver",
+ "nsIStreamListener",
+ "nsIObjectLoadingContent",
+ "nsIChannelEventSink",
+];
+
+/* List copy/pasted from nsHTMLTagList.h, with the second field modified to the
+ correct classinfo (instead of the impl class) in the following cases:
+
+ base
+ blockquote
+ dir
+ dl
+ embed
+ menu
+ ol
+ param
+ q
+ ul
+ wbr
+ head
+ html
+ */
+
+HTML_TAG("a", "Anchor");
+HTML_TAG("abbr", "");
+HTML_TAG("acronym", "");
+HTML_TAG("address", "");
+HTML_TAG("area", "Area");
+HTML_TAG("article", "");
+HTML_TAG("aside", "");
+HTML_TAG("b", "");
+HTML_TAG("base", "Base");
+HTML_TAG("bdi", "")
+HTML_TAG("bdo", "");
+HTML_TAG("bgsound", "Unknown");
+HTML_TAG("big", "");
+HTML_TAG("blockquote", "Quote");
+HTML_TAG("body", "Body");
+HTML_TAG("br", "BR");
+HTML_TAG("button", "Button");
+HTML_TAG("canvas", "Canvas");
+HTML_TAG("caption", "TableCaption");
+HTML_TAG("center", "");
+HTML_TAG("cite", "");
+HTML_TAG("code", "");
+HTML_TAG("col", "TableCol");
+HTML_TAG("colgroup", "TableCol");
+HTML_TAG("data", "Data");
+HTML_TAG("datalist", "DataList");
+HTML_TAG("dd", "");
+HTML_TAG("del", "Mod");
+HTML_TAG("dfn", "");
+HTML_TAG("dir", "Directory");
+HTML_TAG("div", "Div");
+HTML_TAG("dl", "DList");
+HTML_TAG("dt", "");
+HTML_TAG("em", "");
+HTML_TAG("embed", "Embed", [], objectIfaces);
+HTML_TAG("fieldset", "FieldSet");
+HTML_TAG("figcaption", "")
+HTML_TAG("figure", "")
+HTML_TAG("font", "Font");
+HTML_TAG("footer", "")
+HTML_TAG("form", "Form");
+HTML_TAG("frame", "Frame", [ "nsIDOMMozBrowserFrame" ]);
+HTML_TAG("frameset", "FrameSet");
+HTML_TAG("h1", "Heading");
+HTML_TAG("h2", "Heading");
+HTML_TAG("h3", "Heading");
+HTML_TAG("h4", "Heading");
+HTML_TAG("h5", "Heading");
+HTML_TAG("h6", "Heading");
+HTML_TAG("head", "Head");
+HTML_TAG("header", "")
+HTML_TAG("hgroup", "")
+HTML_TAG("hr", "HR");
+HTML_TAG("html", "Html");
+HTML_TAG("i", "");
+HTML_TAG("iframe", "IFrame", [ "nsIDOMMozBrowserFrame" ]);
+HTML_TAG("image", "");
+HTML_TAG("img", "Image", [ "nsIImageLoadingContent" ], []);
+HTML_TAG("input", "Input", [], [ "imgINotificationObserver",
+ "nsIImageLoadingContent" ]);
+HTML_TAG("ins", "Mod");
+HTML_TAG("kbd", "");
+HTML_TAG("keygen", "Unknown");
+HTML_TAG("label", "Label");
+HTML_TAG("legend", "Legend");
+HTML_TAG("li", "LI");
+HTML_TAG("link", "Link");
+HTML_TAG("listing", "Pre");
+HTML_TAG("main", "");
+HTML_TAG("map", "Map");
+HTML_TAG("mark", "");
+HTML_TAG("marquee", "Marquee");
+HTML_TAG("menu", "Menu");
+HTML_TAG("meta", "Meta");
+HTML_TAG("meter", "Meter");
+HTML_TAG("multicol", "Unknown");
+HTML_TAG("nav", "")
+HTML_TAG("nobr", "");
+HTML_TAG("noembed", "");
+HTML_TAG("noframes", "");
+HTML_TAG("noscript", "");
+HTML_TAG("object", "Object", [], objectIfaces);
+HTML_TAG("ol", "OList");
+HTML_TAG("optgroup", "OptGroup");
+HTML_TAG("option", "Option");
+HTML_TAG("p", "Paragraph");
+HTML_TAG("param", "Param");
+HTML_TAG("plaintext", "");
+HTML_TAG("pre", "Pre");
+HTML_TAG("q", "Quote");
+HTML_TAG("rb", "");
+HTML_TAG("rp", "");
+HTML_TAG("rt", "");
+HTML_TAG("rtc", "");
+HTML_TAG("ruby", "");
+HTML_TAG("s", "");
+HTML_TAG("samp", "");
+HTML_TAG("script", "Script", [ "nsIScriptLoaderObserver" ], []);
+HTML_TAG("section", "")
+HTML_TAG("select", "Select");
+HTML_TAG("small", "");
+HTML_TAG("span", "Span");
+HTML_TAG("strike", "");
+HTML_TAG("strong", "");
+HTML_TAG("style", "Style");
+HTML_TAG("sub", "");
+HTML_TAG("sup", "");
+HTML_TAG("table", "Table");
+HTML_TAG("tbody", "TableSection");
+HTML_TAG("td", "TableCell");
+HTML_TAG("textarea", "TextArea");
+HTML_TAG("tfoot", "TableSection");
+HTML_TAG("th", "TableCell");
+HTML_TAG("thead", "TableSection");
+HTML_TAG("template", "Template");
+HTML_TAG("time", "Time");
+HTML_TAG("title", "Title");
+HTML_TAG("tr", "TableRow");
+HTML_TAG("tt", "");
+HTML_TAG("u", "");
+HTML_TAG("ul", "UList");
+HTML_TAG("var", "");
+HTML_TAG("wbr", "");
+HTML_TAG("xmp", "Pre");
+
+function tagName(aTag) {
+ return "<" + aTag + ">";
+}
+
+for (var tag of allTags) {
+ var node = document.createElement(tag);
+
+ // Have to use the proto's toString(), since HTMLAnchorElement and company
+ // override toString().
+ var nodeString = HTMLElement.prototype.toString.apply(node);
+
+ // Debug builds have extra info, so chop off after "Element" if it's followed
+ // by ' ' or ']'
+ nodeString = nodeString.replace(/Element[\] ].*/, "Element");
+
+ var classInfoString = getClassName(tag);
+ is(nodeString, "[object " + classInfoString,
+ "Unexpected classname for " + tagName(tag));
+ is(node instanceof window[classInfoString], true,
+ tagName(tag) + " not an instance of " + classInfos[tag]);
+
+ if (classInfoString != 'HTMLUnknownElement') {
+ is(node instanceof HTMLUnknownElement, false,
+ tagName(tag) + " is an instance of HTMLUnknownElement");
+ } else {
+ is(node instanceof HTMLUnknownElement, true,
+ tagName(tag) + " is an instance of HTMLUnknownElement");
+ }
+
+ // Check that each node QIs to all the things we expect it to QI to
+ for (var iface of interfaces[tag].concat(interfacesNonClassinfo[tag])) {
+ is(iface in SpecialPowers.Ci, true,
+ iface + " not in Components.interfaces");
+ is(node instanceof SpecialPowers.Ci[iface], true,
+ tagName(tag) + " does not QI to " + iface);
+ }
+}
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug390975.html b/dom/html/test/test_bug390975.html
new file mode 100644
index 0000000000..8a7e09b807
--- /dev/null
+++ b/dom/html/test/test_bug390975.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=390975
+-->
+<head>
+ <title>Test for Bug 390975</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=390975">Mozilla Bug 390975</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <table id="table1">
+ <form id="form1">
+ <input>
+ <input>
+ <tr><td>
+ <input>
+ <input>
+ <input>
+ </td></tr>
+ </form>
+ </table>
+
+ <table id="table2">
+ <form id="form2">
+ <input>
+ <input>
+ <tr id="row2"><td>
+ <input>
+ <input>
+ <input>
+ </td></tr>
+ </form>
+ </table>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 390975 **/
+var form = $("form1");
+is(form.elements.length, 5, "Unexpected elements length");
+
+$("table1").remove();
+is(form.elements.length, 3, "Should have lost control outside table");
+
+form.remove();
+is(form.elements.length, 0, "Should have lost control outside form");
+
+form = $("form2");
+is(form.elements.length, 5, "Unexpected elements length");
+
+$("row2").remove();
+is(form.elements.length, 2, "Should have lost controls inside table row");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug391994.html b/dom/html/test/test_bug391994.html
new file mode 100644
index 0000000000..8dfa6cc772
--- /dev/null
+++ b/dom/html/test/test_bug391994.html
@@ -0,0 +1,184 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=391994
+-->
+<head>
+ <title>Test for Bug 391994</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=391994">Mozilla Bug 391994</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 391994 **/
+var testNumber = 0;
+
+function assertSelected(aOption, aExpectDefaultSelected, aExpectSelected) {
+ ++testNumber;
+ is(aOption.defaultSelected, aExpectDefaultSelected,
+ "Asserting default-selected state for option " + testNumber);
+ is(aOption.selected, aExpectSelected,
+ "Asserting selected state for option " + testNumber);
+}
+
+function assertSame(aSel1, aSel2Str, aTestNumber) {
+ var div = document.createElement("div");
+ div.innerHTML = aSel2Str;
+ sel2 = div.firstChild;
+ is(aSel1.options.length, sel2.options.length,
+ "Length should be same in select test " + aTestNumber);
+ is(aSel1.selectedIndex, sel2.selectedIndex,
+ "Selected index should be same in select test " + aTestNumber);
+ for (var i = 0; i < aSel1.options.length; ++i) {
+ is(aSel1.options[i].selected, sel2.options[i].selected,
+ "Options[" + i + "].selected should be the same in select test " +
+ aTestNumber);
+ is(aSel1.options[i].defaultSelected, sel2.options[i].defaultSelected,
+ "Options[" + i +
+ "].defaultSelected should be the same in select test " +
+ aTestNumber);
+ }
+}
+
+// Creation methods
+var opt = document.createElement("option");
+assertSelected(opt, false, false);
+
+opt = new Option();
+assertSelected(opt, false, false);
+
+// Setting of defaultSelected
+opt = new Option();
+opt.setAttribute("selected", "selected");
+assertSelected(opt, true, true);
+
+opt = new Option();
+opt.defaultSelected = true;
+assertSelected(opt, true, true);
+is(opt.hasAttribute("selected"), true, "Attribute should be set");
+is(opt.getAttribute("selected"), "",
+ "Attribute should be set to empty string");
+
+// Setting of selected
+opt = new Option();
+opt.selected = false;
+assertSelected(opt, false, false);
+
+opt = new Option();
+opt.selected = true;
+assertSelected(opt, false, true);
+
+// Interaction of selected and defaultSelected
+opt = new Option();
+opt.selected;
+opt.setAttribute("selected", "selected");
+assertSelected(opt, true, true);
+
+opt = new Option();
+opt.selected = false;
+opt.setAttribute("selected", "selected");
+assertSelected(opt, true, false);
+
+opt = new Option();
+opt.setAttribute("selected", "selected");
+opt.selected = true;
+opt.removeAttribute("selected");
+assertSelected(opt, false, true);
+
+// First test of putting things in a <select>: Adding default-selected option
+// should select it.
+var sel = document.createElement("select");
+sel.appendChild(new Option());
+is(sel.selectedIndex, 0, "First option should be selected");
+assertSelected(sel.firstChild, false, true);
+
+sel.appendChild(new Option());
+is(sel.selectedIndex, 0, "First option should still be selected");
+assertSelected(sel.firstChild, false, true);
+assertSelected(sel.firstChild.nextSibling, false, false);
+
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+assertSelected(sel.firstChild, false, false);
+assertSelected(sel.firstChild.nextSibling, false, false);
+assertSelected(opt, true, true);
+is(opt, sel.firstChild.nextSibling.nextSibling, "What happened here?");
+is(sel.options[0], sel.firstChild, "Unexpected option 0");
+is(sel.options[1], sel.firstChild.nextSibling, "Unexpected option 1");
+is(sel.options[2], opt, "Unexpected option 2");
+is(sel.selectedIndex, 2, "Unexpected selectedIndex in select test 1");
+
+assertSame(sel, "<select><option><option><option selected></select>", 1);
+
+// Second test of putting things in a <select>: Adding two default-selected
+// options should select the second one.
+sel = document.createElement("select");
+sel.appendChild(new Option());
+sel.appendChild(new Option());
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+assertSelected(sel.options[0], false, false);
+assertSelected(sel.options[1], false, false);
+assertSelected(sel.options[2], true, false);
+assertSelected(sel.options[3], true, true);
+is(sel.selectedIndex, 3, "Unexpected selectedIndex in select test 2");
+
+assertSame(sel,
+ "<select><option><option><option selected><option selected></select>", 2);
+
+// Third test of putting things in a <select>: adding a selected option earlier
+// than another selected option should make the new option selected.
+sel = document.createElement("select");
+sel.appendChild(new Option());
+sel.appendChild(new Option());
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+opt = new Option();
+opt.defaultSelected = true;
+sel.options[0] = opt;
+assertSelected(sel.options[0], true, true);
+assertSelected(sel.options[1], false, false);
+assertSelected(sel.options[2], true, false);
+is(sel.selectedIndex, 0, "Unexpected selectedIndex in select test 3");
+
+// Fourth test of putting things in a <select>: Just like second test, but with
+// a <select multiple>
+sel = document.createElement("select");
+sel.multiple = true;
+sel.appendChild(new Option());
+sel.appendChild(new Option());
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+assertSelected(sel.options[0], false, false);
+assertSelected(sel.options[1], false, false);
+assertSelected(sel.options[2], true, true);
+assertSelected(sel.options[3], true, true);
+is(sel.selectedIndex, 2, "Unexpected selectedIndex in select test 4");
+
+assertSame(sel,
+ "<select multiple><option><option>" +
+ "<option selected><option selected></select>",
+ 4);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug394700.html b/dom/html/test/test_bug394700.html
new file mode 100644
index 0000000000..fb6a54421b
--- /dev/null
+++ b/dom/html/test/test_bug394700.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=394700
+-->
+<head>
+ <title>Test for Bug 394700</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=394700">Mozilla Bug 394700</a>
+<p id="display"></p>
+<div id="content">
+ <select><option id="A">A</option><option id="B">B</option></select>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 394700 **/
+
+function remove(q1) { q1.remove(); }
+
+function testSelectedIndex()
+{
+ document.addEventListener("DOMNodeRemoved", foo);
+ remove(document.getElementById("B"));
+ document.removeEventListener("DOMNodeRemoved", foo);
+
+ function foo()
+ {
+ document.removeEventListener("DOMNodeRemoved", foo);
+ remove(document.getElementById("A"));
+ }
+ var selectElement = document.getElementsByTagName("select")[0];
+ is(selectElement.selectedIndex, -1, "Wrong selected index!");
+ is(selectElement.length, 0, "Select shouldn't have any options!");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testSelectedIndex);
+addLoadEvent(SimpleTest.finish);
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug395107.html b/dom/html/test/test_bug395107.html
new file mode 100644
index 0000000000..8273cd6c6e
--- /dev/null
+++ b/dom/html/test/test_bug395107.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=395107
+-->
+<head>
+ <title>Test for Bug 395107</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=395107">Mozilla Bug 395107</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 395107 **/
+var testNumber = 0;
+
+function assertSelected(aOption, aExpectDefaultSelected, aExpectSelected) {
+ ++testNumber;
+ is(aOption.defaultSelected, aExpectDefaultSelected,
+ "Asserting default-selected state for option " + testNumber);
+ is(aOption.selected, aExpectSelected,
+ "Asserting selected state for option " + testNumber);
+}
+
+function assertSame(aSel1, aSel2Str, aTestNumber) {
+ var div = document.createElement("div");
+ div.innerHTML = aSel2Str;
+ sel2 = div.firstChild;
+ is(aSel1.options.length, sel2.options.length,
+ "Length should be same in select test " + aTestNumber);
+ is(aSel1.selectedIndex, sel2.selectedIndex,
+ "Selected index should be same in select test " + aTestNumber);
+ for (var i = 0; i < aSel1.options.length; ++i) {
+ is(aSel1.options[i].selected, sel2.options[i].selected,
+ "Options[" + i + "].selected should be the same in select test " +
+ aTestNumber);
+ is(aSel1.options[i].defaultSelected, sel2.options[i].defaultSelected,
+ "Options[" + i +
+ "].defaultSelected should be the same in select test " +
+ aTestNumber);
+ }
+}
+
+// In a single-select, setting an option selected should deselect an
+// existing selected option.
+var sel = document.createElement("select");
+sel.appendChild(new Option());
+is(sel.selectedIndex, 0, "First option should be selected");
+assertSelected(sel.firstChild, false, true);
+sel.appendChild(new Option());
+is(sel.selectedIndex, 0, "First option should still be selected");
+assertSelected(sel.firstChild, false, true);
+assertSelected(sel.firstChild.nextSibling, false, false);
+
+opt = new Option();
+sel.appendChild(opt);
+opt.defaultSelected = true;
+assertSelected(sel.firstChild, false, false);
+assertSelected(sel.firstChild.nextSibling, false, false);
+assertSelected(opt, true, true);
+is(opt, sel.firstChild.nextSibling.nextSibling, "What happened here?");
+is(sel.options[0], sel.firstChild, "Unexpected option 0");
+is(sel.options[1], sel.firstChild.nextSibling, "Unexpected option 1");
+is(sel.options[2], opt, "Unexpected option 2");
+is(sel.selectedIndex, 2, "Unexpected selectedIndex in select test 1");
+
+assertSame(sel, "<select><option><option><option selected></select>", 1);
+
+// Same, but with the option that gets set selected earlier in the select
+sel = document.createElement("select");
+sel.appendChild(new Option());
+sel.appendChild(new Option());
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+opt = new Option();
+sel.options[0] = opt;
+opt.defaultSelected = true;
+assertSelected(sel.options[0], true, true);
+assertSelected(sel.options[1], false, false);
+assertSelected(sel.options[2], true, false);
+is(sel.selectedIndex, 0, "Unexpected selectedIndex in select test 2");
+
+// And now try unselecting options
+sel = document.createElement("select");
+sel.appendChild(new Option());
+opt = new Option();
+opt.defaultSelected = true;
+sel.appendChild(opt);
+sel.appendChild(new Option());
+opt.defaultSelected = false;
+
+assertSelected(sel.options[0], false, true);
+assertSelected(sel.options[1], false, false);
+assertSelected(sel.options[2], false, false);
+is(sel.selectedIndex, 0, "Unexpected selectedIndex in select test 2");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug401160.xhtml b/dom/html/test/test_bug401160.xhtml
new file mode 100644
index 0000000000..bb9fe47111
--- /dev/null
+++ b/dom/html/test/test_bug401160.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=401160
+-->
+<head>
+ <title>Test for Bug 401160</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=401160">Mozilla Bug 401160</a>
+<label id="label" contenteditable="true"><legend></legend><div></div></label>
+
+<pre id="test">
+<script type="text/javascript">
+
+function do_test() {
+ document.getElementById('label').focus();
+ ok(true, "This is crash test - the test succeeded if we reach this line")
+}
+
+do_test();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug402680.html b/dom/html/test/test_bug402680.html
new file mode 100644
index 0000000000..517942772e
--- /dev/null
+++ b/dom/html/test/test_bug402680.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=402680
+-->
+<head>
+ <title>Test for Bug 402680</title>
+ <script>
+ var activeElementIsNull = (document.activeElement == null);
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=402680">Mozilla Bug 402680</a>
+<p id="display"></p>
+<div id="content">
+ <input type="text">
+ <textarea></textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 402680 **/
+
+ok(activeElementIsNull,
+ "Before document has body, active element should be null");
+
+function testActiveElement() {
+ ok(document.body == document.activeElement,
+ "After page load body element should be the active element!");
+ var input = document.getElementsByTagName("input")[0];
+ input.focus();
+ ok(document.activeElement == input,
+ "Input element isn't the active element!");
+ var textarea = document.getElementsByTagName("textarea")[0];
+ textarea.focus();
+ ok(document.activeElement == textarea,
+ "Textarea element isn't the active element!");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testActiveElement);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug403868.html b/dom/html/test/test_bug403868.html
new file mode 100644
index 0000000000..43118a0683
--- /dev/null
+++ b/dom/html/test/test_bug403868.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=403868
+-->
+<head>
+ <title>Test for Bug 403868</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=403868">Mozilla Bug 403868</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 403868 **/
+function createSpan(id, insertionPoint) {
+ var s = document.createElement("span");
+ s.id = id;
+ $("content").insertBefore(s, insertionPoint);
+ return s;
+}
+
+var s1a = createSpan("test1", null);
+is(document.getElementById("test1"), s1a,
+ "Only one span with id=test1 in the tree; should work!");
+
+var s2a = createSpan("test1", null);
+is(document.getElementById("test1"), s1a,
+ "Appending span with id=test1 doesn't change which one comes first");
+
+var s3a = createSpan("test1", s2a);
+is(document.getElementById("test1"), s1a,
+ "Inserting span with id=test1 not at the beginning; doesn't matter");
+
+var s4a = createSpan("test1", s1a);
+is(document.getElementById("test1"), s4a,
+ "Inserting span with id=test1 at the beginning changes which one is first");
+
+s4a.remove();
+is(document.getElementById("test1"), s1a,
+ "First-created span with id=test1 is first again");
+
+s1a.remove();
+is(document.getElementById("test1"), s3a,
+ "Third-created span with id=test1 is first now");
+
+// Start the id hashtable
+for (var i = 0; i < 256; ++i) {
+ document.getElementById("no-such-id-in-the-document" + i);
+}
+
+var s1b = createSpan("test2", null);
+is(document.getElementById("test2"), s1b,
+ "Only one span with id=test2 in the tree; should work!");
+
+var s2b = createSpan("test2", null);
+is(document.getElementById("test2"), s1b,
+ "Appending span with id=test2 doesn't change which one comes first");
+
+var s3b = createSpan("test2", s2b);
+is(document.getElementById("test2"), s1b,
+ "Inserting span with id=test2 not at the beginning; doesn't matter");
+
+var s4b = createSpan("test2", s1b);
+is(document.getElementById("test2"), s4b,
+ "Inserting span with id=test2 at the beginning changes which one is first");
+
+s4b.remove();
+is(document.getElementById("test2"), s1b,
+ "First-created span with id=test2 is first again");
+
+s1b.remove();
+is(document.getElementById("test2"), s3b,
+ "Third-created span with id=test2 is first now");
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug403868.xhtml b/dom/html/test/test_bug403868.xhtml
new file mode 100644
index 0000000000..53c2a24d57
--- /dev/null
+++ b/dom/html/test/test_bug403868.xhtml
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=403868
+-->
+<head>
+ <title>Test for Bug 403868</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=403868">Mozilla Bug 403868</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 403868 **/
+function createSpan(id, insertionPoint) {
+ var s = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
+ s.id = id;
+ $("content").insertBefore(s, insertionPoint);
+ return s;
+}
+
+var s1a = createSpan("test1", null);
+is(document.getElementById("test1"), s1a,
+ "Only one span with id=test1 in the tree; should work!");
+
+var s2a = createSpan("test1", null);
+is(document.getElementById("test1"), s1a,
+ "Appending span with id=test1 doesn't change which one comes first");
+
+var s3a = createSpan("test1", s2a);
+is(document.getElementById("test1"), s1a,
+ "Inserting span with id=test1 not at the beginning; doesn't matter");
+
+var s4a = createSpan("test1", s1a);
+is(document.getElementById("test1"), s4a,
+ "Inserting span with id=test1 at the beginning changes which one is first");
+
+s4a.remove();
+is(document.getElementById("test1"), s1a,
+ "First-created span with id=test1 is first again");
+
+s1a.remove();
+is(document.getElementById("test1"), s3a,
+ "Third-created span with id=test1 is first now");
+
+// Start the id hashtable
+for (var i = 0; i < 256; ++i) {
+ document.getElementById("no-such-id-in-the-document" + i);
+}
+
+var s1b = createSpan("test2", null);
+is(document.getElementById("test2"), s1b,
+ "Only one span with id=test2 in the tree; should work!");
+
+var s2b = createSpan("test2", null);
+is(document.getElementById("test2"), s1b,
+ "Appending span with id=test2 doesn't change which one comes first");
+
+var s3b = createSpan("test2", s2b);
+is(document.getElementById("test2"), s1b,
+ "Inserting span with id=test2 not at the beginning; doesn't matter");
+
+var s4b = createSpan("test2", s1b);
+is(document.getElementById("test2"), s4b,
+ "Inserting span with id=test2 at the beginning changes which one is first");
+
+s4b.remove();
+is(document.getElementById("test2"), s1b,
+ "First-created span with id=test2 is first again");
+
+s1b.remove();
+is(document.getElementById("test2"), s3b,
+ "Third-created span with id=test2 is first now");
+
+]]>
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug405242.html b/dom/html/test/test_bug405242.html
new file mode 100644
index 0000000000..b8999dc9f6
--- /dev/null
+++ b/dom/html/test/test_bug405242.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=405242
+-->
+<head>
+ <title>Test for Bug 405252</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=405242">Mozilla Bug 405242</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 405242 **/
+var sel = document.createElement("select");
+sel.appendChild(new Option());
+sel.appendChild(new Option());
+sel.appendChild(new Option());
+opt = new Option();
+opt.value = 10;
+sel.appendChild(opt);
+sel.options.remove(0);
+sel.options.remove(1000);
+sel.options.remove(-1);
+is(sel.length, 3, "Unexpected option collection length");
+is(sel[2].value, "10", "Unexpected remained option");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug406596.html b/dom/html/test/test_bug406596.html
new file mode 100644
index 0000000000..6886d078be
--- /dev/null
+++ b/dom/html/test/test_bug406596.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=406596
+-->
+<head>
+ <title>Test for Bug 406596</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=406596">Mozilla Bug 406596</a>
+<div id="content">
+ <div id="edit" contenteditable="true">This text is editable, you can change its content <a href="#" id="a" tabindex="0">ABCDEFGHIJKLMNOPQRSTUV</a> <input type="submit" value="abcd" id="b"></input> <img src="foo.png" id="c"></div>
+ <div tabindex="0">This text is not editable but is focusable</div>
+ <div tabindex="0">This text is not editable but is focusable</div>
+ <a href="#" id="d" contenteditable="true">ABCDEFGHIJKLMNOPQRSTUV</a>
+ <div tabindex="0">This text is not editable but is focusable</div>
+ <div tabindex="0">This text is not editable but is focusable</div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 406596 **/
+
+function testTabbing(click, focus, selectionOffset) {
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+
+ var elem = document.getElementById(click);
+ var rect = elem.getBoundingClientRect();
+ var selection = window.getSelection();
+
+ var x = (rect.left + rect.right) / 4;
+ var y = (rect.top + rect.bottom) / 2;
+ wu.sendMouseEvent("mousedown", x, y, 0, 1, 0);
+ wu.sendMouseEvent("mousemove", x + selectionOffset, y, 0, 1, 0);
+ wu.sendMouseEvent("mouseup", x + selectionOffset, y, 0, 1, 0);
+ if (selectionOffset) {
+ is(selection.rangeCount, 1, "there should be one range in the selection");
+ var range = selection.getRangeAt(0);
+ }
+ var focusedElement = document.activeElement;
+ is(focusedElement, document.getElementById(focus),
+ "clicking should move focus to the contentEditable node");
+ synthesizeKey("KEY_Tab");
+ synthesizeKey("KEY_Tab");
+ synthesizeKey("KEY_Tab", {shiftKey: true});
+ synthesizeKey("KEY_Tab", {shiftKey: true});
+ is(document.activeElement, focusedElement,
+ "tab/shift-tab should move focus back to the contentEditable node");
+ if (selectionOffset) {
+ is(selection.rangeCount, 1,
+ "there should still be one range in the selection");
+ var newRange = selection.getRangeAt(0);
+ is(newRange.compareBoundaryPoints(Range.START_TO_START, range), 0,
+ "the selection should be the same as before the tabbing");
+ is(newRange.compareBoundaryPoints(Range.END_TO_END, range), 0,
+ "the selection should be the same as before the tabbing");
+ }
+}
+
+function test() {
+ window.getSelection().removeAllRanges();
+ testTabbing("edit", "edit", 0);
+ testTabbing("a", "edit", 0);
+ testTabbing("d", "d", 0);
+ testTabbing("edit", "edit", 10);
+ testTabbing("a", "edit", 10);
+ testTabbing("d", "d", 10);
+
+ SimpleTest.finish();
+}
+
+window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ setTimeout(test, 0);
+};
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug417760.html b/dom/html/test/test_bug417760.html
new file mode 100644
index 0000000000..52a3c1b425
--- /dev/null
+++ b/dom/html/test/test_bug417760.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=417760
+-->
+<head>
+ <title>cannot focus() img with tabindex="-1"</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ img {
+ border: 5px solid white;
+ }
+ img:focus {
+ border: 5px solid black;
+ }
+ </style>
+
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="text/javascript">
+ function checkFocus(aExpected, aTabIndex)
+ {
+ elemCurr = document.activeElement.getAttribute("id");
+ is(elemCurr, aExpected, "Element with tabIndex " + aTabIndex
+ + " did not receive focus!");
+ }
+
+ function doTest()
+ {
+ // First, test img with tabindex = 0
+ document.getElementById("img-tabindex-0").focus();
+ checkFocus("img-tabindex-0", 0);
+
+ // now test the img with tabindex = -1
+ document.getElementById("img-tabindex-minus-1").focus();
+ checkFocus("img-tabindex-minus-1", -1);
+
+ // now test the img without tabindex, should NOT receive focus!
+ document.getElementById("img-no-tabindex").focus();
+ checkFocus("img-tabindex-minus-1", null);
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ </script>
+</head>
+
+<body>
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=417760">Mozilla Bug 417760</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ </pre>
+ <br>img tabindex="0":
+ <img id="img-tabindex-0"
+ src="file_bug417760.png"
+ alt="MoCo logo" tabindex="0"/>
+ <br>img tabindex="-1":
+ <img id="img-tabindex-minus-1"
+ src="file_bug417760.png"
+ alt="MoCo logo" tabindex="-1"/>
+ <br>img without tabindex:
+ <img id="img-no-tabindex"
+ src="file_bug417760.png"
+ alt="MoCo logo"/>
+</body>
+</html>
diff --git a/dom/html/test/test_bug421640.html b/dom/html/test/test_bug421640.html
new file mode 100644
index 0000000000..c63d026d1f
--- /dev/null
+++ b/dom/html/test/test_bug421640.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=421640
+-->
+<head>
+ <title>Test for Bug 421640</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=421640">Mozilla Bug 421640</a>
+<div id="content">
+ <div id="edit" contenteditable="true">This text is editable</div>
+ <div><button id="button">Test</button></div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 421640 **/
+
+function test(click, focus, nextFocus) {
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+
+ var selection = window.getSelection();
+ var edit = document.getElementById("edit");
+ var text = edit.firstChild;
+
+ selection.removeAllRanges();
+
+ var rect = edit.getBoundingClientRect();
+ wu.sendMouseEvent("mousedown", rect.left + 1, rect.top + 1, 0, 1, 0);
+ wu.sendMouseEvent("mousemove", rect.right - 1, rect.top + 1, 0, 1, 0);
+ wu.sendMouseEvent("mouseup", rect.right - 1, rect.top + 1, 0, 1, 0);
+
+ is(selection.anchorNode, text, "");
+
+ rect = document.getElementById("button").getBoundingClientRect();
+ wu.sendMouseEvent("mousedown", rect.left + 10, rect.top + 1, 0, 1, 0);
+ wu.sendMouseEvent("mouseup", rect.left + 10, rect.top + 1, 0, 1, 0);
+
+ is(selection.anchorNode, text, "");
+
+ SimpleTest.finish();
+}
+
+window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ setTimeout(test, 0);
+};
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug424698.html b/dom/html/test/test_bug424698.html
new file mode 100644
index 0000000000..b59190e53d
--- /dev/null
+++ b/dom/html/test/test_bug424698.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=424698
+-->
+<head>
+ <title>Test for Bug 424698</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=424698">Mozilla Bug 424698</a>
+<p id="display">
+<input id="i1">
+<input id="target">
+<textarea id="i2"></textarea>
+<textarea id="target2"></textarea>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 424698 **/
+var i = $("i1");
+is(i.value, "", "Value should be empty string");
+i.defaultValue = "test";
+is(i.value, "test", "Setting defaultValue should work");
+i.defaultValue = "test2";
+is(i.value, "test2", "Setting defaultValue multiple times should work");
+
+// Now let's hide and reshow things
+i.style.display = "none";
+is(i.offsetWidth, 0, "Input didn't hide?");
+i.style.display = "";
+isnot(i.offsetWidth, 0, "Input didn't show?");
+is(i.value, "test2", "Hiding/showing should not affect value");
+i.defaultValue = "test3";
+is(i.value, "test3", "Setting defaultValue after hide/show should work");
+
+// Make sure typing works ok
+i = $("target");
+i.focus(); // Otherwise editor gets confused when we send the key events
+is(i.value, "", "Value should be empty string in second control");
+sendString("2test2");
+is(i.value, "2test2", 'We just typed the string "2test2"');
+i.defaultValue = "2test3";
+is(i.value, "2test2", "Setting defaultValue after typing should not work");
+i.style.display = "none";
+is(i.offsetWidth, 0, "Second input didn't hide?");
+i.style.display = "";
+isnot(i.offsetWidth, 0, "Second input didn't show?");
+is(i.value, "2test2", "Hiding/showing second input should not affect value");
+i.defaultValue = "2test4";
+is(i.value, "2test2", "Setting defaultValue after hide/show should not work if we typed");
+
+i = $("i2");
+is(i.value, "", "Textarea value should be empty string");
+i.defaultValue = "test";
+is(i.value, "test", "Setting textarea defaultValue should work");
+i.defaultValue = "test2";
+is(i.value, "test2", "Setting textarea defaultValue multiple times should work");
+
+// Now let's hide and reshow things
+i.style.display = "none";
+is(i.offsetWidth, 0, "Textarea didn't hide?");
+i.style.display = "";
+isnot(i.offsetWidth, 0, "Textarea didn't show?");
+is(i.value, "test2", "Hiding/showing textarea should not affect value");
+i.defaultValue = "test3";
+is(i.value, "test3", "Setting textarea defaultValue after hide/show should work");
+
+// Make sure typing works ok
+i = $("target2");
+i.focus(); // Otherwise editor gets confused when we send the key events
+is(i.value, "", "Textarea value should be empty string in second control");
+sendString("2test2");
+is(i.value, "2test2", 'We just typed the string "2test2"');
+i.defaultValue = "2test3";
+is(i.value, "2test2", "Setting textarea defaultValue after typing should not work");
+i.style.display = "none";
+is(i.offsetWidth, 0, "Second textarea didn't hide?");
+i.style.display = "";
+isnot(i.offsetWidth, 0, "Second textarea didn't show?");
+is(i.value, "2test2", "Hiding/showing second textarea should not affect value");
+i.defaultValue = "2test4";
+is(i.value, "2test2", "Setting textarea defaultValue after hide/show should not work if we typed");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug428135.xhtml b/dom/html/test/test_bug428135.xhtml
new file mode 100644
index 0000000000..ce269e2f8c
--- /dev/null
+++ b/dom/html/test/test_bug428135.xhtml
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=428135
+-->
+<head>
+ <title>Test for Bug 428135</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=428135">Mozilla Bug 428135</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 428135 **/
+
+var expectedCurrentTargets = new Array();
+
+function d(el, ename) {
+ var e = document.createEvent("Events");
+ e.initEvent(ename, true, true);
+ el.dispatchEvent(e);
+}
+
+function testListener(e) {
+ e.preventDefault();
+ var expected = expectedCurrentTargets.shift();
+ ok(expected == e.currentTarget,
+ "Unexpected current target [" + e.currentTarget + "], event=" + e.type +
+ ", phase=" + e.eventPhase + ", target should have been " + expected);
+}
+
+function getAndAddListeners(elname) {
+ var el = document;
+ if (elname) {
+ el = document.getElementById(elname);
+ }
+ el.addEventListener("submit", testListener, true);
+ el.addEventListener("submit", testListener);
+ el.addEventListener("reset", testListener, true);
+ el.addEventListener("reset", testListener);
+ el.addEventListener("fooEvent", testListener, true);
+ el.addEventListener("fooEvent", testListener);
+ return el;
+}
+
+function testSubmitResetEvents() {
+ getAndAddListeners(null);
+ var outerForm = getAndAddListeners("outerForm");
+ var outerSubmit = getAndAddListeners("outerSubmit");
+ var outerReset = getAndAddListeners("outerReset");
+ var outerSubmitDispatcher = getAndAddListeners("outerSubmitDispatcher");
+ var outerResetDispatcher = getAndAddListeners("outerResetDispatcher");
+ var outerChild = getAndAddListeners("outerChild");
+ var innerForm = getAndAddListeners("innerForm");
+ var innerSubmit = getAndAddListeners("innerSubmit");
+ var innerReset = getAndAddListeners("innerReset");
+ var innerSubmitDispatcher = getAndAddListeners("innerSubmitDispatcher");
+ var innerResetDispatcher = getAndAddListeners("innerResetDispatcher");
+
+ expectedCurrentTargets = new Array(document, outerForm, outerForm, document);
+ outerSubmit.click();
+ ok(!expectedCurrentTargets.length,
+ "(1) expectedCurrentTargets isn't empty!");
+
+ expectedCurrentTargets = new Array(document, outerForm, outerForm, document);
+ outerReset.click();
+ ok(!expectedCurrentTargets.length,
+ "(2) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, submit shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerSubmitDispatcher, outerSubmitDispatcher);
+ outerSubmitDispatcher.click();
+ ok(!expectedCurrentTargets.length,
+ "(3) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, reset shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerResetDispatcher, outerResetDispatcher);
+ outerResetDispatcher.click();
+ ok(!expectedCurrentTargets.length,
+ "(4) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, submit shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerForm, outerChild);
+ innerSubmit.click();
+ ok(!expectedCurrentTargets.length,
+ "(5) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, reset shouldn't propagate
+ // back to outerForm and document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerForm, outerChild);
+ innerReset.click();
+ ok(!expectedCurrentTargets.length,
+ "(6) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, submit shouldn't propagate
+ // back to inner/outerForm or document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerSubmitDispatcher,
+ innerSubmitDispatcher);
+ innerSubmitDispatcher.click();
+ ok(!expectedCurrentTargets.length,
+ "(7) expectedCurrentTargets isn't empty!");
+
+ // Because of bug 428135, reset shouldn't propagate
+ // back to inner/outerForm or document!
+ expectedCurrentTargets =
+ new Array(document, outerForm, outerChild, innerForm, innerResetDispatcher,
+ innerResetDispatcher);
+ innerResetDispatcher.click();
+ ok(!expectedCurrentTargets.length,
+ "(8) expectedCurrentTargets isn't empty!");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(testSubmitResetEvents);
+addLoadEvent(SimpleTest.finish);
+
+
+]]>
+</script>
+</pre>
+<form id="outerForm">
+ <input type="submit" value="outer" id="outerSubmit"/>
+ <input type="reset" value="reset outer" id="outerReset"/>
+ <input type="button" value="dispatch submit" onclick="d(this, 'submit')"
+ id="outerSubmitDispatcher"/>
+ <input type="button" value="dispatch reset" onclick="d(this, 'reset')"
+ id="outerResetDispatcher"/>
+ <div id="outerChild">
+ <form id="innerForm">
+ <input type="submit" value="inner" id="innerSubmit"/>
+ <input type="reset" value="reset inner" id="innerReset"/>
+ <input type="button" value="dispatch submit" onclick="d(this, 'submit')"
+ id="innerSubmitDispatcher"/>
+ <input type="button" value="dispatch reset" onclick="d(this, 'reset')"
+ id="innerResetDispatcher"/>
+ </form>
+ </div>
+</form>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug430351.html b/dom/html/test/test_bug430351.html
new file mode 100644
index 0000000000..8cee4fe24f
--- /dev/null
+++ b/dom/html/test/test_bug430351.html
@@ -0,0 +1,523 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=430351
+-->
+<head>
+ <title>Test for Bug 430351</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=430351">Mozilla Bug 430351</a>
+<p id="display"></p>
+<div id="content">
+ <div id="parent"></div>
+ <div id="editableParent" contenteditable="true"></div>
+ <iframe id="frame"></iframe>
+ <map name="map"><area></map>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 430351 **/
+
+var focusableElements = [
+ "<a tabindex=\"-1\"></a>",
+ "<a tabindex=\"0\"></a>",
+ "<a tabindex=\"0\" disabled></a>",
+ "<a tabindex=\"1\"></a>",
+ "<a contenteditable=\"true\"></a>",
+
+ "<a href=\"#\"></a>",
+ "<a href=\"#\" tabindex=\"-1\"></a>",
+ "<a href=\"#\" tabindex=\"0\"></a>",
+ "<a href=\"#\" tabindex=\"0\" disabled></a>",
+ "<a href=\"#\" tabindex=\"1\"></a>",
+ "<a href=\"#\" contenteditable=\"true\"></a>",
+ "<a href=\"#\" disabled></a>",
+
+ "<button></button>",
+ "<button tabindex=\"-1\"></button>",
+ "<button tabindex=\"0\"></button>",
+ "<button tabindex=\"1\"></button>",
+ "<button contenteditable=\"true\"></button>",
+
+ "<button type=\"reset\"></button>",
+ "<button type=\"reset\" tabindex=\"-1\"></button>",
+ "<button type=\"reset\" tabindex=\"0\"></button>",
+ "<button type=\"reset\" tabindex=\"1\"></button>",
+ "<button type=\"reset\" contenteditable=\"true\"></button>",
+
+ "<button type=\"submit\"></button>",
+ "<button type=\"submit\" tabindex=\"-1\"></button>",
+ "<button type=\"submit\" tabindex=\"0\"></button>",
+ "<button type=\"submit\" tabindex=\"1\"></button>",
+ "<button type=\"submit\" contenteditable=\"true\"></button>",
+
+ "<div tabindex=\"-1\"></div>",
+ "<div tabindex=\"0\"></div>",
+ "<div tabindex=\"1\"></div>",
+ "<div contenteditable=\"true\"></div>",
+ "<div tabindex=\"0\" disabled></div>",
+
+ "<embed>",
+ "<embed tabindex=\"-1\">",
+ "<embed tabindex=\"0\">",
+ "<embed tabindex=\"0\" disabled>",
+ "<embed tabindex=\"1\">",
+ "<embed disabled>",
+ "<embed contenteditable=\"true\">",
+
+ "<iframe contenteditable=\"true\"></iframe>",
+
+ "<iframe src=\"about:blank\"></iframe>",
+ "<iframe src=\"about:blank\" disabled></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"-1\"></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"0\"></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"0\" disabled></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"1\"></iframe>",
+ "<iframe src=\"about:blank\" contenteditable=\"true\"></iframe>",
+
+ "<iframe></iframe>",
+ "<iframe tabindex=\"-1\"></iframe>",
+ "<iframe tabindex=\"0\"></iframe>",
+ "<iframe tabindex=\"0\" disabled></iframe>",
+ "<iframe tabindex=\"1\"></iframe>",
+ "<iframe disabled></iframe>",
+
+ "<img tabindex=\"-1\">",
+ "<img tabindex=\"0\">",
+ "<img tabindex=\"0\" disabled>",
+ "<img tabindex=\"1\">",
+
+ "<input>",
+ "<input tabindex=\"-1\">",
+ "<input tabindex=\"0\">",
+ "<input tabindex=\"1\">",
+ "<input contenteditable=\"true\">",
+
+ "<input type=\"button\">",
+ "<input type=\"button\" tabindex=\"-1\">",
+ "<input type=\"button\" tabindex=\"0\">",
+ "<input type=\"button\" tabindex=\"1\">",
+ "<input type=\"button\" contenteditable=\"true\">",
+
+ "<input type=\"checkbox\">",
+ "<input type=\"checkbox\" tabindex=\"-1\">",
+ "<input type=\"checkbox\" tabindex=\"0\">",
+ "<input type=\"checkbox\" tabindex=\"1\">",
+ "<input type=\"checkbox\" contenteditable=\"true\">",
+
+ "<input type=\"image\">",
+ "<input type=\"image\" tabindex=\"-1\">",
+ "<input type=\"image\" tabindex=\"0\">",
+ "<input type=\"image\" tabindex=\"1\">",
+ "<input type=\"image\" contenteditable=\"true\">",
+
+ "<input type=\"password\">",
+ "<input type=\"password\" tabindex=\"-1\">",
+ "<input type=\"password\" tabindex=\"0\">",
+ "<input type=\"password\" tabindex=\"1\">",
+ "<input type=\"password\" contenteditable=\"true\">",
+
+ "<input type=\"radio\">",
+ "<input type=\"radio\" tabindex=\"-1\">",
+ "<input type=\"radio\" tabindex=\"0\">",
+ "<input type=\"radio\" tabindex=\"1\">",
+ "<input type=\"radio\" contenteditable=\"true\">",
+ "<input type=\"radio\" checked>",
+ "<form><input type=\"radio\" name=\"foo\"></form>",
+
+ "<input type=\"reset\">",
+ "<input type=\"reset\" tabindex=\"-1\">",
+ "<input type=\"reset\" tabindex=\"0\">",
+ "<input type=\"reset\" tabindex=\"1\">",
+ "<input type=\"reset\" contenteditable=\"true\">",
+
+ "<input type=\"submit\">",
+ "<input type=\"submit\" tabindex=\"-1\">",
+ "<input type=\"submit\" tabindex=\"0\">",
+ "<input type=\"submit\" tabindex=\"1\">",
+ "<input type=\"submit\" contenteditable=\"true\">",
+
+ "<input type=\"text\">",
+ "<input type=\"text\" tabindex=\"-1\">",
+ "<input type=\"text\" tabindex=\"0\">",
+ "<input type=\"text\" tabindex=\"1\">",
+ "<input type=\"text\" contenteditable=\"true\">",
+
+ "<input type=\"number\">",
+ "<input type=\"number\" tabindex=\"-1\">",
+ "<input type=\"number\" tabindex=\"0\">",
+ "<input type=\"number\" tabindex=\"1\">",
+ "<input type=\"number\" contenteditable=\"true\">",
+
+ "<object tabindex=\"-1\"></object>",
+ "<object tabindex=\"0\"></object>",
+ "<object tabindex=\"1\"></object>",
+ "<object contenteditable=\"true\"></object>",
+
+ "<object classid=\"java:a\"></object>",
+ "<object classid=\"java:a\" tabindex=\"-1\"></object>",
+ "<object classid=\"java:a\" tabindex=\"0\"></object>",
+ "<object classid=\"java:a\" tabindex=\"0\" disabled></object>",
+ "<object classid=\"java:a\" tabindex=\"1\"></object>",
+ "<object classid=\"java:a\" disabled></object>",
+ "<object classid=\"java:a\" contenteditable=\"true\"></object>",
+
+ "<select></select>",
+ "<select tabindex=\"-1\"></select>",
+ "<select tabindex=\"0\"></select>",
+ "<select tabindex=\"1\"></select>",
+ "<select contenteditable=\"true\"></select>",
+
+ "<option tabindex='-1'></option>",
+ "<option tabindex='0'></option>",
+ "<option tabindex='1'></option>",
+ "<option contenteditable></option>",
+
+ "<optgroup tabindex='-1'></optgroup>",
+ "<optgroup tabindex='0'></optgroup>",
+ "<optgroup tabindex='1'></optgroup>",
+ "<optgroup contenteditable></optgroup>"
+];
+
+var nonFocusableElements = [
+ "<a></a>",
+ "<a disabled></a>",
+
+ "<button tabindex=\"0\" disabled></button>",
+ "<button disabled></button>",
+
+ "<button type=\"reset\" tabindex=\"0\" disabled></button>",
+ "<button type=\"reset\" disabled></button>",
+
+ "<button type=\"submit\" tabindex=\"0\" disabled></button>",
+ "<button type=\"submit\" disabled></button>",
+
+ "<div></div>",
+ "<div disabled></div>",
+
+ "<img>",
+ "<img disabled>",
+ "<img contenteditable=\"true\">",
+
+ "<img usemap=\"#map\">",
+ "<img usemap=\"#map\" tabindex=\"-1\">",
+ "<img usemap=\"#map\" tabindex=\"0\">",
+ "<img usemap=\"#map\" tabindex=\"0\" disabled>",
+ "<img usemap=\"#map\" tabindex=\"1\">",
+ "<img usemap=\"#map\" disabled>",
+ "<img usemap=\"#map\" contenteditable=\"true\">",
+
+ "<input tabindex=\"0\" disabled>",
+ "<input disabled>",
+
+ "<input type=\"button\" tabindex=\"0\" disabled>",
+ "<input type=\"button\" disabled>",
+
+ "<input type=\"checkbox\" tabindex=\"0\" disabled>",
+ "<input type=\"checkbox\" disabled>",
+
+ "<input type=\"file\" tabindex=\"0\" disabled>",
+ "<input type=\"file\" disabled>",
+
+ "<input type=\"hidden\">",
+ "<input type=\"hidden\" tabindex=\"-1\">",
+ "<input type=\"hidden\" tabindex=\"0\">",
+ "<input type=\"hidden\" tabindex=\"0\" disabled>",
+ "<input type=\"hidden\" tabindex=\"1\">",
+ "<input type=\"hidden\" disabled>",
+ "<input type=\"hidden\" contenteditable=\"true\">",
+
+ "<input type=\"image\" tabindex=\"0\" disabled>",
+ "<input type=\"image\" disabled>",
+
+ "<input type=\"password\" tabindex=\"0\" disabled>",
+ "<input type=\"password\" disabled>",
+
+ "<input type=\"radio\" tabindex=\"0\" disabled>",
+ "<input type=\"radio\" disabled>",
+
+ "<input type=\"reset\" tabindex=\"0\" disabled>",
+ "<input type=\"reset\" disabled>",
+
+ "<input type=\"submit\" tabindex=\"0\" disabled>",
+ "<input type=\"submit\" disabled>",
+
+ "<input type=\"text\" tabindex=\"0\" disabled>",
+ "<input type=\"text\" disabled>",
+
+ "<object></object>",
+
+ "<select tabindex=\"0\" disabled></select>",
+ "<select disabled></select>",
+
+ "<option></option>",
+ "<option tabindex='1' disabled></option>",
+
+ "<optgroup></optgroup>",
+ "<optgroup tabindex='1' disabled></optgroup>"
+];
+
+var focusableInContentEditable = [
+ "<button></button>",
+ "<button tabindex=\"-1\"></button>",
+ "<button tabindex=\"0\"></button>",
+ "<button tabindex=\"1\"></button>",
+ "<button contenteditable=\"true\"></button>",
+
+ "<button type=\"reset\"></button>",
+ "<button type=\"reset\" tabindex=\"-1\"></button>",
+ "<button type=\"reset\" tabindex=\"0\"></button>",
+ "<button type=\"reset\" tabindex=\"1\"></button>",
+ "<button type=\"reset\" contenteditable=\"true\"></button>",
+
+ "<button type=\"submit\"></button>",
+ "<button type=\"submit\" tabindex=\"-1\"></button>",
+ "<button type=\"submit\" tabindex=\"0\"></button>",
+ "<button type=\"submit\" tabindex=\"1\"></button>",
+ "<button type=\"submit\" contenteditable=\"true\"></button>",
+
+ "<div tabindex=\"-1\"></div>",
+ "<div tabindex=\"0\"></div>",
+ "<div tabindex=\"1\"></div>",
+ "<div tabindex=\"0\" disabled></div>",
+
+ "<embed>",
+ "<embed tabindex=\"-1\">",
+ "<embed tabindex=\"0\">",
+ "<embed tabindex=\"0\" disabled>",
+ "<embed tabindex=\"1\">",
+ "<embed disabled>",
+ "<embed contenteditable=\"true\">",
+
+ "<iframe src=\"about:blank\"></iframe>",
+ "<iframe></iframe>",
+ "<iframe src=\"about:blank\" disabled></iframe>",
+ "<iframe disabled></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"-1\"></iframe>",
+ "<iframe tabindex=\"-1\"></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"0\"></iframe>",
+ "<iframe tabindex=\"0\"></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"0\" disabled></iframe>",
+ "<iframe tabindex=\"0\" disabled></iframe>",
+ "<iframe src=\"about:blank\" tabindex=\"1\"></iframe>",
+ "<iframe tabindex=\"1\"></iframe>",
+ "<iframe src=\"about:blank\" contenteditable=\"true\"></iframe>",
+ "<iframe contenteditable=\"true\"></iframe>",
+
+ "<img tabindex=\"-1\">",
+ "<img tabindex=\"0\">",
+ "<img tabindex=\"0\" disabled>",
+ "<img tabindex=\"1\">",
+
+ "<input>",
+ "<input tabindex=\"-1\">",
+ "<input tabindex=\"0\">",
+ "<input tabindex=\"1\">",
+ "<input contenteditable=\"true\">",
+
+ "<input type=\"button\">",
+ "<input type=\"button\" tabindex=\"-1\">",
+ "<input type=\"button\" tabindex=\"0\">",
+ "<input type=\"button\" tabindex=\"1\">",
+ "<input type=\"button\" contenteditable=\"true\">",
+
+ "<input type=\"file\">",
+ "<input type=\"file\" tabindex=\"-1\">",
+ "<input type=\"file\" tabindex=\"0\">",
+ "<input type=\"file\" tabindex=\"1\">",
+ "<input type=\"file\" contenteditable=\"true\">",
+
+ "<input type=\"checkbox\">",
+ "<input type=\"checkbox\" tabindex=\"-1\">",
+ "<input type=\"checkbox\" tabindex=\"0\">",
+ "<input type=\"checkbox\" tabindex=\"1\">",
+ "<input type=\"checkbox\" contenteditable=\"true\">",
+
+ "<input type=\"image\">",
+ "<input type=\"image\" tabindex=\"-1\">",
+ "<input type=\"image\" tabindex=\"0\">",
+ "<input type=\"image\" tabindex=\"1\">",
+ "<input type=\"image\" contenteditable=\"true\">",
+
+ "<input type=\"password\">",
+ "<input type=\"password\" tabindex=\"-1\">",
+ "<input type=\"password\" tabindex=\"0\">",
+ "<input type=\"password\" tabindex=\"1\">",
+ "<input type=\"password\" contenteditable=\"true\">",
+
+ "<input type=\"radio\">",
+ "<input type=\"radio\" tabindex=\"-1\">",
+ "<input type=\"radio\" tabindex=\"0\">",
+ "<input type=\"radio\" tabindex=\"1\">",
+ "<input type=\"radio\" contenteditable=\"true\">",
+ "<input type=\"radio\" checked>",
+ "<form><input type=\"radio\" name=\"foo\"></form>",
+
+ "<input type=\"reset\">",
+ "<input type=\"reset\" tabindex=\"-1\">",
+ "<input type=\"reset\" tabindex=\"0\">",
+ "<input type=\"reset\" tabindex=\"1\">",
+ "<input type=\"reset\" contenteditable=\"true\">",
+
+ "<input type=\"submit\">",
+ "<input type=\"submit\" tabindex=\"-1\">",
+ "<input type=\"submit\" tabindex=\"0\">",
+ "<input type=\"submit\" tabindex=\"1\">",
+ "<input type=\"submit\" contenteditable=\"true\">",
+
+ "<input type=\"text\">",
+ "<input type=\"text\" tabindex=\"-1\">",
+ "<input type=\"text\" tabindex=\"0\">",
+ "<input type=\"text\" tabindex=\"1\">",
+ "<input type=\"text\" contenteditable=\"true\">",
+
+ "<input type=\"number\">",
+ "<input type=\"number\" tabindex=\"-1\">",
+ "<input type=\"number\" tabindex=\"0\">",
+ "<input type=\"number\" tabindex=\"1\">",
+ "<input type=\"number\" contenteditable=\"true\">",
+
+ "<object tabindex=\"-1\"></object>",
+ "<object tabindex=\"0\"></object>",
+ "<object tabindex=\"1\"></object>",
+
+ // Disabled doesn't work for <object>.
+ "<object tabindex=\"0\" disabled></object>",
+ "<object disabled></object>",
+
+ "<select></select>",
+ "<select tabindex=\"-1\"></select>",
+ "<select tabindex=\"0\"></select>",
+ "<select tabindex=\"1\"></select>",
+ "<select contenteditable=\"true\"></select>",
+
+ "<option tabindex='-1'></option>",
+ "<option tabindex='0'></option>",
+ "<option tabindex='1'></option>",
+
+ "<optgroup tabindex='-1'></optgroup>",
+ "<optgroup tabindex='0'></optgroup>",
+ "<optgroup tabindex='1'></optgroup>"
+];
+
+var focusableInDesignMode = [
+ "<embed>",
+ "<embed tabindex=\"-1\">",
+ "<embed tabindex=\"0\">",
+ "<embed tabindex=\"0\" disabled>",
+ "<embed tabindex=\"1\">",
+ "<embed disabled>",
+ "<embed contenteditable=\"true\">",
+
+ "<img tabindex=\"-1\">",
+ "<img tabindex=\"0\">",
+ "<img tabindex=\"0\" disabled>",
+ "<img tabindex=\"1\">",
+];
+
+// Can't currently test these, need a plugin.
+var focusableElementsTODO = [
+ "<object classid=\"java:a\"></object>",
+ "<object classid=\"java:a\" tabindex=\"-1\"></object>",
+ "<object classid=\"java:a\" tabindex=\"0\"></object>",
+ "<object classid=\"java:a\" tabindex=\"0\" disabled></object>",
+ "<object classid=\"java:a\" tabindex=\"1\"></object>",
+ "<object classid=\"java:a\" disabled></object>",
+ "<object classid=\"java:a\" contenteditable=\"true\"></object>",
+];
+
+var serializer = new XMLSerializer();
+
+function testElements(parent, tags, shouldBeFocusable)
+{
+ var focusable, errorSuffix = "";
+ if (parent.ownerDocument.designMode == "on") {
+ focusable = focusableInDesignMode;
+ errorSuffix = " in a document with designMode=on";
+ }
+ else if (parent.contentEditable == "true") {
+ focusable = focusableInContentEditable;
+ }
+
+ for (var tag of tags) {
+ parent.ownerDocument.body.focus();
+
+ if (focusableElementsTODO.indexOf(tag) > -1) {
+ todo_is(parent.ownerDocument.activeElement, parent.firstChild,
+ tag + " should be focusable" + errorSuffix);
+ continue;
+ }
+
+ parent.innerHTML = tag;
+
+ // Focus the deepest descendant.
+ var descendant = parent;
+ while ((descendant = descendant.firstChild))
+ element = descendant;
+
+ if (element.nodeName == "IFRAME")
+ var foo = element.contentDocument;
+
+ element.focus();
+
+ var errorPrefix = serializer.serializeToString(element) + " in " +
+ serializer.serializeToString(parent);
+
+ try {
+ // Make sure activeElement doesn't point to a
+ // native anonymous element.
+ parent.ownerDocument.activeElement.localName;
+ } catch (ex) {
+ ok(false, ex + errorPrefix + errorSuffix);
+ }
+ if (focusable ? focusable.indexOf(tag) > -1 : shouldBeFocusable) {
+ is(parent.ownerDocument.activeElement, element,
+ errorPrefix + " should be focusable" + errorSuffix);
+ }
+ else {
+ isnot(parent.ownerDocument.activeElement, element,
+ errorPrefix + " should not be focusable" + errorSuffix);
+ }
+
+ parent.innerHTML = "";
+ }
+}
+
+function test()
+{
+ var parent = document.getElementById("parent");
+ var editableParent = document.getElementById("editableParent");
+
+ testElements(parent, focusableElements, true);
+ testElements(parent, nonFocusableElements, false);
+
+ testElements(editableParent, focusableElements, true);
+ testElements(editableParent, nonFocusableElements, false);
+
+ var frame = document.getElementById("frame");
+ frame.contentDocument.body.innerHTML = document.getElementById("content").innerHTML;
+ frame.contentDocument.designMode = "on";
+ parent = frame.contentDocument.getElementById("parent");
+ editableParent = frame.contentDocument.getElementById("editableParent");
+
+ testElements(parent, focusableElements, false);
+ testElements(parent, nonFocusableElements, false);
+
+ testElements(editableParent, focusableElements, false);
+ testElements(editableParent, nonFocusableElements, false);
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(test);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug435128.html b/dom/html/test/test_bug435128.html
new file mode 100644
index 0000000000..0f4cf7cdb0
--- /dev/null
+++ b/dom/html/test/test_bug435128.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=435128
+-->
+<head>
+ <title>Test for Bug 435128</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435128">Mozilla Bug 435128</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="content" src="data:text/html;charset=utf-8,%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody%3E%0A%3Ciframe%20id%3D%22a%22%3E%3C/iframe%3E%0A%3Cscript%3E%0Afunction%20doe%28%29%20%7B%0Avar%20x%20%3D%20window.frames%5B0%5D.document%3B%0A%0Avar%20y%3Ddocument.getElementById%28%27a%27%29%3B%0Ay.parentNode.removeChild%28y%29%3B%0A%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0Atry%20%7Bx.write%28%27t%27%29%3B%7D%20catch%28e%29%20%7B%7D%0A%7D%0AsetTimeout%28%27doe%28%29%27%2C20%29%3B%0A%3C/script%3E%0A%3C/body%3E%3C/html%3E" style="width: 1000px; height: 200px;"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 435128 **/
+
+SimpleTest.waitForExplicitFinish();
+
+setTimeout(finish, 60000);
+
+function doe2() {
+ document.getElementById('content').src = document.getElementById('content').src;
+}
+setInterval(doe2, 400);
+
+function finish()
+{
+ ok(true, "This is a mochikit version of a crash test. To complete is to pass.");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug441930.html b/dom/html/test/test_bug441930.html
new file mode 100644
index 0000000000..dcb7926734
--- /dev/null
+++ b/dom/html/test/test_bug441930.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=441930
+-->
+<head>
+ <title>Test for Bug 441930</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=441930">Mozilla Bug 441930</a>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 441930: see bug441930_iframe.html **/
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<p id="display">
+ <iframe src="bug441930_iframe.html"></iframe>
+</p>
+<div id="content" style="display: none">
+</div>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug442801.html b/dom/html/test/test_bug442801.html
new file mode 100644
index 0000000000..1a93d94f14
--- /dev/null
+++ b/dom/html/test/test_bug442801.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=442801
+-->
+<head>
+ <title>Test for Bug 442801</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=442801">Mozilla Bug 442801</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<div contenteditable="true">
+<p id="ce_true" contenteditable="true">contenteditable true</p>
+</div>
+
+<div contenteditable="true">
+<p id="ce_false" contenteditable="false">contenteditable false</p>
+</div>
+
+<div contenteditable="true">
+<p id="ce_empty" contenteditable="">contenteditable empty</p>
+</div>
+
+<div contenteditable="true">
+<p id="ce_inherit" contenteditable="inherit">contenteditable inherit</p>
+</div>
+
+<div contenteditable="true">
+<p id="ce_none" >contenteditable none</p>
+</div>
+
+
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 442801 **/
+
+is(window.getComputedStyle($("ce_true")).getPropertyValue("-moz-user-modify"),
+ "read-write",
+ "parent contenteditable is true, contenteditable is true; user-modify should be read-write");
+is(window.getComputedStyle($("ce_false")).getPropertyValue("-moz-user-modify"),
+ "read-only",
+ "parent contenteditable is true, contenteditable is false; user-modify should be read-only");
+is(window.getComputedStyle($("ce_empty")).getPropertyValue("-moz-user-modify"),
+ "read-write",
+ "parent contenteditable is true, contenteditable is empty; user-modify should be read-write");
+is(window.getComputedStyle($("ce_inherit")).getPropertyValue("-moz-user-modify"),
+ "read-write",
+ "parent contenteditable is true, contenteditable is inherit; user-modify should be read-write");
+is(window.getComputedStyle($("ce_none")).getPropertyValue("-moz-user-modify"),
+ "read-write",
+ "parent contenteditable is true, contenteditable is none; user-modify should be read-write");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug445004.html b/dom/html/test/test_bug445004.html
new file mode 100644
index 0000000000..02fc79f425
--- /dev/null
+++ b/dom/html/test/test_bug445004.html
@@ -0,0 +1,138 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=445004
+-->
+<head>
+ <title>Test for Bug 445004</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=445004">Mozilla Bug 445004</a>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 445004 **/
+is(window.location.hostname, "mochi.test", "Unexpected hostname");
+is(window.location.port, "8888", "Unexpected port; fix testcase");
+
+SimpleTest.waitForExplicitFinish();
+
+var loads = 1;
+
+function loadStarted() {
+ ++loads;
+}
+function loadEnded() {
+ --loads;
+ if (loads == 0) {
+ doTest();
+ }
+}
+
+window.onload = loadEnded;
+
+function getMessage(evt) {
+ ok(evt.data == "start" || evt.data == "end", "Must have start or end");
+ if (evt.data == "start")
+ loadStarted();
+ else
+ loadEnded();
+}
+
+window.addEventListener("message", getMessage);
+
+function checkURI(uri, name, type) {
+ var host = uri.match(/^http:\/\/([a-z.0-9]*)/)[1];
+ var file = uri.match(/([^\/]*).png$/)[1];
+ is(host, file, "Unexpected base URI for test " + name +
+ " when testing " + type);
+}
+
+function checkFrame(num) {
+ // Just snarf our data
+ var outer = SpecialPowers.wrap(window.frames[num]);
+ name = outer.name;
+
+ is(outer.document.baseURI,
+ "http://example.org/tests/dom/html/test/bug445004-outer.html",
+ "Unexpected base URI for " + name);
+
+ var iswrite = name.match(/write/);
+
+ var inner = outer.frames[0];
+ if (iswrite) {
+ is(inner.document.baseURI,
+ "http://example.org/tests/dom/html/test/bug445004-outer.html",
+ "Unexpected inner base URI for " + name);
+ } else {
+ is(inner.document.baseURI,
+ "http://test1.example.org/tests/dom/html/test/bug445004-inner.html",
+ "Unexpected inner base URI for " + name);
+ }
+
+ var isrel = name.match(/rel/);
+ var offsite = name.match(/offsite/);
+
+ if (!iswrite) {
+ if ((isrel && !offsite) || (!isrel && offsite)) {
+ is(inner.location.hostname, outer.location.hostname,
+ "Unexpected hostnames for " + name);
+ } else {
+ isnot(inner.location.hostname, outer.location.hostname,
+ "Unexpected hostnames for " + name);
+ }
+ }
+
+ checkURI(inner.frames[0].location.href, name, "direct location");
+ checkURI(inner.frames[1].document.getElementsByTagName("img")[0].src,
+ name, "direct write");
+ if (!iswrite) {
+ is(inner.frames[1].location.hostname, inner.location.hostname,
+ "Incorrect hostname for " + name + " direct write")
+ }
+ checkURI(inner.frames[2].location.href, name, "indirect location");
+ checkURI(inner.frames[3].document.getElementsByTagName("img")[0].src,
+ name, "indirect write");
+ if (!iswrite) {
+ is(inner.frames[3].location.hostname, outer.location.hostname,
+ "Incorrect hostname for " + name + " indirect write")
+ }
+ checkURI(inner.document.getElementsByTagName("img")[0].src,
+ name, "direct image load");
+}
+
+
+function doTest() {
+ for (var num = 0; num < 5; ++num) {
+ checkFrame(num);
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<p id="display">
+ <iframe
+ src="http://example.org/tests/dom/html/test/bug445004-outer-rel.html"
+ name="bug445004-outer-rel.html"></iframe>
+ <iframe
+ src="http://test1.example.org/tests/dom/html/test/bug445004-outer-rel.html"
+ name="bug445004-outer-rel.html offsite"></iframe>
+ <iframe
+ src="http://example.org/tests/dom/html/test/bug445004-outer-abs.html"
+ name="bug445004-outer-abs.html"></iframe>
+ <iframe
+ src="http://test1.example.org/tests/dom/html/test/bug445004-outer-abs.html"
+ name="bug445004-outer-abs.html offsite"></iframe>
+ <iframe
+ src="http://example.org/tests/dom/html/test/bug445004-outer-write.html"
+ name="bug445004-outer-write.html"></iframe>
+</p>
+</body>
+</html>
diff --git a/dom/html/test/test_bug446483.html b/dom/html/test/test_bug446483.html
new file mode 100644
index 0000000000..9821670da7
--- /dev/null
+++ b/dom/html/test/test_bug446483.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=446483
+-->
+<head>
+ <title>Test for Bug 446483</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=446483">Mozilla Bug 446483</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 446483 **/
+
+function gc() {
+ SpecialPowers.gc();
+}
+
+function runTest() {
+ document.getElementById('display').innerHTML =
+ '<iframe src="bug446483-iframe.html"><\/iframe>\n' +
+ '<iframe src="bug446483-iframe.html"><\/iframe>\n';
+
+ setInterval(gc, 1000);
+
+ setTimeout(function() {
+ document.getElementById('display').innerHTML = '';
+ ok(true, '');
+ SimpleTest.finish();
+ }, 4000);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+addLoadEvent(runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug448166.html b/dom/html/test/test_bug448166.html
new file mode 100644
index 0000000000..45b47fcb3f
--- /dev/null
+++ b/dom/html/test/test_bug448166.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=448166
+-->
+<head>
+ <meta charset="utf-8" />
+ <title>Test for Bug 448166</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448166">Mozilla Bug 448166</a>
+<p id="display">
+ <a id="test" href="http://www.moz&#xdc00;illa.org">should not be Mozilla</a>
+ <a id="control" href="http://www.mozilla.org">should not be Mozilla</a>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 448166 **/
+isnot($("test").href, "http://www.mozilla.org/",
+ "Should notice unpaired surrogate");
+is($("test").href, "http://www.moz�illa.org",
+ "URL parser fails. Href returns original input string");
+
+SimpleTest.doesThrow(() => { new URL($("test").href);}, "URL parser rejects input");
+
+is($("control").href, "http://www.mozilla.org/",
+ "Just making sure .href works");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug448564.html b/dom/html/test/test_bug448564.html
new file mode 100644
index 0000000000..bfb61af8dd
--- /dev/null
+++ b/dom/html/test/test_bug448564.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=448564
+-->
+<head>
+ <title>Test for Bug 448564</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448564">Mozilla Bug 448564</a>
+<p id="display">
+ <iframe src="bug448564-iframe-1.html"></iframe>
+ <iframe src="bug448564-iframe-2.html"></iframe>
+ <iframe src="bug448564-iframe-3.html"></iframe>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 448564 **/
+
+/**
+ * The three iframes are going to be loaded with some dirty constructed forms.
+ * Each of them will be submitted before the load event and a SJS will replace
+ * the frame content with the query string.
+ * Then, on the load event, our test file will check the content of each iframes
+ * and check if the query string were correctly formatted (implying that all
+ * iframes were correctly submitted.
+ */
+
+function checkQueryString(frame) {
+ var queryString = frame.document.body.textContent;
+ is(queryString.split("&").sort().join("&"),
+ "a=aval&b=bval&c=cval&d=dval",
+ "Not all form fields were properly submitted.");
+}
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ checkQueryString(frames[0]);
+ checkQueryString(frames[1]);
+ checkQueryString(frames[2]);
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug456229.html b/dom/html/test/test_bug456229.html
new file mode 100644
index 0000000000..c9d6c36054
--- /dev/null
+++ b/dom/html/test/test_bug456229.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=456229
+-->
+<head>
+ <title>Test for Bug 456229</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=456229">Mozilla Bug 456229</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input id='i' type="search">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 456229 **/
+
+// More checks are done in test_bug551670.html.
+
+var i = document.getElementById('i');
+is(i.type, 'search', "Search state should be recognized");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug458037.xhtml b/dom/html/test/test_bug458037.xhtml
new file mode 100644
index 0000000000..c8ae3e1191
--- /dev/null
+++ b/dom/html/test/test_bug458037.xhtml
@@ -0,0 +1,112 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=458037
+-->
+<head>
+ <title>Test for Bug 458037</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=458037">Mozilla Bug 458037</a>
+<p id="display"></p>
+<div id="content" >
+<div id="a"></div>
+<div id="b" contenteditable="true"></div>
+<div id="c" contenteditable="false"></div>
+<div id="d" contenteditable="inherit"></div>
+<div contenteditable="true">
+ <div id="e"></div>
+</div>
+<div contenteditable="false">
+ <div id="f"></div>
+</div>
+<div contenteditable="true">
+ <div id="g" contenteditable="false"></div>
+</div>
+<div contenteditable="false">
+ <div id="h" contenteditable="true"></div>
+</div>
+<div contenteditable="true">
+ <div id="i" contenteditable="inherit"></div>
+</div>
+<div contenteditable="false">
+ <div id="j" contenteditable="inherit"></div>
+</div>
+<div contenteditable="true">
+ <xul:box>
+ <div id="k"></div>
+ </xul:box>
+</div>
+<div contenteditable="false">
+ <xul:box>
+ <div id="l"></div>
+ </xul:box>
+</div>
+<div contenteditable="true">
+ <xul:box>
+ <div id="m" contenteditable="inherit"></div>
+ </xul:box>
+</div>
+<div contenteditable="false">
+ <xul:box>
+ <div id="n" contenteditable="inherit"></div>
+ </xul:box>
+</div>
+<div id="x"></div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 458037 **/
+
+function test(id, expected) {
+ is(document.getElementById(id).isContentEditable, expected,
+ "Element " + id + " should " + (expected ? "" : "not ") + "be editable");
+}
+
+document.addEventListener("DOMContentLoaded", function() {
+ test("a", false);
+ test("b", true);
+ test("c", false);
+ test("d", false);
+ test("e", true);
+ test("f", false);
+ test("g", false);
+ test("h", true);
+ test("i", true);
+ test("j", false);
+ test("k", true);
+ test("l", false);
+ test("m", true);
+ test("n", false);
+
+ var d = document.getElementById("x");
+ test("x", false);
+ d.setAttribute("contenteditable", "true");
+ test("x", true);
+ d.setAttribute("contenteditable", "false");
+ test("x", false);
+ d.setAttribute("contenteditable", "inherit");
+ test("x", false);
+ d.removeAttribute("contenteditable");
+ test("x", false);
+ d.contentEditable = "true";
+ test("x", true);
+ d.contentEditable = "false";
+ test("x", false);
+ d.contentEditable = "inherit";
+ test("x", false);
+
+ // Make sure that isContentEditable is read-only
+ var origValue = d.isContentEditable;
+ d.isContentEditable = !origValue;
+ is(d.isContentEditable, origValue, "isContentEditable should be read only");
+});
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug460568.html b/dom/html/test/test_bug460568.html
new file mode 100644
index 0000000000..db379e6fcc
--- /dev/null
+++ b/dom/html/test/test_bug460568.html
@@ -0,0 +1,144 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=460568
+-->
+<head>
+ <title>Test for Bug 460568</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=460568">Mozilla Bug 460568</a>
+<p id="display"><a href="" id="anchor">a[href]</a></p>
+<div id="editor">
+ <a href="" id="anchorInEditor">a[href] in editor</a>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 460568 **/
+
+function runTest()
+{
+ var editor = document.getElementById("editor");
+ var anchor = document.getElementById("anchor");
+ var anchorInEditor = document.getElementById("anchorInEditor");
+
+ var focused;
+ anchorInEditor.onfocus = function() { focused = true; };
+
+ function isReallyEditable()
+ {
+ editor.focus();
+ var range = document.createRange();
+ range.selectNodeContents(editor);
+ var prevStr = range.toString();
+
+ var docShell = SpecialPowers.wrap(window).docShell;
+ var controller =
+ docShell.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+ .getInterface(SpecialPowers.Ci.nsISelectionDisplay)
+ .QueryInterface(SpecialPowers.Ci.nsISelectionController);
+ var sel = controller.getSelection(controller.SELECTION_NORMAL);
+ sel.collapse(anchorInEditor, 0);
+ sendString("a");
+ range.selectNodeContents(editor);
+ return prevStr != range.toString();
+ }
+
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("contenteditable", "true");
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+
+ // for bug 502273
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("dummy", "dummy");
+ editor.removeAttribute("dummy");
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true (after dummy attribute was removed)");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("contenteditable", "false");
+ anchorInEditor.focus();
+ is(focused, true, "focus didn't move to element in contenteditable=false");
+ is(isReallyEditable(), false, "can edit by a key event");
+
+ // for bug 502273
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("dummy", "dummy");
+ editor.removeAttribute("dummy");
+ anchorInEditor.focus();
+ is(focused, true, "focus moved to element in contenteditable=true (after dummy attribute was removed)");
+ is(isReallyEditable(), false, "cannot edit by a key event");
+
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("contenteditable", "true");
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+
+ // for bug 502273
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("dummy", "dummy");
+ editor.removeAttribute("dummy");
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true (after dummy attribute was removed)");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+
+ focused = false;
+ anchor.focus();
+ editor.removeAttribute("contenteditable");
+ anchorInEditor.focus();
+ is(focused, true, "focus didn't move to element in contenteditable removed element");
+ is(isReallyEditable(), false, "can edit by a key event");
+
+ focused = false;
+ anchor.focus();
+ editor.contentEditable = true;
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true by property");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+
+ focused = false;
+ anchor.focus();
+ editor.contentEditable = false;
+ anchorInEditor.focus();
+ is(focused, true, "focus didn't move to element in contenteditable=false by property");
+ is(isReallyEditable(), false, "can edit by a key event");
+
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("contenteditable", "true");
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+
+ // for bug 502273
+ focused = false;
+ anchor.focus();
+ editor.setAttribute("dummy", "dummy");
+ editor.removeAttribute("dummy");
+ anchorInEditor.focus();
+ is(focused, false, "focus moved to element in contenteditable=true (after dummy attribute was removed)");
+ is(isReallyEditable(), true, "cannot edit by a key event");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug463104.html b/dom/html/test/test_bug463104.html
new file mode 100644
index 0000000000..c44419120d
--- /dev/null
+++ b/dom/html/test/test_bug463104.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Noninteger coordinates test</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="a" style="position: fixed; left: 5.5px; top: 5.5px; width: 100px; height: 100px; background: blue"></div>
+<p style="margin-top: 110px">
+<script>
+var a = document.getElementById("a");
+isnot(a, document.elementFromPoint(5, 5), "a shouldn't be found");
+isnot(a, document.elementFromPoint(5.25, 5.25), "a shouldn't be found");
+is(a, document.elementFromPoint(5.5, 5.5), "a should be found");
+is(a, document.elementFromPoint(5.75, 5.75), "a should be found");
+is(a, document.elementFromPoint(6, 6), "a should be found");
+is(a, document.elementFromPoint(105, 105), "a should be found");
+is(a, document.elementFromPoint(105.25, 105.25), "a should be found");
+isnot(a, document.elementFromPoint(105.5, 105.5), "a shouldn't be found");
+isnot(a, document.elementFromPoint(105.75, 105.75), "a shouldn't be found");
+isnot(a, document.elementFromPoint(106, 106), "a shouldn't be found");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug478251.html b/dom/html/test/test_bug478251.html
new file mode 100644
index 0000000000..e33e7b04e2
--- /dev/null
+++ b/dom/html/test/test_bug478251.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=478251
+-->
+<head>
+ <title>Test for Bug 478251</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478251">Mozilla Bug 478251</a>
+<p id="display"><iframe id="t"></iframe></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 478251 **/
+var doc = $("t").contentDocument;
+doc.open();
+doc.write();
+doc.close();
+is(doc.documentElement.textContent, "", "Writing || failed");
+
+doc.open();
+doc.write(null);
+doc.close();
+is(doc.documentElement.textContent, "null", "Writing |null| failed");
+
+doc.open();
+doc.write(null, null);
+doc.close();
+is(doc.documentElement.textContent, "nullnull", "Writing |null, null| failed");
+
+doc.open();
+doc.write(undefined);
+doc.close();
+is(doc.documentElement.textContent, "undefined", "Writing |undefined| failed");
+
+doc.open();
+doc.write(undefined, undefined);
+doc.close();
+is(doc.documentElement.textContent, "undefinedundefined", "Writing |undefined, undefined| failed");
+
+doc.open();
+doc.writeln();
+doc.close();
+ok(doc.documentElement.textContent == "\n" || doc.documentElement.textContent == "", "Writing |\\n| failed");
+
+doc.open();
+doc.writeln(null);
+doc.close();
+is(doc.documentElement.textContent, "null\n", "Writing |null\\n| failed");
+
+doc.open();
+doc.writeln(null, null);
+doc.close();
+is(doc.documentElement.textContent, "nullnull\n", "Writing |null, null\\n| failed");
+
+doc.open();
+doc.writeln(undefined);
+doc.close();
+is(doc.documentElement.textContent, "undefined\n", "Writing |undefined\\n| failed");
+
+doc.open();
+doc.writeln(undefined, undefined);
+doc.close();
+is(doc.documentElement.textContent, "undefinedundefined\n", "Writing |undefined, undefined\\n| failed");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug481335.xhtml b/dom/html/test/test_bug481335.xhtml
new file mode 100644
index 0000000000..8fdd145222
--- /dev/null
+++ b/dom/html/test/test_bug481335.xhtml
@@ -0,0 +1,122 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=481335
+-->
+<head>
+ <title>Test for Bug 481335</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ a { color:blue; }
+ a:visited { color:red; }
+ </style>
+ <base href="https://example.com/" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=481335">Mozilla Bug 481335</a>
+<p id="display">
+ <a id="t">A link</a>
+ <iframe id="i"></iframe>
+</p>
+<p id="newparent"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 481335 **/
+SimpleTest.waitForExplicitFinish();
+var rand = Date.now() + "-" + Math.random();
+
+is($("t").href, "",
+ "Unexpected href before set");
+is($("t").href, "",
+ "Unexpected cached href before set");
+
+$("t").setAttribute("href", rand);
+is($("t").href, "https://example.com/" + rand,
+ "Unexpected href after set");
+is($("t").href, "https://example.com/" + rand,
+ "Unexpected cached href after set");
+const unvisitedColor = "rgb(0, 0, 255)";
+const visitedColor = "rgb(255, 0, 0)";
+
+let tests = testIterator();
+function continueTest() {
+ tests.next();
+}
+
+function checkLinkColor(aElmId, aExpectedColor, aMessage) {
+ // Because link coloring is asynchronous, we wait until we get the right
+ // result, or we will time out (resulting in a failure).
+ function getColor() {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ return utils.getVisitedDependentComputedStyle($(aElmId), "", "color");
+ }
+ while (getColor() != aExpectedColor) {
+ requestIdleCallback(continueTest);
+ return false;
+ }
+ is(getColor(), aExpectedColor, aMessage);
+ return true;
+}
+
+let win;
+
+function* testIterator() {
+ // After first load
+ $("newparent").appendChild($("t"));
+ is($("t").href, "https://example.com/" + rand,
+ "Unexpected href after move");
+ is($("t").href, "https://example.com/" + rand,
+ "Unexpected cached href after move");
+ while (!checkLinkColor("t", unvisitedColor, "Should be unvisited now"))
+ yield undefined;
+
+ win.close();
+ win = window.open($("t").href, "_blank");
+
+ // After second load
+ while (!checkLinkColor("t", visitedColor, "Should be visited now"))
+ yield undefined;
+ $("t").pathname = rand;
+ while (!checkLinkColor("t", visitedColor,
+ "Should still be visited after setting pathname to its existing value")) {
+ yield undefined;
+ }
+
+ /* TODO uncomment this test with the landing of bug 534526. See
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=461199#c167
+ $("t").pathname += "x";
+ while (!checkLinkColor("t", unvisitedColor,
+ "Should not be visited after changing pathname")) {
+ yield undefined;
+ }
+ $("t").pathname = $("t").pathname;
+ while (!checkLinkColor("t", unvisitedColor,
+ "Should not be visited after setting unvisited pathname to existing value")) {
+ yield undefined;
+ }
+ */
+
+ win.close();
+ win = window.open($("t").href, "_blank");
+
+ // After third load
+ while (!checkLinkColor("t", visitedColor,
+ "Should be visited now after third load")) {
+ yield undefined;
+ }
+ win.close();
+ SimpleTest.finish();
+}
+
+addLoadEvent(function() {
+ win = window.open($("t").href, "_blank");
+ requestIdleCallback(continueTest);
+});
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug481440.html b/dom/html/test/test_bug481440.html
new file mode 100644
index 0000000000..ab26b63e97
--- /dev/null
+++ b/dom/html/test/test_bug481440.html
@@ -0,0 +1,30 @@
+<!--Test must be in quirks mode for document.all to work-->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=481440
+-->
+<head>
+ <title>Test for Bug 481440</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=481440">Mozilla Bug 481440</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input name="x" id="y">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 481440 **/
+// Do a bunch of getElementById calls to catch hashtables auto-going live
+for (var i = 0; i < 500; ++i) {
+ document.getElementById(i);
+}
+is(document.all.x, document.getElementById("y"),
+ "Unexpected node");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug481647.html b/dom/html/test/test_bug481647.html
new file mode 100644
index 0000000000..b74fb7997e
--- /dev/null
+++ b/dom/html/test/test_bug481647.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=481647
+-->
+<head>
+ <title>Test for Bug 481647</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=481647">Mozilla Bug 481647</a>
+<p id="display">
+ <iframe src="javascript:'aaa'"></iframe>
+ <iframe src="javascript:document.write('aaa'); document.close();"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 481647 **/
+SimpleTest.waitForExplicitFinish()
+
+function testFrame(num) {
+ is(window.frames[num].document.baseURI, document.baseURI,
+ "Unexpected base URI in frame " + num);
+}
+
+addLoadEvent(function() {
+ for (var i = 0; i < 2; ++i) {
+ testFrame(i);
+ }
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug482659.html b/dom/html/test/test_bug482659.html
new file mode 100644
index 0000000000..df2c66a747
--- /dev/null
+++ b/dom/html/test/test_bug482659.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=482659
+-->
+<head>
+ <title>Test for Bug 482659</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=482659">Mozilla Bug 482659</a>
+<p id="display">
+ <iframe></iframe>
+ <iframe src="about:blank"></iframe>
+ <iframe></iframe>
+ <iframe src="about:blank"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 482659 **/
+SimpleTest.waitForExplicitFinish()
+
+function testFrame(num) {
+ is(window.frames[num].document.baseURI, document.baseURI,
+ "Unexpected base URI in frame " + num);
+ is(window.frames[num].document.documentURI, "about:blank",
+ "Unexpected document URI in frame " + num);
+}
+
+function appendScript(doc) {
+ var s = doc.createElement("script");
+ s.textContent = "document.write('executed'); document.close()";
+ doc.body.appendChild(s);
+}
+
+function verifyScriptRan(num) {
+ is(window.frames[num].document.documentElement.textContent, "executed",
+ "write didn't happen in frame " + num);
+}
+
+addLoadEvent(function() {
+/* document.write part of test disabled due to bug 483818
+ appendScript(window.frames[2].document);
+ appendScript(window.frames[3].document);
+
+ verifyScriptRan(2);
+ verifyScriptRan(3);
+*/
+ for (var i = 0; i < 4; ++i) {
+ testFrame(i);
+ }
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug486741.html b/dom/html/test/test_bug486741.html
new file mode 100644
index 0000000000..a20cd44e5e
--- /dev/null
+++ b/dom/html/test/test_bug486741.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=486741
+-->
+<head>
+ <title>Test for Bug 486741</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=486741">Mozilla Bug 486741</a>
+<p id="display"><iframe id="f"></iframe></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 486741 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ var d = $("f").contentDocument;
+ var root = d.documentElement;
+ is(root.tagName, "HTML", "Unexpected root");
+
+ d.open();
+ isnot(d.documentElement, root, "Shouldn't have the old root element");
+
+ d.write("Test");
+ d.close();
+
+ isnot(d.documentElement, root, "Still shouldn't have the old root element");
+ is(d.documentElement.tagName, "HTML", "Unexpected new root after write");
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug489532.html b/dom/html/test/test_bug489532.html
new file mode 100644
index 0000000000..ac28c35482
--- /dev/null
+++ b/dom/html/test/test_bug489532.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=489532
+-->
+<head>
+ <title>Test for Bug 489532</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=489532">Mozilla Bug 489532</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 489532 **/
+try {
+ document.createElement("<div>");
+ ok(false, "Should throw.")
+} catch (e) {
+ is(e.name, "InvalidCharacterError",
+ "Expected InvalidCharacterError.");
+ ok(e instanceof DOMException, "Expected DOMException.");
+ is(e.code, DOMException.INVALID_CHARACTER_ERR,
+ "Expected INVALID_CHARACTER_ERR.");
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug497242.xhtml b/dom/html/test/test_bug497242.xhtml
new file mode 100644
index 0000000000..943c46ddc9
--- /dev/null
+++ b/dom/html/test/test_bug497242.xhtml
@@ -0,0 +1,41 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=497242
+-->
+<head>
+ <title>Test for Bug 497242</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=497242">Mozilla Bug 497242</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form name="foo"/>
+ <form name="foo"/>
+ <form name="bar"/>
+ <form name="bar" xmlns=""/>
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 497242 **/
+is(document.getElementsByName("foo").length, 2,
+ "Should find both forms with name 'foo'");
+is(document.getElementsByName("foo")[0],
+ document.getElementsByTagName("form")[0],
+ "Unexpected first foo");
+is(document.getElementsByName("foo")[1],
+ document.getElementsByTagName("form")[1],
+ "Unexpected second foo");
+is(document.getElementsByName("bar").length, 1,
+ "Should find only the HTML form with name 'bar'");
+is(document.getElementsByName("bar")[0],
+ document.getElementsByTagName("form")[2],
+ "Unexpected bar");
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug499092.html b/dom/html/test/test_bug499092.html
new file mode 100644
index 0000000000..d5d019bc54
--- /dev/null
+++ b/dom/html/test/test_bug499092.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=499092
+-->
+<head>
+ <title>Test for Bug 499092</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=499092">Mozilla Bug 499092</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 499092 **/
+SimpleTest.waitForExplicitFinish();
+var content = document.getElementById("content");
+
+function testHtml() {
+ is(this.contentDocument.title, "HTML OK");
+ SimpleTest.finish();
+}
+
+function testXml() {
+ is(this.contentDocument.title, "XML OK");
+ var iframeHtml = document.createElement("iframe");
+ iframeHtml.onload = testHtml;
+ iframeHtml.src = "bug499092.html";
+ content.appendChild(iframeHtml);
+}
+
+var iframeXml = document.createElement("iframe");
+iframeXml.onload = testXml;
+iframeXml.src = "bug499092.xml";
+content.appendChild(iframeXml);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug500885.html b/dom/html/test/test_bug500885.html
new file mode 100644
index 0000000000..3ab9225a4c
--- /dev/null
+++ b/dom/html/test/test_bug500885.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500885
+-->
+<head>
+ <title>Test for Bug 500885</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500885">Mozilla Bug 500885</a>
+<div>
+ <input id="file" type="file" />
+</div>
+<script type="text/javascript">
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+MockFilePicker.returnValue = MockFilePicker.returnOK;
+
+async function test() {
+ // SpecialPowers.DOMWindowUtils doesn't appear to fire mouseEvents correctly
+ var wu = SpecialPowers.getDOMWindowUtils(window);
+
+ try {
+ var domActivateEvents;
+ var fileInput = document.getElementById("file");
+ var rect = fileInput.getBoundingClientRect();
+
+ fileInput.addEventListener ("DOMActivate", function (e) {
+ ok("detail" in e, "DOMActivate should have .detail");
+ is(e.detail, 1, ".detail should be 1");
+ domActivateEvents++;
+ });
+
+ fileInput.scrollIntoView({ behaviour: "smooth" });
+ await promiseApzFlushedRepaints();
+
+ domActivateEvents = 0;
+ wu.sendMouseEvent("mousedown", rect.left + 5, rect.top + 5, 0, 1, 0);
+ wu.sendMouseEvent("mouseup", rect.left + 5, rect.top + 5, 0, 1, 0);
+ is(domActivateEvents, 1, "click on button should fire 1 DOMActivate event");
+
+ domActivateEvents = 0;
+ wu.sendMouseEvent("mousedown", rect.right - 5, rect.top + 5, 0, 1, 0);
+ wu.sendMouseEvent("mouseup", rect.right - 5, rect.top + 5, 0, 1, 0);
+ is(domActivateEvents, 1, "click on text field should fire 1 DOMActivate event");
+ } finally {
+ SimpleTest.executeSoon(function() {
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ });
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(test);
+
+</script>
+</body>
+
+</html>
diff --git a/dom/html/test/test_bug512367.html b/dom/html/test/test_bug512367.html
new file mode 100644
index 0000000000..35af18a5a1
--- /dev/null
+++ b/dom/html/test/test_bug512367.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=512367
+-->
+<head>
+ <title>Test for Bug 512367</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=512367">Mozilla Bug 512367</a>
+<p id="display">
+ <iframe src="bug369370-popup.png" id="i" style="width:200px; height:200px"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var frame = document.getElementById("i");
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ SpecialPowers.setFullZoom(frame.contentWindow, 1.5);
+
+ setTimeout(function() {
+ synthesizeMouse(frame, 30, 30, {});
+
+ is(SpecialPowers.getFullZoom(frame.contentWindow), 1.5, "Zoom in the image frame should not have been reset");
+
+ SimpleTest.finish();
+ }, 0);
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug514856.html b/dom/html/test/test_bug514856.html
new file mode 100644
index 0000000000..77b8feecbd
--- /dev/null
+++ b/dom/html/test/test_bug514856.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514856
+-->
+<head>
+ <title>Test for Bug 514856</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=514856">Mozilla Bug 514856</a>
+<p id="display"></p>
+<div id="content">
+ <iframe id="testFrame" src="bug514856_iframe.html"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 514856 **/
+
+function beginTest() {
+ var ifr = document.getElementById("testFrame");
+ var win = ifr.contentWindow;
+
+ // After the click, the load event should be fired.
+ ifr.addEventListener('load', function() {
+ testDone();
+ });
+
+ // synthesizeMouse adds getBoundingClientRect left and top to the offsets but
+ // in that particular case, we don't want that.
+ var rect = ifr.getBoundingClientRect();
+ var left = rect.left;
+ var top = rect.top;
+
+ synthesizeMouse(ifr, 10 - left, 10 - top, { type: "mousemove" }, win);
+ synthesizeMouse(ifr, 12 - left, 12 - top, { type: "mousemove" }, win);
+ synthesizeMouse(ifr, 14 - left, 14 - top, { type: "mousemove" }, win);
+ synthesizeMouse(ifr, 16 - left, 16 - top, { }, win);
+}
+
+function testDone() {
+ var ifr = document.getElementById("testFrame");
+ var url = new String(ifr.contentWindow.location);
+
+ is(url.indexOf("?10,10"), -1, "Shouldn't have ?10,10 in the URL!");
+ is(url.indexOf("?12,12"), -1, "Shouldn't have ?12,12 in the URL!");
+ is(url.indexOf("?14,14"), -1, "Shouldn't have ?14,14 in the URL!");
+ isnot(url.indexOf("?16,16"), -1, "Should have ?16,16 in the URL!");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(beginTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug518122.html b/dom/html/test/test_bug518122.html
new file mode 100644
index 0000000000..acb9d78d0a
--- /dev/null
+++ b/dom/html/test/test_bug518122.html
@@ -0,0 +1,126 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=518122
+-->
+<head>
+ <title>Test for Bug 518122</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=518122">Mozilla Bug 518122</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 518122 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+var simple_tests = [ ["foo", "foo"],
+ ["", ""],
+ [null, ""],
+ [undefined , "undefined"],
+ ["\n", "\n"],
+ ["\r", "\n"],
+ ["\rfoo", "\nfoo"],
+ ["foo\r", "foo\n"],
+ ["foo\rbar", "foo\nbar"],
+ ["foo\rbar\r", "foo\nbar\n"],
+ ["\r\n", "\n"],
+ ["\r\nfoo", "\nfoo"],
+ ["foo\r\n", "foo\n"],
+ ["foo\r\nbar", "foo\nbar"],
+ ["foo\r\nbar\r\n", "foo\nbar\n"] ];
+
+var value_append_tests = [ ["foo", "bar", "foobar"],
+ ["foo", "foo", "foofoo"],
+ ["foobar", "bar", "foobarbar"],
+ ["foobar", "foo", "foobarfoo"],
+ ["foo\n", "foo", "foo\nfoo"],
+ ["foo\r", "foo", "foo\nfoo"],
+ ["foo\r\n", "foo", "foo\nfoo"],
+ ["\n", "\n", "\n\n"],
+ ["\r", "\r", "\n\n"],
+ ["\r\n", "\r\n", "\n\n"],
+ ["\r", "\r\n", "\n\n"],
+ ["\r\n", "\r", "\n\n"],
+ [null, null, "null"],
+ [null, undefined, "undefined"],
+ ["", "", ""]
+ ];
+
+
+var simple_tests_for_input = [ ["foo", "foo"],
+ ["", ""],
+ [null, ""],
+ [undefined , "undefined"],
+ ["\n", ""],
+ ["\r", ""],
+ ["\rfoo", "foo"],
+ ["foo\r", "foo"],
+ ["foo\rbar", "foobar"],
+ ["foo\rbar\r", "foobar"],
+ ["\r\n", ""],
+ ["\r\nfoo", "foo"],
+ ["foo\r\n", "foo"],
+ ["foo\r\nbar", "foobar"],
+ ["foo\r\nbar\r\n", "foobar"] ];
+
+var value_append_tests_for_input = [ ["foo", "bar", "foobar"],
+ ["foo", "foo", "foofoo"],
+ ["foobar", "bar", "foobarbar"],
+ ["foobar", "foo", "foobarfoo"],
+ ["foo\n", "foo", "foofoo"],
+ ["foo\r", "foo", "foofoo"],
+ ["foo\r\n", "foo", "foofoo"],
+ ["\n", "\n", ""],
+ ["\r", "\r", ""],
+ ["\r\n", "\r\n", ""],
+ ["\r", "\r\n", ""],
+ ["\r\n", "\r", ""],
+ [null, null, "null"],
+ [null, undefined, "undefined"],
+ ["", "", ""]
+ ];
+function runTestsFor(el, simpleTests, appendTests) {
+ for(var i = 0; i < simpleTests.length; ++i) {
+ el.value = simpleTests[i][0];
+ is(el.value, simpleTests[i][1], "Wrong value (wrap=" + el.getAttribute('wrap') + ", simple_test=" + i + ")");
+ }
+ for (var j = 0; j < appendTests.length; ++j) {
+ el.value = appendTests[j][0];
+ el.value += appendTests[j][1];
+ is(el.value, appendTests[j][2], "Wrong value (wrap=" + el.getAttribute('wrap') + ", value_append_test=" + j + ")");
+ }
+}
+
+function runTests() {
+ var textareas = document.getElementsByTagName("textarea");
+ for (var i = 0; i < textareas.length; ++i) {
+ runTestsFor(textareas[i], simple_tests, value_append_tests);
+ }
+ var input = document.getElementsByTagName("input")[0];
+ runTestsFor(input, simple_tests_for_input, value_append_tests_for_input);
+ // initialize the editor
+ input.focus();
+ input.blur();
+ runTestsFor(input, simple_tests_for_input, value_append_tests_for_input);
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+<textarea cols="30" rows="7" wrap="none"></textarea>
+<textarea cols="30" rows="7" wrap="off"></textarea><br>
+<textarea cols="30" rows="7" wrap="soft"></textarea>
+<textarea cols="30" rows="7" wrap="hard"></textarea>
+<input type="text">
+</body>
+</html>
diff --git a/dom/html/test/test_bug519987.html b/dom/html/test/test_bug519987.html
new file mode 100644
index 0000000000..875368c9b0
--- /dev/null
+++ b/dom/html/test/test_bug519987.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=519987
+-->
+<head>
+ <title>Test for Bug 519987</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=519987">Mozilla Bug 519987</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 519987 **/
+var xmlns = 'http://www.w3.org/1999/xhtml';
+is((new Image()).namespaceURI, xmlns, "Unexpected namespace for new Image()");
+is((new Audio()).namespaceURI, xmlns, "Unexpected namespace for new Audio()");
+var titles = document.getElementsByTagName("title");
+var t = titles[0];
+t.remove();
+document.title = "abcdefg";
+is(titles[0].namespaceURI, xmlns, "Unexpected namespace for new <title>");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug523771.html b/dom/html/test/test_bug523771.html
new file mode 100644
index 0000000000..9f6af3de76
--- /dev/null
+++ b/dom/html/test/test_bug523771.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=523771
+-->
+<head>
+ <title>Test for Bug 523771</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=523771">Mozilla Bug 523771</a>
+<p id="display"></p>
+<iframe name="target_iframe" id="target_iframe"></iframe>
+<form action="form_submit_server.sjs" target="target_iframe" id="form"
+method="POST" enctype="multipart/form-data">
+ <input id=singleFile name=singleFile type=file>
+ <input id=multiFile name=multiFile type=file multiple>
+</form>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+singleFileInput = document.getElementById('singleFile');
+multiFileInput = document.getElementById('multiFile');
+var input1File = { name: "523771_file1", type: "", body: "file1 contents"};
+var input2Files =
+ [{ name: "523771_file2", type: "", body: "second file contents" },
+ { name: "523771_file3.txt", type: "text/plain", body: "123456" },
+ { name: "523771_file4.html", type: "text/html", body: "<html>content</html>" }
+ ];
+
+SimpleTest.waitForExplicitFinish();
+
+function setFileInputs () {
+ var f = createFileWithData(input1File.name, input1File.body, input1File.type);
+ SpecialPowers.wrap(singleFileInput).mozSetFileArray([f]);
+
+ var input2FileNames = [];
+ for (file of input2Files) {
+ f = createFileWithData(file.name, file.body, file.type);
+ input2FileNames.push(f);
+ }
+ SpecialPowers.wrap(multiFileInput).mozSetFileArray(input2FileNames);
+}
+
+function createFileWithData(fileName, fileData, fileType) {
+ return new File([fileData], fileName, { type: fileType });
+}
+
+function cleanupFiles() {
+ singleFileInput.value = "";
+ multiFileInput.value = "";
+}
+
+is(singleFileInput.files.length, 0, "single-file .files.length"); // bug 524421
+is(multiFileInput.files.length, 0, "multi-file .files.length"); // bug 524421
+
+setFileInputs();
+
+is(singleFileInput.multiple, false, "single-file input .multiple");
+is(multiFileInput.multiple, true, "multi-file input .multiple");
+is(singleFileInput.value, 'C:\\fakepath\\' + input1File.name, "single-file input .value");
+is(multiFileInput.value, 'C:\\fakepath\\' + input2Files[0].name, "multi-file input .value");
+is(singleFileInput.files[0].name, input1File.name, "single-file input .files[n].name");
+is(singleFileInput.files[0].size, input1File.body.length, "single-file input .files[n].size");
+is(singleFileInput.files[0].type, input1File.type, "single-file input .files[n].type");
+for(i = 0; i < input2Files.length; ++i) {
+ is(multiFileInput.files[i].name, input2Files[i].name, "multi-file input .files[n].name");
+ is(multiFileInput.files[i].size, input2Files[i].body.length, "multi-file input .files[n].size");
+ is(multiFileInput.files[i].type, input2Files[i].type, "multi-file input .files[n].type");
+}
+
+document.getElementById('form').submit();
+iframe = document.getElementById('target_iframe');
+iframe.onload = function() {
+ response = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ is(response[0].headers["Content-Disposition"],
+ "form-data; name=\"singleFile\"; filename=\"" + input1File.name +
+ "\"",
+ "singleFile Content-Disposition");
+ is(response[0].headers["Content-Type"], input1File.type || "application/octet-stream",
+ "singleFile Content-Type");
+ is(response[0].body, input1File.body, "singleFile body");
+
+ for(i = 0; i < input2Files.length; ++i) {
+ is(response[i + 1].headers["Content-Disposition"],
+ "form-data; name=\"multiFile\"; filename=\"" + input2Files[i].name +
+ "\"",
+ "multiFile Content-Disposition");
+ is(response[i + 1].headers["Content-Type"], input2Files[i].type || "application/octet-stream",
+ "multiFile Content-Type");
+ is(response[i + 1].body, input2Files[i].body, "multiFile body");
+ }
+
+ cleanupFiles();
+
+ is(singleFileInput.files.length, 0, "single-file .files.length"); // bug 524421
+ is(multiFileInput.files.length, 0, "multi-file .files.length"); // bug 524421
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug529819.html b/dom/html/test/test_bug529819.html
new file mode 100644
index 0000000000..4147a341b2
--- /dev/null
+++ b/dom/html/test/test_bug529819.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=529819
+-->
+<head>
+ <title>Test for Bug 529819</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=529819">Mozilla Bug 529819</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<form id="form">
+ <input name="foo" id="foo">
+ <input name="bar" type="radio">
+ <input name="bar" id="bar" type="radio">
+</form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 529819 **/
+is($("form").elements.foo instanceof HTMLInputElement, true, "Should have an element here");
+is($("form").elements.bar instanceof HTMLInputElement, false, "Should have a list here");
+is($("form").elements.bar.length, 2, "Should have a list with two elements here");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug529859.html b/dom/html/test/test_bug529859.html
new file mode 100644
index 0000000000..9d607c8a22
--- /dev/null
+++ b/dom/html/test/test_bug529859.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=529859
+-->
+<head>
+ <title>Test for Bug 529859</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=529859">Mozilla Bug 529859</a>
+<div id="content">
+ <iframe name="target_iframe" id="target_iframe"></iframe>
+ <form action="form_submit_server.sjs" target="target_iframe" id="form"
+ method="POST" enctype="multipart/form-data">
+ <input id="emptyFileInput" name="emptyFileInput" type="file">
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 529859 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ $("target_iframe").onload = function() {
+ var response = JSON.parse(this.contentDocument.documentElement.textContent);
+ is(response.length, 1, "Unexpected number of inputs");
+ is(response[0].headers["Content-Disposition"],
+ "form-data; name=\"emptyFileInput\"; filename=\"\"",
+ "Incorrect content-disposition");
+ is(response[0].headers["Content-Type"], "application/octet-stream",
+ "Unexpected content-type");
+ SimpleTest.finish();
+ }
+ $("form").submit();
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug535043.html b/dom/html/test/test_bug535043.html
new file mode 100644
index 0000000000..3eb046b3c5
--- /dev/null
+++ b/dom/html/test/test_bug535043.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=535043
+-->
+<head>
+ <title>Test for Bug 535043</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=535043">Mozilla Bug 535043</a>
+<p id="display"></p>
+<div id="content">
+ <textarea></textarea>
+ <textarea maxlength="-1"></textarea>
+ <textarea maxlength="0"></textarea>
+ <textarea maxlength="2"></textarea>
+</div>
+<pre id="test">
+<script type="text/javascript">
+
+/** Test for Bug 535043 **/
+function checkTextArea(textArea) {
+ textArea.value = '';
+ textArea.focus();
+ for (var j = 0; j < 3; j++) {
+ sendString("x");
+ }
+ var htmlMaxLength = textArea.getAttribute('maxlength');
+ var domMaxLength = textArea.maxLength;
+ if (htmlMaxLength == null) {
+ is(domMaxLength, -1,
+ 'maxlength is unset but maxLength DOM attribute is not -1');
+ } else if (htmlMaxLength < 0) {
+ // Per the HTML5 spec, out-of-range values are supposed to translate to -1,
+ // not 0, but they don't?
+ is(domMaxLength, -1,
+ 'maxlength is out of range but maxLength DOM attribute is not -1');
+ } else {
+ is(domMaxLength, parseInt(htmlMaxLength),
+ 'maxlength in DOM does not match provided value');
+ }
+ if (textArea.maxLength == -1) {
+ is(textArea.value.length, 3,
+ 'textarea with maxLength -1 should have no length limit');
+ } else {
+ is(textArea.value.length, textArea.maxLength, 'textarea has maxLength ' +
+ textArea.maxLength + ' but length ' + textArea.value.length );
+ }
+}
+
+SimpleTest.waitForFocus(function() {
+ var textAreas = document.getElementsByTagName('textarea');
+ for (var i = 0; i < textAreas.length; i++) {
+ checkTextArea(textAreas[i]);
+ }
+
+ textArea = textAreas[0];
+ testNums = [-42, -1, 0, 2];
+ for (var i = 0; i < testNums.length; i++) {
+ textArea.removeAttribute('maxlength');
+
+ var caught = false;
+ try {
+ textArea.maxLength = testNums[i];
+ } catch (e) {
+ caught = true;
+ }
+ if (testNums[i] < 0) {
+ ok(caught, 'Setting negative maxLength should throw exception');
+ } else {
+ ok(!caught, 'Setting nonnegative maxLength should not throw exception');
+ }
+ checkTextArea(textArea);
+
+ textArea.setAttribute('maxlength', testNums[i]);
+ checkTextArea(textArea);
+ }
+
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug536891.html b/dom/html/test/test_bug536891.html
new file mode 100644
index 0000000000..89bb93d1b0
--- /dev/null
+++ b/dom/html/test/test_bug536891.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=536891
+-->
+<head>
+ <title>Test for Bug 536891</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=536891">Mozilla Bug 536891</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<textarea id="t" maxlength="-2" minlength="-2"></textarea>
+<input id="i" type="text" maxlength="-2" minlength="-2">
+<input id="p" type="password" maxlength="-2" minlength="-2">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 536891 **/
+
+function checkNegativeMinMaxLength(element)
+{
+ for(let type of ["min", "max"]) {
+ /* value is set to -2 initially in the document, see above */
+ is(element[type + "Length"], -1, "negative " + type + "Length should be considered invalid and represented as -1");
+
+ // changing the property to an negative value should throw (see bug 536895).
+ for(let value of [-15, -2147483648]) { // PR_INT32_MIN
+ let threw = false;
+ try {
+ element[type + "Length"] = value;
+ } catch(e) {
+ threw = true;
+ }
+ is(threw, true, "setting " + type + "Length property to " + value + " should throw");
+ }
+ element[type + "Length"] = "non-numerical value";
+ is(element[type + "Length"], 0, "setting " + type + "Length property to a non-numerical value should set it to zero");
+
+
+ element.setAttribute(type + 'Length', -15);
+ is(element[type + "Length"], -1, "negative " + type + "Length is not processed correctly when set dynamically");
+ is(element.getAttribute(type + 'Length'), "-15", type + "Length attribute doesn't return the correct value");
+
+ element.setAttribute(type + 'Length', 0);
+ is(element[type + "Length"], 0, "zero " + type + "Length is not processed correctly");
+ element.setAttribute(type + 'Length', 2147483647); // PR_INT32_MAX
+ is(element[type + "Length"], 2147483647, "negative " + type + "Length is not processed correctly");
+ element.setAttribute(type + 'Length', -2147483648); // PR_INT32_MIN
+ is(element[type + "Length"], -1, "negative " + type + "Length is not processed correctly");
+ element.setAttribute(type + 'Length', 'non-numerical-value');
+ is(element[type + "Length"], -1, "non-numerical value should be considered invalid and represented as -1");
+ }
+}
+
+/* TODO: correct behavior may be checked for email, telephone, url and search input types */
+checkNegativeMinMaxLength(document.getElementById('t'));
+checkNegativeMinMaxLength(document.getElementById('i'));
+checkNegativeMinMaxLength(document.getElementById('p'));
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug536895.html b/dom/html/test/test_bug536895.html
new file mode 100644
index 0000000000..c135432260
--- /dev/null
+++ b/dom/html/test/test_bug536895.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=536895
+-->
+<head>
+ <title>Test for Bug 536895</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=536895">Mozilla Bug 536895</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<textarea id="t"></textarea>
+<input id="i" type="text">
+<input id="p" type="password">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 536895 **/
+
+function checkNegativeMaxLengthException(element)
+{
+ caught = false;
+ try {
+ element.setAttribute('maxLength', -10);
+ } catch(e) {
+ caught = true;
+ }
+ ok(!caught, "Setting maxLength attribute to a negative value shouldn't throw an exception");
+
+ caught = false;
+ try {
+ element.maxLength = -20;
+ } catch(e) {
+ is(e.name, "IndexSizeError", "Should be an IndexSizeError exception");
+ caught = true;
+ }
+ ok(caught, "Setting negative maxLength from the DOM should throw an exception");
+
+ is(element.getAttribute('maxLength'), "-10", "When the exception is raised, the maxLength attribute shouldn't change");
+}
+
+/* TODO: correct behavior may be checked for email, telephone, url and search input types */
+checkNegativeMaxLengthException(document.getElementById('t'));
+checkNegativeMaxLengthException(document.getElementById('i'));
+checkNegativeMaxLengthException(document.getElementById('p'));
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug546995.html b/dom/html/test/test_bug546995.html
new file mode 100644
index 0000000000..ff4d80ec45
--- /dev/null
+++ b/dom/html/test/test_bug546995.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=546995
+-->
+<head>
+ <title>Test for Bug 546995</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=546995">Mozilla Bug 546995</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <select id='s'></select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 546995 **/
+
+/* This test in only testing IDL reflection, another one is testing the behavior */
+
+function checkAutofocusIDLAttribute(element)
+{
+ ok('autofocus' in element, "Element has the autofocus IDL attribute");
+ ok(!element.autofocus, "autofocus default value is false");
+ element.setAttribute('autofocus', 'autofocus');
+ ok(element.autofocus, "autofocus should be enabled");
+ element.removeAttribute('autofocus');
+ ok(!element.autofocus, "autofocus should be disabled");
+}
+
+// TODO: keygen should be added when correctly implemented, see bug 101019.
+checkAutofocusIDLAttribute(document.getElementById('s'));
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug547850.html b/dom/html/test/test_bug547850.html
new file mode 100644
index 0000000000..a2e0323ec8
--- /dev/null
+++ b/dom/html/test/test_bug547850.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=547850
+-->
+<head>
+ <title>Test for Bug 547850</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=547850">Mozilla Bug 547850</a>
+<script>
+document.write("<div id=content><f\u00c5></f\u00c5><r\u00e5></r\u00e5>");
+document.write("<span g\u00c5=a1 t\u00e5=a2></span></div>");
+</script>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var ch = $('content').childNodes;
+is(ch[0].localName, "f\u00c5", "upper case localName");
+is(ch[1].localName, "r\u00e5", "lower case localName");
+is(ch[0].nodeName, "F\u00c5", "upper case nodeName");
+is(ch[1].nodeName, "R\u00e5", "lower case nodeName");
+is(ch[0].tagName, "F\u00c5", "upper case tagName");
+is(ch[1].tagName, "R\u00e5", "lower case tagName");
+is(ch[2].getAttribute("g\u00c5"), "a1", "upper case attr name");
+is(ch[2].getAttribute("t\u00e5"), "a2", "lower case attr name");
+is(ch[2].getAttribute("G\u00c5"), "a1", "upper case attr name");
+is(ch[2].getAttribute("T\u00e5"), "a2", "lower case attr name");
+is(ch[2].getAttribute("g\u00e5"), null, "wrong lower case attr name");
+is(ch[2].getAttribute("t\u00c5"), null, "wrong upper case attr name");
+is($('content').getElementsByTagName("f\u00c5")[0], ch[0], "gEBTN upper case");
+is($('content').getElementsByTagName("f\u00c5").length, 1, "gEBTN upper case length");
+is($('content').getElementsByTagName("r\u00e5")[0], ch[1], "gEBTN lower case");
+is($('content').getElementsByTagName("r\u00e5").length, 1, "gEBTN lower case length");
+is($('content').getElementsByTagName("F\u00c5")[0], ch[0], "gEBTN upper case");
+is($('content').getElementsByTagName("F\u00c5").length, 1, "gEBTN upper case length");
+is($('content').getElementsByTagName("R\u00e5")[0], ch[1], "gEBTN lower case");
+is($('content').getElementsByTagName("R\u00e5").length, 1, "gEBTN lower case length");
+is($('content').getElementsByTagName("f\u00e5").length, 0, "gEBTN wrong upper case");
+is($('content').getElementsByTagName("r\u00c5").length, 0, "gEBTN wrong lower case");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug551846.html b/dom/html/test/test_bug551846.html
new file mode 100644
index 0000000000..4950b1e452
--- /dev/null
+++ b/dom/html/test/test_bug551846.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=551846
+-->
+<head>
+ <title>Test for Bug 551846</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=551846">Mozilla Bug 551846</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <select id='s'>
+ <option>Tulip</option>
+ <option>Lily</option>
+ <option>Gagea</option>
+ <option>Snowflake</option>
+ <option>Ismene</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 551846 **/
+
+function checkSizeReflection(element, defaultValue)
+{
+ is(element.size, defaultValue, "Default size should be " + defaultValue);
+
+ element.setAttribute('size', -15);
+ is(element.size, defaultValue,
+ "The reflecting IDL attribute should return the default value when content attribute value is invalid");
+ is(element.getAttribute('size'), "-15",
+ "The content attribute should containt the previously set value");
+
+ element.setAttribute('size', 0);
+ is(element.size, 0,
+ "0 should be considered as a valid value");
+ is(element.getAttribute('size'), "0",
+ "The content attribute should containt the previously set value");
+
+ element.setAttribute('size', 2147483647); /* PR_INT32_MAX */
+ is(element.size, 2147483647,
+ "PR_INT32_MAX should be considered as a valid value");
+ is(element.getAttribute('size'), "2147483647",
+ "The content attribute should containt the previously set value");
+
+ element.setAttribute('size', -2147483648); /* PR_INT32_MIN */
+ is(element.size, defaultValue,
+ "The reflecting IDL attribute should return the default value when content attribute value is invalid");
+ is(element.getAttribute('size'), "-2147483648",
+ "The content attribute should containt the previously set value");
+
+ element.setAttribute('size', 'non-numerical-value');
+ is(element.size, defaultValue,
+ "The reflecting IDL attribute should return the default value when content attribute value is invalid");
+ is(element.getAttribute('size'), 'non-numerical-value',
+ "The content attribute should containt the previously set value");
+
+ element.setAttribute('size', 4294967294); /* PR_INT32_MAX * 2 */
+ is(element.size, defaultValue,
+ "Value greater than PR_INT32_MAX should be considered as invalid");
+ is(element.getAttribute('size'), "4294967294",
+ "The content attribute should containt the previously set value");
+
+ element.setAttribute('size', -4294967296); /* PR_INT32_MIN * 2 */
+ is(element.size, defaultValue,
+ "The reflecting IDL attribute should return the default value when content attribute value is invalid");
+ is(element.getAttribute('size'), "-4294967296",
+ "The content attribute should containt the previously set value");
+
+ element.size = defaultValue + 1;
+ element.removeAttribute('size');
+ is(element.size, defaultValue,
+ "When the attribute is removed, the size should be the default size");
+
+ element.setAttribute('size', 'foobar');
+ is(element.size, defaultValue,
+ "The reflecting IDL attribute should return the default value when content attribute value is invalid");
+ element.removeAttribute('size');
+ is(element.size, defaultValue,
+ "When the attribute is removed, the size should be the default size");
+}
+
+function checkSetSizeException(element)
+{
+ var caught = false;
+
+ try {
+ element.size = 1;
+ } catch(e) {
+ caught = true;
+ }
+ ok(!caught, "Setting a positive size shouldn't throw an exception");
+
+ caught = false;
+ try {
+ element.size = 0;
+ } catch(e) {
+ caught = true;
+ }
+ ok(!caught, "Setting a size to 0 from the IDL shouldn't throw an exception");
+
+ element.size = 1;
+
+ caught = false;
+ try {
+ element.size = -1;
+ } catch(e) {
+ caught = true;
+ }
+ ok(!caught, "Setting a negative size from the IDL shouldn't throw an exception");
+
+ is(element.size, 0, "The size should now be equal to the minimum non-negative value");
+
+ caught = false;
+ try {
+ element.setAttribute('size', -10);
+ } catch(e) {
+ caught = true;
+ }
+ ok(!caught, "Setting an invalid size in the content attribute shouldn't throw an exception");
+
+ // reverting to defalut
+ element.removeAttribute('size');
+}
+
+function checkSizeWhenChangeMultiple(element, aDefaultNonMultiple, aDefaultMultiple)
+{
+ s.setAttribute('size', -1)
+ is(s.size, aDefaultNonMultiple, "Size IDL attribute should be 1");
+
+ s.multiple = true;
+ is(s.size, aDefaultMultiple, "Size IDL attribute should be 4");
+
+ is(s.getAttribute('size'), "-1", "Size content attribute should be -1");
+
+ s.setAttribute('size', -2);
+ is(s.size, aDefaultMultiple, "Size IDL attribute should be 4");
+
+ s.multiple = false;
+ is(s.size, aDefaultNonMultiple, "Size IDL attribute should be 1");
+
+ is(s.getAttribute('size'), "-2", "Size content attribute should be -2");
+}
+
+var s = document.getElementById('s');
+
+checkSizeReflection(s, 0);
+checkSetSizeException(s);
+
+s.setAttribute('multiple', 'true');
+checkSizeReflection(s, 0);
+checkSetSizeException(s);
+s.removeAttribute('multiple');
+
+checkSizeWhenChangeMultiple(s, 0, 0);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug555567.html b/dom/html/test/test_bug555567.html
new file mode 100644
index 0000000000..0857955275
--- /dev/null
+++ b/dom/html/test/test_bug555567.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=555567
+-->
+<head>
+ <title>Test for Bug 555567</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=555567">Mozilla Bug 555567</a>
+<div id='content' style="display: none">
+ <form>
+ <fieldset>
+ <legend id="a"></legend>
+ </fieldset>
+ <legend id="b"></legend>
+ </form>
+ <legend id="c"></legend>
+</div>
+<pre id="test">
+<p id="display"></p>
+<script type="application/javascript">
+
+/** Test for Bug 555567 **/
+
+var a = document.getElementById('a');
+var b = document.getElementById('b');
+var c = document.getElementById('c');
+
+isnot(a.form, null,
+ "First legend element should have a not null form IDL attribute");
+is(b.form, null,
+ "Second legend element should have a null form IDL attribute");
+is(c.form, null,
+ "Third legend element should have a null form IDL attribute");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug556645.html b/dom/html/test/test_bug556645.html
new file mode 100644
index 0000000000..3c308f9ef6
--- /dev/null
+++ b/dom/html/test/test_bug556645.html
@@ -0,0 +1,73 @@
+<html>
+<head>
+ <title>Test for Bug 556645 and Bug 1848196</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ const object = document.createElement("object");
+ object.setAttribute("type", "text/html");
+ object.setAttribute("width", "200");
+ object.setAttribute("height", "200");
+ document.body.appendChild(object);
+ const promiseLoadObject = new Promise(resolve => {
+ object.addEventListener("load", resolve, {once: true});
+ });
+ object.setAttribute("data", "object_bug556645.html");
+ await promiseLoadObject;
+ runTest(object);
+ object.remove();
+
+ const embed = document.createElement("embed");
+ embed.setAttribute("type", "text/html");
+ embed.setAttribute("width", "200");
+ embed.setAttribute("height", "200");
+ document.body.appendChild(embed);
+ const promiseLoadEmbed = new Promise(resolve => {
+ embed.addEventListener("load", resolve, {once: true});
+ });
+ embed.setAttribute("src", "object_bug556645.html");
+ await promiseLoadEmbed;
+ runTest(embed);
+ embed.remove();
+
+ SimpleTest.finish();
+});
+
+function runTest(aObjectOrEmbed)
+{
+ const desc = `<${aObjectOrEmbed.tagName.toLowerCase()}>`;
+ const childDoc = aObjectOrEmbed.contentDocument || aObjectOrEmbed.getSVGDocument();
+ const body = childDoc.body;
+ is(document.activeElement, document.body, `${desc}: focus in parent before`);
+ is(childDoc.activeElement, body, `${desc}: focus in child before`);
+
+ const button = childDoc.querySelector("button");
+ button.focus();
+ childDoc.defaultView.focus();
+ is(document.activeElement, aObjectOrEmbed, `${desc}: focus in parent after focus()`);
+ is(childDoc.activeElement, button, `${desc}: focus in child after focus()`);
+
+ button.blur();
+ const pbutton = document.getElementById("pbutton");
+ pbutton.focus();
+
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement, aObjectOrEmbed, `${desc}: focus in parent after tab`);
+ is(childDoc.activeElement, childDoc.documentElement, `${desc}: focus in child after tab`);
+
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement, aObjectOrEmbed, `${desc}: focus in parent after tab 2`);
+ is(childDoc.activeElement, button, `${desc}: focus in child after tab 2`);
+}
+
+</script>
+
+<button id="pbutton">Parent</button>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug557087-1.html b/dom/html/test/test_bug557087-1.html
new file mode 100644
index 0000000000..9bd2068e8d
--- /dev/null
+++ b/dom/html/test/test_bug557087-1.html
@@ -0,0 +1,129 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557087
+-->
+<head>
+ <title>Test for Bug 557087</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557087">Mozilla Bug 557087</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script>
+
+/** Test for Bug 557087 **/
+
+function checkDisabledAttribute(aFieldset)
+{
+ ok('disabled' in aFieldset,
+ "fieldset elements should have the disabled attribute");
+
+ ok(!aFieldset.disabled,
+ "fieldset elements disabled attribute should be disabled");
+ is(aFieldset.getAttribute('disabled'), null,
+ "fieldset elements disabled attribute should be disabled");
+
+ aFieldset.disabled = true;
+ ok(aFieldset.disabled,
+ "fieldset elements disabled attribute should be enabled");
+ isnot(aFieldset.getAttribute('disabled'), null,
+ "fieldset elements disabled attribute should be enabled");
+
+ aFieldset.removeAttribute('disabled');
+ aFieldset.setAttribute('disabled', '');
+ ok(aFieldset.disabled,
+ "fieldset elements disabled attribute should be enabled");
+ isnot(aFieldset.getAttribute('disabled'), null,
+ "fieldset elements disabled attribute should be enabled");
+
+ aFieldset.removeAttribute('disabled');
+ ok(!aFieldset.disabled,
+ "fieldset elements disabled attribute should be disabled");
+ is(aFieldset.getAttribute('disabled'), null,
+ "fieldset elements disabled attribute should be disabled");
+}
+
+function checkDisabledPseudoClass(aFieldset)
+{
+ is(document.querySelector(":disabled"), null,
+ "no elements should have :disabled applied to them");
+
+ aFieldset.disabled = true;
+ is(document.querySelector(":disabled"), aFieldset,
+ ":disabled should apply to fieldset elements");
+
+ aFieldset.disabled = false;
+ is(document.querySelector(":disabled"), null,
+ "no elements should have :disabled applied to them");
+}
+
+function checkEnabledPseudoClass(aFieldset)
+{
+ is(document.querySelector(":enabled"), aFieldset,
+ ":enabled should apply to fieldset elements");
+
+ aFieldset.disabled = true;
+ is(document.querySelector(":enabled"), null,
+ "no elements should have :enabled applied to them");
+
+ aFieldset.disabled = false;
+ is(document.querySelector(":enabled"), aFieldset,
+ ":enabled should apply to fieldset elements");
+}
+
+function checkFocus(aFieldset)
+{
+ aFieldset.disabled = true;
+ aFieldset.setAttribute('tabindex', 1);
+
+ aFieldset.focus();
+
+ isnot(document.activeElement, aFieldset,
+ "fieldset can't be focused when disabled");
+ aFieldset.removeAttribute('tabindex');
+ aFieldset.disabled = false;
+}
+
+function checkClickEvent(aFieldset)
+{
+ var clickHandled = false;
+
+ aFieldset.disabled = true;
+
+ aFieldset.addEventListener("click", function(aEvent) {
+ clickHandled = true;
+ }, {once: true});
+
+ sendMouseEvent({type:'click'}, aFieldset);
+ SimpleTest.executeSoon(function() {
+ ok(clickHandled, "When disabled, fieldset should not prevent click events");
+ SimpleTest.finish();
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({
+ set: [["dom.forms.fieldset_disable_only_descendants.enabled", true]]
+}).then(() => {
+ var fieldset = document.createElement("fieldset");
+ var content = document.getElementById('content');
+ content.appendChild(fieldset);
+
+ checkDisabledAttribute(fieldset);
+ checkDisabledPseudoClass(fieldset);
+ checkEnabledPseudoClass(fieldset);
+ checkFocus(fieldset);
+ checkClickEvent(fieldset);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug557087-2.html b/dom/html/test/test_bug557087-2.html
new file mode 100644
index 0000000000..435e924f84
--- /dev/null
+++ b/dom/html/test/test_bug557087-2.html
@@ -0,0 +1,363 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557087
+-->
+<head>
+ <title>Test for Bug 557087</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557087">Mozilla Bug 557087</a>
+<p id="display"></p>
+<div id="content" style="display:none;">
+</div>
+<pre id="test">
+<script>
+
+/** Test for Bug 557087 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var elementsPreventingClick = [ "input", "button", "select", "textarea" ];
+var elementsWithClick = [ "option", "optgroup", "output", "label", "object", "fieldset" ];
+var gHandled = 0;
+
+function clickShouldNotHappenHandler(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldNotHappenHandler);
+ ok(false, "click event should be prevented! (test1)");
+ if (++gHandled >= elementsWithClick.length) {
+ test2();
+ }
+}
+
+function clickShouldNotHappenHandler2(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldNotHappenHandler3);
+ ok(false, "click event should be prevented! (test2)");
+ if (++gHandled >= elementsWithClick.length) {
+ test3();
+ }
+}
+
+function clickShouldNotHappenHandler5(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldNotHappenHandler5);
+ ok(false, "click event should be prevented! (test5)");
+ if (++gHandled >= elementsWithClick.length) {
+ test6();
+ }
+}
+
+function clickShouldNotHappenHandler7(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldNotHappenHandler7);
+ ok(false, "click event should be prevented! (test7)");
+ if (++gHandled >= elementsWithClick.length) {
+ test8();
+ }
+}
+
+function clickShouldHappenHandler(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler);
+ ok(true, "click event has been correctly received (test1)");
+ if (++gHandled >= elementsWithClick.length) {
+ test2();
+ }
+}
+
+function clickShouldHappenHandler2(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler2);
+ ok(true, "click event has been correctly received (test2)");
+ if (++gHandled >= elementsWithClick.length) {
+ test3();
+ }
+}
+
+function clickShouldHappenHandler3(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler3);
+ ok(true, "click event has been correctly received (test3)");
+ if (++gHandled >= (elementsWithClick.length +
+ elementsPreventingClick.length)) {
+ test4();
+ }
+}
+
+function clickShouldHappenHandler4(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler4);
+ ok(true, "click event has been correctly received (test4)");
+ if (++gHandled >= (elementsWithClick.length +
+ elementsPreventingClick.length)) {
+ test5();
+ }
+}
+
+function clickShouldHappenHandler5(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler5);
+ ok(true, "click event has been correctly received (test5)");
+ if (++gHandled >= elementsWithClick.length) {
+ test6();
+ }
+}
+
+function clickShouldHappenHandler6(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler6);
+ ok(true, "click event has been correctly received (test6)");
+ if (++gHandled >= (elementsWithClick.length +
+ elementsPreventingClick.length)) {
+ test7();
+ }
+}
+
+function clickShouldHappenHandler7(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler7);
+ ok(true, "click event has been correctly received (test5)");
+ if (++gHandled >= elementsWithClick.length) {
+ test8();
+ }
+}
+
+function clickShouldHappenHandler8(aEvent)
+{
+ aEvent.target.removeEventListener("click", clickShouldHappenHandler8);
+ ok(true, "click event has been correctly received (test8)");
+ if (++gHandled >= (elementsWithClick.length +
+ elementsPreventingClick.length)) {
+ SimpleTest.finish();
+ }
+}
+
+var fieldset1 = document.createElement("fieldset");
+var fieldset2 = document.createElement("fieldset");
+var legendA = document.createElement("legend");
+var legendB = document.createElement("legend");
+var content = document.getElementById('content');
+fieldset1.disabled = true;
+content.appendChild(fieldset1);
+fieldset1.appendChild(fieldset2);
+
+function clean()
+{
+ var count = fieldset2.children.length;
+ for (var i=0; i<count; ++i) {
+ if (fieldset2.children[i] != legendA &&
+ fieldset2.children[i] != legendB) {
+ fieldset2.removeChild(fieldset2.children[i]);
+ }
+ }
+}
+
+function test1()
+{
+ gHandled = 0;
+
+ // Initialize children without click expected.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldNotHappenHandler);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test2()
+{
+ gHandled = 0;
+ fieldset1.disabled = false;
+ fieldset2.disabled = true;
+
+ // Initialize children without click expected.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldNotHappenHandler2);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler2);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test3()
+{
+ gHandled = 0;
+ fieldset1.disabled = false;
+ fieldset2.disabled = false;
+
+ // All elements should accept the click.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler3);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler3);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test4()
+{
+ gHandled = 0;
+ fieldset1.disabled = false;
+ fieldset2.disabled = true;
+
+ fieldset2.appendChild(legendA);
+
+ // All elements should accept the click.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ legendA.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler4);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ legendA.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler4);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test5()
+{
+ gHandled = 0;
+ fieldset2.insertBefore(legendB, legendA);
+
+ // Initialize children without click expected.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ legendA.appendChild(element);
+ element.addEventListener("click", clickShouldNotHappenHandler5);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ legendA.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler5);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test6()
+{
+ gHandled = 0;
+ fieldset2.removeChild(legendB);
+ fieldset1.disabled = true;
+ fieldset2.disabled = false;
+
+ fieldset1.appendChild(legendA);
+ legendA.appendChild(fieldset2);
+
+ // All elements should accept the click.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler6);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ fieldset2.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler6);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test7()
+{
+ gHandled = 0;
+ fieldset1.disabled = true;
+ fieldset2.disabled = false;
+
+ fieldset1.appendChild(fieldset2);
+ fieldset2.appendChild(legendA);
+
+ // All elements should accept the click.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ legendA.appendChild(element);
+ element.addEventListener("click", clickShouldNotHappenHandler7);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ legendA.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler7);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+function test8()
+{
+ gHandled = 0;
+ fieldset1.disabled = true;
+ fieldset2.disabled = true;
+
+ fieldset1.appendChild(legendA);
+ legendA.appendChild(fieldset2);
+ fieldset2.appendChild(legendB);
+
+ // All elements should accept the click.
+ for (var name of elementsPreventingClick) {
+ var element = document.createElement(name);
+ legendB.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler8);
+ sendMouseEvent({type:'click'}, element);
+ }
+
+ // Initialize children with click expected.
+ for (var name of elementsWithClick) {
+ var element = document.createElement(name);
+ legendB.appendChild(element);
+ element.addEventListener("click", clickShouldHappenHandler8);
+ sendMouseEvent({type:'click'}, element);
+ }
+}
+
+SpecialPowers.pushPrefEnv({
+ set: [["dom.forms.fieldset_disable_only_descendants.enabled", true]]
+}).then(() => {
+ test1();
+})
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug557087-3.html b/dom/html/test/test_bug557087-3.html
new file mode 100644
index 0000000000..98d0b0de4b
--- /dev/null
+++ b/dom/html/test/test_bug557087-3.html
@@ -0,0 +1,215 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557087
+-->
+<head>
+ <title>Test for Bug 557087</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557087">Mozilla Bug 557087</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 557087 **/
+
+function checkValueMissing(aElement, aExpected)
+{
+ var msg = aExpected ? aElement.tagName + " should suffer from value missing"
+ : aElement.tagName + " should not suffer from value missing"
+ is(aElement.validity.valueMissing, aExpected, msg);
+}
+
+function checkCandidateForConstraintValidation(aElement, aExpected)
+{
+ var msg = aExpected ? aElement.tagName + " should be candidate for constraint validation"
+ : aElement.tagName + " should not be candidate for constraint validation"
+ is(aElement.willValidate, aExpected, msg);
+}
+
+function checkDisabledPseudoClass(aElement, aDisabled)
+{
+ var disabledElements = document.querySelectorAll(":disabled");
+ var found = false;
+
+ for (var e of disabledElements) {
+ if (aElement == e) {
+ found = true;
+ break;
+ }
+ }
+
+ var msg = aDisabled ? aElement.tagName + " should have :disabled applying"
+ : aElement.tagName + " should not have :disabled applying";
+ ok(aDisabled ? found : !found, msg);
+}
+
+function checkEnabledPseudoClass(aElement, aEnabled)
+{
+ var enabledElements = document.querySelectorAll(":enabled");
+ var found = false;
+
+ for (var e of enabledElements) {
+ if (aElement == e) {
+ found = true;
+ break;
+ }
+ }
+
+ var msg = aEnabled ? aElement.tagName + " should have :enabled applying"
+ : aElement.tagName + " should not have :enabled applying";
+ ok(aEnabled ? found : !found, msg);
+}
+
+function checkFocus(aElement, aExpected)
+{
+ aElement.setAttribute('tabindex', 1);
+
+ // We use the focus manager so we can test <label>.
+ var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"]
+ .getService(SpecialPowers.Ci.nsIFocusManager);
+ fm.setFocus(aElement, 0);
+
+ if (aExpected) {
+ is(document.activeElement, aElement, "element should be focused");
+ } else {
+ isnot(document.activeElement, aElement, "element should not be focused");
+ }
+
+ aElement.blur();
+ aElement.removeAttribute('tabindex');
+}
+
+var elements = [ "input", "button", "select", "textarea", "fieldset", "option",
+ "optgroup", "label", "output", "object" ];
+
+var testData = {
+/* tag name | affected by disabled | test focus | test pseudo-classes | test willValidate */
+ "INPUT": [ true, true, true, true, true ],
+ "BUTTON": [ true, true, true, false, false ],
+ "SELECT": [ true, true, true, true, false ],
+ "TEXTAREA": [ true, true, true, true, true ],
+ "FIELDSET": [ true, true, true, false, false ],
+ "OPTION": [ false, true, true, false, false ],
+ "OPTGROUP": [ false, true, true, false, false ],
+ "OBJECT": [ false, true, false, false, false ],
+ "LABEL": [ false, true, false, false, false ],
+ "OUTPUT": [ false, true, false, false, false ],
+};
+
+/**
+ * For not candidate elements without disabled attribute and not submittable,
+ * we only have to check that focus and click works even inside a disabled
+ * fieldset.
+ */
+function checkElement(aElement, aDisabled)
+{
+ var data = testData[aElement.tagName];
+ var expected = data[0] ? !aDisabled : true;
+
+ if (data[1]) {
+ checkFocus(aElement, expected);
+ }
+
+ if (data[2]) {
+ checkEnabledPseudoClass(aElement, data[0] ? !aDisabled : true);
+ checkDisabledPseudoClass(aElement, data[0] ? aDisabled : false);
+ }
+
+ if (data[3]) {
+ checkCandidateForConstraintValidation(aElement, expected);
+ }
+
+ if (data[4]) {
+ checkValueMissing(aElement, expected);
+ }
+}
+
+var fieldset1 = document.createElement("fieldset");
+var fieldset2 = document.createElement("fieldset");
+var legendA = document.createElement("legend");
+var legendB = document.createElement("legend");
+var content = document.getElementById('content');
+content.appendChild(fieldset1);
+fieldset1.appendChild(fieldset2);
+fieldset2.disabled = true;
+
+for (var data of elements) {
+ var element = document.createElement(data);
+
+ if (data[4]) {
+ element.required = true;
+ }
+
+ fieldset1.disabled = false;
+ fieldset2.appendChild(element);
+
+ checkElement(element, fieldset2.disabled);
+
+ // Make sure changes are correctly managed.
+ fieldset2.disabled = false;
+ checkElement(element, fieldset2.disabled);
+ fieldset2.disabled = true;
+ checkElement(element, fieldset2.disabled);
+
+ // Make sure if a fieldset which is not the first fieldset is disabled, the
+ // elements inside the second fielset are disabled.
+ fieldset2.disabled = false;
+ fieldset1.disabled = true;
+ checkElement(element, fieldset1.disabled);
+
+ // Make sure the state change of the inner fieldset will not confuse.
+ fieldset2.disabled = true;
+ fieldset2.disabled = false;
+ checkElement(element, fieldset1.disabled);
+
+
+ /* legend tests */
+
+ // elements in the first legend of a disabled fieldset should not be disabled.
+ fieldset2.disabled = true;
+ fieldset1.disabled = false;
+ legendA.appendChild(element);
+ fieldset2.appendChild(legendA);
+ checkElement(element, false);
+
+ // elements in the second legend should be disabled
+ fieldset2.insertBefore(legendB, legendA);
+ checkElement(element, fieldset2.disabled);
+ fieldset2.removeChild(legendB);
+
+ // Elements in the first legend of a fieldset disabled by another fieldset
+ // should be disabled.
+ fieldset1.disabled = true;
+ checkElement(element, fieldset1.disabled);
+
+ // Elements inside a fieldset inside the first legend of a disabled fieldset
+ // should not be diasbled.
+ fieldset2.disabled = false;
+ fieldset1.appendChild(legendA);
+ legendA.appendChild(fieldset2);
+ fieldset2.appendChild(element);
+ checkElement(element, false);
+
+ // Elements inside the first legend of a disabled fieldset inside the first
+ // legend of a disabled fieldset should not be disabled.
+ fieldset2.disabled = false;
+ fieldset2.appendChild(legendB);
+ legendB.appendChild(element);
+ checkElement(element, false);
+ fieldset2.removeChild(legendB);
+ fieldset1.appendChild(fieldset2);
+
+ element.remove();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug557087-4.html b/dom/html/test/test_bug557087-4.html
new file mode 100644
index 0000000000..72aa0f1dc2
--- /dev/null
+++ b/dom/html/test/test_bug557087-4.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557087
+-->
+<head>
+ <title>Test for Bug 557087</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557087">Mozilla Bug 557087</a>
+<p id="display"></p>
+<div id="content">
+ <iframe name='f'></iframe>
+ <form target='f' action="data:text/html">
+ <input type='text' id='a'>
+ <input type='checkbox' id='b'>
+ <input type='radio' id='c'>
+ <fieldset disabled>
+ <fieldset>
+ <input type='submit' id='s'>
+ </fieldset>
+ </fieldset>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 557087 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gExpectedSubmits = 6;
+var gSubmitReceived = 0;
+var gEnd = false;
+
+var fieldsets = document.getElementsByTagName("fieldset");
+var form = document.forms[0];
+
+form.addEventListener("submit", function() {
+ ok(gEnd, gEnd ? "expected submit" : "non expected submit");
+ if (++gSubmitReceived >= gExpectedSubmits) {
+ form.removeEventListener("submit", arguments.callee);
+ SimpleTest.finish();
+ }
+});
+
+var inputs = [
+ document.getElementById('a'),
+ document.getElementById('b'),
+ document.getElementById('c'),
+];
+
+function doSubmit()
+{
+ for (e of inputs) {
+ e.focus();
+ synthesizeKey("KEY_Enter");
+ }
+}
+
+SimpleTest.waitForFocus(function() {
+ doSubmit();
+
+ fieldsets[1].disabled = true;
+ fieldsets[0].disabled = false;
+ doSubmit();
+
+ fieldsets[0].disabled = false;
+ fieldsets[1].disabled = false;
+
+ gEnd = true;
+ doSubmit();
+
+ // Simple check that we can submit from inside a legend even if the fieldset
+ // is disabled.
+ var legend = document.createElement("legend");
+ fieldsets[0].appendChild(legend);
+ fieldsets[0].disabled = true;
+ legend.appendChild(document.getElementById('s'));
+
+ doSubmit();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug557087-5.html b/dom/html/test/test_bug557087-5.html
new file mode 100644
index 0000000000..9d58e0ba93
--- /dev/null
+++ b/dom/html/test/test_bug557087-5.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557087
+-->
+<head>
+ <title>Test for Bug 557087</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557087">Mozilla Bug 557087</a>
+<p id="display"></p>
+<div id="content">
+ <iframe name='t'></iframe>
+ <form target='t' action="dummy_page.html">
+ <fieldset disabled>
+ <fieldset>
+ <input name='i' value='i'>
+ <textarea name='t'>t</textarea>
+ <select name='s'><option>s</option></select>
+ </fieldset>
+ </fieldset>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 557087 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+const BASE_URI = `${location.origin}/tests/dom/html/test/dummy_page.html`;
+var testResults = [
+ BASE_URI + "?",
+ BASE_URI + "?",
+ BASE_URI + "?i=i&t=t&s=s",
+ BASE_URI + "?i=i&t=t&s=s",
+];
+var gTestCount = 0;
+
+var form = document.forms[0];
+var iframe = document.getElementsByTagName('iframe')[0];
+var fieldsets = document.getElementsByTagName('fieldset');
+
+function runTest()
+{
+ iframe.addEventListener("load", function() {
+ is(iframe.contentWindow.location.href, testResults[gTestCount],
+ testResults[gTestCount] + " should have been loaded");
+
+ switch (++gTestCount) {
+ case 1:
+ fieldsets[1].disabled = true;
+ fieldsets[0].disabled = false;
+ form.submit();
+ SimpleTest.executeSoon(function() {
+ form.submit()
+ });
+ break;
+ case 2:
+ fieldsets[0].disabled = false;
+ fieldsets[1].disabled = false;
+ SimpleTest.executeSoon(function() {
+ form.submit()
+ });
+ break;
+ case 3:
+ // Elements inside the first legend of a disabled fieldset are submittable.
+ fieldsets[0].disabled = true;
+ fieldsets[1].disabled = true;
+ var legend = document.createElement("legend");
+ fieldsets[0].appendChild(legend);
+ while (fieldsets[1].firstChild) {
+ legend.appendChild(fieldsets[1].firstChild);
+ }
+ SimpleTest.executeSoon(function() {
+ form.submit()
+ });
+ break;
+ default:
+ iframe.removeEventListener("load", arguments.callee);
+ SimpleTest.executeSoon(SimpleTest.finish);
+ }
+ });
+
+ form.submit();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug557087-6.html b/dom/html/test/test_bug557087-6.html
new file mode 100644
index 0000000000..8d7bf08e5c
--- /dev/null
+++ b/dom/html/test/test_bug557087-6.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557087
+-->
+<head>
+ <title>Test for Bug 557087</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557087">Mozilla Bug 557087</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <fieldset disabled>
+ <input>
+ </fieldset>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 557087 **/
+
+// Testing random stuff following review comments.
+
+var fieldset = document.getElementsByTagName("fieldset")[0];
+
+is(fieldset.elements.length, 1,
+ "there should be one element inside the fieldset");
+is(fieldset.elements[0], document.getElementsByTagName("input")[0],
+ "input should be the element inside the fieldset");
+
+document.body.removeChild(document.getElementById('content'));
+is(fieldset.querySelector("input:disabled"), fieldset.elements[0],
+ "the input should still be disabled");
+
+fieldset.disabled = false;
+is(fieldset.querySelector("input:enabled"), fieldset.elements[0],
+ "the input should be enabled");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug557620.html b/dom/html/test/test_bug557620.html
new file mode 100644
index 0000000000..9a05988963
--- /dev/null
+++ b/dom/html/test/test_bug557620.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557620
+-->
+<head>
+ <title>Test for Bug 557620</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557620">Mozilla Bug 557620</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input type="tel" id='i'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 557620 **/
+
+// More checks are done in test_bug551670.html.
+
+var tel = document.getElementById('i');
+is(tel.type, 'tel', "input with type='tel' should return 'tel'");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug558788-1.html b/dom/html/test/test_bug558788-1.html
new file mode 100644
index 0000000000..5dc4a1f34b
--- /dev/null
+++ b/dom/html/test/test_bug558788-1.html
@@ -0,0 +1,212 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=558788
+-->
+<head>
+ <title>Test for Bug 558788</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input, textarea { background-color: rgb(0,0,0) !important; }
+ :-moz-any(input,textarea):valid { background-color: rgb(0,255,0) !important; }
+ :-moz-any(input,textarea):invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=558788">Mozilla Bug 558788</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 558788 **/
+
+/**
+ * This test checks the behavior of :valid and :invalid pseudo-classes
+ * when the user is typing/interacting with the element.
+ * Only <input> and <textarea> elements can have there validity changed by an
+ * user input.
+ */
+
+var gContent = document.getElementById('content');
+
+function checkValidApplies(elmt)
+{
+ is(window.getComputedStyle(elmt).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+}
+
+function checkInvalidApplies(elmt, aTodo)
+{
+ if (aTodo) {
+ todo_is(window.getComputedStyle(elmt).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+ return;
+ }
+ is(window.getComputedStyle(elmt).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+}
+
+function checkMissing(elementName)
+{
+ var element = document.createElement(elementName);
+ element.required = true;
+ gContent.appendChild(element);
+ checkInvalidApplies(element);
+
+ element.focus();
+
+ sendString("a");
+ checkValidApplies(element);
+
+ synthesizeKey("KEY_Backspace");
+ checkInvalidApplies(element);
+
+ gContent.removeChild(element);
+}
+
+function checkTooLong(elementName)
+{
+ var element = document.createElement(elementName);
+ element.value = "foo";
+ element.maxLength = 2;
+ gContent.appendChild(element);
+ checkInvalidApplies(element, true);
+
+ element.focus();
+
+ synthesizeKey("KEY_Backspace");
+ checkValidApplies(element);
+ gContent.removeChild(element);
+}
+
+function checkTextAreaMissing()
+{
+ checkMissing('textarea');
+}
+
+function checkTextAreaTooLong()
+{
+ checkTooLong('textarea');
+}
+
+function checkTextArea()
+{
+ checkTextAreaMissing();
+ checkTextAreaTooLong();
+}
+
+function checkInputMissing()
+{
+ checkMissing('input');
+}
+
+function checkInputTooLong()
+{
+ checkTooLong('input');
+}
+
+function checkInputEmail()
+{
+ var element = document.createElement('input');
+ element.type = 'email';
+ gContent.appendChild(element);
+ checkValidApplies(element);
+
+ element.focus();
+
+ sendString("a");
+ checkInvalidApplies(element);
+
+ sendString("@b.c");
+ checkValidApplies(element);
+
+ synthesizeKey("KEY_Backspace");
+ for (var i=0; i<4; ++i) {
+ if (i == 1) {
+ // a@b is a valid value.
+ checkValidApplies(element);
+ } else {
+ checkInvalidApplies(element);
+ }
+ synthesizeKey("KEY_Backspace");
+ }
+ checkValidApplies(element);
+
+ gContent.removeChild(element);
+}
+
+function checkInputURL()
+{
+ var element = document.createElement('input');
+ element.type = 'url';
+ gContent.appendChild(element);
+ checkValidApplies(element);
+
+ element.focus();
+
+ sendString("h");
+ checkInvalidApplies(element);
+
+ sendString("ttp://mozilla.org");
+ checkValidApplies(element);
+
+ for (var i=0; i<10; ++i) {
+ synthesizeKey("KEY_Backspace");
+ checkValidApplies(element);
+ }
+
+ synthesizeKey("KEY_Backspace");
+ // "http://" is now invalid
+ for (var i=0; i<7; ++i) {
+ checkInvalidApplies(element);
+ synthesizeKey("KEY_Backspace");
+ }
+ checkValidApplies(element);
+
+ gContent.removeChild(element);
+}
+
+function checkInputPattern()
+{
+ var element = document.createElement('input');
+ element.pattern = "[0-9]*"
+ gContent.appendChild(element);
+ checkValidApplies(element);
+
+ element.focus();
+
+ sendString("0");
+ checkValidApplies(element);
+
+ sendString("a");
+ checkInvalidApplies(element);
+
+ synthesizeKey("KEY_Backspace");
+ checkValidApplies(element);
+
+ synthesizeKey("KEY_Backspace");
+ checkValidApplies(element);
+
+ gContent.removeChild(element);
+}
+
+function checkInput()
+{
+ checkInputMissing();
+ checkInputTooLong();
+ checkInputEmail();
+ checkInputURL();
+ checkInputPattern();
+}
+
+checkTextArea();
+checkInput();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug558788-2.html b/dom/html/test/test_bug558788-2.html
new file mode 100644
index 0000000000..8224159d38
--- /dev/null
+++ b/dom/html/test/test_bug558788-2.html
@@ -0,0 +1,174 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=558788
+-->
+<head>
+ <title>Test for Bug 558788</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=558788">Mozilla Bug 558788</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 558788 **/
+
+var validElementsDescription = [
+ /* element type value required pattern maxlength minlength */
+ /* <input> */
+ [ "input", null, null, null, null, null, null ],
+ /* <input required value='foo'> */
+ [ "input", null, "foo", true, null, null, null ],
+ /* <input type='email'> */
+ [ "input", "email", null, null, null, null, null ],
+ /* <input type='email' value='foo@mozilla.org'> */
+ [ "input", "email", "foo@mozilla.org", null, null, null, null ],
+ /* <input type='url'> */
+ [ "input", "url", null, null, null, null, null ],
+ /* <input type='url' value='http://mozilla.org'> */
+ [ "input", "url", "http://mozilla.org", null, null, null, null ],
+ /* <input pattern='\\d\\d'> */
+ [ "input", null, null, null, "\\d\\d", null, null ],
+ /* <input pattern='\\d\\d' value='42'> */
+ [ "input", null, "42", null, "\\d\\d", null, null ],
+ /* <input maxlength='3'> - still valid until user interaction */
+ [ "input", null, null, null, null, "3", null ],
+ /* <input maxlength='3'> */
+ [ "input", null, "fooo", null, null, "3", null ],
+ /* <input minlength='3'> - still valid until user interaction */
+ [ "input", null, null, null, null, null, "3" ],
+ /* <input minlength='3'> */
+ [ "input", null, "fo", null, null, null, "3" ],
+ /* <textarea></textarea> */
+ [ "textarea", null, null, null, null, null, null ],
+ /* <textarea required>foo</textarea> */
+ [ "textarea", null, "foo", true, null, null, null ]
+];
+
+var invalidElementsDescription = [
+ /* element type value required pattern maxlength minlength valid-value */
+ /* <input required> */
+ [ "input", null, null, true, null, null, null, "foo" ],
+ /* <input type='email' value='foo'> */
+ [ "input", "email", "foo", null, null, null, null, "foo@mozilla.org" ],
+ /* <input type='url' value='foo'> */
+ [ "input", "url", "foo", null, null, null, null, "http://mozilla.org" ],
+ /* <input pattern='\\d\\d' value='foo'> */
+ [ "input", null, "foo", null, "\\d\\d", null, null, "42" ],
+ /* <input maxlength='3'> - still valid until user interaction */
+ [ "input", null, "foooo", null, null, "3", null, "foo" ],
+ /* <input minlength='3'> - still valid until user interaction */
+ [ "input", null, "foo", null, null, null, "3", "foo" ],
+ /* <textarea required></textarea> */
+ [ "textarea", null, null, true, null, null, null, "foo" ],
+];
+
+var validElements = [];
+var invalidElements = [];
+
+function appendElements(aElementsDesc, aElements)
+{
+ var content = document.getElementById('content');
+ var length = aElementsDesc.length;
+
+ for (var i=0; i<length; ++i) {
+ var e = document.createElement(aElementsDesc[i][0]);
+ if (aElementsDesc[i][1]) {
+ e.type = aElementsDesc[i][1];
+ }
+ if (aElementsDesc[i][2]) {
+ e.value = aElementsDesc[i][2];
+ }
+ if (aElementsDesc[i][3]) {
+ e.required = true;
+ }
+ if (aElementsDesc[i][4]) {
+ e.pattern = aElementsDesc[i][4];
+ }
+ if (aElementsDesc[i][5]) {
+ e.maxLength = aElementsDesc[i][5];
+ }
+ if (aElementsDesc[i][6]) {
+ e.minLength = aElementsDesc[i][6];
+ }
+
+ content.appendChild(e);
+
+ // Adding the element to the appropriate list.
+ aElements.push(e);
+ }
+}
+
+function compareArrayWithSelector(aElements, aSelector)
+{
+ var aSelectorElements = document.querySelectorAll(aSelector);
+
+ is(aSelectorElements.length, aElements.length,
+ aSelector + " selector should return the correct number of elements");
+
+ if (aSelectorElements.length != aElements.length) {
+ return;
+ }
+
+ var length = aElements.length;
+ for (var i=0; i<length; ++i) {
+ is(aSelectorElements[i], aElements[i],
+ aSelector + " should return the correct elements");
+ }
+}
+
+function makeMinMaxLengthElementsActuallyInvalid(aInvalidElements,
+ aInvalidElementsDesc)
+{
+ // min/maxlength elements are not invalid until user edits them
+ var length = aInvalidElementsDesc.length;
+
+ for (var i=0; i<length; ++i) {
+ var e = aInvalidElements[i];
+ if (aInvalidElementsDesc[i][5]) { // maxlength
+ e.focus();
+ synthesizeKey("KEY_Backspace");
+ } else if (aInvalidElementsDesc[i][6]) { // minlength
+ e.focus();
+ synthesizeKey("KEY_Backspace");
+ }
+ }
+}
+
+function makeInvalidElementsValid(aInvalidElements,
+ aInvalidElementsDesc,
+ aValidElements)
+{
+ var length = aInvalidElementsDesc.length;
+
+ for (var i=0; i<length; ++i) {
+ var e = aInvalidElements.shift();
+ e.value = aInvalidElementsDesc[i][7];
+ aValidElements.push(e);
+ }
+}
+
+appendElements(validElementsDescription, validElements);
+appendElements(invalidElementsDescription, invalidElements);
+
+makeMinMaxLengthElementsActuallyInvalid(invalidElements, invalidElementsDescription);
+
+compareArrayWithSelector(validElements, ":valid");
+compareArrayWithSelector(invalidElements, ":invalid");
+
+makeInvalidElementsValid(invalidElements, invalidElementsDescription,
+ validElements);
+
+compareArrayWithSelector(validElements, ":valid");
+compareArrayWithSelector(invalidElements, ":invalid");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug560112.html b/dom/html/test/test_bug560112.html
new file mode 100644
index 0000000000..48aaad8dc5
--- /dev/null
+++ b/dom/html/test/test_bug560112.html
@@ -0,0 +1,211 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=560112
+-->
+<head>
+ <title>Test for Bug 560112</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=560112">Mozilla Bug 560112</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 560112 **/
+
+/**
+ * Sets dataset property. Checks data attribute "attr".
+ * Gets dataset property. Checks data attribute "attr".
+ * Overwrites dataset property Checks data attribute "attr".
+ * Deletes dataset property. Checks data attribute "attr".
+ */
+function SetGetOverwriteDel(attr, prop)
+{
+ var el = document.createElement('div');
+
+ // Set property.
+ is(prop in el.dataset, false, 'Property should not be in dataset before setting.');
+ el.dataset[prop] = "zzzzzz";
+ is(prop in el.dataset, true, 'Property should be in dataset after setting.');
+ ok(el.hasAttribute(attr), 'Element should have data attribute for dataset property "' + prop + '".');
+
+ // Get property.
+ is(el.dataset[prop], "zzzzzz", 'Dataset property "' + prop + '" should have value "zzzzzz".');
+ is(el.getAttribute(attr), "zzzzzz", 'Attribute "' + attr + '" should have value "zzzzzz".');
+
+ // Overwrite property.
+ el.dataset[prop] = "yyyyyy";
+ is(el.dataset[prop], "yyyyyy", 'Dataset property "' + prop + '" should have value "yyyyyy".');
+ is(el.getAttribute(attr), "yyyyyy", 'Attribute "' + attr + '" should have value "yyyyyy".');
+
+ // Delete property.
+ delete el.dataset[prop];
+ ok(!el.hasAttribute(attr), 'Element should not have data attribute for dataset property "' + prop + '".');
+ is(prop in el.dataset, false, 'Deleted property should not be in dataset.');
+}
+
+/**
+ * Sets dataset property and expects exception.
+ */
+function SetExpectException(prop)
+{
+ var el = document.createElement('div');
+
+ try {
+ el.dataset[prop] = "xxxxxx";
+ ok(false, 'Exception should have been thrown.');
+ } catch (ex) {
+ ok(true, 'Exception should have been thrown.');
+ }
+}
+
+/**
+ * Adds attributes in "attrList" to element.
+ * Deletes attributes in "delList" from element.
+ * Checks for "numProp" properties in dataset.
+ */
+function DelAttrEnumerate(attrList, delList, numProp)
+{
+ var el = document.createElement('div');
+
+ // Adds attributes in "attrList".
+ for (var i = 0; i < attrList.length; ++i) {
+ el.setAttribute(attrList[i], "aaaaaa");
+ }
+
+ // Remove attributes in "delList".
+ for (var i = 0; i < delList.length; ++i) {
+ el.removeAttribute(delList[i]);
+ }
+
+ var numPropCounted = 0;
+
+ for (var prop in el.dataset) {
+ if (el.dataset[prop] == "aaaaaa") {
+ ++numPropCounted;
+ }
+ }
+
+ is(numPropCounted, numProp, 'Number of enumerable dataset properties is incorrent after attribute removal.');
+}
+
+/**
+ * Adds attributes in "attrList" to element.
+ * Checks for "numProp" properties in dataset.
+ */
+function Enumerate(attrList, numProp)
+{
+ var el = document.createElement('div');
+
+ // Adds attributes in "attrList" to element.
+ for (var i = 0; i < attrList.length; ++i) {
+ el.setAttribute(attrList[i], "aaaaaa");
+ }
+
+ var numPropCounted = 0;
+
+ for (var prop in el.dataset) {
+ if (el.dataset[prop] == "aaaaaa") {
+ ++numPropCounted;
+ }
+ }
+
+ is(numPropCounted, numProp, 'Number of enumerable dataset properties is incorrect.');
+}
+
+/**
+ * Adds dataset property then removes attribute from element and check for presence of
+ * properties using the "in" operator.
+ */
+function AddPropDelAttr(attr, prop)
+{
+ var el = document.createElement('div');
+
+ el.dataset[prop] = 'dddddd';
+ is(prop in el.dataset, true, 'Operator "in" should return true after setting property.');
+ el.removeAttribute(attr);
+ is(prop in el.dataset, false, 'Operator "in" should return false for removed attribute.');
+}
+
+/**
+ * Adds then removes attribute from element and check for presence of properties using the
+ * "in" operator.
+ */
+function AddDelAttr(attr, prop)
+{
+ var el = document.createElement('div');
+
+ el.setAttribute(attr, 'dddddd');
+ is(prop in el.dataset, true, 'Operator "in" should return true after setting attribute.');
+ el.removeAttribute(attr);
+ is(prop in el.dataset, false, 'Operator "in" should return false for removed attribute.');
+}
+
+// Typical use case.
+SetGetOverwriteDel('data-property', 'property');
+SetGetOverwriteDel('data-a-longer-property', 'aLongerProperty');
+
+AddDelAttr('data-property', 'property');
+AddDelAttr('data-a-longer-property', 'aLongerProperty');
+
+AddPropDelAttr('data-property', 'property');
+AddPropDelAttr('data-a-longer-property', 'aLongerProperty');
+
+// Empty property name.
+SetGetOverwriteDel('data-', '');
+
+// Leading dash characters.
+SetGetOverwriteDel('data--', '-');
+SetGetOverwriteDel('data--d', 'D');
+SetGetOverwriteDel('data---d', '-D');
+
+// Trailing dash characters.
+SetGetOverwriteDel('data-d-', 'd-');
+SetGetOverwriteDel('data-d--', 'd--');
+SetGetOverwriteDel('data-d-d-', 'dD-');
+
+// "data-" in attribute name.
+SetGetOverwriteDel('data-data-', 'data-');
+SetGetOverwriteDel('data-data-data-', 'dataData-');
+
+// Longer attribute.
+SetGetOverwriteDel('data-long-long-long-long-long-long-long-long-long-long-long-long-long', 'longLongLongLongLongLongLongLongLongLongLongLongLong');
+
+var longAttr = 'data-long';
+var longProp = 'long';
+for (var i = 0; i < 30000; ++i) {
+ // Create really long attribute and property names.
+ longAttr += '-long';
+ longProp += 'Long';
+}
+
+SetGetOverwriteDel(longAttr, longProp);
+
+// Syntax error in setting dataset property (dash followed by lower case).
+SetExpectException('-a');
+SetExpectException('a-a');
+SetExpectException('a-a-a');
+
+// Invalid character.
+SetExpectException('a a');
+
+// Enumeration over dataset properties.
+Enumerate(['data-a-big-fish'], 1);
+Enumerate(['dat-a-big-fish'], 0);
+Enumerate(['data-'], 1);
+Enumerate(['data-', 'data-more-data'], 2);
+Enumerate(['daaata-', 'data-more-data'], 1);
+
+// Delete data attributes and enumerate properties.
+DelAttrEnumerate(['data-one', 'data-two'], ['data-one'], 1);
+DelAttrEnumerate(['data-one', 'data-two'], ['data-three'], 2);
+DelAttrEnumerate(['data-one', 'data-two'], ['one'], 2);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug561634.html b/dom/html/test/test_bug561634.html
new file mode 100644
index 0000000000..1eab90508f
--- /dev/null
+++ b/dom/html/test/test_bug561634.html
@@ -0,0 +1,126 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=561634
+-->
+<head>
+ <title>Test for Bug 561634</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=561634">Mozilla Bug 561634</a>
+<p id="display"></p>
+<div id="content" style="display: none;">
+ <form>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 561634 **/
+
+function checkEmptyForm()
+{
+ ok(document.forms[0].checkValidity(), "An empty form is valid");
+}
+
+function checkBarredFromConstraintValidation()
+{
+ var f = document.forms[0];
+ var fs = document.createElement('fieldset');
+ var i = document.createElement('input');
+
+ f.appendChild(fs);
+ i.type = 'hidden';
+ f.appendChild(i);
+
+ fs.setCustomValidity("foo");
+ i.setCustomValidity("foo");
+
+ ok(f.checkValidity(),
+ "A form with invalid element barred from constraint validation should be valid");
+
+ f.removeChild(i);
+ f.removeChild(fs);
+}
+
+function checkValid()
+{
+ var f = document.forms[0];
+ var i = document.createElement('input');
+ f.appendChild(i);
+
+ ok(f.checkValidity(), "A form with valid elements is valid");
+
+ f.removeChild(i);
+}
+
+function checkInvalid()
+{
+ var f = document.forms[0];
+ var i = document.createElement('input');
+ f.appendChild(i);
+
+ i.setCustomValidity("foo");
+ ok(!f.checkValidity(), "A form with invalid elements is invalid");
+
+ var i2 = document.createElement('input');
+ f.appendChild(i2);
+ ok(!f.checkValidity(),
+ "A form with at least one invalid element is invalid");
+
+ f.removeChild(i2);
+ f.removeChild(i);
+}
+
+function checkInvalidEvent()
+{
+ var f = document.forms[0];
+ var i = document.createElement('input');
+ f.appendChild(i);
+ var i2 = document.createElement('input');
+ f.appendChild(i2);
+
+ i.setCustomValidity("foo");
+
+ var invalidEventForInvalidElement = false;
+ var invalidEventForValidElement = false;
+
+ i.addEventListener("invalid", function (e) {
+ invalidEventForInvalidElement = true;
+ ok(e.cancelable, "invalid event should be cancelable");
+ ok(!e.bubbles, "invalid event should not bubble");
+ });
+
+ i2.addEventListener("invalid", function (e) {
+ invalidEventForValidElement = true;
+ });
+
+ f.checkValidity();
+
+ setTimeout(function() {
+ ok(invalidEventForInvalidElement,
+ "invalid event should be fired on invalid elements");
+ ok(!invalidEventForValidElement,
+ "invalid event should not be fired on valid elements");
+
+ f.removeChild(i2);
+ f.removeChild(i);
+
+ SimpleTest.finish();
+ }, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+checkEmptyForm();
+checkBarredFromConstraintValidation();
+checkValid();
+checkInvalid();
+checkInvalidEvent(); // will call finish().
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug561636.html b/dom/html/test/test_bug561636.html
new file mode 100644
index 0000000000..da51b07607
--- /dev/null
+++ b/dom/html/test/test_bug561636.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=561636
+-->
+<head>
+ <title>Test for Bug 561636</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=561636">Mozilla Bug 561636</a>
+<p id="display"></p>
+<iframe style='width:50px; height: 50px;' name='t'></iframe>
+<iframe style='width:50px; height: 50px;' name='t2' id='i'></iframe>
+<div id="content">
+ <form target='t' action='data:text/html,'>
+ <input required>
+ <input id='a' type='submit'>
+ </form>
+ <form target='t' action='data:text/html,'>
+ <input type='checkbox' required>
+ <button id='b' type='submit'></button>
+ </form>
+ <form target='t' action='data:text/html,'>
+ <input id='c' required>
+ </form>
+ <form target='t' action='data:text/html,'>
+ <input>
+ <input id='s2' type='submit'>
+ </form>
+ <form target='t2' action='data:text/html,'>
+ <input required>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 561636 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTest);
+
+function runTest()
+{
+ var formSubmitted = [ false, false ];
+ var invalidHandled = false;
+
+ // Initialize
+ document.forms[0].addEventListener('submit', function(aEvent) {
+ formSubmitted[0] = true;
+ }, {once: true});
+
+ document.forms[1].addEventListener('submit', function(aEvent) {
+ formSubmitted[1] = true;
+ }, {once: true});
+
+ document.forms[2].addEventListener('submit', function(aEvent) {
+ formSubmitted[2] = true;
+ }, {once: true});
+
+ document.forms[3].addEventListener('submit', function(aEvent) {
+ formSubmitted[3] = true;
+
+ ok(!formSubmitted[0], "Form 1 should not have been submitted because invalid");
+ ok(!formSubmitted[1], "Form 2 should not have been submitted because invalid");
+ ok(!formSubmitted[2], "Form 3 should not have been submitted because invalid");
+ ok(formSubmitted[3], "Form 4 should have been submitted because valid");
+
+ // Next test.
+ document.forms[4].submit();
+ }, {once: true});
+
+ document.forms[4].elements[0].addEventListener('invalid', function(aEvent) {
+ invalidHandled = true;
+ }, {once: true});
+
+ document.getElementById('i').addEventListener('load', function(aEvent) {
+ SimpleTest.executeSoon(function () {
+ ok(true, "Form 5 should have been submitted because submit() has been used even if invalid");
+ ok(!invalidHandled, "Invalid event should not have been sent");
+
+ SimpleTest.finish();
+ });
+ }, {once: true});
+
+ document.getElementById('a').click();
+ document.getElementById('b').click();
+ var c = document.getElementById('c');
+ c.focus();
+ synthesizeKey("KEY_Enter");
+ document.getElementById('s2').click();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug561640.html b/dom/html/test/test_bug561640.html
new file mode 100644
index 0000000000..ed7bf84126
--- /dev/null
+++ b/dom/html/test/test_bug561640.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=561640
+-->
+<head>
+ <title>Test for Bug 561640</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ input, textarea { background-color: rgb(0,0,0) !important; }
+ :-moz-any(input,textarea):valid { background-color: rgb(0,255,0) !important; }
+ :-moz-any(input,textarea):invalid { background-color: rgb(255,0,0) !important; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=561640">Mozilla Bug 561640</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 561640 **/
+
+var elements = [ 'input', 'textarea' ];
+var content = document.getElementById('content');
+
+function checkValid(elmt)
+{
+ ok(!elmt.validity.tooLong, "element should not be too long");
+ is(window.getComputedStyle(elmt).getPropertyValue('background-color'),
+ "rgb(0, 255, 0)", ":valid pseudo-class should apply");
+}
+
+function checkInvalid(elmt)
+{
+ todo(elmt.validity.tooLong, "element should be too long");
+ todo_is(window.getComputedStyle(elmt).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+}
+
+for (var elmtName of elements) {
+ var elmt = document.createElement(elmtName);
+ content.appendChild(elmt);
+
+ if (elmtName == 'textarea') {
+ elmt.textContent = 'foo';
+ } else {
+ elmt.setAttribute('value', 'foo');
+ }
+ elmt.maxLength = 2;
+ checkValid(elmt);
+
+ elmt.value = 'a';
+ checkValid(elmt);
+
+ if (elmtName == 'textarea') {
+ elmt.textContent = 'f';
+ } else {
+ elmt.setAttribute('value', 'f');
+ }
+ elmt.value = 'bar';
+ checkInvalid(elmt);
+
+ content.removeChild(elmt);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug564001.html b/dom/html/test/test_bug564001.html
new file mode 100644
index 0000000000..3815ac8cf9
--- /dev/null
+++ b/dom/html/test/test_bug564001.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=564001
+-->
+<head>
+ <title>Test for Bug 564001</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=564001">Mozilla Bug 564001</a>
+<p id="display"><img usemap=#map src=image.png></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 564001 **/
+SimpleTest.waitForExplicitFinish();
+
+var wrongArea = document.createElement("area");
+wrongArea.shape = "default";
+wrongArea.href = "#FAIL";
+var wrongMap = document.createElement("map");
+wrongMap.name = "map";
+wrongMap.appendChild(wrongArea);
+document.body.appendChild(wrongMap);
+
+var rightArea = document.createElement("area");
+rightArea.shape = "default";
+rightArea.href = "#PASS";
+var rightMap = document.createElement("map");
+rightMap.name = "map";
+rightMap.appendChild(rightArea);
+document.body.insertBefore(rightMap, wrongMap);
+
+var images = document.getElementsByTagName("img");
+onhashchange = function() {
+ is(location.hash, "#PASS", "Should get the first map in tree order.");
+ SimpleTest.finish();
+};
+SimpleTest.waitForFocus(() => synthesizeMouse(images[0], 50, 50, {}));
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug566046.html b/dom/html/test/test_bug566046.html
new file mode 100644
index 0000000000..88c59a4e61
--- /dev/null
+++ b/dom/html/test/test_bug566046.html
@@ -0,0 +1,200 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=566046
+-->
+<head>
+ <title>Test for Bug 566046</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <base>
+ <base target='frame2'>
+ <base target=''>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566046">Mozilla Bug 566046</a>
+<p id="display"></p>
+<style>
+ iframe { width: 130px; height: 100px;}
+</style>
+<iframe name='frame1' id='frame1'></iframe>
+<iframe name='frame2' id='frame2'></iframe>
+<iframe name='frame3' id='frame3'></iframe>
+<iframe name='frame4' id='frame4'></iframe>
+<iframe name='frame5' id='frame5'></iframe>
+<iframe name='frame5bis' id='frame5bis'></iframe>
+<iframe name='frame6' id='frame6'></iframe>
+<iframe name='frame7' id='frame7'></iframe>
+<iframe name='frame8' id='frame8'></iframe>
+<iframe name='frame9' id='frame9'></iframe>
+<div id="content">
+ <form target='frame1' action="dummy_page.html" method="GET">
+ <input name='foo' value='foo'>
+ </form>
+ <form action="dummy_page.html" method="GET">
+ <input name='bar' value='bar'>
+ </form>
+ <form target="">
+ </form>
+
+ <!-- submit controls with formtarget that are validated with a CLICK -->
+ <form target="tulip" action="dummy_page.html" method="GET">
+ <input name='tulip' value='tulip'>
+ <input type='submit' id='is' formtarget='frame3'>
+ </form>
+ <form action="dummy_page.html" method="GET">
+ <input name='foobar' value='foobar'>
+ <input type='image' id='ii' formtarget='frame4'>
+ </form>
+ <form action="dummy_page.html" method="GET">
+ <input name='tulip2' value='tulip2'>
+ <button type='submit' id='bs' formtarget='frame5'>submit</button>
+ </form>
+ <form action="dummy_page.html" method="GET">
+ <input name='tulip3' value='tulip3'>
+ <button type='submit' id='bsbis' formtarget='frame5bis'>submit</button>
+ </form>
+
+ <!-- submit controls with formtarget that are validated with ENTER -->
+ <form target="tulip" action="dummy_page.html" method="GET">
+ <input name='footulip' value='footulip'>
+ <input type='submit' id='is2' formtarget='frame6'>
+ </form>
+ <form action="dummy_page.html" method="GET">
+ <input name='tulipfoobar' value='tulipfoobar'>
+ <input type='image' id='ii2' formtarget='frame7'>
+ </form>
+ <form action="dummy_page.html" method="GET">
+ <input name='tulipbar' value='tulipbar'>
+ <button type='submit' id='bs2' formtarget='frame8'>submit</button>
+ </form>
+
+ <!-- check that a which is not a submit control do not use @formtarget -->
+ <form target='frame9' action="dummy_page.html" method="GET">
+ <input id='enter' name='input' value='enter' formtarget='frame6'>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 566046 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ setTimeout(runTests, 0);
+});
+
+const BASE_URI = `${location.origin}/tests/dom/html/test/dummy_page.html`;
+var gTestResults = {
+ frame1: BASE_URI + "?foo=foo",
+ frame2: BASE_URI + "?bar=bar",
+ frame3: BASE_URI + "?tulip=tulip",
+ frame4: BASE_URI + "?foobar=foobar&x=0&y=0",
+ frame5: BASE_URI + "?tulip2=tulip2",
+ frame5bis: BASE_URI + "?tulip3=tulip3",
+ frame6: BASE_URI + "?footulip=footulip",
+ frame7: BASE_URI + "?tulipfoobar=tulipfoobar&x=0&y=0",
+ frame8: BASE_URI + "?tulipbar=tulipbar",
+ frame9: BASE_URI + "?input=enter",
+};
+
+var gPendingLoad = 0; // Has to be set after depending on the frames number.
+
+function runTests()
+{
+ // Check the target IDL attribute.
+ for (var i=0; i<document.forms.length; ++i) {
+ var testValue = document.forms[i].getAttribute('target');
+ is(document.forms[i].target, testValue ? testValue : "",
+ "target IDL attribute should reflect the target content attribute");
+ }
+
+ // We add a load event for the frames which will be called when the forms
+ // will be submitted.
+ var frames = [ document.getElementById('frame1'),
+ document.getElementById('frame2'),
+ document.getElementById('frame3'),
+ document.getElementById('frame4'),
+ document.getElementById('frame5'),
+ document.getElementById('frame5bis'),
+ document.getElementById('frame6'),
+ document.getElementById('frame7'),
+ document.getElementById('frame8'),
+ document.getElementById('frame9'),
+ ];
+ gPendingLoad = frames.length;
+
+ for (var i=0; i<frames.length; i++) {
+ frames[i].setAttribute('onload', "frameLoaded(this);");
+ }
+
+ // Submitting only the forms with a valid target.
+ document.forms[0].submit();
+ document.forms[1].submit();
+
+ /**
+ * We are going to focus each element before interacting with either for
+ * simulating the ENTER key (synthesizeKey) or a click (synthesizeMouse) or
+ * using .click(). This because it may be needed (ENTER) and because we want
+ * to have the element visible in the iframe.
+ *
+ * Focusing the first element (id='is') is launching the tests.
+ */
+ document.getElementById('is').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('is'), 5, 5, {});
+ document.getElementById('ii').focus();
+ }, {once: true});
+
+ document.getElementById('ii').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('ii'), 5, 5, {});
+ document.getElementById('bs').focus();
+ }, {once: true});
+
+ document.getElementById('bs').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('bs'), 5, 5, {});
+ document.getElementById('bsbis').focus();
+ }, {once: true});
+
+ document.getElementById('bsbis').addEventListener('focus', function(aEvent) {
+ document.getElementById('bsbis').click();
+ document.getElementById('is2').focus();
+ }, {once: true});
+
+ document.getElementById('is2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('ii2').focus();
+ }, {once: true});
+
+ document.getElementById('ii2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('bs2').focus();
+ }, {once: true});
+
+ document.getElementById('bs2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('enter').focus();
+ }, {once: true});
+
+ document.getElementById('enter').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ }, {once: true});
+
+ document.getElementById('is').focus();
+}
+
+function frameLoaded(aFrame) {
+ // Check if when target is unspecified, it fallback correctly to the base
+ // element target attribute.
+ is(aFrame.contentWindow.location.href, gTestResults[aFrame.name],
+ "the target attribute doesn't have the correct behavior");
+
+ if (--gPendingLoad == 0) {
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug567938-1.html b/dom/html/test/test_bug567938-1.html
new file mode 100644
index 0000000000..0c5d78f1c0
--- /dev/null
+++ b/dom/html/test/test_bug567938-1.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=567938
+-->
+<head>
+ <title>Test for Bug 567938</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTests();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=567938">Mozilla Bug 567938</a>
+<p id="display"></p>
+<iframe id='iframe' name="submit_frame" style="visibility: hidden;"></iframe>
+<div id="content" style="display: none">
+ <form id='f' method='get' target='submit_frame'>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 567938 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gTestData = ["submit", "image"];
+var gCurrentTest = 0;
+
+function initializeNextTest()
+{
+ var form = document.forms[0];
+
+ // Cleaning-up.
+ form.textContent = "";
+
+ // Add the new element.
+ var element = document.createElement("input");
+ element.id = 'i';
+ element.type = gTestData[gCurrentTest];
+ element.onclick = function() { form.submit(); return false; };
+ form.action = gTestData[gCurrentTest];
+ form.appendChild(element);
+
+ sendMouseEvent({type: 'click'}, 'i');
+}
+
+function runTests()
+{
+ document.getElementById('iframe').addEventListener('load', function(aEvent) {
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/${gTestData[gCurrentTest]}?`,
+ "The form should have been submitted");
+ gCurrentTest++;
+ if (gCurrentTest < gTestData.length) {
+ initializeNextTest();
+ } else {
+ aEvent.target.removeEventListener('load', arguments.callee);
+ SimpleTest.finish();
+ }
+ });
+
+ initializeNextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug567938-2.html b/dom/html/test/test_bug567938-2.html
new file mode 100644
index 0000000000..4a97516805
--- /dev/null
+++ b/dom/html/test/test_bug567938-2.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=567938
+-->
+<head>
+ <title>Test for Bug 567938</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=567938">Mozilla Bug 567938</a>
+<p id="display"></p>
+<iframe id='iframe' name="submit_frame" style="visibility: hidden;"></iframe>
+<div id="content" style="display: none">
+ <form id='f' method='get' target='submit_frame'>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 567938 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+var gTestData = ["submit", "image"];
+var gCurrentTest = 0;
+
+function initializeNextTest()
+{
+ var form = document.forms[0];
+
+ // Cleaning-up.
+ form.textContent = "";
+
+ // Add the new element.
+ var element = document.createElement("input");
+ element.id = 'i';
+ element.type = gTestData[gCurrentTest];
+ element.onclick = function() { form.submit(); element.type='text'; };
+ form.action = gTestData[gCurrentTest];
+ form.appendChild(element);
+
+ sendMouseEvent({type: 'click'}, 'i');
+}
+
+function runTests()
+{
+ document.getElementById('iframe').addEventListener('load', function(aEvent) {
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/${gTestData[gCurrentTest]}?`,
+ "The form should have been submitted");
+ gCurrentTest++;
+ if (gCurrentTest < gTestData.length) {
+ initializeNextTest();
+ } else {
+ aEvent.target.removeEventListener('load', arguments.callee);
+ SimpleTest.finish();
+ }
+ });
+
+ initializeNextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug567938-3.html b/dom/html/test/test_bug567938-3.html
new file mode 100644
index 0000000000..3c23129466
--- /dev/null
+++ b/dom/html/test/test_bug567938-3.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=567938
+-->
+<head>
+ <title>Test for Bug 567938</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=567938">Mozilla Bug 567938</a>
+<p id="display"></p>
+<iframe id='iframe' name="submit_frame" style="visibility: hidden;"></iframe>
+<div id="content" style="display: none">
+ <form id='f' method='get' target='submit_frame'>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 567938 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+var gTestData = ["submit", "image"];
+var gCurrentTest = 0;
+
+function initializeNextTest()
+{
+ var form = document.forms[0];
+
+ // Cleaning-up.
+ form.textContent = "";
+
+ // Add the new element.
+ var element = document.createElement("input");
+ element.id = 'i';
+ element.type = gTestData[gCurrentTest];
+ // eslint-disable-next-line no-implied-eval
+ element.onclick = function() { setTimeout("document.forms[0].submit();",0); return false; };
+ form.appendChild(element);
+ form.action = gTestData[gCurrentTest];
+
+ sendMouseEvent({type: 'click'}, 'i');
+}
+
+function runTests()
+{
+ document.getElementById('iframe').addEventListener('load', function(aEvent) {
+ is(frames.submit_frame.location.href,
+ `${location.origin}/tests/dom/html/test/${gTestData[gCurrentTest]}?`,
+ "The form should have been submitted");
+ gCurrentTest++;
+ if (gCurrentTest < gTestData.length) {
+ initializeNextTest();
+ } else {
+ aEvent.target.removeEventListener('load', arguments.callee);
+ SimpleTest.finish();
+ }
+ });
+
+ initializeNextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug567938-4.html b/dom/html/test/test_bug567938-4.html
new file mode 100644
index 0000000000..f04ac27f5f
--- /dev/null
+++ b/dom/html/test/test_bug567938-4.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=567938
+-->
+<head>
+ <title>Test for Bug 567938</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=567938">Mozilla Bug 567938</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input id='i' type='checkbox' onclick="return false;">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 567938 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+function runTests()
+{
+ document.getElementById('i').checked = false;
+
+ document.getElementById('i').addEventListener('click', function(aEvent) {
+ SimpleTest.executeSoon(function() {
+ ok(!aEvent.target.checked, "the input should not be checked");
+ SimpleTest.finish();
+ });
+ }, {once: true});
+
+ sendMouseEvent({type: 'click'}, 'i');
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug569955.html b/dom/html/test/test_bug569955.html
new file mode 100644
index 0000000000..0ed5ce88c7
--- /dev/null
+++ b/dom/html/test/test_bug569955.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=569955
+-->
+<head>
+ <title>Test for Bug 569955</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=569955">Mozilla Bug 569955</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input id='i' autofocus>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 569955 **/
+
+function runTests()
+{
+ isnot(document.activeElement, document.getElementById('i'),
+ "not rendered elements can't be autofocused");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ setTimeout(runTests, 0);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug573969.html b/dom/html/test/test_bug573969.html
new file mode 100644
index 0000000000..f6020d1445
--- /dev/null
+++ b/dom/html/test/test_bug573969.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=573969
+-->
+<head>
+ <title>Test for Bug 573969</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=573969">Mozilla Bug 573969</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <xmp id='x'></xmp>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 573969 **/
+
+var testData = [
+ '<div>foo</div>',
+ '&lt;div&gt;&lt;/div&gt;',
+];
+
+var x = document.getElementById('x');
+
+for (v of testData) {
+ x.innerHTML = v;
+ is(x.innerHTML, v, "innerHTML value should not be escaped");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug57600.html b/dom/html/test/test_bug57600.html
new file mode 100644
index 0000000000..fc7037bd32
--- /dev/null
+++ b/dom/html/test/test_bug57600.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=57600
+-->
+<head>
+ <title>Test for Bug 57600</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=57600">Mozilla Bug 57600</a>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 57600 **/
+SimpleTest.waitForExplicitFinish();
+var count = 0;
+function disp(win) {
+ var d = win ? win.document : self.testname.document;
+ var str = 'You should see this';
+ d.open();
+ d.write(str);
+ d.close();
+ is(d.documentElement.textContent, str, "Unexpected text");
+ if (++count == 2) {
+ SimpleTest.finish();
+ }
+}
+</script>
+</pre>
+<p id="display">
+ <iframe src="javascript:'<body onload=&quot;this.onerror = parent.onerror; parent.disp(self)&quot;></body>'">
+ </iframe>
+ <iframe name="testname" src="javascript:'<body onload=&quot;this.onerror = parent.onerror; parent.disp()&quot;></body>'">
+ </iframe>
+</p>
+</body>
+</html>
diff --git a/dom/html/test/test_bug579079.html b/dom/html/test/test_bug579079.html
new file mode 100644
index 0000000000..e5ec429226
--- /dev/null
+++ b/dom/html/test/test_bug579079.html
@@ -0,0 +1,40 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=579079
+-->
+<head>
+ <title>Test for Bug 579079</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=579079">Mozilla Bug 579079</a>
+
+<div id="foo">
+ <img name="img1">
+ <form name="form1"></form>
+ <applet name="applet1"></applet>
+ <embed name="embed1"></embed>
+ <object name="object1"></object>
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var img = document.img1;
+var form = document.form1;
+var embed = document.embed1;
+var object = document.object1;
+$("foo").innerHTML = $("foo").innerHTML;
+isnot(document.img1, img);
+ok(document.img1 instanceof HTMLImageElement);
+isnot(document.form1, form);
+ok(document.form1 instanceof HTMLFormElement);
+isnot(document.embed1, embed);
+ok(document.embed1 instanceof HTMLEmbedElement);
+isnot(document.object1, object);
+ok(document.object1 instanceof HTMLObjectElement);
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug582412-1.html b/dom/html/test/test_bug582412-1.html
new file mode 100644
index 0000000000..f1256e3d5a
--- /dev/null
+++ b/dom/html/test/test_bug582412-1.html
@@ -0,0 +1,200 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=566160
+-->
+<head>
+ <title>Test for Bug 566160</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566160">Mozilla Bug 566160</a>
+<p id="display"></p>
+<style>
+ iframe { width: 130px; height: 100px;}
+</style>
+<iframe name='frame1' id='frame1'></iframe>
+<iframe name='frame2' id='frame2'></iframe>
+<iframe name='frame3' id='frame3'></iframe>
+<iframe name='frame3bis' id='frame3bis'></iframe>
+<iframe name='frame4' id='frame4'></iframe>
+<iframe name='frame5' id='frame5'></iframe>
+<iframe name='frame6' id='frame6'></iframe>
+<iframe name='frame7' id='frame7'></iframe>
+<iframe name='frame8' id='frame8'></iframe>
+<iframe name='frame9' id='frame9'></iframe>
+<div id="content">
+ <!-- submit controls with formaction that are validated with a CLICK -->
+ <form target="frame1" action="dummy_page.html" method="POST">
+ <input name='foo' value='foo'>
+ <input type='submit' id='is' formmethod="GET">
+ </form>
+ <form target="frame2" action="dummy_page.html" method="POST">
+ <input name='bar' value='bar'>
+ <input type='image' id='ii' formmethod="GET">
+ </form>
+ <form target="frame3" action="dummy_page.html" method="POST">
+ <input name='tulip' value='tulip'>
+ <button type='submit' id='bs' formmethod="GET">submit</button>
+ </form>
+ <form target="frame3bis" action="dummy_page.html" method="POST">
+ <input name='tulipbis' value='tulipbis'>
+ <button type='submit' id='bsbis' formmethod="GET">submit</button>
+ </form>
+
+ <!-- submit controls with formaction that are validated with ENTER -->
+ <form target="frame4" action="dummy_page.html" method="POST">
+ <input name='footulip' value='footulip'>
+ <input type='submit' id='is2' formmethod="GET">
+ </form>
+ <form target="frame5" action="dummy_page.html" method="POST">
+ <input name='foobar' value='foobar'>
+ <input type='image' id='ii2' formmethod="GET">
+ </form>
+ <form target="frame6" action="dummy_page.html" method="POST">
+ <input name='tulip2' value='tulip2'>
+ <button type='submit' id='bs2' formmethod="GET">submit</button>
+ </form>
+
+ <!-- check that when submitting a from from an element
+ which is not a submit control, @formaction isn't used -->
+ <form target='frame7' action="dummy_page.html" method="GET">
+ <input id='enter' name='input' value='enter' formmethod="POST">
+ </form>
+
+ <!-- If formmethod isn't set, it's default value shouldn't be used -->
+ <form target="frame8" action="dummy_page.html" method="POST">
+ <input name='tulip8' value='tulip8'>
+ <input type='submit' id='i8'>
+ </form>
+
+ <!-- If formmethod is set but has an invalid value, the default value should
+ be used. -->
+ <form target="frame9" action="dummy_page.html" method="POST">
+ <input name='tulip9' value='tulip9'>
+ <input type='submit' id='i9' formmethod="">
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 566160 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ setTimeout(runTests, 0);
+});
+
+const BASE_URI = `${location.origin}/tests/dom/html/test/dummy_page.html`;
+var gTestResults = {
+ frame1: BASE_URI + "?foo=foo",
+ frame2: BASE_URI + "?bar=bar&x=0&y=0",
+ frame3: BASE_URI + "?tulip=tulip",
+ frame3bis: BASE_URI + "?tulipbis=tulipbis",
+ frame4: BASE_URI + "?footulip=footulip",
+ frame5: BASE_URI + "?foobar=foobar&x=0&y=0",
+ frame6: BASE_URI + "?tulip2=tulip2",
+ frame7: BASE_URI + "?input=enter",
+ frame8: BASE_URI + "",
+ frame9: BASE_URI + "?tulip9=tulip9",
+};
+
+var gPendingLoad = 0; // Has to be set after depending on the frames number.
+
+function runTests()
+{
+ // We add a load event for the frames which will be called when the forms
+ // will be submitted.
+ var frames = [ document.getElementById('frame1'),
+ document.getElementById('frame2'),
+ document.getElementById('frame3'),
+ document.getElementById('frame3bis'),
+ document.getElementById('frame4'),
+ document.getElementById('frame5'),
+ document.getElementById('frame6'),
+ document.getElementById('frame7'),
+ document.getElementById('frame8'),
+ document.getElementById('frame9'),
+ ];
+ gPendingLoad = frames.length;
+
+ for (var i=0; i<frames.length; i++) {
+ frames[i].setAttribute('onload', "frameLoaded(this);");
+ }
+
+ /**
+ * We are going to focus each element before interacting with either for
+ * simulating the ENTER key (synthesizeKey) or a click (synthesizeMouse) or
+ * using .click(). This because it may be needed (ENTER) and because we want
+ * to have the element visible in the iframe.
+ *
+ * Focusing the first element (id='is') is launching the tests.
+ */
+ document.getElementById('is').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('is'), 5, 5, {});
+ document.getElementById('ii').focus();
+ }, {once: true});
+
+ document.getElementById('ii').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('ii'), 5, 5, {});
+ document.getElementById('bs').focus();
+ }, {once: true});
+
+ document.getElementById('bs').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('bs'), 5, 5, {});
+ document.getElementById('bsbis').focus();
+ }, {once: true});
+
+ document.getElementById('bsbis').addEventListener('focus', function(aEvent) {
+ document.getElementById('bsbis').click();
+ document.getElementById('is2').focus();
+ }, {once: true});
+
+ document.getElementById('is2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('ii2').focus();
+ }, {once: true});
+
+ document.getElementById('ii2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('bs2').focus();
+ }, {once: true});
+
+ document.getElementById('bs2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('enter').focus();
+ }, {once: true});
+
+ document.getElementById('enter').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('i8').focus();
+ }, {once: true});
+
+ document.getElementById('i8').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('i9').focus();
+ }, {once: true});
+
+ document.getElementById('i9').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ }, {once: true});
+
+ document.getElementById('is').focus();
+}
+
+function frameLoaded(aFrame) {
+ // Check if formaction/action has the correct behavior.
+ is(aFrame.contentWindow.location.href, gTestResults[aFrame.name],
+ "the method/formmethod attribute doesn't have the correct behavior");
+
+ if (--gPendingLoad == 0) {
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug582412-2.html b/dom/html/test/test_bug582412-2.html
new file mode 100644
index 0000000000..b5ff8fc81e
--- /dev/null
+++ b/dom/html/test/test_bug582412-2.html
@@ -0,0 +1,199 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=566160
+-->
+<head>
+ <title>Test for Bug 566160</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=566160">Mozilla Bug 566160</a>
+<p id="display"></p>
+<style>
+ iframe { width: 130px; height: 100px;}
+</style>
+<iframe name='frame1' id='frame1'></iframe>
+<iframe name='frame2' id='frame2'></iframe>
+<iframe name='frame3' id='frame3'></iframe>
+<iframe name='frame3bis' id='frame3bis'></iframe>
+<iframe name='frame4' id='frame4'></iframe>
+<iframe name='frame5' id='frame5'></iframe>
+<iframe name='frame6' id='frame6'></iframe>
+<iframe name='frame7' id='frame7'></iframe>
+<iframe name='frame8' id='frame8'></iframe>
+<iframe name='frame9' id='frame9'></iframe>
+<div id="content">
+ <!-- submit controls with formaction that are validated with a CLICK -->
+ <form target="frame1" action="form_submit_server.sjs" method="POST">
+ <input name='foo' value='foo'>
+ <input type='submit' id='is' formenctype='multipart/form-data'>
+ </form>
+ <form target="frame2" action="form_submit_server.sjs" method="POST">
+ <input name='bar' value='bar'>
+ <input type='image' id='ii' formenctype='multipart/form-data'>
+ </form>
+ <form target="frame3" action="form_submit_server.sjs" method="POST">
+ <input name='tulip' value='tulip'>
+ <button type='submit' id='bs' formenctype="multipart/form-data">submit</button>
+ </form>
+ <form target="frame3bis" action="form_submit_server.sjs" method="POST">
+ <input name='tulipbis' value='tulipbis'>
+ <button type='submit' id='bsbis' formenctype="multipart/form-data">submit</button>
+ </form>
+
+ <!-- submit controls with formaction that are validated with ENTER -->
+ <form target="frame4" action="form_submit_server.sjs" method="POST">
+ <input name='footulip' value='footulip'>
+ <input type='submit' id='is2' formenctype="multipart/form-data">
+ </form>
+ <form target="frame5" action="form_submit_server.sjs" method="POST">
+ <input name='foobar' value='foobar'>
+ <input type='image' id='ii2' formenctype="multipart/form-data">
+ </form>
+ <form target="frame6" action="form_submit_server.sjs" method="POST">
+ <input name='tulip2' value='tulip2'>
+ <button type='submit' id='bs2' formenctype="multipart/form-data">submit</button>
+ </form>
+
+ <!-- check that when submitting a from from an element
+ which is not a submit control, @formaction isn't used -->
+ <form target='frame7' action="form_submit_server.sjs" method="POST">
+ <input id='enter' name='input' value='enter' formenctype="multipart/form-data">
+ </form>
+
+ <!-- If formenctype isn't set, it's default value shouldn't be used -->
+ <form target="frame8" action="form_submit_server.sjs" method="POST" enctype="multipart/form-data">
+ <input name='tulip8' value='tulip8'>
+ <input type='submit' id='i8'>
+ </form>
+
+ <!-- If formenctype is set but has an invalid value, the default value should
+ be used. -->
+ <form target="frame9" action="form_submit_server.sjs" method="POST" enctype="multipart/form-data">
+ <input name='tulip9' value='tulip9'>
+ <input type='submit' id='i9' formenctype="">
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 566160 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ setTimeout(runTests, 0);
+});
+
+var gTestResults = {
+ frame1: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"foo\\\"\"},\"body\":\"foo\"}]',
+ frame2: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"bar\\\"\"},\"body\":\"bar\"},{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"x\\\"\"},\"body\":\"0\"},{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"y\\\"\"},\"body\":\"0\"}]',
+ frame3: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"tulip\\\"\"},\"body\":\"tulip\"}]',
+ frame3bis: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"tulipbis\\\"\"},\"body\":\"tulipbis\"}]',
+ frame4: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"footulip\\\"\"},\"body\":\"footulip\"}]',
+ frame5: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"foobar\\\"\"},\"body\":\"foobar\"},{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"x\\\"\"},\"body\":\"0\"},{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"y\\\"\"},\"body\":\"0\"}]',
+ frame6: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"tulip2\\\"\"},\"body\":\"tulip2\"}]',
+ frame7: '[]',
+ frame8: '[{\"headers\":{\"Content-Disposition\":\"form-data; name=\\\"tulip8\\\"\"},\"body\":\"tulip8\"}]',
+ frame9: '[]',
+};
+
+var gPendingLoad = 0; // Has to be set after depending on the frames number.
+
+function runTests()
+{
+ // We add a load event for the frames which will be called when the forms
+ // will be submitted.
+ var frames = [ document.getElementById('frame1'),
+ document.getElementById('frame2'),
+ document.getElementById('frame3'),
+ document.getElementById('frame3bis'),
+ document.getElementById('frame4'),
+ document.getElementById('frame5'),
+ document.getElementById('frame6'),
+ document.getElementById('frame7'),
+ document.getElementById('frame8'),
+ document.getElementById('frame9'),
+ ];
+ gPendingLoad = frames.length;
+
+ for (var i=0; i<frames.length; i++) {
+ frames[i].setAttribute('onload', "frameLoaded(this);");
+ }
+
+ /**
+ * We are going to focus each element before interacting with either for
+ * simulating the ENTER key (synthesizeKey) or a click (synthesizeMouse) or
+ * using .click(). This because it may be needed (ENTER) and because we want
+ * to have the element visible in the iframe.
+ *
+ * Focusing the first element (id='is') is launching the tests.
+ */
+ document.getElementById('is').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('is'), 5, 5, {});
+ document.getElementById('ii').focus();
+ }, {once: true});
+
+ document.getElementById('ii').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('ii'), 5, 5, {});
+ document.getElementById('bs').focus();
+ }, {once: true});
+
+ document.getElementById('bs').addEventListener('focus', function(aEvent) {
+ synthesizeMouse(document.getElementById('bs'), 5, 5, {});
+ document.getElementById('bsbis').focus();
+ }, {once: true});
+
+ document.getElementById('bsbis').addEventListener('focus', function(aEvent) {
+ document.getElementById('bsbis').click();
+ document.getElementById('is2').focus();
+ }, {once: true});
+
+ document.getElementById('is2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('ii2').focus();
+ }, {once: true});
+
+ document.getElementById('ii2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('bs2').focus();
+ }, {once: true});
+
+ document.getElementById('bs2').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('enter').focus();
+ }, {once: true});
+
+ document.getElementById('enter').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('i8').focus();
+ }, {once: true});
+
+ document.getElementById('i8').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ document.getElementById('i9').focus();
+ }, {once: true});
+
+ document.getElementById('i9').addEventListener('focus', function(aEvent) {
+ synthesizeKey("KEY_Enter");
+ }, {once: true});
+
+ document.getElementById('is').focus();
+}
+
+function frameLoaded(aFrame) {
+ // Check if formaction/action has the correct behavior.
+ is(aFrame.contentDocument.documentElement.textContent, gTestResults[aFrame.name],
+ "the enctype/formenctype attribute doesn't have the correct behavior");
+
+ if (--gPendingLoad == 0) {
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug583514.html b/dom/html/test/test_bug583514.html
new file mode 100644
index 0000000000..a3dead89aa
--- /dev/null
+++ b/dom/html/test/test_bug583514.html
@@ -0,0 +1,71 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=583514
+-->
+<head>
+ <title>Test for Bug 583514</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=583514">Mozilla Bug 583514</a>
+<p id="display"></p>
+<div id="content">
+ <input>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 583514 **/
+
+var gExpectDivClick = false;
+var gExpectInputClick = false;
+
+var div = document.getElementById('content');
+var input = document.getElementsByTagName('input')[0];
+
+div.addEventListener('click', function() {
+ ok(gExpectDivClick, "click event received on div and expected status was: " +
+ gExpectDivClick);
+});
+
+input.addEventListener('click', function() {
+ ok(gExpectInputClick, "click event received on input and expected status was: " +
+ gExpectInputClick);
+});
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ var body = document.body;
+
+ body.addEventListener('click', function(aEvent) {
+ if (aEvent.target == input) {
+ body.removeEventListener('click', arguments.callee);
+ }
+
+ ok(true, "click event received on body");
+
+ SimpleTest.executeSoon(function() {
+ isnot(document.activeElement, input, "input shouldn't have been focused");
+ isnot(document.activeElement, div, "div shouldn't have been focused");
+
+ if (aEvent.target == input) {
+ SimpleTest.finish();
+ } else {
+ gExpectDivClick = true;
+ gExpectInputClick = true;
+ input.click();
+ }
+ });
+ });
+
+ gExpectDivClick = true;
+ div.click();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug583533.html b/dom/html/test/test_bug583533.html
new file mode 100644
index 0000000000..c0b8c92e95
--- /dev/null
+++ b/dom/html/test/test_bug583533.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML>
+<html>
+ <!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=583533
+-->
+ <head>
+ <title>Test for Bug 583514</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=583533">Mozilla Bug 583533</a>
+ <p id="display"></p>
+ <div id="content">
+ <div id="e" accesskey="a">
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ /** Test for Bug 583533 **/
+
+ var sbs = SpecialPowers.Cc['@mozilla.org/intl/stringbundle;1'].
+ getService(SpecialPowers.Ci.nsIStringBundleService);
+ var bundle = sbs.createBundle("chrome://global-platform/locale/platformKeys.properties");
+
+ var shiftText = bundle.GetStringFromName("VK_SHIFT");
+ var altText = bundle.GetStringFromName("VK_ALT");
+ var controlText = bundle.GetStringFromName("VK_CONTROL");
+ var metaText = bundle.GetStringFromName("VK_COMMAND_OR_WIN");
+ var separatorText = bundle.GetStringFromName("MODIFIER_SEPARATOR");
+
+ var modifier = SpecialPowers.getIntPref("ui.key.contentAccess");
+
+ var isShift;
+ var isAlt;
+ var isControl;
+ var isMeta;
+
+ is(modifier < 16 && modifier >= 0, true, "Modifier in range");
+
+ // There are no consts for the mask of this prefs.
+ if (modifier & 8) {
+ isMeta = true;
+ }
+ if (modifier & 1) {
+ isShift = true;
+ }
+ if (modifier & 2) {
+ isControl = true;
+ }
+ if (modifier & 4) {
+ isAlt = true;
+ }
+
+ var label = "";
+
+ if (isControl)
+ label += controlText + separatorText;
+ if (isMeta)
+ label += metaText + separatorText;
+ if (isAlt)
+ label += altText + separatorText;
+ if (isShift)
+ label += shiftText + separatorText;
+
+ label += document.getElementById("e").accessKey;
+
+ is(label, document.getElementById("e").accessKeyLabel, "JS and C++ agree on accessKeyLabel");
+
+ /** Test for Bug 808964 **/
+
+ var div = document.createElement("div");
+ document.body.appendChild(div);
+
+ is(div.accessKeyLabel, "", "accessKeyLabel should be empty string");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug586763.html b/dom/html/test/test_bug586763.html
new file mode 100644
index 0000000000..b396cb8bc9
--- /dev/null
+++ b/dom/html/test/test_bug586763.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=586763
+-->
+<title>Test for Bug 586763</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=586763">Mozilla Bug 586763</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 586763 **/
+var tests = [
+ ["ol", "start", 1],
+ ["li", "value", 0],
+ ["object", "hspace", 0],
+ ["object", "vspace", 0],
+ ["img", "hspace", 0],
+ ["img", "vspace", 0],
+ ["video", "height", 0],
+ ["video", "width", 0],
+ ["pre", "width", 0],
+ ["textarea", "cols", 20],
+ ["textarea", "rows", 2],
+ ["span", "tabIndex", -1],
+ ["frame", "tabIndex", 0],
+ ["a", "tabIndex", 0],
+ ["area", "tabIndex", 0],
+ ["button", "tabIndex", 0],
+ ["input", "tabIndex", 0],
+ ["object", "tabIndex", 0],
+ ["select", "tabIndex", 0],
+ ["textarea", "tabIndex", 0],
+];
+
+for (var i = 0; i < tests.length; i++) {
+ is(document.createElement(tests[i][0])[tests[i][1]], tests[i][2], "Reflected attribute " + tests[i][0] + "." + tests[i][1] + " should default to " + tests[i][2]);
+}
+</script>
+</pre>
diff --git a/dom/html/test/test_bug586786.html b/dom/html/test/test_bug586786.html
new file mode 100644
index 0000000000..3cfa7fa4b4
--- /dev/null
+++ b/dom/html/test/test_bug586786.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=586786
+-->
+<head>
+ <title>Test for Bug 586786</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=586786">Mozilla Bug 586786</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 586786 **/
+
+var elements = ["col", "colgroup", "tbody", "tfoot", "thead", "tr", "td", "th"];
+
+for(var i = 0; i < elements.length; i++)
+{
+ reflectString({
+ element: document.createElement(elements[i]),
+ attribute: "align",
+ otherValues: [ "left", "right", "center", "justify", "char" ]
+ });
+
+ reflectString({
+ element: document.createElement(elements[i]),
+ attribute: "vAlign",
+ otherValues: [ "top", "middle", "bottom", "baseline" ]
+ });
+
+ reflectString({
+ element: document.createElement(elements[i]),
+ attribute: {idl: "ch", content: "char"}
+ });
+}
+
+// table.border, table.width
+reflectString({
+ element: document.createElement("table"),
+ attribute: "border"
+});
+
+reflectString({
+ element: document.createElement("table"),
+ attribute: "width"
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug587469.html b/dom/html/test/test_bug587469.html
new file mode 100644
index 0000000000..b0e941296a
--- /dev/null
+++ b/dom/html/test/test_bug587469.html
@@ -0,0 +1,41 @@
+<!-- Quirks -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=587469
+-->
+<head>
+ <title>Test for Bug 587469</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=587469">Mozilla Bug 587469</a>
+<p id="display">
+<map name=a></map>
+<map name=a><area shape=rect coords=25,25,75,75 href=#fail></map>
+<img usemap=#a src=image.png>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 587469 **/
+SimpleTest.waitForExplicitFinish();
+function finish() {
+ is(location.hash, "", "Should not have changed the hash.");
+ SimpleTest.finish();
+}
+SimpleTest.waitForFocus(function() {
+ synthesizeMouse(document.getElementsByTagName("img")[0], 50, 50, {});
+ // Hit the event loop twice before doing the test
+ setTimeout(function() {
+ setTimeout(finish, 0);
+ }, 0);
+});
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug589.html b/dom/html/test/test_bug589.html
new file mode 100644
index 0000000000..3a4ac666a7
--- /dev/null
+++ b/dom/html/test/test_bug589.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589
+-->
+<head>
+ <title>Test for Bug 589</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+<style type="text/css">
+.letters {list-style-type: upper-alpha;}
+.numbers {list-style-type: decimal;}
+</style>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589">Mozilla Bug 589</a>
+<p id="display"></p>
+<div id="content" >
+
+<OL id="thelist" class="letters">
+<LI id="liA">This list should feature...
+<LI id="liB">...letters for each item...
+<LI id="li3" class="numbers">...except this one.
+</OL>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 589 **/
+
+is(computedStyle($("liA"),"list-style-type"),"upper-alpha");
+is(computedStyle($("liB"),"list-style-type"),"upper-alpha");
+is(computedStyle($("li3"),"list-style-type"),"decimal");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug590353-1.html b/dom/html/test/test_bug590353-1.html
new file mode 100644
index 0000000000..af79317f36
--- /dev/null
+++ b/dom/html/test/test_bug590353-1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=590353
+-->
+<head>
+ <title>Test for Bug 590353</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=590353">Mozilla Bug 590353</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 590353 **/
+
+var testData = ['checkbox', 'radio'];
+
+for (var data of testData) {
+ var e = document.createElement('input');
+ e.type = data;
+ e.checked = true;
+ e.value = "foo";
+
+ is(e.value, "foo", "foo should be the new " + data + "value");
+ is(e.getAttribute('value'), "foo", "foo should be the new " + data +
+ " value attribute value");
+ ok(e.checked, data + " should still be checked");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug590353-2.html b/dom/html/test/test_bug590353-2.html
new file mode 100644
index 0000000000..9ef6e40e23
--- /dev/null
+++ b/dom/html/test/test_bug590353-2.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=590353
+-->
+<head>
+ <title>Test for Bug 590353</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=590353">Mozilla Bug 590353</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 590353 **/
+
+var testData = [
+ [ "text", "foo", "" ],
+ [ "email", "foo@bar.com", "" ],
+ [ "url", "http:///foo.com", "" ],
+ [ "tel", "555 555 555 555", "" ],
+ [ "search", "foo", "" ],
+ [ "password", "secret", "" ],
+ [ "hidden", "foo", "foo" ],
+ [ "button", "foo", "foo" ],
+ [ "reset", "foo", "foo" ],
+ [ "submit", "foo", "foo" ],
+ [ "checkbox", true, false ],
+ [ "radio", true, false ],
+ [ "file", "590353_file", "" ],
+];
+
+function createFileWithData(fileName, fileData) {
+ return new File([new Blob([fileData], { type: "text/plain" })], fileName);
+}
+
+var content = document.getElementById('content');
+var form = document.createElement('form');
+content.appendChild(form);
+
+for (var data of testData) {
+ var e = document.createElement('input');
+ e.type = data[0];
+
+ if (data[0] == 'checkbox' || data[0] == 'radio') {
+ e.checked = data[1];
+ } else if (data[0] == 'file') {
+ var file = createFileWithData(data[1], "file content");
+ SpecialPowers.wrap(e).mozSetFileArray([file]);
+ } else {
+ e.value = data[1];
+ }
+
+ form.appendChild(e);
+}
+
+form.reset();
+
+var size = form.elements.length;
+for (var i=0; i<size; ++i) {
+ var e = form.elements[i];
+
+ if (e.type == 'radio' || e.type == 'checkbox') {
+ is(e.checked, testData[i][2],
+ "the element checked value should be " + testData[i][2]);
+ } else {
+ is(e.value, testData[i][2],
+ "the element value should be " + testData[i][2]);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug590363.html b/dom/html/test/test_bug590363.html
new file mode 100644
index 0000000000..de12079a71
--- /dev/null
+++ b/dom/html/test/test_bug590363.html
@@ -0,0 +1,133 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=590363
+-->
+<head>
+ <title>Test for Bug 590363</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=590363">Mozilla Bug 590363</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 590363 **/
+
+var testData = [
+ /* type to test | is the value reset when changing to file then reverting */
+ [ "button", false ],
+ [ "checkbox", false ],
+ [ "hidden", false ],
+ [ "reset", false ],
+ [ "image", false ],
+ [ "radio", false ],
+ [ "submit", false ],
+ [ "tel", true ],
+ [ "text", true ],
+ [ "url", true ],
+ [ "email", true ],
+ [ "search", true ],
+ [ "password", true ],
+ [ "number", true ],
+ [ "date", true ],
+ [ "time", true ],
+ [ "range", true ],
+ [ "color", true ],
+ [ 'month', true ],
+ [ 'week', true ],
+ [ 'datetime-local', true ]
+ // 'file' is treated separatly.
+];
+
+var nonTrivialSanitizing = [ 'number', 'date', 'time', 'color', 'month', 'week',
+ 'datetime-local' ];
+
+var length = testData.length;
+for (var i=0; i<length; ++i) {
+ for (var j=0; j<length; ++j) {
+ var e = document.createElement('input');
+ e.type = testData[i][0];
+
+ var expectedValue;
+
+ // range will sanitize its value to 50 (the default) if it isn't a valid
+ // number. We need to handle that specially.
+ if (testData[j][0] == 'range' || testData[i][0] == 'range') {
+ if (testData[j][0] == 'date' || testData[j][0] == 'time' ||
+ testData[j][0] == 'month' || testData[j][0] == 'week' ||
+ testData[j][0] == 'datetime-local') {
+ expectedValue = '';
+ } else if (testData[j][0] == 'color') {
+ expectedValue = '#000000';
+ } else {
+ expectedValue = '50';
+ }
+ } else if (testData[i][0] == 'color' || testData[j][0] == 'color') {
+ if (testData[j][0] == 'number' || testData[j][0] == 'date' ||
+ testData[j][0] == 'time' || testData[j][0] == 'month' ||
+ testData[j][0] == 'week' || testData[j][0] == 'datetime-local') {
+ expectedValue = ''
+ } else {
+ expectedValue = '#000000';
+ }
+ } else if (nonTrivialSanitizing.includes(testData[i][0]) &&
+ nonTrivialSanitizing.includes(testData[j][0])) {
+ expectedValue = '';
+ } else if (testData[i][0] == 'number' || testData[j][0] == 'number') {
+ expectedValue = '42';
+ } else if (testData[i][0] == 'date' || testData[j][0] == 'date') {
+ expectedValue = '2012-12-21';
+ } else if (testData[i][0] == 'time' || testData[j][0] == 'time') {
+ expectedValue = '21:21';
+ } else if (testData[i][0] == 'month' || testData[j][0] == 'month') {
+ expectedValue = '2013-03';
+ } else if (testData[i][0] == 'week' || testData[j][0] == 'week') {
+ expectedValue = '2016-W35';
+ } else if (testData[i][0] == 'datetime-local' ||
+ testData[j][0] == 'datetime-local') {
+ expectedValue = '2016-11-07T16:40';
+ } else {
+ expectedValue = "foo";
+ }
+ e.value = expectedValue;
+
+ e.type = testData[j][0];
+ is(e.value, expectedValue, ".value should still return the same value after " +
+ "changing type from " + testData[i][0] + " to " + testData[j][0]);
+ }
+}
+
+// For type='file' .value doesn't behave the same way.
+// We are just going to check that we do not loose the value.
+for (var data of testData) {
+ var e = document.createElement('input');
+ e.type = data[0];
+ e.value = 'foo';
+ e.type = 'file';
+ e.type = data[0];
+
+ if (data[0] == 'range') {
+ is(e.value, '50', ".value should still return the same value after " +
+ "changing type from " + data[0] + " to 'file' then reverting to " + data[0]);
+ } else if (data[0] == 'color') {
+ is(e.value, '#000000', ".value should have been reset to the default color after " +
+ "changing type from " + data[0] + " to 'file' then reverting to " + data[0]);
+ } else if (data[1]) {
+ is(e.value, '', ".value should have been reset to the empty string after " +
+ "changing type from " + data[0] + " to 'file' then reverting to " + data[0]);
+ } else {
+ is(e.value, 'foo', ".value should still return the same value after " +
+ "changing type from " + data[0] + " to 'file' then reverting to " + data[0]);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug592802.html b/dom/html/test/test_bug592802.html
new file mode 100644
index 0000000000..e8b30d84c8
--- /dev/null
+++ b/dom/html/test/test_bug592802.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=592802
+-->
+<head>
+ <title>Test for Bug 592802</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=592802">Mozilla Bug 592802</a>
+<p id="display"></p>
+<div id="content">
+ <input id='a' type='file'>
+ <input id='a2' type='file'>
+</div>
+<button id='b' onclick="document.getElementById('a').click();">Show Filepicker</button>
+<button id='b2' onclick="document.getElementById('a2').click();">Show Filepicker</button>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 592802 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+var testData = [
+/* visibility | display | multiple */
+ [ "", "", false ],
+ [ "hidden", "", false ],
+ [ "", "none", false ],
+ [ "", "", true ],
+ [ "hidden", "", true ],
+ [ "", "none", true ],
+];
+
+var testCounter = 0;
+var testNb = testData.length;
+
+function finished()
+{
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(function() {
+ // mockFilePicker will simulate a cancel for the first time the file picker will be shown.
+ MockFilePicker.returnValue = MockFilePicker.returnCancel;
+
+ var b2 = document.getElementById('b2');
+ b2.focus(); // Be sure the element is visible.
+ document.getElementById('b2').addEventListener("change", function(aEvent) {
+ ok(false, "When cancel is received, change should not fire");
+ }, {once: true});
+ b2.click();
+
+ // Now, we can launch tests when file picker isn't canceled.
+ MockFilePicker.useBlobFile();
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+
+ var b = document.getElementById('b');
+ b.focus(); // Be sure the element is visible.
+
+ document.getElementById('a').addEventListener("change", function(aEvent) {
+ ok(true, "change event correctly sent");
+ ok(aEvent.bubbles, "change event should bubble");
+ ok(!aEvent.cancelable, "change event should not be cancelable");
+ testCounter++;
+
+ if (testCounter >= testNb) {
+ aEvent.target.removeEventListener("change", arguments.callee);
+ SimpleTest.executeSoon(finished);
+ } else {
+ var data = testData[testCounter];
+ var a = document.getElementById('a');
+ a.style.visibility = data[0];
+ a.style.display = data[1];
+ a.multiple = data[2];
+
+ SimpleTest.executeSoon(function() {
+ b.click();
+ });
+ }
+ });
+
+ b.click();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug593689.html b/dom/html/test/test_bug593689.html
new file mode 100644
index 0000000000..14cfa8f0fb
--- /dev/null
+++ b/dom/html/test/test_bug593689.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=593689
+-->
+<head>
+ <title>Test for Bug 593689</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=593689">Mozilla Bug 593689</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 593689 **/
+function testWidth(w) {
+ var img = new Image(w);
+ is(img.width, w|0, "Unexpected handling of '" + w + "' width");
+}
+
+testWidth(1);
+testWidth(0);
+testWidth("xxx");
+testWidth(null);
+testWidth(undefined);
+testWidth({});
+testWidth({ valueOf() { return 10; } });
+
+function testHeight(h) {
+ var img = new Image(100, h);
+ is(img.height, h|0, "Unexpected handling of '" + h + "' height");
+}
+
+testHeight(1);
+testHeight(0);
+testHeight("xxx");
+testHeight(null);
+testHeight(undefined);
+testHeight({});
+testHeight({ valueOf() { return 10; } });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug595429.html b/dom/html/test/test_bug595429.html
new file mode 100644
index 0000000000..9a9215bd6c
--- /dev/null
+++ b/dom/html/test/test_bug595429.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=595429
+-->
+<head>
+ <title>Test for Bug 595429</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=595429">Mozilla Bug 595429</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 595429 **/
+
+var fieldset = document.createElement("fieldset");
+
+ok("name" in fieldset, "<fieldset> should have a name IDL attribute");
+
+var testData = [
+ "",
+ " ",
+ "foo",
+ "foo bar",
+];
+
+is(fieldset.getAttribute("name"), null,
+ "By default, name content attribute should be null");
+is(fieldset.name, "",
+ "By default, name IDL attribute should be the empty string");
+
+for (var data of testData) {
+ fieldset.setAttribute("name", data);
+ is(fieldset.getAttribute("name"), data,
+ "name content attribute should be " + data);
+ is(fieldset.name, data, "name IDL attribute should be " + data);
+
+ fieldset.setAttribute("name", "");
+ fieldset.name = data;
+ is(fieldset.getAttribute("name"), data,
+ "name content attribute should be " + data);
+ is(fieldset.name, data, "name IDL attribute should be " + data);
+}
+
+fieldset.removeAttribute("name");
+is(fieldset.getAttribute("name"), null,
+ "name content attribute should be null");
+is(fieldset.name, "", "name IDL attribute should be the empty string");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug595447.html b/dom/html/test/test_bug595447.html
new file mode 100644
index 0000000000..e647364879
--- /dev/null
+++ b/dom/html/test/test_bug595447.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=595447
+-->
+<head>
+ <title>Test for Bug 595447</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=595447">Mozilla Bug 595447</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 595447 **/
+
+var fieldset = document.createElement("fieldset");
+
+ok("type" in fieldset, "fieldset element should have a type IDL attribute");
+is(fieldset.type, "fieldset", "fieldset.type should return 'fieldset'");
+fieldset.type = "foo";
+is(fieldset.type, "fieldset", "fieldset.type is readonly");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug595449.html b/dom/html/test/test_bug595449.html
new file mode 100644
index 0000000000..5649b246ae
--- /dev/null
+++ b/dom/html/test/test_bug595449.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=595449
+-->
+<head>
+ <title>Test for Bug 595449</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=595449">Mozilla Bug 595449</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 595449 **/
+
+var fieldset = document.createElement("fieldset");
+
+ok("elements" in fieldset,
+ "fieldset element should have an 'elements' IDL attribute");
+
+ok(fieldset.elements instanceof HTMLCollection,
+ "fieldset.elements should be an instance of HTMLCollection");
+
+// https://www.w3.org/Bugs/Public/show_bug.cgi?id=23356
+todo(fieldset.elements instanceof HTMLFormControlsCollection,
+ "fieldset.elements should be an instance of HTMLFormControlsCollection");
+
+is(fieldset.elements.length, 0, "Nothing should be in fieldset.elements");
+
+var oldElements = fieldset.elements;
+
+is(fieldset.elements, oldElements,
+ "fieldset.elements should always return the same object");
+
+var tmpElement = document.createElement("input");
+
+fieldset.appendChild(tmpElement);
+
+is(fieldset.elements.length, 1,
+ "fieldset.elements should now contain one element");
+
+is(fieldset.elements[0], tmpElement,
+ "fieldset.elements[0] should be the input element");
+
+tmpElement.name = "foo";
+is(fieldset.elements.foo, tmpElement,
+ "we should be able to access to an element using it's name as a property on .elements");
+
+is(fieldset.elements, oldElements,
+ "fieldset.elements should always return the same object");
+
+fieldset.removeChild(tmpElement);
+
+var testData = [
+ [ "<input>", 1 , [ HTMLInputElement ] ],
+ [ "<button></button>", 1, [ HTMLButtonElement ] ],
+ [ "<button><input></button>", 2, [ HTMLButtonElement, HTMLInputElement ] ],
+ [ "<object>", 1, [ HTMLObjectElement ] ],
+ [ "<output></output>", 1, [ HTMLOutputElement ] ],
+ [ "<select></select>", 1, [ HTMLSelectElement ] ],
+ [ "<select><option>foo</option></select>", 1, [ HTMLSelectElement ] ],
+ [ "<select><option>foo</option><input></select>", 2, [ HTMLSelectElement, HTMLInputElement ] ],
+ [ "<textarea></textarea>", 1, [ HTMLTextAreaElement ] ],
+ [ "<label>foo</label>", 0 ],
+ [ "<progress>", 0 ],
+ [ "<meter>", 0 ],
+ [ "<keygen>", 0 ],
+ [ "<legend></legend>", 0 ],
+ [ "<legend><input></legend>", 1, [ HTMLInputElement ] ],
+ [ "<legend><input></legend><legend><input></legend>", 2, [ HTMLInputElement, HTMLInputElement ] ],
+ [ "<legend><input></legend><input>", 2, [ HTMLInputElement, HTMLInputElement ] ],
+ [ "<fieldset></fieldset>", 1, [ HTMLFieldSetElement ] ],
+ [ "<fieldset><input></fieldset>", 2, [ HTMLFieldSetElement, HTMLInputElement ] ],
+ [ "<fieldset><fieldset><input></fieldset></fieldset>", 3, [ HTMLFieldSetElement, HTMLFieldSetElement, HTMLInputElement ] ],
+ [ "<button></button><fieldset></fieldset><input><keygen><object><output></output><select></select><textarea></textarea>", 7, [ HTMLButtonElement, HTMLFieldSetElement, HTMLInputElement, HTMLObjectElement, HTMLOutputElement, HTMLSelectElement, HTMLTextAreaElement ] ],
+];
+
+for (var data of testData) {
+ fieldset.innerHTML = data[0];
+ is(fieldset.elements.length, data[1],
+ "fieldset.elements should contain " + data[1] + " elements");
+
+ for (var i=0; i<data[1]; ++i) {
+ ok(fieldset.elements[i] instanceof data[2][i],
+ "fieldset.elements[" + i + "] should be instance of " + data[2][i])
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug596350.html b/dom/html/test/test_bug596350.html
new file mode 100644
index 0000000000..72e2f7ce73
--- /dev/null
+++ b/dom/html/test/test_bug596350.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=596350
+-->
+<head>
+ <title>Test for Bug 596350</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=596350">Mozilla Bug 596350</a>
+<p id="display"></p>
+<div id="content">
+ <object></object>
+ <object data="iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMsALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0vr4MkhoXe0rZigAAAABJRU5ErkJggg=="></object>
+ <object data="data:text/html,foo"></object>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 596350 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+var testData = [
+// Object 0
+ [ 0, null, 0 ],
+ [ 0, "1", 1 ],
+ [ 0, "-1", -1 ],
+ [ 0, "0", 0 ],
+ [ 0, "foo", 0 ],
+// Object 1
+ [ 1, null, 0 ],
+ [ 1, "1", 1 ],
+// Object 2
+ [ 2, null, 0 ],
+ [ 2, "1", 1 ],
+ [ 2, "-1", -1 ],
+];
+
+var objects = document.getElementsByTagName("object");
+
+function runTests()
+{
+ for (var data of testData) {
+ var obj = objects[data[0]];
+
+ if (data[1]) {
+ obj.setAttribute("tabindex", data[1]);
+ }
+
+ is(obj.tabIndex, data[2], "tabIndex value should be " + data[2]);
+
+ obj.removeAttribute("tabindex");
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug596511.html b/dom/html/test/test_bug596511.html
new file mode 100644
index 0000000000..42b93e4632
--- /dev/null
+++ b/dom/html/test/test_bug596511.html
@@ -0,0 +1,237 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=596511
+-->
+<head>
+ <title>Test for Bug 596511</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ select:valid { background-color: green; }
+ select:invalid { background-color: red; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=596511">Mozilla Bug 596511</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script>
+
+/** Test for Bug 596511 **/
+
+function checkNotSufferingFromBeingMissing(element, aTodo)
+{
+ if (aTodo) {
+ ok = todo;
+ is = todo_is;
+ }
+
+ ok(!element.validity.valueMissing,
+ "Element should not suffer from value missing");
+ ok(element.validity.valid, "Element should be valid");
+ ok(element.checkValidity(), "Element should be valid");
+
+ is(element.validationMessage, "",
+ "Validation message should be the empty string");
+
+ ok(element.matches(":valid"), ":valid pseudo-class should apply");
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(0, 128, 0)", ":valid pseudo-class should apply");
+
+ if (aTodo) {
+ ok = SimpleTest.ok;
+ is = SimpleTest.is;
+ }
+}
+
+function checkSufferingFromBeingMissing(element, aTodo)
+{
+ if (aTodo) {
+ ok = todo;
+ is = todo_is;
+ }
+
+ ok(element.validity.valueMissing, "Element should suffer from value missing");
+ ok(!element.validity.valid, "Element should not be valid");
+ ok(!element.checkValidity(), "Element should not be valid");
+
+ is(element.validationMessage, "Please select an item in the list.",
+ "Validation message is wrong");
+
+ is(window.getComputedStyle(element).getPropertyValue('background-color'),
+ "rgb(255, 0, 0)", ":invalid pseudo-class should apply");
+
+ if (aTodo) {
+ ok = SimpleTest.ok;
+ is = SimpleTest.is;
+ }
+}
+
+function checkRequiredAttribute(element)
+{
+ ok('required' in element, "select should have a required attribute");
+
+ ok(!element.required, "select required attribute should be disabled");
+ is(element.getAttribute('required'), null,
+ "select required attribute should be disabled");
+
+ element.required = true;
+ ok(element.required, "select required attribute should be enabled");
+ isnot(element.getAttribute('required'), null,
+ "select required attribute should be enabled");
+
+ element.removeAttribute('required');
+ element.setAttribute('required', '');
+ ok(element.required, "select required attribute should be enabled");
+ isnot(element.getAttribute('required'), null,
+ "select required attribute should be enabled");
+
+ element.removeAttribute('required');
+ ok(!element.required, "select required attribute should be disabled");
+ is(element.getAttribute('required'), null,
+ "select required attribute should be disabled");
+}
+
+function checkRequiredAndOptionalSelectors(element)
+{
+ is(document.querySelector("select:optional"), element,
+ "select should be optional");
+ is(document.querySelector("select:required"), null,
+ "select shouldn't be required");
+
+ element.required = true;
+
+ is(document.querySelector("select:optional"), null,
+ "select shouldn't be optional");
+ is(document.querySelector("select:required"), element,
+ "select should be required");
+
+ element.required = false;
+}
+
+function checkInvalidWhenValueMissing(element)
+{
+ checkNotSufferingFromBeingMissing(select);
+
+ element.required = true;
+ checkSufferingFromBeingMissing(select);
+
+ /**
+ * Non-multiple and size=1.
+ */
+ select.appendChild(new Option());
+ checkSufferingFromBeingMissing(select);
+
+ // When removing the required attribute, element should not be invalid.
+ element.required = false;
+ checkNotSufferingFromBeingMissing(select);
+
+ element.required = true;
+ select.options[0].textContent = "foo";
+ // TODO: having that working would require us to add a mutation observer on
+ // the select element.
+ checkNotSufferingFromBeingMissing(select, true);
+
+ select.remove(0);
+ checkSufferingFromBeingMissing(select);
+
+ select.add(new Option("foo", "foo"), null);
+ checkNotSufferingFromBeingMissing(select);
+
+ select.add(new Option(), null);
+ checkNotSufferingFromBeingMissing(select);
+
+ // The placeholder label can only be the first option, so a selected empty second option is valid
+ select.options[1].selected = true;
+ checkNotSufferingFromBeingMissing(select);
+
+ select.selectedIndex = 0;
+ checkNotSufferingFromBeingMissing(select);
+
+ select.add(select.options[0]);
+ select.selectedIndex = 0;
+ checkSufferingFromBeingMissing(select);
+
+ select.remove(0);
+ checkNotSufferingFromBeingMissing(select);
+
+ select.options[0].disabled = true;
+ // TODO: having that working would require us to add a mutation observer on
+ // the select element.
+ checkSufferingFromBeingMissing(select, true);
+
+ select.options[0].disabled = false
+ select.remove(0);
+ checkSufferingFromBeingMissing(select);
+
+ var option = new Option("foo", "foo");
+ option.disabled = true;
+ select.add(option, null);
+ select.add(new Option("bar"), null);
+ option.selected = true;
+ checkNotSufferingFromBeingMissing(select);
+
+ select.remove(0);
+ select.remove(0);
+
+ /**
+ * Non-multiple and size > 1.
+ * Everything should be the same except moving the selection.
+ */
+ select.multiple = false;
+ select.size = 4;
+ checkSufferingFromBeingMissing(select);
+
+ // Setting defaultSelected to true should not make the option selected
+ select.add(new Option("", "", true), null);
+ checkSufferingFromBeingMissing(select);
+ select.remove(0);
+
+ select.add(new Option("", "", true, true), null);
+ checkNotSufferingFromBeingMissing(select);
+
+ select.add(new Option("foo", "foo"), null);
+ select.remove(0);
+ checkSufferingFromBeingMissing(select);
+
+ select.options[0].selected = true;
+ checkNotSufferingFromBeingMissing(select);
+
+ select.remove(0);
+
+ /**
+ * Multiple, any size.
+ * We can select more than one element and at least needs a value.
+ */
+ select.multiple = true;
+ select.size = 4;
+ checkSufferingFromBeingMissing(select);
+
+ select.add(new Option("", "", true), null);
+ checkSufferingFromBeingMissing(select);
+
+ select.add(new Option("", "", true), null);
+ checkSufferingFromBeingMissing(select);
+
+ select.add(new Option("foo"), null);
+ checkSufferingFromBeingMissing(select);
+
+ select.options[2].selected = true;
+ checkNotSufferingFromBeingMissing(select);
+}
+
+var select = document.createElement("select");
+var content = document.getElementById('content');
+content.appendChild(select);
+
+checkRequiredAttribute(select);
+checkRequiredAndOptionalSelectors(select);
+checkInvalidWhenValueMissing(select);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug598643.html b/dom/html/test/test_bug598643.html
new file mode 100644
index 0000000000..12f91fdec4
--- /dev/null
+++ b/dom/html/test/test_bug598643.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=598643
+-->
+<head>
+ <title>Test for Bug 598643</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=598643">Mozilla Bug 598643</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 598643 **/
+
+function createFileWithData(fileName, fileData)
+{
+ return new File([new Blob([fileData], { type: "text/plain" })], fileName);
+}
+
+function testFileControl(aElement)
+{
+ aElement.type = 'file';
+
+ var file = createFileWithData("file_bug598643", "file content");
+ SpecialPowers.wrap(aElement).mozSetFileArray([file]);
+
+ ok(aElement.validity.valid, "the file control should be valid");
+ ok(!aElement.validity.tooLong,
+ "the file control shouldn't suffer from being too long");
+}
+
+var types = [
+ // These types can be too long.
+ [ "text", "email", "password", "url", "search", "tel" ],
+ // These types can't be too long.
+ [ "radio", "checkbox", "submit", "button", "reset", "image", "hidden",
+ 'number', 'range', 'date', 'time', 'color', 'month', 'week',
+ 'datetime-local' ],
+];
+
+var input = document.createElement("input");
+input.maxLength = 1;
+input.value = "foo";
+
+// Too long types.
+for (type of types[0]) {
+ input.type = type
+ if (type == 'email') {
+ input.value = "foo@bar.com";
+ } else if (type == 'url') {
+ input.value = 'http://foo.org';
+ }
+
+ todo(!input.validity.valid, "the element should be invalid [type=" + type + "]");
+ todo(input.validity.tooLong,
+ "the element should suffer from being too long [type=" + type + "]");
+
+ if (type == 'email' || type == 'url') {
+ input.value = 'foo';
+ }
+}
+
+// Not too long types.
+for (type of types[1]) {
+ input.type = type
+ ok(input.validity.valid, "the element should be valid [type=" + type + "]");
+ ok(!input.validity.tooLong,
+ "the element shouldn't suffer from being too long [type=" + type + "]");
+}
+
+testFileControl(input);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug598833-1.html b/dom/html/test/test_bug598833-1.html
new file mode 100644
index 0000000000..5fbbe221e5
--- /dev/null
+++ b/dom/html/test/test_bug598833-1.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=598833
+-->
+<head>
+ <title>Test for Bug 598833</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=598833">Mozilla Bug 598833</a>
+<p id="display">
+ <fieldset disabled>
+ <select id="s" multiple required>
+ <option>one</option>
+ </select>
+ </fieldset>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 598833 **/
+var s = $("s");
+is(s.matches(":invalid"), false, "Disabled select should not be invalid");
+is(s.matches(":valid"), false, "Disabled select should not be valid");
+var p = s.parentNode;
+p.removeChild(s);
+is(s.matches(":invalid"), true,
+ "Required valueless select not in tree should be invalid");
+is(s.matches(":valid"), false,
+ "Required valueless select not in tree should not be valid");
+p.appendChild(s);
+p.disabled = false;
+is(s.matches(":invalid"), true,
+ "Required valueless select should be invalid");
+is(s.matches(":valid"), false,
+ "Required valueless select should not be valid");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug600155.html b/dom/html/test/test_bug600155.html
new file mode 100644
index 0000000000..893dfe31f1
--- /dev/null
+++ b/dom/html/test/test_bug600155.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=600155
+-->
+<head>
+ <title>Test for Bug 600155</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=600155">Mozilla Bug 600155</a>
+<p id="display"></p>
+<div id='content' style='display:none;'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 600155 **/
+
+var subjectForConstraintValidation = [ "input", "select", "textarea" ];
+var content = document.getElementById('content');
+
+for (var eName of subjectForConstraintValidation) {
+ var e = document.createElement(eName);
+ content.appendChild(e);
+ e.setCustomValidity("foo");
+ if ("required" in e) {
+ e.required = true;
+ } else {
+ e.setCustomValidity("bar");
+ }
+
+ // At this point, the element is invalid.
+ is(e.validationMessage, "foo",
+ "the validation message should be the author one");
+
+ content.removeChild(e);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug601030.html b/dom/html/test/test_bug601030.html
new file mode 100644
index 0000000000..f9a277f471
--- /dev/null
+++ b/dom/html/test/test_bug601030.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=601030
+-->
+<head>
+ <title>Test for Bug 601030</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=601030">Mozilla Bug 601030</a>
+<p id="display"></p>
+<div id="content">
+ <iframe src="data:text/html,<input autofocus>"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 601030 **/
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ var f = document.createElement("iframe");
+ var content = document.getElementById('content');
+
+ f.addEventListener("load", function() {
+ SimpleTest.executeSoon(function() {
+ isnot(document.activeElement, f,
+ "autofocus should not work when another frame is inserted in the document");
+
+ content.removeChild(f);
+ content.removeChild(document.getElementsByTagName('iframe')[0]);
+ f = document.createElement('iframe');
+ f.addEventListener("load", function() {
+ isnot(document.activeElement, f,
+ "autofocus should not work in a frame if the top document is already loaded");
+ SimpleTest.finish();
+ }, {once: true});
+ f.src = "data:text/html,<input autofocus>";
+ content.appendChild(f);
+ });
+ }, {once: true});
+
+ f.src = "data:text/html,<input autofocus>";
+ content.appendChild(f);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug605124-1.html b/dom/html/test/test_bug605124-1.html
new file mode 100644
index 0000000000..d252987ee9
--- /dev/null
+++ b/dom/html/test/test_bug605124-1.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605124
+-->
+<head>
+ <title>Test for Bug 605124</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605124">Mozilla Bug 605124</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ <textarea required></textarea>
+ <input required>
+ <select required></select>
+ <button type='submit'></button>
+ </form>
+
+ <table>
+ <form>
+ <tr>
+ <textarea required></textarea>
+ <input required>
+ <select required></select>
+ <button type='submit'></button>
+ </tr>
+ </form>
+ </table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605124 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+ is(aElement.matches(":user-invalid"), aExpected,
+ "matches(':user-invalid') should return " + aExpected + " for " + aElement);
+}
+
+var content = document.getElementById('content');
+var textarea = document.getElementsByTagName('textarea')[0];
+var input = document.getElementsByTagName('input')[0];
+var select = document.getElementsByTagName('select')[0];
+var button = document.getElementsByTagName('button')[0];
+var form = document.forms[0];
+
+checkPseudoClass(textarea, false);
+checkPseudoClass(input, false);
+checkPseudoClass(select, false);
+
+// Try to submit.
+button.click();
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+// No longer in the form.
+content.appendChild(textarea);
+content.appendChild(input);
+content.appendChild(select);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+// Back in the form.
+form.appendChild(textarea);
+form.appendChild(input);
+form.appendChild(select);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+/* Case when elements get orphaned. */
+var textarea = document.getElementsByTagName('textarea')[1];
+var input = document.getElementsByTagName('input')[1];
+var select = document.getElementsByTagName('select')[1];
+var button = document.getElementsByTagName('button')[1];
+var form = document.forms[1];
+
+// Try to submit.
+button.click();
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+// Remove the form.
+document.getElementsByTagName('table')[0].removeChild(form);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug605124-2.html b/dom/html/test/test_bug605124-2.html
new file mode 100644
index 0000000000..07e3e5afcd
--- /dev/null
+++ b/dom/html/test/test_bug605124-2.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605124
+-->
+<head>
+ <title>Test for Bug 605124</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605124">Mozilla Bug 605124</a>
+<p id="display"></p>
+<div id="content">
+ <input required>
+ <textarea required></textarea>
+ <select required>
+ <option value="">foo</option>
+ <option>bar</option>
+ </select>
+ <select multiple required>
+ <option value="">foo</option>
+ <option>bar</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605124 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+ is(aElement.matches(":-moz-ui-invalid"), aExpected,
+ "matches(':-moz-ui-invalid') should return " + aExpected + " for " + aElement);
+}
+
+function checkElement(aElement)
+{
+ checkPseudoClass(aElement, false);
+
+ // Focusing while :-moz-ui-invalid doesn't apply,
+ // the pseudo-class should not apply while typing.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+ // with keys
+ sendString("f");
+ checkPseudoClass(aElement, false);
+ synthesizeKey("KEY_Backspace");
+ checkPseudoClass(aElement, false);
+ // with .value
+ aElement.value = 'f';
+ checkPseudoClass(aElement, false);
+ aElement.value = '';
+ checkPseudoClass(aElement, false);
+
+ aElement.blur();
+ checkPseudoClass(aElement, true);
+
+ // Focusing while :-moz-ui-invalid applies,
+ // the pseudo-class should apply while typing if appropriate.
+ aElement.focus();
+ checkPseudoClass(aElement, true);
+ // with keys
+ sendString("f");
+ checkPseudoClass(aElement, false);
+ synthesizeKey("KEY_Backspace");
+ checkPseudoClass(aElement, true);
+ // with .value
+ aElement.value = 'f';
+ checkPseudoClass(aElement, false);
+ aElement.value = '';
+ checkPseudoClass(aElement, true);
+}
+
+function checkSelectElement(aElement)
+{
+ checkPseudoClass(aElement, false);
+
+ // Focusing while :-moz-ui-invalid doesn't apply,
+ // the pseudo-class should not apply while changing selection.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+
+ aElement.selectedIndex = 1;
+ checkPseudoClass(aElement, false);
+ aElement.selectedIndex = 0;
+ checkPseudoClass(aElement, false);
+
+ aElement.blur();
+ checkPseudoClass(aElement, false);
+
+ // Focusing while :-moz-ui-invalid applies,
+ // the pseudo-class should apply while changing selection if appropriate.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+
+ aElement.selectedIndex = 1;
+ checkPseudoClass(aElement, false);
+ aElement.selectedIndex = 0;
+ checkPseudoClass(aElement, false);
+ aElement.selectedIndex = 1;
+ checkPseudoClass(aElement, false);
+
+ aElement.blur();
+ checkPseudoClass(aElement, false);
+}
+
+checkElement(document.getElementsByTagName('input')[0]);
+checkElement(document.getElementsByTagName('textarea')[0]);
+checkSelectElement(document.getElementsByTagName('select')[0]);
+checkSelectElement(document.getElementsByTagName('select')[1]);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug605125-1.html b/dom/html/test/test_bug605125-1.html
new file mode 100644
index 0000000000..8b49b9bbc1
--- /dev/null
+++ b/dom/html/test/test_bug605125-1.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605125
+-->
+<head>
+ <title>Test for Bug 605125</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605125">Mozilla Bug 605125</a>
+<p id="display"></p>
+<div id="content">
+ <form id='f1'>
+ <textarea></textarea>
+ <input>
+ <button type='submit'></button>
+ <select></select>
+ </form>
+
+ <table>
+ <form id='f2'>
+ <tr>
+ <textarea></textarea>
+ <input>
+ <button type='submit'></button>
+ <select></select>
+ </tr>
+ </form>
+ </table>
+ <input form='f1' required>
+ <input form='f2' required>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605125 **/
+
+/**
+ * NOTE: this test is very similar to 605124-1.html.
+ */
+
+function checkPseudoClass(aElement, aExpected)
+{
+ is(aElement.matches(":-moz-ui-valid"), aExpected,
+ "matches(':-moz-ui-valid') should return " + aExpected + " for " + aElement);
+}
+
+var content = document.getElementById('content');
+var textarea = document.getElementsByTagName('textarea')[0];
+var input = document.getElementsByTagName('input')[0];
+var button = document.getElementsByTagName('button')[0];
+var select = document.getElementsByTagName('select')[0];
+var form = document.forms[0];
+
+checkPseudoClass(textarea, false);
+checkPseudoClass(input, false);
+checkPseudoClass(select, false);
+
+// Try to submit.
+button.click();
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+// No longer in the form.
+content.appendChild(textarea);
+content.appendChild(input);
+content.appendChild(select);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+// Back in the form.
+form.appendChild(textarea);
+form.appendChild(input);
+form.appendChild(select);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+/* Case when elements get orphaned. */
+var textarea = document.getElementsByTagName('textarea')[1];
+var input = document.getElementsByTagName('input')[1];
+var button = document.getElementsByTagName('button')[1];
+var select = document.getElementsByTagName('select')[1];
+var form = document.forms[1];
+
+// Try to submit.
+button.click();
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+// Remove the form.
+document.getElementsByTagName('table')[0].removeChild(form);
+checkPseudoClass(textarea, true);
+checkPseudoClass(input, true);
+checkPseudoClass(select, true);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug605125-2.html b/dom/html/test/test_bug605125-2.html
new file mode 100644
index 0000000000..ea1195e189
--- /dev/null
+++ b/dom/html/test/test_bug605125-2.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605125
+-->
+<head>
+ <title>Test for Bug 605125</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605125">Mozilla Bug 605125</a>
+<p id="display"></p>
+<div id="content">
+ <input>
+ <textarea></textarea>
+ <select>
+ <option value="">foo</option>
+ <option>bar</option>
+ </select>
+ <select multiple>
+ <option value="">foo</option>
+ <option>bar</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605125 **/
+
+function checkPseudoClass(aElement, aExpected)
+{
+ is(aElement.matches(":user-valid"), aExpected,
+ "matches(':user-valid') should return " + aExpected + " for " + aElement.outerHTML);
+}
+
+function checkElement(aElement)
+{
+ checkPseudoClass(aElement, false);
+
+ // Focusing while :user-valid doesn't apply,
+ // the pseudo-class should not apply while typing.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+ // with keys
+ sendString("f");
+ checkPseudoClass(aElement, false);
+ synthesizeKey("KEY_Backspace");
+ checkPseudoClass(aElement, false);
+ // with .value
+ aElement.value = 'f';
+ checkPseudoClass(aElement, false);
+ aElement.value = '';
+ checkPseudoClass(aElement, false);
+
+ aElement.blur();
+ checkPseudoClass(aElement, true);
+
+ // Focusing while :user-valid applies,
+ // the pseudo-class should apply while typing if appropriate.
+ aElement.focus();
+ checkPseudoClass(aElement, true);
+ // with keys
+ sendString("f");
+ checkPseudoClass(aElement, true);
+ synthesizeKey("KEY_Backspace");
+ checkPseudoClass(aElement, true);
+ // with .value
+ aElement.value = 'f';
+ checkPseudoClass(aElement, true);
+ aElement.value = '';
+ checkPseudoClass(aElement, true);
+
+ aElement.blur();
+ aElement.required = true;
+ checkPseudoClass(aElement, false);
+
+ // Focusing while :user-invalid applies,
+ // the pseudo-class should apply while typing if appropriate.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+ // with keys
+ sendString("f");
+ checkPseudoClass(aElement, true);
+ synthesizeKey("KEY_Backspace");
+ checkPseudoClass(aElement, false);
+ // with .value
+ aElement.value = 'f';
+ checkPseudoClass(aElement, true);
+ aElement.value = '';
+ checkPseudoClass(aElement, false);
+}
+
+function checkSelectElement(aElement)
+{
+ checkPseudoClass(aElement, false);
+
+ if (!aElement.multiple && navigator.platform.startsWith("Mac")) {
+ // Arrow key on macOS opens the popup.
+ return;
+ }
+
+ // Focusing while :user-valid doesn't apply,
+ // the pseudo-class should not apply while changing selection.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+
+ synthesizeKey("KEY_ArrowDown");
+ checkPseudoClass(aElement, true);
+
+ // Focusing while :user-valid applies,
+ // the pseudo-class should apply while changing selection if appropriate.
+ aElement.focus();
+ checkPseudoClass(aElement, true);
+
+ aElement.selectedIndex = 1;
+ checkPseudoClass(aElement, true);
+ aElement.selectedIndex = 0;
+ checkPseudoClass(aElement, true);
+
+ aElement.blur();
+ aElement.required = true;
+ // select set with multiple is only invalid if no option is selected
+ if (aElement.multiple) {
+ aElement.selectedIndex = -1;
+ }
+ checkPseudoClass(aElement, false);
+
+ // Focusing while :user-invalid applies,
+ // the pseudo-class should apply while changing selection if appropriate.
+ aElement.focus();
+ checkPseudoClass(aElement, false);
+
+ synthesizeKey("KEY_ArrowDown");
+ checkPseudoClass(aElement, true);
+ aElement.selectedIndex = 0;
+ checkPseudoClass(aElement, aElement.multiple);
+}
+
+checkElement(document.getElementsByTagName('input')[0]);
+checkElement(document.getElementsByTagName('textarea')[0]);
+checkSelectElement(document.getElementsByTagName('select')[0]);
+checkSelectElement(document.getElementsByTagName('select')[1]);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug606817.html b/dom/html/test/test_bug606817.html
new file mode 100644
index 0000000000..4564753a93
--- /dev/null
+++ b/dom/html/test/test_bug606817.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=606817
+-->
+<head>
+ <title>Test for Bug 606817</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=606817">Mozilla Bug 606817</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 606817 **/
+
+var messageMaxLength = 256;
+
+function checkMessage(aInput, aMsg, aWithTerminalPeriod)
+{
+ ok(aInput.validationMessage != aMsg,
+ "Original content-defined message should have been truncate");
+ is(aInput.validationMessage.length - aInput.validationMessage.indexOf("_42_"),
+ aWithTerminalPeriod ? messageMaxLength+1 : messageMaxLength,
+ "validation message should be 256 characters length");
+}
+
+var input = document.createElement("input");
+
+var msg = "";
+for (var i=0; i<75; ++i) {
+ msg += "_42_";
+}
+// msg is now 300 chars long
+
+// Testing with setCustomValidity().
+input.setCustomValidity(msg);
+checkMessage(input, msg, false);
+
+// Cleaning.
+input.setCustomValidity("");
+
+// Testing with pattern and titl.
+input.pattern = "[0-9]*";
+input.value = "foo";
+input.title = msg;
+checkMessage(input, msg, true);
+
+// Cleaning.
+input.removeAttribute("pattern");
+input.removeAttribute("title");
+input.value = "";
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug607145.html b/dom/html/test/test_bug607145.html
new file mode 100644
index 0000000000..28ab3fe8ba
--- /dev/null
+++ b/dom/html/test/test_bug607145.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=607145
+-->
+<head>
+ <title>Test for Bug 607145</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=607145">Mozilla Bug 607145</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+var xoriginParams;
+/** Test for Bug 607145 **/
+
+/**
+ * This is not really reflecting an URL as the HTML5 specs want to.
+ * It's how .action is reflected in Gecko (might change later).
+ *
+ * If this changes, add reflectURL for "formAction" in
+ * dom/html/test/forms/test_input_attributes_reflection.html and
+ * "action" in
+ * dom/html/test/forms/test_form_attributes_reflection.html
+ */
+function reflectURL(aElement, aAttr)
+{
+ var idl = aAttr;
+ var attr = aAttr.toLowerCase();
+ var elmtName = aElement.tagName.toLowerCase();
+
+ var url = location.href.replace(/\?.*/, "");
+ var dir = url.replace(/test_bug607145.html[^\/]*$/, "");
+ var parentDir = dir.replace(/test\/$/, "");
+ ok(idl in aElement, idl + " should be available in " + elmtName);
+
+ // Default values.
+ is(aElement[idl].split("?")[0], url, "." + idl + " default value should be the document's URL");
+ is(aElement.getAttribute(attr), null,
+ "@" + attr + " default value should be null");
+
+ var values = [
+ /* value to set, resolved value */
+ [ "foo.html", dir + "foo.html" ],
+ [ "data:text/html,<html></html>", "data:text/html,<html></html>" ],
+ [ "http://example.org/", "http://example.org/" ],
+ [ "//example.org/", "http://example.org/" ],
+ [ "?foo=bar", url + "?foo=bar" ],
+ [ "#foo", url + "#foo" ],
+ [ "", url ],
+ [ " ", url ],
+ [ "../", parentDir ],
+ [ "...", dir + "..." ],
+ // invalid URL
+ [ "http://a b/", "http://a b/" ], // TODO: doesn't follow the specs, should be "".
+ ];
+
+ for (var value of values) {
+ aElement[idl] = value[0];
+ is(aElement[idl].replace(xoriginParams, ""), value[1], "." + idl + " value should be " + value[1]);
+ is(aElement.getAttribute(attr), value[0],
+ "@" + attr + " value should be " + value[0]);
+ }
+
+ for (var value of values) {
+ aElement.setAttribute(attr, value[0]);
+ is(aElement[idl].replace(xoriginParams, ""), value[1], "." + idl + " value should be " + value[1]);
+ is(aElement.getAttribute(attr), value[0],
+ "@" + attr + " value should be " + value[0]);
+ }
+}
+
+
+xoriginParams = window.location.search;
+
+reflectURL(document.createElement("form"), "action");
+reflectURL(document.createElement("input"), "formAction");
+reflectURL(document.createElement("button"), "formAction");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug610212.html b/dom/html/test/test_bug610212.html
new file mode 100644
index 0000000000..69838f7e4d
--- /dev/null
+++ b/dom/html/test/test_bug610212.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=610212
+-->
+<head>
+ <title>Test for Bug 610212</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=610212">Mozilla Bug 610212</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 610212 **/
+
+var canvas = document.createElement('canvas');
+
+reflectUnsignedInt({
+ element: canvas,
+ attribute: "width",
+ nonZero: false,
+ defaultValue: 300,
+});
+
+reflectUnsignedInt({
+ element: canvas,
+ attribute: "height",
+ nonZero: false,
+ defaultValue: 150,
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug610687.html b/dom/html/test/test_bug610687.html
new file mode 100644
index 0000000000..fd6950cce4
--- /dev/null
+++ b/dom/html/test/test_bug610687.html
@@ -0,0 +1,195 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=610687
+-->
+<head>
+ <title>Test for Bug 610687</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=610687">Mozilla Bug 610687</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ <input type='radio' name='a'>
+ <input type='radio' name='a'>
+ <input type='radio' name='b'>
+ </form>
+ <input type='radio' name='a'>
+ <input type='radio' name='a'>
+ <input type='radio' name='b'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 610687 **/
+
+function checkPseudoClasses(aElement, aValid, aValidUI, aInvalidUI)
+{
+ if (aValid) {
+ ok(aElement.matches(":valid"), ":valid should apply");
+ } else {
+ ok(aElement.matches(":invalid"), ":invalid should apply");
+ }
+
+ is(aElement.matches(":user-valid"), aValidUI,
+ aValid ? ":user-valid should apply" : ":user-valid should not apply");
+
+ is(aElement.matches(":user-invalid"), aInvalidUI,
+ aInvalidUI ? ":user-invalid should apply" : ":user-invalid should not apply");
+
+ if (aInvalidUI && (aValid || aValidUI)) {
+ ok(false, ":invalid can't apply with :valid or :user-valid");
+ }
+}
+
+/**
+ * r1 and r2 should be in the same group.
+ * r3 should be in another group.
+ * form can be null.
+ */
+function checkRadios(r1, r2, r3, form)
+{
+ // Default state.
+ checkPseudoClasses(r1, true, false, false);
+ checkPseudoClasses(r2, true, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ // Suffering from being missing (without ui-invalid).
+ r1.required = true;
+ checkPseudoClasses(r1, false, false, false);
+ checkPseudoClasses(r2, false, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ // Do not suffer from being missing (with ui-valid).
+ r1.click();
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, true, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ // Do not suffer from being missing (with ui-valid).
+ r1.checked = false;
+ r1.required = false;
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, true, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ // Suffering from being missing (with ui-invalid) with required set on one radio
+ // and the checked state changed on another.
+ r1.required = true;
+ r2.checked = false;
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, false, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ // Do not suffer from being missing (with ui-valid) by checking the radio which
+ // hasn't the required attribute.
+ r2.checked = true;
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, true, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ // .setCustomValidity() should not affect the entire group.
+ r1.checked = false; r2.checked = false; r3.checked = false;
+ r1.required = false;
+ r1.setCustomValidity('foo');
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, true, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ r1.setCustomValidity('');
+ r2.setCustomValidity('foo');
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+ checkPseudoClasses(r3, true, false, false);
+
+ r2.setCustomValidity('');
+ r3.setCustomValidity('foo');
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, true, false, false);
+ checkPseudoClasses(r3, false, false, false);
+
+ // Removing the radio with the required attribute should make the group valid.
+ r1.setCustomValidity('');
+ r2.setCustomValidity('');
+ r1.required = false;
+ r2.required = true;
+ r1.checked = r2.checked = false;
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, false, false, false);
+
+ var p = r2.parentNode;
+ p.removeChild(r2);
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+
+ p.appendChild(r2);
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, false, false, false);
+
+ // Adding a radio element to an invalid group should make it invalid.
+ p.removeChild(r1);
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+
+ p.appendChild(r1);
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, false, false, false);
+
+ // Adding a checked radio element to an invalid group should make it valid.
+ p.removeChild(r1);
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+
+ r1.checked = true;
+ p.appendChild(r1);
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, true, false, false);
+ r1.checked = false;
+
+ // Adding an invalid radio element by changing the name attribute.
+ r2.name = 'c';
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+
+ r2.name = 'a';
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, false, false, false);
+
+ // Adding an element to an invalid radio group by changing the name attribute.
+ r1.name = 'c';
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+
+ r1.name = 'a';
+ checkPseudoClasses(r1, false, false, true);
+ checkPseudoClasses(r2, false, false, false);
+
+ // Adding a checked element to an invalid radio group with the name attribute.
+ r1.name = 'c';
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, false, false, false);
+
+ r1.checked = true;
+ r1.name = 'a';
+ checkPseudoClasses(r1, true, true, false);
+ checkPseudoClasses(r2, true, false, false);
+ r1.checked = false;
+}
+
+var r1 = document.getElementsByTagName('input')[0];
+var r2 = document.getElementsByTagName('input')[1];
+var r3 = document.getElementsByTagName('input')[2];
+checkRadios(r1, r2, r3);
+
+r1 = document.getElementsByTagName('input')[3];
+r2 = document.getElementsByTagName('input')[4];
+r3 = document.getElementsByTagName('input')[5];
+checkRadios(r1, r2, r3);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug611189.html b/dom/html/test/test_bug611189.html
new file mode 100644
index 0000000000..d798fd4393
--- /dev/null
+++ b/dom/html/test/test_bug611189.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=611189
+-->
+<head>
+ <title>Test for Bug 611189</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=611189">Mozilla Bug 611189</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 611189 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(async function() {
+ var i = document.createElement("input");
+ var b = document.getElementById("content");
+ b.appendChild(i);
+ b.clientWidth; // bind to frame
+ i.focus(); // initialize editor
+ var before = await snapshotWindow(window, true);
+ i.value = "L"; // set the value
+ i.style.display = "none";
+ b.clientWidth; // unbind from frame
+ i.value = ""; // set the value without a frame
+ i.style.display = "";
+ b.clientWidth; // rebind to frame
+ is(i.value, "", "Input's value should be correctly updated");
+ var after = await snapshotWindow(window, true);
+ ok(compareSnapshots(before, after, true), "The correct value should be rendered inside the control");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug612730.html b/dom/html/test/test_bug612730.html
new file mode 100644
index 0000000000..ccdfc1241d
--- /dev/null
+++ b/dom/html/test/test_bug612730.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=612730
+-->
+<head>
+ <title>Test for Bug 612730</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=612730">Mozilla Bug 612730</a>
+<p id="display"></p>
+<div id="content">
+ <select multiple required>
+ <option value="">foo</option>
+ <option value="">bar</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 612730 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest()
+{
+ var select = document.getElementsByTagName('select')[0];
+
+ select.addEventListener("focus", function() {
+ isnot(select.selectedIndex, -1, "Something should have been selected");
+
+ ok(!select.matches(":-moz-ui-invalid"),
+ ":-moz-ui-invalid should not apply");
+ ok(!select.matches(":-moz-ui-valid"),
+ ":-moz-ui-valid should not apply");
+
+ SimpleTest.finish();
+ }, {once: true});
+
+ synthesizeMouse(select, 5, 5, {});
+}
+
+SimpleTest.waitForFocus(runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug613019.html b/dom/html/test/test_bug613019.html
new file mode 100644
index 0000000000..3f96ce2542
--- /dev/null
+++ b/dom/html/test/test_bug613019.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=613019
+-->
+<head>
+ <title>Test for Bug 613019</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=613019">Mozilla Bug 613019</a>
+<div id="content">
+ <input type="text" maxlength="2" style="width:200px" value="Test">
+ <textarea maxlength="2" style="width:200px">Test</textarea>
+ <input type="text" minlength="6" style="width:200px" value="Test">
+ <textarea minlength="6" style="width:200px">Test</textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 613019 **/
+
+function testInteractivityOfMaxLength(elem) {
+ // verify that user interactivity is necessary for validity state to apply.
+ is(elem.value, "Test", "Element has incorrect starting value.");
+ is(elem.validity.tooLong, false, "Element should not be tooLong.");
+
+ elem.setSelectionRange(elem.value.length, elem.value.length)
+ elem.focus();
+
+ synthesizeKey("KEY_Backspace");
+ is(elem.value, "Tes", "Element value was not changed correctly.");
+ is(elem.validity.tooLong, true, "Element should still be tooLong.");
+
+ synthesizeKey("KEY_Backspace");
+ is(elem.value, "Te", "Element value was not changed correctly.");
+ is(elem.validity.tooLong, false, "Element should no longer be tooLong.");
+
+ elem.value = "Test";
+ is(elem.validity.tooLong, false,
+ "Element should not be tooLong after non-interactive value change.");
+}
+
+function testInteractivityOfMinLength(elem) {
+ // verify that user interactivity is necessary for validity state to apply.
+ is(elem.value, "Test", "Element has incorrect starting value.");
+ is(elem.validity.tooLong, false, "Element should not be tooShort.");
+
+ elem.setSelectionRange(elem.value.length, elem.value.length)
+ elem.focus();
+
+ sendString("e");
+ is(elem.value, "Teste", "Element value was not changed correctly.");
+ is(elem.validity.tooShort, true, "Element should still be tooShort.");
+
+ sendString("d");
+ is(elem.value, "Tested", "Element value was not changed correctly.");
+ is(elem.validity.tooShort, false, "Element should no longer be tooShort.");
+
+ elem.value = "Test";
+ is(elem.validity.tooShort, false,
+ "Element should not be tooShort after non-interactive value change.");
+}
+
+function test() {
+ window.getSelection().removeAllRanges();
+ testInteractivityOfMaxLength(document.querySelector("input[type=text][maxlength]"));
+ testInteractivityOfMaxLength(document.querySelector("textarea[maxlength]"));
+ testInteractivityOfMinLength(document.querySelector("input[type=text][minlength]"));
+ testInteractivityOfMinLength(document.querySelector("textarea[minlength]"));
+ SimpleTest.finish();
+}
+
+window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ setTimeout(test, 0);
+};
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug613113.html b/dom/html/test/test_bug613113.html
new file mode 100644
index 0000000000..3308246af0
--- /dev/null
+++ b/dom/html/test/test_bug613113.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=613113
+-->
+<head>
+ <title>Test for Bug 613113</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=613113">Mozilla Bug 613113</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe name='f'></iframe>
+ <form target='f' action="data:text/html,">
+ <output></output>
+ <button></button>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 613113 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var invalidEvent = false;
+
+var form = document.forms[0];
+var button = document.getElementsByTagName('button')[0];
+var output = document.getElementsByTagName('output')[0];
+
+output.addEventListener("invalid", function() {
+ ok(false, "invalid event should have been send");
+});
+
+form.addEventListener("submit", function() {
+ ok(true, "submit has been caught");
+ setTimeout(function() {
+ SimpleTest.finish();
+ }, 0);
+});
+
+output.setCustomValidity("foo");
+
+button.click();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug613722.html b/dom/html/test/test_bug613722.html
new file mode 100644
index 0000000000..4fbead4507
--- /dev/null
+++ b/dom/html/test/test_bug613722.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=613722
+-->
+<head>
+ <title>Test for Bug 613722</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=613722">Mozilla Bug 613722</a>
+<p id="display"></p>
+<div id="content">
+ <embed src="test_plugin.tst" hidden>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 613722 **/
+
+var rect = document.getElementsByTagName('embed')[0].getBoundingClientRect();
+
+var hasFrame = rect.left != 0 || rect.right != 0 || rect.top != 0 ||
+ rect.bottom != 0;
+
+ok(hasFrame, "embed should have a frame with hidden set");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug613979.html b/dom/html/test/test_bug613979.html
new file mode 100644
index 0000000000..40921d8064
--- /dev/null
+++ b/dom/html/test/test_bug613979.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=613979
+-->
+<head>
+ <title>Test for Bug 613979</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=613979">Mozilla Bug 613979</a>
+<p id="display"></p>
+<div id="content">
+ <input required>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 613979 **/
+
+var testNum = 0;
+var input = document.getElementsByTagName('input')[0];
+
+input.addEventListener("input", function() {
+ if (testNum == 0) {
+ ok(input.validity.valid, "input should be valid");
+ testNum++;
+ SimpleTest.executeSoon(function() {
+ synthesizeKey("KEY_Backspace");
+ });
+ } else if (testNum == 1) {
+ ok(!input.validity.valid, "input should not be valid");
+ input.removeEventListener("input", arguments.callee);
+ SimpleTest.finish();
+ }
+});
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ input.focus();
+ sendString("a");
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug615595.html b/dom/html/test/test_bug615595.html
new file mode 100644
index 0000000000..4e3d002498
--- /dev/null
+++ b/dom/html/test/test_bug615595.html
Binary files differ
diff --git a/dom/html/test/test_bug615833.html b/dom/html/test/test_bug615833.html
new file mode 100644
index 0000000000..530603daee
--- /dev/null
+++ b/dom/html/test/test_bug615833.html
@@ -0,0 +1,141 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=615697
+-->
+<head>
+ <title>Test for Bug 615697</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=615697">Mozilla Bug 615697</a>
+<p id="display"></p>
+<div id="content">
+ <input>
+ <textarea></textarea>
+ <input type='radio'>
+ <input type='checkbox'>
+ <select>
+ <option>foo</option>
+ <option>bar</option>
+ </select>
+ <select multiple size='1'>
+ <option>tulip</option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 615697 **/
+
+/**
+ * This test is making all elements trigger 'change' event.
+ * You should read the test from bottom to top:
+ * events are registered from the last one to the first one.
+ *
+ * Sometimes, elements are focused before a click. This might sound useless
+ * but it guarantees to have the element visible before simulating the click.
+ */
+
+var input = document.getElementsByTagName('input')[0];
+var textarea = document.getElementsByTagName('textarea')[0];
+var radio = document.getElementsByTagName('input')[1];
+var checkbox= document.getElementsByTagName('input')[2];
+var select = document.getElementsByTagName('select')[0];
+var selectMultiple = document.getElementsByTagName('select')[1];
+
+function checkChangeEvent(aEvent)
+{
+ ok(aEvent.bubbles, "change event should bubble");
+ ok(!aEvent.cancelable, "change event shouldn't be cancelable");
+}
+
+selectMultiple.addEventListener("change", function(aEvent) {
+ checkChangeEvent(aEvent);
+ SimpleTest.finish();
+}, {once: true});
+
+selectMultiple.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(selectMultiple, {});
+ });
+}, {once: true});
+
+select.addEventListener("change", function(aEvent) {
+ checkChangeEvent(aEvent);
+ selectMultiple.focus();
+}, {once: true});
+
+select.addEventListener("keyup", function() {
+ select.blur();
+}, {once: true});
+
+select.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function () {
+ synthesizeKey("KEY_ArrowDown");
+ });
+}, {once: true});
+
+checkbox.addEventListener("change", function(aEvent) {
+ checkChangeEvent(aEvent);
+ select.focus();
+}, {once: true});
+
+checkbox.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(checkbox, {});
+ });
+}, {once: true});
+
+radio.addEventListener("change", function(aEvent) {
+ checkChangeEvent(aEvent);
+ checkbox.focus();
+}, {once: true});
+
+radio.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function () {
+ synthesizeMouseAtCenter(radio, {});
+ });
+}, {once: true});
+
+textarea.addEventListener("change", function(aEvent) {
+ checkChangeEvent(aEvent);
+ radio.focus();
+}, {once: true});
+
+textarea.addEventListener("input", function() {
+ textarea.blur();
+}, {once: true});
+
+textarea.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function () {
+ sendString("f");
+ });
+}, {once: true});
+
+input.addEventListener("change", function(aEvent) {
+ checkChangeEvent(aEvent);
+ textarea.focus();
+}, {once: true});
+
+input.addEventListener("input", function() {
+ input.blur();
+}, {once: true});
+
+input.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function () {
+ sendString("f");
+ });
+}, {once: true});
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ input.focus();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug618948.html b/dom/html/test/test_bug618948.html
new file mode 100644
index 0000000000..04a9347261
--- /dev/null
+++ b/dom/html/test/test_bug618948.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=618948
+-->
+<head>
+ <title>Test for Bug 618948</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=618948">Mozilla Bug 618948</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ <input type='email' id='i'>
+ <button>submit</button>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 618948 **/
+
+var events = ["focus", "input", "change", "invalid" ];
+
+var handled = ({});
+
+function eventHandler(event)
+{
+ dump("\n" + event.type + "\n");
+ handled[event.type] = true;
+}
+
+function beginTest()
+{
+ for (var e of events) {
+ handled[e] = false;
+ }
+
+ i.focus();
+}
+
+function endTest()
+{
+ for (var e of events) {
+ ok(handled[e], "on" + e + " should have been called");
+ }
+
+ SimpleTest.finish();
+}
+
+var i = document.getElementsByTagName('input')[0];
+var b = document.getElementsByTagName('button')[0];
+
+i.onfocus = function(event) {
+ eventHandler(event);
+ sendString("f");
+ i.onfocus = null;
+};
+
+i.oninput = function(event) {
+ eventHandler(event);
+ b.focus();
+ i.oninput = null;
+};
+
+i.onchange = function(event) {
+ eventHandler(event);
+ i.onchange = null;
+ synthesizeMouseAtCenter(b, {});
+};
+
+i.oninvalid = function(event) {
+ eventHandler(event);
+ i.oninvalid = null;
+ endTest();
+};
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(beginTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug619278.html b/dom/html/test/test_bug619278.html
new file mode 100644
index 0000000000..56f704c037
--- /dev/null
+++ b/dom/html/test/test_bug619278.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=619278
+-->
+<head>
+ <title>Test for Bug 619278</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619278">Mozilla Bug 619278</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ <input required><button>submit</button>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 619278 **/
+
+function doElementMatchesSelector(aElement, aSelector)
+{
+ ok(aElement.matches(aSelector),
+ aSelector + " should match for " + aElement);
+}
+
+var e = document.forms[0].elements[0];
+
+e.addEventListener("invalid", function(event) {
+ e.addEventListener("invalid", arguments.callee);
+
+ SimpleTest.executeSoon(function() {
+ doElementMatchesSelector(e, ":-moz-ui-invalid");
+ SimpleTest.finish();
+ });
+});
+
+e.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function() {
+ synthesizeKey("KEY_Enter");
+ });
+}, {once: true});
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ e.focus();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug622597.html b/dom/html/test/test_bug622597.html
new file mode 100644
index 0000000000..ead4887a1d
--- /dev/null
+++ b/dom/html/test/test_bug622597.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=622597
+-->
+<head>
+ <title>Test for Bug 622597</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=622597">Mozilla Bug 622597</a>
+<p id="display"></p>
+<div id="content">
+ <form>
+ <input required>
+ <textarea required></textarea>
+ <select required><option value="">foo</option><option selected>bar</option></select>
+ <button>submit</button>
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 622597 **/
+
+var form = document.forms[0];
+var input = form.elements[0];
+var textarea = form.elements[1];
+var select = form.elements[2];
+var button = form.elements[3];
+
+function checkPseudoClasses(aElement, aValid, aInvalid)
+{
+ is(aElement.matches(":-moz-ui-valid"), aValid,
+ aValid ? aElement + " should match :-moz-ui-valid"
+ : aElement + " should not match :-moz-ui-valid");
+ is(aElement.matches(":-moz-ui-invalid"), aInvalid,
+ aInvalid ? aElement + " should match :-moz-ui-invalid"
+ : aElement + " should not match :-moz-ui-invalid");
+ if (aValid && aInvalid) {
+ ok(false,
+ aElement + " should not match :-moz-ui-valid AND :-moz-ui-invalid");
+ }
+}
+
+select.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function() {
+ form.noValidate = false;
+ SimpleTest.executeSoon(function() {
+ checkPseudoClasses(select, false, true);
+ SimpleTest.finish();
+ });
+ });
+}, {once: true});
+
+textarea.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function() {
+ form.noValidate = false;
+ SimpleTest.executeSoon(function() {
+ checkPseudoClasses(textarea, false, true);
+ form.noValidate = true;
+ select.selectedIndex = 0;
+ select.focus();
+ });
+ });
+}, {once: true});
+
+input.addEventListener("invalid", function() {
+ input.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function() {
+ form.noValidate = false;
+ SimpleTest.executeSoon(function() {
+ checkPseudoClasses(input, false, true);
+ form.noValidate = true;
+ textarea.value = '';
+ textarea.focus();
+ });
+ });
+ }, {once: true});
+
+ SimpleTest.executeSoon(function() {
+ form.noValidate = true;
+ input.blur();
+ input.value = '';
+ input.focus();
+ });
+}, {once: true});
+
+button.addEventListener("focus", function() {
+ SimpleTest.executeSoon(function() {
+ synthesizeKey("KEY_Enter");
+ });
+}, {once: true});
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ button.focus();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug623291.html b/dom/html/test/test_bug623291.html
new file mode 100644
index 0000000000..c7b7ab7ea4
--- /dev/null
+++ b/dom/html/test/test_bug623291.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=623291
+-->
+<head>
+ <title>Test for Bug 623291</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=623291">Mozilla Bug 623291</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<input id="textField" onfocus="next()" onblur="done();">
+<button id="b">a button</button>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 623291 **/
+
+function runTest() {
+ document.getElementById("textField").focus();
+}
+
+function next() {
+ synthesizeMouseAtCenter(document.getElementById('b'), {}, window);
+}
+
+function done() {
+ isnot(document.activeElement, document.getElementById("textField"),
+ "TextField should not be active anymore!");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug6296.html b/dom/html/test/test_bug6296.html
new file mode 100644
index 0000000000..6e74ce8ec2
--- /dev/null
+++ b/dom/html/test/test_bug6296.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=6296
+-->
+<head>
+ <title>Test for Bug 6296</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=6296">Mozilla Bug 6296</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <A HREF="../testdata/test.gif" id="foo" NAME="anchor1" ALT="this is a test of the image
+ attribute">Hi</A>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 6296 **/
+is($("foo").name, "anchor1", "accessing an anchor name should work, and not crash either!")
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug629801.html b/dom/html/test/test_bug629801.html
new file mode 100644
index 0000000000..073979a5fe
--- /dev/null
+++ b/dom/html/test/test_bug629801.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629801
+-->
+<head>
+ <title>Test for Bug 629801</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629801">Mozilla Bug 629801</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<div itemscope>
+ This tests itemValue on time elements, first with no datetime attribute, then with no text content,
+ then with both.
+ <time id="t1" itemprop="a">May 10th 2009</time>
+ <time id="t2" itemprop="b" datetime="2009-05-10"></time>
+ <time id="t3" itemprop="c" datetime="2009-05-10">May 10th 2009</time>
+</div>
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 629801 **/
+
+var t1 = document.getElementById("t1"),
+ t2 = document.getElementById("t2"),
+ t3 = document.getElementById("t3"),
+ t4 = document.createElement("time");
+
+// .dateTime IDL
+is(t1.dateTime, "", "dateTime is properly set to empty string if datetime attributeis absent");
+is(t2.dateTime, "2009-05-10", "dateTime is properly set to datetime attribute with datetime and no text content");
+is(t3.dateTime, "2009-05-10", "dateTime is properly set to datetime attribute with datetime and text content");
+
+// dateTime reflects datetime attribute
+reflectString({
+ element: t4,
+ attribute: "dateTime"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug633058.html b/dom/html/test/test_bug633058.html
new file mode 100644
index 0000000000..a92f1f9369
--- /dev/null
+++ b/dom/html/test/test_bug633058.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=633058
+-->
+<head>
+ <title>Test for Bug 633058</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=633058">Mozilla Bug 633058</a>
+<p id="display"></p>
+<div id="content">
+ <input>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 633058 **/
+
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(startTest);
+
+function startTest() {
+ var nbExpectedKeyDown = 8;
+ var nbExpectedKeyPress = 1;
+ var inputGotKeyPress = 0;
+ var inputGotKeyDown = 0;
+ var divGotKeyPress = 0;
+ var divGotKeyDown = 0;
+
+ var input = document.getElementsByTagName('input')[0];
+ var content = document.getElementById('content');
+
+ content.addEventListener("keydown", () => { divGotKeyDown++; });
+ content.addEventListener("keypress", () => { divGotKeyPress++; });
+ input.addEventListener("keydown", () => { inputGotKeyDown++; });
+ input.addEventListener("keypress", () => { inputGotKeyPress++; });
+
+ input.addEventListener('focus', function() {
+ SimpleTest.executeSoon(() => {
+ synthesizeKey('KEY_ArrowUp');
+ synthesizeKey('KEY_ArrowLeft');
+ synthesizeKey('KEY_ArrowRight');
+ synthesizeKey('KEY_ArrowDown');
+ synthesizeKey('KEY_Backspace');
+ synthesizeKey('KEY_Delete');
+ synthesizeKey('KEY_Escape');
+ synthesizeKey('KEY_Enter'); // Will dispatch keypress event even in strict behavior.
+
+ is(inputGotKeyDown, nbExpectedKeyDown, "input got all keydown events");
+ is(inputGotKeyPress, nbExpectedKeyPress, "input got all keypress events");
+ is(divGotKeyDown, nbExpectedKeyDown, "div got all keydown events");
+ is(divGotKeyPress, nbExpectedKeyPress, "div got all keypress events");
+ SimpleTest.finish();
+ });
+ }, {once: true});
+ input.focus();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug636336.html b/dom/html/test/test_bug636336.html
new file mode 100644
index 0000000000..314e941f84
--- /dev/null
+++ b/dom/html/test/test_bug636336.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=636336
+-->
+<head>
+ <title>Test for Bug 636336</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=636336">Mozilla Bug 636336</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 636336 **/
+function testIt(tag) {
+ var elem = document.createElement(tag);
+ elem.setAttribute("src", " ");
+ is(elem.getAttribute("src"), " ",
+ tag + " src attribute setter should not strip whitespace");
+ elem.setAttribute("src", " test ");
+ is(elem.getAttribute("src"), " test ",
+ tag + " src attribute setter should not strip whitespace around non-whitespace");
+ is(elem.src, window.location.href.replace(/\?.*/, "")
+ .replace(/test_bug636336\.html$/, "test"),
+ tag + ".src should strip whitespace as needed");
+}
+
+testIt("img");
+testIt("source");
+testIt("audio");
+testIt("video");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug641219.html b/dom/html/test/test_bug641219.html
new file mode 100644
index 0000000000..44dc15cf41
--- /dev/null
+++ b/dom/html/test/test_bug641219.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=641219
+-->
+<head>
+ <title>Test for Bug 641219</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=641219">Mozilla Bug 641219</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<div id="div">
+<font></font>
+<svg><font/></svg>
+</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 641219 **/
+var HTML = "http://www.w3.org/1999/xhtml",
+ SVG = "http://www.w3.org/2000/svg";
+var wrapper = document.getElementById("div");
+is(wrapper.getElementsByTagName("FONT").length, 1);
+is(wrapper.getElementsByTagName("FONT")[0].namespaceURI, HTML);
+is(wrapper.getElementsByTagName("font").length, 2);
+is(wrapper.getElementsByTagName("font")[0].namespaceURI, HTML);
+is(wrapper.getElementsByTagName("font")[1].namespaceURI, SVG);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug643051.html b/dom/html/test/test_bug643051.html
new file mode 100644
index 0000000000..5719d1d2db
--- /dev/null
+++ b/dom/html/test/test_bug643051.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=643051
+-->
+<head>
+ <title>Test for Bug 643051</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=643051">Mozilla Bug 643051</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({
+ "set": [
+ // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default"
+ ["network.cookie.sameSite.laxByDefault", false],
+ ]
+}, () => {
+ /** Test for Bug 643051 **/
+ document.cookie = "a=; expires=Thu, 01-Jan-1970 00:00:01 GMT"; // clear cookie
+ document.cookie = "a2=; expires=Thu, 01-Jan-1970 00:00:01 GMT"; // clear cookie
+ document.cookie = "a3=; expires=Thu, 01-Jan-1970 00:00:01 GMT"; // clear cookie
+
+ // single cookie, should work
+ document.cookie = "a=bar";
+ is(document.cookie, "a=bar", "Can't read stored cookie!");
+
+ document.cookie = "a2=bar\na3=bar";
+ is(document.cookie, "a=bar; a2=bar", "Wrong cookie value");
+
+ document.cookie = "a2=baz; a3=bar";
+ is(document.cookie, "a=bar; a2=baz", "Wrong cookie value");
+
+ // clear cookies again to avoid affecting other tests
+ document.cookie = "a=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
+ document.cookie = "a2=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
+ document.cookie = "a3=; expires=Thu, 01-Jan-1970 00:00:01 GMT";
+
+ SpecialPowers.clearUserPref("network.cookie.sameSite.laxByDefault");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug646157.html b/dom/html/test/test_bug646157.html
new file mode 100644
index 0000000000..ea0cbedaf0
--- /dev/null
+++ b/dom/html/test/test_bug646157.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=646157
+-->
+<head>
+ <title>Test for Bug 646157</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=646157">Mozilla Bug 646157</a>
+<p id="display"></p>
+<div id="content">
+ <label id="l1"/><input id="c1" type='checkbox'>
+ <label id="l2"/><input id="c2" type='checkbox'>
+ <label id="l3"/><input id="c3" type='checkbox'>
+ <label id="l4"/><input id="c4" type='checkbox'>
+ <label id="l5"/><input id="c5" type='checkbox'>
+ <label id="l6"/><input id="c6" type='checkbox'>
+ <label id="l7"/><input id="c7" type='checkbox'>
+ <label id="l8"/><input id="c8" type='checkbox'>
+ <label id="l9"/><input id="c9" type='checkbox'>
+ <label id="l10"/><input id="c10" type='checkbox'>
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 646157 **/
+
+var expectedClicks = {
+ // [ Direct clicks, bubbled clicks, synthetic clicks]
+ l1: [0, 2, 1],
+ l2: [0, 2, 1],
+ l3: [0, 2, 1],
+ l4: [0, 2, 1],
+ l5: [0, 2, 1],
+ l6: [0, 2, 1],
+ l7: [0, 2, 1],
+ l8: [0, 2, 1],
+ l9: [0, 2, 1],
+ l10:[1, 2, 1],
+ c1: [0, 0, 0],
+ c2: [0, 0, 0],
+ c3: [0, 0, 0],
+ c4: [0, 0, 0],
+ c5: [0, 0, 0],
+ c6: [0, 0, 0],
+ c7: [0, 0, 0],
+ c8: [0, 0, 0],
+ c9: [0, 0, 0],
+ c10:[1, 1, 1]
+};
+
+function clickhandler(e) {
+ if (!e.currentTarget.clickCount)
+ e.currentTarget.clickCount = 1;
+ else
+ e.currentTarget.clickCount++;
+
+ if (e.currentTarget === e.target)
+ e.currentTarget.directClickCount = 1;
+
+ if (e.target != document.getElementById("l10")) {
+ if (!e.currentTarget.synthClickCount)
+ e.currentTarget.synthClickCount = 1;
+ else
+ e.currentTarget.synthClickCount++;
+ }
+}
+
+for (var i = 1; i <= 10; i++) {
+ document.getElementById("l" + i).addEventListener('click', clickhandler);
+ document.getElementById("c" + i).addEventListener('click', clickhandler);
+}
+
+document.getElementById("l10").click();
+
+function check(thing) {
+ var expected = expectedClicks[thing.id];
+ is(thing.directClickCount || 0, expected[0], "Wrong number of direct clicks");
+ is(thing.clickCount || 0, expected[1], "Wrong number of clicks");
+ is(thing.synthClickCount || 0, expected[2], "Wrong number of synthetic clicks");
+}
+
+// Compare them all
+for (var i = 1; i <= 10; i++) {
+ check(document.getElementById("l" + i));
+ check(document.getElementById("c" + i));
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug649134.html b/dom/html/test/test_bug649134.html
new file mode 100644
index 0000000000..52d644fb14
--- /dev/null
+++ b/dom/html/test/test_bug649134.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=649134
+-->
+<head>
+ <title>Test for Bug 649134</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=649134">Mozilla Bug 649134</a>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 649134 **/
+SimpleTest.waitForExplicitFinish();
+
+var calls = 0;
+function finish() {
+ if (++calls == 4)
+ SimpleTest.finish();
+}
+function verifyNoLoad(iframe) {
+ ok(iframe.contentDocument.body.offsetHeight > 0,
+ "HTTP Link stylesheet was ignored " + iframe.src);
+ finish();
+}
+var verifyLoadCalls = 0;
+function verifyLoad(iframe) {
+ if (++verifyLoadCalls == 2) {
+ ok(indexContent == iframe.contentDocument.body.innerHTML,
+ "bug649134/ loads bug649134/index.html " + iframe.src);
+ }
+ finish();
+}
+function indexLoad(iframe) {
+ indexContent = iframe.contentDocument.body.innerHTML;
+ verifyLoad(iframe);
+}
+
+</script>
+</pre>
+<p id="display">
+<!-- Note: the extra sub-directory is needed for the test, see bug 649134 comment 14 -->
+<iframe onload="verifyNoLoad(this);" src="bug649134/file_bug649134-1.sjs"></iframe>
+<iframe onload="verifyNoLoad(this);" src="bug649134/file_bug649134-2.sjs"></iframe>
+<iframe onload="verifyLoad(this);" src="bug649134/"></iframe> <!-- verify that mochitest server loads index.html -->
+<iframe onload="indexLoad(this);" src="bug649134/index.html"></iframe>
+</p>
+</body>
+</html>
diff --git a/dom/html/test/test_bug651956.html b/dom/html/test/test_bug651956.html
new file mode 100644
index 0000000000..bd059bab94
--- /dev/null
+++ b/dom/html/test/test_bug651956.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=651956
+-->
+<head>
+ <title>Test for Bug 651956</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=651956">Mozilla Bug 651956</a>
+<p id="display"></p>
+<div id="content">
+ <input>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 651956 **/
+
+var input = document.getElementsByTagName('input')[0];
+
+var gotInputEvent = false;
+
+input.addEventListener("input", function() {
+ gotInputEvent = true;
+}, {once: true});
+
+input.addEventListener("focus", function() {
+ synthesizeKey("KEY_Escape");
+
+ setTimeout(function() {
+ ok(!gotInputEvent, "No input event should have been sent.");
+ SimpleTest.finish();
+ });
+}, {once: true});
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ input.focus();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug658746.html b/dom/html/test/test_bug658746.html
new file mode 100644
index 0000000000..260b345300
--- /dev/null
+++ b/dom/html/test/test_bug658746.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=658746
+-->
+<head>
+ <title>Test for Bug 658746</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=658746">Mozilla Bug 658746</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 658746 **/
+
+/**
+ * Sets property, gets property and deletes property.
+ */
+function SetGetDelete(prop)
+{
+ var el = document.createElement('div');
+
+ el.dataset[prop] = 'aaaaaa';
+ is(el.dataset[prop], 'aaaaaa', 'Dataset property "' + prop + '" should have been set.');
+
+ delete el.dataset[prop];
+ is(el.dataset[prop], undefined, 'Dataset property"' + prop + '" should have been deleted.');
+}
+
+/**
+ * Gets, deletes and sets property. Expects exception while trying to set property.
+ */
+function SetExpectException(prop)
+{
+ var el = document.createElement('div');
+
+ is(el.dataset[prop], undefined, 'Dataset property "' + prop + '" should be undefined.');
+ delete el.dataset[prop];
+
+ try {
+ el.dataset[prop] = "xxxxxx";
+ ok(false, 'Exception should have been thrown when setting "' + prop + '".');
+ } catch (ex) {
+ ok(true, 'Exception should have been thrown.');
+ }
+}
+
+// Numbers as properties.
+SetGetDelete(-12345678901234567000);
+SetGetDelete(-1);
+SetGetDelete(0);
+SetGetDelete(1);
+SetGetDelete(12345678901234567000);
+
+// Floating point numbers as properties.
+SetGetDelete(-1.1);
+SetGetDelete(0.0);
+SetGetDelete(1.1);
+
+// Hexadecimal numbers as properties.
+SetGetDelete(0x3);
+SetGetDelete(0xa);
+
+// Octal numbers as properties.
+SetGetDelete(0o3);
+SetGetDelete(0o7);
+
+// String numbers as properties.
+SetGetDelete('0');
+SetGetDelete('01');
+SetGetDelete('0x1');
+
+// Undefined as property.
+SetGetDelete(undefined);
+
+// Empty arrays as properties.
+SetGetDelete(new Array());
+SetGetDelete([]);
+
+// Non-empty array and object as properties.
+SetExpectException(['a', 'b']);
+SetExpectException({'a':'b'});
+
+// Objects as properties.
+SetExpectException(new Object());
+SetExpectException(document);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug659596.html b/dom/html/test/test_bug659596.html
new file mode 100644
index 0000000000..79a55a7608
--- /dev/null
+++ b/dom/html/test/test_bug659596.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=659596
+-->
+<head>
+ <title>Test for Bug 659596</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=659596">Mozilla Bug 659596</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 659596 **/
+
+function checkReflection(option, attribute) {
+ /**
+ * Getting.
+ */
+
+ // When attribute isn't present.
+ var tests = [ "", "foo" ];
+ for (var test of tests) {
+ option.removeAttribute(attribute);
+ option.textContent = test;
+ is(option.getAttribute(attribute), null,
+ "option " + attribute + "'s value should be null");
+ is(option[attribute], option.textContent,
+ "option." + attribute + " should reflect the text content when the attribute isn't set");
+ }
+
+ // When attribute is present.
+ tests = [
+ [ "", "" ],
+ [ "", "foo" ],
+ [ "foo", "bar" ],
+ [ "foo", "" ],
+ ];
+ for (var test of tests) {
+ option.setAttribute(attribute, test[0]);
+ option.textContent = test[1];
+ is(option[attribute], option.getAttribute(attribute),
+ "option." + attribute + " should reflect the content attribute when it is set");
+ }
+
+ /**
+ * Setting.
+ */
+
+ // When attribute isn't present.
+ tests = [
+ [ "", "new" ],
+ [ "foo", "new" ],
+ ];
+ for (var test of tests) {
+ option.removeAttribute(attribute);
+ option.textContent = test[0];
+ option[attribute] = test[1]
+
+ is(option.getAttribute(attribute), test[1],
+ "when setting, the content attribute should change");
+ is(option.textContent, test[0],
+ "when setting, the text content should not change");
+ }
+
+ // When attribute is present.
+ tests = [
+ [ "", "", "new" ],
+ [ "", "foo", "new" ],
+ [ "foo", "bar", "new" ],
+ [ "foo", "", "new" ],
+ ];
+ for (var test of tests) {
+ option.setAttribute(attribute, test[0]);
+ option.textContent = test[1];
+ option[attribute] = test[2];
+
+ is(option.getAttribute(attribute), test[2],
+ "when setting, the content attribute should change");
+ is(option.textContent, test[1],
+ "when setting, the text content should not change");
+ }
+}
+
+var option = document.createElement("option");
+
+checkReflection(option, "value");
+checkReflection(option, "label");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug659743.xml b/dom/html/test/test_bug659743.xml
new file mode 100644
index 0000000000..12236bdc02
--- /dev/null
+++ b/dom/html/test/test_bug659743.xml
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=659743
+-->
+<head>
+ <title>Test for Bug 659743</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=659743">Mozilla Bug 659743</a>
+<p id="display">
+<map name="a">
+<area shape="rect" coords="25,25,75,75" href="#x"/>
+</map>
+<map id="b">
+<area shape="rect" coords="25,25,75,75" href="#y"/>
+</map>
+<map name="a">
+<area shape="rect" coords="25,25,75,75" href="#FAIL"/>
+</map>
+<map id="b">
+<area shape="rect" coords="25,25,75,75" href="#FAIL"/>
+</map>
+
+<img usemap="#a" src="image.png"/>
+<img usemap="#b" src="image.png"/>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 659743 **/
+SimpleTest.waitForExplicitFinish();
+var images = document.getElementsByTagName("img");
+var second = false;
+onhashchange = function() {
+ if (!second) {
+ second = true;
+ is(location.hash, "#x", "First map");
+ SimpleTest.waitForFocus(() => synthesizeMouse(images[1], 50, 50, {}));
+ } else {
+ is(location.hash, "#y", "Second map");
+ SimpleTest.finish();
+ }
+};
+SimpleTest.waitForFocus(() => synthesizeMouse(images[0], 50, 50, {}));
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug660663.html b/dom/html/test/test_bug660663.html
new file mode 100644
index 0000000000..dcc208dfe5
--- /dev/null
+++ b/dom/html/test/test_bug660663.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660663
+-->
+<head>
+ <title>Test for Bug 660663</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660663">Mozilla Bug 660663</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 660663 **/
+reflectLimitedEnumerated({
+ element: document.createElement("div"),
+ attribute: "dir",
+ validValues: ["ltr", "rtl", "auto"],
+ invalidValues: ["cheesecake", ""]
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug660959-1.html b/dom/html/test/test_bug660959-1.html
new file mode 100644
index 0000000000..930b31b4d3
--- /dev/null
+++ b/dom/html/test/test_bug660959-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660959
+-->
+<head>
+ <title>Test for Bug 660959</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660959">Mozilla Bug 660959</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <a href="#" id="testa"></a>
+</div>
+<pre id="test">
+<script>
+ is($("content").querySelector(":link, :visited"), $("testa"),
+ "Should find a link even in a display:none subtree");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug660959-2.html b/dom/html/test/test_bug660959-2.html
new file mode 100644
index 0000000000..a7dfb2e3d5
--- /dev/null
+++ b/dom/html/test/test_bug660959-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660959
+-->
+<head>
+ <title>Test for Bug 660959</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ :link, :visited {
+ color: red;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660959">Mozilla Bug 660959</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <a href="#" id="a"></a>
+</div>
+<pre id="test">
+<script type="application/javascript">
+ var a = document.getElementById("a");
+ is(window.getComputedStyle(a).color, "rgb(255, 0, 0)", "Link is not right color?");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug660959-3.html b/dom/html/test/test_bug660959-3.html
new file mode 100644
index 0000000000..cc39c1eb98
--- /dev/null
+++ b/dom/html/test/test_bug660959-3.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=660959
+-->
+<head>
+ <title>Test for Bug 660959</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=660959">Mozilla Bug 660959</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <a href="http://www.example.com"></a>
+ <div id="foo">
+ <span id="test"></span>
+ </div>
+</div>
+<pre id="test">
+<script>
+ is($("foo").querySelector(":link + * span, :visited + * span"), $("test"),
+ "Should be able to find link siblings even in a display:none subtree");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug666200.html b/dom/html/test/test_bug666200.html
new file mode 100644
index 0000000000..d68966f787
--- /dev/null
+++ b/dom/html/test/test_bug666200.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666200
+-->
+<head>
+ <title>Test for Bug 666200</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666200">Mozilla Bug 666200</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 666200 **/
+var sel = document.createElement("select");
+var opt1 = new Option();
+var opt2 = new Option();
+var opt3 = new Option();
+var opt4 = new Option();
+var opt5 = new Option();
+opt1.value = 1;
+opt2.value = 2;
+opt3.value = 3;
+opt4.value = 4;
+opt5.value = 5;
+sel.add(opt1);
+sel.add(opt2, 0);
+sel.add(opt3, 1000);
+sel.options.add(opt4, opt3);
+sel.add(opt5, undefined);
+is(sel[0], opt2, "1st item should be 2");
+is(sel[1], opt1, "2nd item should be 1");
+is(sel[2], opt4, "3rd item should be 4");
+is(sel[3], opt3, "4th item should be 3");
+is(sel[4], opt5, "5th item should be 5");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug666666.html b/dom/html/test/test_bug666666.html
new file mode 100644
index 0000000000..a3c22d4e0f
--- /dev/null
+++ b/dom/html/test/test_bug666666.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=666666
+-->
+<head>
+ <title>Test for Bug 666666</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=666666">Mozilla Bug 666666</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 666666 **/
+["audio", "video"].forEach(function(element) {
+ reflectLimitedEnumerated({
+ element: document.createElement(element),
+ attribute: "preload",
+ validValues: ["none", "metadata", "auto"],
+ invalidValues: ["cheesecake", ""]
+ });
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug669012.html b/dom/html/test/test_bug669012.html
new file mode 100644
index 0000000000..330286f33d
--- /dev/null
+++ b/dom/html/test/test_bug669012.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=669012
+-->
+<head>
+ <title>Test for Bug 669012</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=669012">Mozilla Bug 669012</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<script>
+var run = 0;
+</script>
+<svg>
+<script>
+run++;
+ok(true, "Should run SVG script without attributes")
+</script>
+<script for=window event=onload>
+run++;
+ok(true, "Should run SVG script with for=window event=onload")
+</script>
+<script for=window event=foo>
+run++;
+ok(true, "Should run SVG script with for=window event=foo")
+</script>
+<script for=foo event=onload>
+run++;
+ok(true, "Should run SVG script with for=foo event=onload")
+</script>
+</svg>
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 669012 **/
+is(run, 4, "Should have run all tests")
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug674558.html b/dom/html/test/test_bug674558.html
new file mode 100644
index 0000000000..ab9713bf35
--- /dev/null
+++ b/dom/html/test/test_bug674558.html
@@ -0,0 +1,287 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674558
+-->
+<head>
+ <title>Test for Bug 674558</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=674558">Mozilla Bug 674558</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 674558 **/
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(startTest);
+
+function startTest() {
+ function textAreaCtor() {
+ return document.createElement("textarea");
+ }
+ var ctors = [textAreaCtor];
+ ["text", "password", "search"].forEach(function(type) {
+ ctors.push(function inputCtor() {
+ var input = document.createElement("input");
+ input.type = type;
+ return input;
+ });
+ });
+
+ for (var ctor in ctors) {
+ test(ctors[ctor]);
+ }
+
+ SimpleTest.finish();
+}
+
+function test(ctor) {
+ var elem = ctor();
+ ok(true, "Testing " + name(elem));
+
+ ok("selectionDirection" in elem, "elem should have the selectionDirection property");
+
+ is(elem.selectionStart, elem.value.length, "Default value");
+ is(elem.selectionEnd, elem.value.length, "Default value");
+ is(elem.selectionDirection, "forward", "Default value");
+
+ var content = document.getElementById("content");
+ content.appendChild(elem);
+
+ function flush() { document.body.clientWidth; }
+ function hide() {
+ content.style.display = "none";
+ flush();
+ }
+ function show() {
+ content.style.display = "";
+ flush();
+ }
+
+ elem.value = "foobar";
+
+ is(elem.selectionStart, elem.value.length, "Default value");
+ is(elem.selectionEnd, elem.value.length, "Default value");
+ is(elem.selectionDirection, "forward", "Default value");
+
+ elem.setSelectionRange(1, 3);
+ is(elem.selectionStart, 1, "Correct value");
+ is(elem.selectionEnd, 3, "Correct value");
+ is(elem.selectionDirection, "forward", "If not set, should default to forward");
+
+ hide();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 3, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 3, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ // extend to right
+ elem.focus();
+ synthesizeKey("VK_RIGHT", {shiftKey: true});
+
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Correct value");
+ is(elem.selectionDirection, "forward", "Still forward");
+
+ hide();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ // change the direction
+ elem.selectionDirection = "backward";
+
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Correct value");
+
+ hide();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ // extend to right again
+ synthesizeKey("VK_RIGHT", {shiftKey: true});
+
+ is(elem.selectionStart, 2, "Correct value");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Still backward");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ elem.selectionEnd = 5;
+
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Correct value");
+ is(elem.selectionDirection, "backward", "Still backward");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ elem.selectionDirection = "none";
+
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "forward", "none not supported");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ elem.selectionDirection = "backward";
+
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Correct Value");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ elem.selectionDirection = "invalid";
+
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Treated as none");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ elem.selectionDirection = "backward";
+
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Correct Value");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ elem.setSelectionRange(1, 4);
+
+ is(elem.selectionStart, 1, "Correct value");
+ is(elem.selectionEnd, 4, "Correct value");
+ is(elem.selectionDirection, "forward", "Correct value");
+
+ hide();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ elem.setSelectionRange(1, 1);
+ synthesizeKey("VK_RIGHT", {shiftKey: true});
+ synthesizeKey("VK_RIGHT", {shiftKey: true});
+ synthesizeKey("VK_RIGHT", {shiftKey: true});
+
+ is(elem.selectionStart, 1, "Correct value");
+ is(elem.selectionEnd, 4, "Correct value");
+ is(elem.selectionDirection, "forward", "Correct value");
+
+ hide();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 1, "Value unchanged");
+ is(elem.selectionEnd, 4, "Value unchanged");
+ is(elem.selectionDirection, "forward", "Value unchanged");
+
+ elem.setSelectionRange(5, 5);
+ synthesizeKey("VK_LEFT", {shiftKey: true});
+ synthesizeKey("VK_LEFT", {shiftKey: true});
+ synthesizeKey("VK_LEFT", {shiftKey: true});
+
+ is(elem.selectionStart, 2, "Correct value");
+ is(elem.selectionEnd, 5, "Correct value");
+ is(elem.selectionDirection, "backward", "Correct value");
+
+ hide();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+
+ show();
+ is(elem.selectionStart, 2, "Value unchanged");
+ is(elem.selectionEnd, 5, "Value unchanged");
+ is(elem.selectionDirection, "backward", "Value unchanged");
+}
+
+function name(elem) {
+ var tag = elem.localName;
+ if (tag == "input") {
+ tag += "[type=" + elem.type + "]";
+ }
+ return tag;
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug674927.html b/dom/html/test/test_bug674927.html
new file mode 100644
index 0000000000..92af594530
--- /dev/null
+++ b/dom/html/test/test_bug674927.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=674927
+-->
+<title>Test for Bug 674927</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<p><span>Hello</span></p>
+<div contenteditable>Contenteditable <i>is</i> splelchecked by default</div>
+<textarea>Textareas are spellchekced by default</textarea>
+<input value="Inputs are not spellcheckde by default">
+<script>
+// Test the effect of setting spellcheck on various elements
+[
+ "html",
+ "body",
+ "p",
+ "span",
+ "div",
+ "i",
+ "textarea",
+ "input",
+].forEach(function(query) {
+ var element = document.querySelector(query);
+
+ // First check what happens if no attributes are set
+ var defaultSpellcheck;
+ if (element.isContentEditable || element.tagName == "TEXTAREA") {
+ defaultSpellcheck = true;
+ } else {
+ defaultSpellcheck = false;
+ }
+ is(element.spellcheck, defaultSpellcheck,
+ "Default spellcheck for <" + element.tagName.toLowerCase() + ">");
+
+ // Now try setting spellcheck on ancestors
+ var ancestor = element;
+ do {
+ testSpellcheck(ancestor, element);
+ ancestor = ancestor.parentNode;
+ } while (ancestor.nodeType == Node.ELEMENT_NODE);
+});
+
+function testSpellcheck(ancestor, element) {
+ ancestor.spellcheck = true;
+ is(element.spellcheck, true,
+ ".spellcheck on <" + element.tagName.toLowerCase() + "> with " +
+ "spellcheck=true on <" + ancestor.tagName.toLowerCase() + ">");
+ ancestor.spellcheck = false;
+ is(element.spellcheck, false,
+ ".spellcheck on <" + element.tagName.toLowerCase() + "> with " +
+ "spellcheck=false on <" + ancestor.tagName.toLowerCase() + ">");
+ ancestor.removeAttribute("spellcheck");
+}
+</script>
diff --git a/dom/html/test/test_bug677495-1.html b/dom/html/test/test_bug677495-1.html
new file mode 100644
index 0000000000..be11d20fd6
--- /dev/null
+++ b/dom/html/test/test_bug677495-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677495
+
+As mandated by the spec, the body of a media document must only contain one child.
+-->
+<head>
+ <title>Test for Bug 571981</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function frameLoaded() {
+ var testframe = document.getElementById('testframe');
+ var testframeChildren = testframe.contentDocument.body.childNodes;
+ is(testframeChildren.length, 1, "Body of video document has 1 child");
+ is(testframeChildren[0].nodeName, "VIDEO", "Only child of body must be a <video> element");
+
+ SimpleTest.finish();
+ }
+</script>
+
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"
+ src="file.webm"></iframe>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug677495.html b/dom/html/test/test_bug677495.html
new file mode 100644
index 0000000000..2145d5899c
--- /dev/null
+++ b/dom/html/test/test_bug677495.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677495
+
+As mandated by the spec, the body of a media document must only contain one child.
+-->
+<head>
+ <title>Test for Bug 571981</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function frameLoaded() {
+ var testframe = document.getElementById('testframe');
+ var testframeChildren = testframe.contentDocument.body.childNodes;
+ is(testframeChildren.length, 1, "Body of image document has 1 child");
+ is(testframeChildren[0].nodeName, "IMG", "Only child of body must be an <img> element");
+
+ SimpleTest.finish();
+ }
+</script>
+
+</head>
+<body>
+ <p id="display"></p>
+
+ <iframe id="testframe" name="testframe" onload="frameLoaded()"
+ src="image.png"></iframe>
+
+</body>
+</html>
diff --git a/dom/html/test/test_bug677658.html b/dom/html/test/test_bug677658.html
new file mode 100644
index 0000000000..79a4088b73
--- /dev/null
+++ b/dom/html/test/test_bug677658.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677658
+-->
+<head>
+ <title>Test for Bug 677658</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677658">Mozilla Bug 677658</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript"><!--
+
+/** Test for Bug 677658 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function testDone() {
+ ok(window.testPassed, "Script shouldn't have run!");
+ SimpleTest.finish();
+}
+
+function test() {
+ window.testPassed = true;
+ document.getElementById("testtarget").innerHTML =
+ "<script async src='data:text/plain, window.testPassed = false;'></script>";
+ SimpleTest.executeSoon(testDone);
+}
+
+// -->
+</script>
+</pre>
+<div id="testtarget"></div>
+</body>
+</html>
diff --git a/dom/html/test/test_bug682886.html b/dom/html/test/test_bug682886.html
new file mode 100644
index 0000000000..cb032738c9
--- /dev/null
+++ b/dom/html/test/test_bug682886.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=682886
+-->
+<head>
+ <title>Test for Bug 682886</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=682886">Mozilla Bug 682886</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 682886 **/
+
+
+ var m = document.createElement("menu");
+ var s = "<menuitem>foo</menuitem>";
+ m.innerHTML = s;
+ is(m.innerHTML, s, "Wrong menuitem serialization!");
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug691.html b/dom/html/test/test_bug691.html
new file mode 100644
index 0000000000..f88df20a54
--- /dev/null
+++ b/dom/html/test/test_bug691.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=691
+-->
+<head>
+ <title>Test for Bug 691</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<script type="text/javascript">
+
+function show(what) {
+ var stage = document.getElementById("stage");
+ if (what == "modularity") {
+ var spaghetti = document.createElement("IMG",null);
+ spaghetti.setAttribute("SRC","nnc_lockup.gif");
+ spaghetti.setAttribute("id","foo");
+ stage.insertBefore(spaghetti,stage.firstChild);
+ }
+}
+
+function remove() {
+ var stage = document.getElementById("stage");
+ var body = document.getElementsByTagName("BODY")[0];
+ while (stage.firstChild) {
+ stage.firstChild.remove();
+ }
+}
+
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=691">Mozilla Bug 691</a>
+<p id="display"></p>
+<div id="content" >
+<ul>
+<li >foo</li>
+</ul>
+<div id="stage">
+</div>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 691 **/
+
+show("modularity");
+remove();
+show("modularity");
+remove();
+show("modularity");
+remove();
+show("modularity");
+
+ok($("foo"), "basic DOM manipulation doesn't crash");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug694.html b/dom/html/test/test_bug694.html
new file mode 100644
index 0000000000..78eb054cfc
--- /dev/null
+++ b/dom/html/test/test_bug694.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=694
+-->
+<head>
+ <title>Test for Bug 694</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=694">Mozilla Bug 694</a>
+<p id="display"></p>
+<div id="content" >
+<img src="/missing_on_purpose" width=123 height=25 alt="Hello, &quot;Quotes&quot; how are you?" id="testimg">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 694 **/
+
+is($("testimg").getAttribute("alt"), "Hello, \"Quotes\" how are you?", "entities in alt attribute works");
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug694503.html b/dom/html/test/test_bug694503.html
new file mode 100644
index 0000000000..4ff10feffc
--- /dev/null
+++ b/dom/html/test/test_bug694503.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=694503
+-->
+<head>
+ <title>Test for Bug 694503</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=694503">Mozilla Bug 694503</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<div>
+<map name="map1">
+ <area onclick="++mapClickCount; event.preventDefault();"
+ coords="0,0,50,50" shape="rect">
+</map>
+</div>
+
+<img id="img"
+ usemap="#map1" alt="Foo bar" src="about:logo">
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 694503 **/
+
+var mapClickCount = 0;
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var m = document.getElementsByTagName("map")[0];
+ var img = document.getElementById('img');
+ var origName = m.name;
+
+ synthesizeMouse(img, 25, 25, {});
+ is(mapClickCount, 1, "Wrong click count (1)");
+
+ m.name = "foo"
+ synthesizeMouse(img, 25, 25, {});
+ is(mapClickCount, 1, "Wrong click count (2)");
+
+ m.removeAttribute("name");
+ m.id = origName;
+ synthesizeMouse(img, 25, 25, {});
+ is(mapClickCount, 2, "Wrong click count (3)");
+
+ // Back to original state
+ m.removeAttribute("id");
+ m.name = origName;
+ synthesizeMouse(img, 25, 25, {});
+ is(mapClickCount, 3, "Wrong click count (4)");
+
+ var p = m.parentNode;
+ p.removeChild(m);
+ synthesizeMouse(img, 25, 25, {});
+ is(mapClickCount, 3, "Wrong click count (5)");
+
+ // Back to original state
+ p.appendChild(m);
+ synthesizeMouse(img, 25, 25, {});
+ is(mapClickCount, 4, "Wrong click count (6)");
+
+ SimpleTest.finish();
+});
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug696.html b/dom/html/test/test_bug696.html
new file mode 100644
index 0000000000..6b3c5d9561
--- /dev/null
+++ b/dom/html/test/test_bug696.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=696
+-->
+<head>
+ <title>Test for Bug 696</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=696">Mozilla Bug 696</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <table><tr id="mytr"><td>Foo</td><td>Bar</td></tr></table>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 696 **/
+var mytr = $("content").getElementsByTagName("TR")[0];
+is(mytr.getAttribute("ID"),"mytr","TR tags expose their ID attribute");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug717819.html b/dom/html/test/test_bug717819.html
new file mode 100644
index 0000000000..b2e04f17ba
--- /dev/null
+++ b/dom/html/test/test_bug717819.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=717819
+-->
+<head>
+ <title>Test for Bug 717819</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=717819">Mozilla Bug 717819</a>
+<p id="display"></p>
+<div id="content">
+ <table style="position: relative; top: 100px;">
+ <tr>
+ <td>
+ <div id="test" style="position: absolute; top: 50px;"></div>
+ </td>
+ </tr>
+ </table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 717819 **/
+var div = document.getElementById("test");
+is(div.offsetTop, 50, "The offsetTop must be calculated correctly");
+is(div.offsetParent, document.querySelector("table"),
+ "The offset should be calculated off of the correct parent");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug741266.html b/dom/html/test/test_bug741266.html
new file mode 100644
index 0000000000..d61e5b6ab0
--- /dev/null
+++ b/dom/html/test/test_bug741266.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=741266
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 741266</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=741266">Mozilla Bug 741266</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 741266 **/
+SimpleTest.waitForExplicitFinish();
+
+var url = URL.createObjectURL(new Blob([""], { type: "text/html" }));
+var w = window.open(url, "", "width=100,height=100");
+w.onload = function() {
+ is(w.innerHeight, 100, "Popup height should be 100 when opened with window.open");
+ // XXXbz On at least some platforms, the innerWidth is off by the scrollbar
+ // width for some reason. So just make sure it's the same for both popups.
+ var width = w.innerWidth;
+ w.close();
+
+ w = document.open(url, "", "width=100,height=100");
+ w.onload = function() {
+ is(w.innerHeight, 100, "Popup height should be 100 when opened with document.open");
+ is(w.innerWidth, width, "Popup width should be the same when opened with document.open");
+ w.close();
+ SimpleTest.finish();
+ };
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug742030.html b/dom/html/test/test_bug742030.html
new file mode 100644
index 0000000000..28185e60db
--- /dev/null
+++ b/dom/html/test/test_bug742030.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=742030
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 742030</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=742030">Mozilla Bug 742030</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 742030 **/
+const str = " color: #ff0000 ";
+var span = document.createElement("span");
+span.setAttribute("style", str);
+is(span.getAttribute("style"), str, "Should have set properly");
+var span2 = span.cloneNode(false);
+is(span2.getAttribute("style"), str, "Should have cloned properly");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug742549.html b/dom/html/test/test_bug742549.html
new file mode 100644
index 0000000000..553493858e
--- /dev/null
+++ b/dom/html/test/test_bug742549.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=742549
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 742549</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=742549">Mozilla Bug 742549</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 742549 **/
+var els = [ document.createElement("script"),
+ document.createElementNS("http://www.w3.org/2000/svg", "script") ]
+
+for (var i = 0; i < els.length; ++i) {
+ reflectLimitedEnumerated({
+ element: els[i],
+ attribute: { content: "crossorigin", idl: "crossOrigin" },
+ // "" is a valid value per spec, but gets mapped to the "anonymous" state,
+ // just like invalid values, so just list it under invalidValues
+ validValues: [ "anonymous", "use-credentials" ],
+ invalidValues: [
+ "", " aNOnYmous ", " UsE-CreDEntIALS ", "foobar", "FOOBAR", " fOoBaR "
+ ],
+ defaultValue: { invalid: "anonymous", missing: null },
+ nullable: true,
+ })
+}
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug745685.html b/dom/html/test/test_bug745685.html
new file mode 100644
index 0000000000..c4544441e7
--- /dev/null
+++ b/dom/html/test/test_bug745685.html
@@ -0,0 +1,105 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=745685
+-->
+<title>Test for Bug 745685</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=745685">Mozilla Bug 745685</a>
+<font>Test text</font>
+<font size=1>1</font>
+<font size=2>2</font>
+<font size=3>3</font>
+<font size=4>4</font>
+<font size=5>5</font>
+<font size=6>6</font>
+<font size=7>7</font>
+<script>
+/** Test for Bug 745685 **/
+
+var referenceSizes = {};
+for (var i = 1; i <= 7; i++) {
+ referenceSizes[i] =
+ getComputedStyle(document.querySelector('[size="' + i + '"]'))
+ .fontSize;
+ if (i > 1) {
+ isnot(referenceSizes[i], referenceSizes[i - 1],
+ "Sanity check: different <font size>s give different .fontSize");
+ }
+}
+
+function testFontSize(input, expected) {
+ var font = document.querySelector("font");
+ font.setAttribute("size", input);
+ is(font.getAttribute("size"), input,
+ "Setting doesn't round-trip (.getAttribute)");
+ is(font.size, input,
+ "Setting doesn't round-trip (.size)");
+ is(getComputedStyle(font).fontSize, referenceSizes[expected],
+ 'Incorrect size for "' + input + '" : expected the same as ' + expected);
+}
+
+function testFontSizes(input, expected) {
+ testFontSize(input, expected);
+ // Leading whitespace
+ testFontSize(" " + input, expected);
+ testFontSize("\t" + input, expected);
+ testFontSize("\n" + input, expected);
+ testFontSize("\f" + input, expected);
+ testFontSize("\r" + input, expected);
+ // Trailing garbage
+ testFontSize(input + "abcd", expected);
+ testFontSize(input + ".5", expected);
+ testFontSize(input + "e2", expected);
+}
+
+// Parse error
+testFontSizes("", 3);
+
+// No sign
+testFontSizes("0", 1);
+testFontSizes("1", 1);
+testFontSizes("2", 2);
+testFontSizes("3", 3);
+testFontSizes("4", 4);
+testFontSizes("5", 5);
+testFontSizes("6", 6);
+testFontSizes("7", 7);
+testFontSizes("8", 7);
+testFontSizes("9", 7);
+testFontSizes("10", 7);
+testFontSizes("10000000000000000000000", 7);
+
+// Minus sign
+testFontSizes("-0", 3);
+testFontSizes("-1", 2);
+testFontSizes("-2", 1);
+testFontSizes("-3", 1);
+testFontSizes("-4", 1);
+testFontSizes("-5", 1);
+testFontSizes("-6", 1);
+testFontSizes("-7", 1);
+testFontSizes("-8", 1);
+testFontSizes("-9", 1);
+testFontSizes("-10", 1);
+testFontSizes("-10000000000000000000000", 1);
+
+// Plus sign
+testFontSizes("+0", 3);
+testFontSizes("+1", 4);
+testFontSizes("+2", 5);
+testFontSizes("+3", 6);
+testFontSizes("+4", 7);
+testFontSizes("+5", 7);
+testFontSizes("+6", 7);
+testFontSizes("+7", 7);
+testFontSizes("+8", 7);
+testFontSizes("+9", 7);
+testFontSizes("+10", 7);
+testFontSizes("+10000000000000000000000", 7);
+
+// Non-HTML5 whitespace
+testFontSize("\b1", 3);
+testFontSize("\v1", 3);
+testFontSize("\0u00a01", 3);
+</script>
diff --git a/dom/html/test/test_bug763626.html b/dom/html/test/test_bug763626.html
new file mode 100644
index 0000000000..11da9d1ad2
--- /dev/null
+++ b/dom/html/test/test_bug763626.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=763626
+-->
+<head>
+<title>Test for Bug 763626</title>
+
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function boom()
+{
+ var r = document.createElement("iframe").sandbox;
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ is("" + r, "", "ToString should return empty string when element is gone");
+ SimpleTest.finish();
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
+
diff --git a/dom/html/test/test_bug765780.html b/dom/html/test/test_bug765780.html
new file mode 100644
index 0000000000..9aee15ea6b
--- /dev/null
+++ b/dom/html/test/test_bug765780.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=765780
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 765780</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ /** Test for Bug 765780 **/
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ var f = $("f");
+ var doc = f.contentDocument;
+ doc.designMode = "on";
+ var s = doc.createElement("script");
+ s.textContent = "parent.called = true;";
+
+ window.called = false;
+ doc.body.appendChild(s);
+ ok(called, "Script in designMode iframe should have run");
+
+ doc = doc.querySelector("iframe").contentDocument;
+ var s = doc.createElement("script");
+ s.textContent = "parent.parent.called = true;";
+
+ window.called = false;
+ doc.body.appendChild(s);
+ ok(called, "Script in designMode iframe's child should have run");
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=765780">Mozilla Bug 765780</a>
+<!-- Important: iframe needs to not be display: none -->
+<p id="display"><iframe id="f" srcdoc="<iframe></iframe>"></iframe> </p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug780993.html b/dom/html/test/test_bug780993.html
new file mode 100644
index 0000000000..14324e8e43
--- /dev/null
+++ b/dom/html/test/test_bug780993.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for bug 780993</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ var select = document.createElement("select");
+ var option = document.createElement("option");
+ select.appendChild(option);
+ assert_equals(select[0], option);
+ select[0] = null;
+ assert_equals(option.parentNode, null);
+ assert_equals(select[0], undefined);
+}, "Should be able to set select[n] to null.");
+test(function() {
+ var select = document.createElement("select");
+ var option = document.createElement("option");
+ var option2 = document.createElement("option");
+ select.appendChild(option);
+ assert_equals(select[0], option);
+ select[0] = option2;
+ assert_equals(option.parentNode, null);
+ assert_equals(option2.parentNode, select);
+ assert_equals(select[0], option2);
+}, "Should be able to set select[n] to an option element");
+test(function() {
+ var select = document.createElement("select");
+ var option = document.createElement("option");
+ select.appendChild(option);
+ assert_equals(select[0], option);
+ assert_throws(null, function() {
+ select[0] = 42;
+ });
+ assert_equals(option.parentNode, select);
+ assert_equals(select[0], option);
+}, "Should not be able to set select[n] to a primitive.");
+</script>
diff --git a/dom/html/test/test_bug787134.html b/dom/html/test/test_bug787134.html
new file mode 100644
index 0000000000..59aee4e463
--- /dev/null
+++ b/dom/html/test/test_bug787134.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=787134
+-->
+<head>
+ <title>Test for Bug 787134</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=787134">Mozilla Bug 787134</a>
+<p id="display"></p>
+<p><a id="link-test1" href="example link">example link</a></p>
+<pre id="test">
+<script>
+ var div = document.createElement('div');
+ div.innerHTML = '<a href=#></a>';
+ var a = div.firstChild;
+ ok(a.matches(':link'), "Should match a link not in a document");
+ is(div.querySelector(':link'), a, "Should find a link not in a document");
+ a = document.querySelector('#link-test1');
+ ok(a.matches(':link'), "Should match a link in a document with an invalid URL");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug797113.html b/dom/html/test/test_bug797113.html
new file mode 100644
index 0000000000..6c246eb3c3
--- /dev/null
+++ b/dom/html/test/test_bug797113.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Test for bug 780993</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<script>
+test(function() {
+ var select = document.createElement("select");
+ var option = document.createElement("option");
+ select.appendChild(option);
+ assert_equals(select.options[0], option);
+ select.options[0] = null;
+ assert_equals(option.parentNode, null);
+ assert_equals(select.options[0], undefined);
+}, "Should be able to set select.options[n] to null.");
+test(function() {
+ var select = document.createElement("select");
+ var option = document.createElement("option");
+ var option2 = document.createElement("option");
+ select.appendChild(option);
+ assert_equals(select.options[0], option);
+ select.options[0] = option2;
+ assert_equals(option.parentNode, null);
+ assert_equals(option2.parentNode, select);
+ assert_equals(select.options[0], option2);
+}, "Should be able to set select.options[n] to an option element");
+test(function() {
+ var select = document.createElement("select");
+ var option = document.createElement("option");
+ select.appendChild(option);
+ assert_equals(select.options[0], option);
+ assert_throws(null, function() {
+ select.options[0] = 42;
+ });
+ assert_equals(option.parentNode, select);
+ assert_equals(select.options[0], option);
+}, "Should not be able to set select.options[n] to a primitive.");
+</script>
diff --git a/dom/html/test/test_bug803677.html b/dom/html/test/test_bug803677.html
new file mode 100644
index 0000000000..640f747528
--- /dev/null
+++ b/dom/html/test/test_bug803677.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=803677
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 803677</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+ .base { border:1px solid gray; }
+ .bad-table { display:table-cell; border:1px solid red; }
+</style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=803677">Mozilla Bug 803677</a>
+<p id="display"></p>
+<div id="content">
+ <p class="base">1</p>
+ <p class="base">2</p>
+ <p class="base">3</p>
+ <p class="base bad-table">4</p>
+ <p class="base">7</p>
+ <p class="base">8</p>
+ <p class="base">9</p>
+</div>
+<pre id="test">
+<script type="application/javascript">
+ var p = document.querySelectorAll(".base");
+ var parent = document.querySelector("body");
+ var prevOffset = 0;
+ for (var i = 0; i < p.length; i++) {
+ var t = 0, e = p[i];
+ is(e.offsetParent, parent, "Offset parent of all paragraphs should be the body.");
+ while (e) {
+ t += e.offsetTop;
+ e = e.offsetParent;
+ }
+ p[i].innerHTML = t;
+
+ ok(t > prevOffset, "Offset should increase down the page");
+ prevOffset = t;
+ }
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug821307.html b/dom/html/test/test_bug821307.html
new file mode 100644
index 0000000000..591018da17
--- /dev/null
+++ b/dom/html/test/test_bug821307.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=821307
+-->
+<head>
+ <title>Test for Bug 821307</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=821307">Mozilla Bug 821307</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<input id='dummy'></input>
+<input type="password" id='input' value='11111111111111111' style="width:40em; font-size:40px;"></input>
+
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var dummy = document.getElementById('dummy');
+ dummy.focus();
+ is(document.activeElement, dummy, "Check dummy element is now focused");
+
+ var input = document.getElementById('input');
+ var rect = input.getBoundingClientRect();
+ synthesizeMouse(input, 100, rect.height/2, {});
+ is(document.activeElement, input, "Check input element is now focused");
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug827126.html b/dom/html/test/test_bug827126.html
new file mode 100644
index 0000000000..c4cf28d44c
--- /dev/null
+++ b/dom/html/test/test_bug827126.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=827126
+-->
+<head>
+ <title>Test for Bug 827126</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=827126">Mozilla Bug 827126</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test to ensure we reflect <img align> correctly **/
+reflectString({
+ element: new Image(),
+ attribute: "align",
+ otherValues: [ "left", "right", "middle", "justify" ]
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug838582.html b/dom/html/test/test_bug838582.html
new file mode 100644
index 0000000000..2d412a041b
--- /dev/null
+++ b/dom/html/test/test_bug838582.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=838582
+-->
+<head>
+ <title>Test for Bug 838582</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=838582">Mozilla Bug 838582</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<textarea id="t">abc</textarea>
+<script type="application/javascript">
+
+/** Test for Bug 838582 **/
+
+var textarea = document.getElementById("t");
+
+is(t.textLength, 3, "Correct textLength for defaultValue");
+t.value = "abcdef";
+is(t.textLength, 6, "Correct textLength for value");
+ok(!("controllers" in t), "Don't have web-visible controllers property");
+ok("controllers" in SpecialPowers.wrap(t), "Have chrome-visible controllers property");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug839371.html b/dom/html/test/test_bug839371.html
new file mode 100644
index 0000000000..5d434a1803
--- /dev/null
+++ b/dom/html/test/test_bug839371.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=839371
+-->
+<head>
+ <title>Test for Bug 839371</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=839371">Mozilla Bug 839371</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<div itemscope>
+ <data id="d1" itemprop="product-id" value="9678AOU879">The Instigator 2000</data>
+</div>
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 839371 **/
+
+var d1 = document.getElementById("d1"),
+ d2 = document.createElement("data");
+
+// .value IDL
+is(d1.value, "9678AOU879", "value property reflects content attribute");
+d1.value = "123";
+is(d1.value, "123", "value property can be set via setter");
+
+// .value reflects value attribute
+reflectString({
+ element: d2,
+ attribute: "value"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug839913.html b/dom/html/test/test_bug839913.html
new file mode 100644
index 0000000000..7397fa3b6b
--- /dev/null
+++ b/dom/html/test/test_bug839913.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for HTMLAreaElement's stringifier</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ var area = document.createElement("area");
+ area.href = "http://example.org/";
+ assert_equals(area.href, "http://example.org/");
+ assert_equals(String(area), "http://example.org/");
+}, "Area elements should stringify to the href attribute");
+</script>
diff --git a/dom/html/test/test_bug841466.html b/dom/html/test/test_bug841466.html
new file mode 100644
index 0000000000..98eb9a305e
--- /dev/null
+++ b/dom/html/test/test_bug841466.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=841466
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 841466</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script>
+ /** Test for Bug 841466 **/
+var els = ['button', 'fieldset', 'input', 'object', 'output', 'select', 'textarea'];
+var code = "try { is(foo, 'bar', 'expected value bar from expando on element ' + localName); } catch (e) { ok(false, String(e)); }";
+els.forEach(function(el) {
+ var f = document.createElement("form");
+ f.foo = "bar";
+ f.innerHTML = '<' + el + ' onclick="' + code + '">';
+ var e = f.firstChild
+ e.dispatchEvent(new Event("click"));
+})
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=841466">Mozilla Bug 841466</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug845057.html b/dom/html/test/test_bug845057.html
new file mode 100644
index 0000000000..ef0d45d9ed
--- /dev/null
+++ b/dom/html/test/test_bug845057.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=845057
+-->
+<head>
+ <title>Test for Bug 845057</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=845057">Mozilla Bug 845057</a>
+<p id="display"></p>
+<div id="content">
+ <iframe id="iframe" sandbox="allow-scripts"></iframe>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ var iframe = document.getElementById("iframe"),
+ attr = iframe.sandbox;
+ // Security enforcement tests for iframe sandbox are in test_iframe_*
+
+ function eq(a, b) {
+ // check if two attributes are qual modulo permutation
+ return ((a+'').split(" ").sort()+'') == ((b+'').split(" ").sort()+'');
+ }
+
+ ok(attr instanceof DOMTokenList,
+ "Iframe sandbox attribute is instace of DOMTokenList");
+ ok(eq(attr, "allow-scripts") &&
+ eq(iframe.getAttribute("sandbox"), "allow-scripts"),
+ "Stringyfied sandbox attribute is same as that of the DOM element");
+
+ ok(attr.contains("allow-scripts") && !attr.contains("allow-same-origin"),
+ "Set membership of attribute elements is ok");
+
+ attr.add("allow-same-origin");
+
+ ok(attr.contains("allow-scripts") && attr.contains("allow-same-origin"),
+ "Attribute contains added atom");
+ ok(eq(attr, "allow-scripts allow-same-origin") &&
+ eq(iframe.getAttribute("sandbox"), "allow-scripts allow-same-origin"),
+ "Stringyfied attribute with new atom is correct");
+
+ attr.add("allow-forms");
+ attr.remove("allow-scripts");
+
+ ok(!attr.contains("allow-scripts") && attr.contains("allow-forms") &&
+ attr.contains("allow-same-origin"),
+ "Attribute does not contain removed atom");
+ ok(eq(attr, "allow-forms allow-same-origin") &&
+ eq(iframe.getAttribute("sandbox"), "allow-forms allow-same-origin"),
+ "Stringyfied attribute with removed atom is correct");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_bug869040.html b/dom/html/test/test_bug869040.html
new file mode 100644
index 0000000000..c7edcd89d9
--- /dev/null
+++ b/dom/html/test/test_bug869040.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=869040
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 869040</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=869040">Mozilla Bug 869040</a>
+<p id="display"></p>
+<div id="content" style="display: none" data-foo="present1" data-bar="present2">
+
+</div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ /** Test for Bug 869040 **/
+ var foo = "default1";
+ var dataset = $("content").dataset;
+ for (var i = 0; i < 100000; ++i)
+ foo = dataset.foo;
+
+ var bar = "default2";
+ for (var j = 0; j < 100; ++j)
+ bar = dataset.bar;
+
+ is(foo, "present1", "Our IC should work");
+ is(bar, "present2", "Our non-IC case should work");
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/test_bug870787.html b/dom/html/test/test_bug870787.html
new file mode 100644
index 0000000000..d6f66dda32
--- /dev/null
+++ b/dom/html/test/test_bug870787.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=870787
+-->
+<head>
+ <title>Test for Bug 870787</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=870787">Mozilla Bug 870787</a>
+
+<p id="msg"></p>
+
+<form id="form0"></form>
+<img name="img0" id="img0id">
+
+<img name="img1" id="img1id" />
+<form id="form1">
+ <img name="img2" id="img2id" />
+</form>
+<img name="img3" id="img3id" />
+
+<table>
+ <form id="form2">
+ <tr><td>
+ <button name="input1" id="input1id" />
+ <input name="input2" id="input2id" />
+ </form>
+</table>
+
+<table>
+ <form id="form3">
+ <tr><td>
+ <img name="img4" id="img4id" />
+ <img name="img5" id="img5id" />
+ </form>
+</table>
+
+<form id="form4"><img id="img6"></form>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 870787 **/
+
+var form0 = document.getElementById("form0");
+ok(form0, "Form0 exists");
+ok(!form0.img0, "Form0.img0 doesn't exist");
+ok(!form0.img0id, "Form0.img0id doesn't exist");
+
+var form1 = document.getElementById("form1");
+ok(form1, "Form1 exists");
+ok(!form1.img1, "Form1.img1 doesn't exist");
+ok(!form1.img1id, "Form1.img1id doesn't exist");
+is(form1.img2, document.getElementById("img2id"), "Form1.img2 exists");
+is(form1.img2id, document.getElementById("img2id"), "Form1.img2id exists");
+ok(!form1.img3, "Form1.img3 doesn't exist");
+ok(!form1.img3id, "Form1.img3id doesn't exist");
+
+var form2 = document.getElementById("form2");
+ok(form2, "Form2 exists");
+is(form2.input1, document.getElementById("input1id"), "Form2.input1 exists");
+is(form2.input1id, document.getElementById("input1id"), "Form2.input1id exists");
+is(form2.input2, document.getElementById("input2id"), "Form2.input2 exists");
+is(form2.input2id, document.getElementById("input2id"), "Form2.input2id exists");
+
+var form3 = document.getElementById("form3");
+ok(form3, "Form3 exists");
+is(form3.img4, document.getElementById("img4id"), "Form3.img4 doesn't exists");
+is(form3.img4id, document.getElementById("img4id"), "Form3.img4id doesn't exists");
+is(form3.img5, document.getElementById("img5id"), "Form3.img5 doesn't exists");
+is(form3.img5id, document.getElementById("img5id"), "Form3.img5id doesn't exists");
+
+var form4 = document.getElementById("form4");
+ok(form4, "Form4 exists");
+is(Object.getOwnPropertyNames(form4.elements).indexOf("img6"), -1, "Form4.elements should not contain img6");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug871161.html b/dom/html/test/test_bug871161.html
new file mode 100644
index 0000000000..c4512621b6
--- /dev/null
+++ b/dom/html/test/test_bug871161.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=871161
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 871161</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 871161 **/
+ SimpleTest.waitForExplicitFinish();
+
+ window.onmessage = function(e) {
+ is(e.data, "windows-1252", "Wrong charset");
+ e.source.close();
+ SimpleTest.finish();
+ }
+
+ function run() {
+ window.open("file_bug871161-1.html");
+ }
+
+ </script>
+</head>
+<body onload="run();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=871161">Mozilla Bug 871161</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug874758.html b/dom/html/test/test_bug874758.html
new file mode 100644
index 0000000000..fa77225ba6
--- /dev/null
+++ b/dom/html/test/test_bug874758.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html data-expando-prop="xyz">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=874758
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 874758</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 874758 **/
+ Object.prototype.expandoProp = 5;
+ is({}.expandoProp, 5, "Should see this on random objects");
+
+ is(document.head.dataset.expandoProp, 5, "Should see this on dataset too");
+ is(document.documentElement.dataset.expandoProp, "xyz",
+ "But if the dataset has it, we should get it from there");
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=874758">Mozilla Bug 874758</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug879319.html b/dom/html/test/test_bug879319.html
new file mode 100644
index 0000000000..692f880449
--- /dev/null
+++ b/dom/html/test/test_bug879319.html
@@ -0,0 +1,92 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=879319
+-->
+<head>
+ <title>Test for Bug 879319</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=879319">Mozilla Bug 879319</a>
+
+<p id="msg"></p>
+
+<form id="form">
+ <img id="img0" name="bar0" />
+</form>
+<input id="input0" name="foo0" form="form" />
+<input id="input1" name="foo1" form="form" />
+<input id="input2" name="foo2" form="form" />
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 879319 **/
+
+var input0 = document.getElementById("input0");
+ok(input0, "input0 exists");
+
+var form = document.getElementById("form");
+ok(form, "form exists");
+is(form.foo0, input0, "Form.foo0 should exist");
+
+ok("foo0" in form.elements, "foo0 in form.elements");
+is(input0.form, form, "input0.form is form");
+
+input0.setAttribute("name", "tmp0");
+ok("tmp0" in form.elements, "tmp0 is in form.elements");
+ok(!("foo0" in form.elements), "foo0 is not in form.elements");
+is(form.tmp0, input0, "Form.tmp0 == input0");
+is(form.foo0, input0, "Form.foo0 is still here");
+
+input0.setAttribute("name", "tmp1");
+ok("tmp1" in form.elements, "tmp1 is in form.elements");
+ok(!("tmp0" in form.elements), "tmp0 is not in form.elements");
+ok(!("foo0" in form.elements), "foo0 is not in form.elements");
+is(form.tmp0, input0, "Form.tmp0 == input0");
+is(form.tmp1, input0, "Form.tmp1 == input0");
+is(form.foo0, input0, "Form.foo0 is still here");
+
+input0.setAttribute("form", "");
+ok(!("foo0" in form.elements), "foo0 is not in form.elements");
+is(form.foo0, undefined, "Form.foo0 should not still be here");
+is(form.tmp0, undefined, "Form.tmp0 should not still be here");
+is(form.tmp1, undefined, "Form.tmp1 should not still be here");
+
+var input1 = document.getElementById("input1");
+ok(input1, "input1 exists");
+is(form.foo1, input1, "Form.foo1 should exist");
+
+ok("foo1" in form.elements, "foo1 in form.elements");
+is(input1.form, form, "input1.form is form");
+
+input1.setAttribute("name", "foo0");
+ok("foo0" in form.elements, "foo0 is in form.elements");
+is(form.foo0, input1, "Form.foo0 should be input1");
+is(form.foo1, input1, "Form.foo1 should be input1");
+
+var input2 = document.getElementById("input2");
+ok(input2, "input2 exists");
+is(form.foo2, input2, "Form.foo2 should exist");
+input2.remove();
+ok(!("foo2" in form.elements), "foo2 is not in form.elements");
+is(form.foo2, undefined, "Form.foo2 should not longer be there");
+
+var img0 = document.getElementById("img0");
+ok(img0, "img0 exists");
+is(form.bar0, img0, "Form.bar0 should exist");
+
+img0.setAttribute("name", "old_bar0");
+is(form.old_bar0, img0, "Form.bar0 is still here");
+is(form.bar0, img0, "Form.bar0 is still here");
+
+img0.remove();
+is(form.bar0, undefined, "Form.bar0 should not be here");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug885024.html b/dom/html/test/test_bug885024.html
new file mode 100644
index 0000000000..96f1783910
--- /dev/null
+++ b/dom/html/test/test_bug885024.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html data-expando-prop="xyz">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=885024
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 885024</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885024">Mozilla Bug 885024</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<img form="t">
+
+<form id="form">
+ <div id="div"></div>
+</form>
+
+<pre id="test">
+ <script type="application/javascript">
+ var img = document.createElement('img');
+ img.setAttribute('id', 'img');
+
+ var div = document.getElementById('div');
+ div.appendChild(img);
+
+ var form = document.getElementById('form');
+ ok(form, "form exists");
+ ok(form.img, "form.img exists");
+
+ var img2 = document.createElement('img');
+ img2.setAttribute('id', 'img2');
+ img2.setAttribute('form', 'blabla');
+ ok(form, "form exists2");
+ div.appendChild(img2);
+ ok(form.img2, "form.img2 exists");
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug893537.html b/dom/html/test/test_bug893537.html
new file mode 100644
index 0000000000..5935529d87
--- /dev/null
+++ b/dom/html/test/test_bug893537.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=893537
+-->
+ <head>
+<title>Test for crash caused by unloading and reloading srcdoc iframes</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=893537">Mozilla Bug 893537</a>
+
+<iframe id="pframe" src="file_bug893537.html"></iframe>
+
+<pre id="test">
+<script>
+ <!-- Bug 895303 -->
+ SimpleTest.expectAssertions(0, 1);
+
+ SimpleTest.waitForExplicitFinish();
+ var pframe = $("pframe");
+
+ var loadState = 1;
+ pframe.contentWindow.addEventListener("load", function () {
+
+ if (loadState == 1) {
+ var iframe = pframe.contentDocument.getElementById("iframe");
+ iframe.removeAttribute("srcdoc");
+ loadState = 2;
+ }
+ if (loadState == 2) {
+ SimpleTest.executeSoon(function () { pframe.contentWindow.location.reload() });
+ loadState = 3;
+ }
+ if (loadState == 3) {
+ ok(true, "This is a mochitest implementation of a crashtest. To finish is to pass");
+ SimpleTest.finish();
+ }
+ });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug95530.html b/dom/html/test/test_bug95530.html
new file mode 100644
index 0000000000..c4a3078d3e
--- /dev/null
+++ b/dom/html/test/test_bug95530.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=95530
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 95530</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 95530 **/
+ function run() {
+ is(document.compatMode, "CSS1Compat", "Ensure we are in standards mode, not quirks mode.");
+
+ var body = document.getElementsByTagName("body");
+
+ is(computedStyle(body[0],"margin-top"), "100px", "Ensure margin-top matches topmargin");
+ is(computedStyle(body[0],"margin-bottom"), "150px", "Ensure margin-bottom matches bottommargin");
+ is(computedStyle(body[0],"margin-left"), "23px", "Ensure margin-left matches leftmargin");
+ is(computedStyle(body[0],"margin-right"), "64px", "Ensure margin-right matches rightmargin");
+ SimpleTest.finish();
+ }
+ SimpleTest.waitForExplicitFinish();
+ window.addEventListener("load", run);
+ </script>
+</head>
+<body topmargin="100" bottommargin="150" leftmargin="23" rightmargin="64">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=95530">Mozilla Bug 95530</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug969346.html b/dom/html/test/test_bug969346.html
new file mode 100644
index 0000000000..5be76c46ec
--- /dev/null
+++ b/dom/html/test/test_bug969346.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=969346
+-->
+<head>
+<title>Nesting of srcdoc iframes is permitted</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=969349">Mozilla Bug 969346</a>
+
+<iframe id="pframe" srcdoc="<iframe id='iframe' srcdoc='I am nested'></iframe"></iframe>
+
+<pre id="test">
+<script>
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function () {
+ var pframe = $("pframe");
+ var pframeDoc = pframe.contentDocument;
+ var iframe = pframeDoc.getElementById("iframe");
+ var innerDoc = iframe.contentDocument;
+
+ is(innerDoc.body.innerHTML, "I am nested", "Nesting not working?");
+ SimpleTest.finish();
+ });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_bug982039.html b/dom/html/test/test_bug982039.html
new file mode 100644
index 0000000000..6b158413bc
--- /dev/null
+++ b/dom/html/test/test_bug982039.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=982039
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 982039</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 982039 **/
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ var f = document.getElementById("testform");
+ f.elements[0].disabled = true;
+ is(f.checkValidity(), false,
+ "Setting a radiobutton to disabled shouldn't make form valid.");
+
+ f.elements[1].checked = true;
+ ok(f.checkValidity(), "Form should be now valid.");
+
+ f.elements[0].required = false;
+ f.elements[1].required = false;
+ f.elements[2].required = false;
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=982039">Mozilla Bug 982039</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<form action="#" id="testform">
+ <input type="radio" name="radio" value="1" required>
+ <input type="radio" name="radio" value="2" required>
+ <input type="radio" name="radio" value="3" required>
+</form>
+</body>
+</html>
diff --git a/dom/html/test/test_change_crossorigin.html b/dom/html/test/test_change_crossorigin.html
new file mode 100644
index 0000000000..303aac7bea
--- /dev/null
+++ b/dom/html/test/test_change_crossorigin.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=696451
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 696451</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 696451 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var img = new Image,
+ canvas = document.createElement("canvas"),
+ ctx = canvas.getContext("2d"),
+ src = "http://example.com/tests/dom/html/test/image-allow-credentials.png",
+ imgDone = false,
+ imgNotAllowedToLoadDone = false;
+
+ img.src = src;
+ img.crossOrigin = "Anonymous";
+
+ img.addEventListener("load", function() {
+ canvas.width = img.width;
+ canvas.height = img.height;
+ ctx.drawImage( img, 0, 0 );
+ try {
+ canvas.toDataURL("image/png");
+ ok(true, "Image was refetched with setting crossOrigin.");
+ } catch (e) {
+ ok(false, "Image was not refetched after setting crossOrigin.");
+ }
+
+ imgDone = true;
+ if (imgDone && imgNotAllowedToLoadDone) {
+ SimpleTest.finish();
+ }
+ });
+
+ img.addEventListener("error", function (event) {
+ ok(false, "Should be able to load cross origin image with proper headers.");
+
+ imgDone = true;
+ if (imgDone && imgNotAllowedToLoadDone) {
+ SimpleTest.finish();
+ }
+ });
+
+ var imgNotAllowedToLoad = new Image;
+
+ imgNotAllowedToLoad.src = "http://example.com/tests/dom/html/test/image.png";
+
+ imgNotAllowedToLoad.crossOrigin = "Anonymous";
+
+ imgNotAllowedToLoad.addEventListener("load", function() {
+ ok(false, "Image should not be allowed to load without " +
+ "allow-cross-origin-access headers.");
+
+ imgNotAllowedToLoadDone = true;
+ if (imgDone && imgNotAllowedToLoadDone) {
+ SimpleTest.finish();
+ }
+ });
+
+ imgNotAllowedToLoad.addEventListener("error", function() {
+ ok(true, "Image should not be allowed to load without " +
+ "allow-cross-origin-access headers.");
+ imgNotAllowedToLoadDone = true;
+ if (imgDone && imgNotAllowedToLoadDone) {
+ SimpleTest.finish();
+ }
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=696451">Mozilla Bug 696451</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_checked.html b/dom/html/test/test_checked.html
new file mode 100644
index 0000000000..d69dcf2a28
--- /dev/null
+++ b/dom/html/test/test_checked.html
@@ -0,0 +1,347 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=418756
+-->
+<head>
+ <title>Test for Bug 418756</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+Mozilla bug
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418756">418756</a>
+<p id="display"></p>
+<div id="content">
+ <form id="f1">
+ </form>
+ <form id="f2">
+ </form>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 418756 **/
+var group1;
+var group2;
+var group3;
+
+function bounce(node) {
+ let n = node.nextSibling;
+ let p = node.parentNode;
+ p.removeChild(node);
+ p.insertBefore(node, n);
+}
+
+var createdNodes = [];
+
+function cleanup() {
+ for (let node of createdNodes) {
+ if (node.parentNode) {
+ node.remove();
+ }
+ }
+
+ createdNodes = [];
+}
+
+var typeMapper = {
+ 'c': 'checkbox',
+ 'r': 'radio'
+};
+
+var id = 0;
+
+// type can be 'c' for 'checkbox' and 'r' for 'radio'
+function createNode(type, name, checked) {
+ let node = document.createElement("input");
+ node.setAttribute("type", typeMapper[type]);
+ if (checked) {
+ node.setAttribute("checked", "checked");
+ }
+ node.setAttribute("id", type + (++id));
+ node.setAttribute("name", name);
+ createdNodes.push(node);
+ return node;
+}
+
+var types = ['c', 'r'];
+
+// First make sure that setting .checked makes .defaultChecked changes no
+// longer affect .checked.
+for (let type of types) {
+ let n = createNode(type, '', false);
+ is(n.defaultChecked, false, "Bogus defaultChecked on " + typeMapper[type]);
+ is(n.checked, false, "Bogus checked on " + typeMapper[type]);
+ n.defaultChecked = true;
+ is(n.defaultChecked, true, "Bogus defaultChecked on " + typeMapper[type] +
+ "after mutation");
+ is(n.checked, true, "Bogus checked on " + typeMapper[type] +
+ "after mutation");
+ n.checked = false;
+ is(n.defaultChecked, true, "Bogus defaultChecked on " + typeMapper[type] +
+ "after second mutation");
+ is(n.checked, false, "Bogus checked on " + typeMapper[type] +
+ "after second mutation");
+ n.defaultChecked = false;
+ is(n.defaultChecked, false, "Bogus defaultChecked on " + typeMapper[type] +
+ "after third mutation");
+ is(n.checked, false, "Bogus checked on " + typeMapper[type] +
+ "after third mutation");
+ n.defaultChecked = true;
+ is(n.defaultChecked, true, "Bogus defaultChecked on " + typeMapper[type] +
+ "after fourth mutation");
+ is(n.checked, false, "Bogus checked on " + typeMapper[type] +
+ "after fourth mutation");
+}
+
+cleanup();
+
+// Now check that bouncing a control that's the only one of its kind has no
+// effect
+for (let type of types) {
+ let n = createNode(type, 'test1', true);
+ $("f1").appendChild(n);
+ n.checked = false;
+ n.defaultChecked = false;
+ bounce(n);
+ n.defaultChecked = true;
+ is(n.checked, false, "We set .checked on this " + typeMapper[type]);
+}
+
+cleanup();
+
+// Now check that playing with a single radio in a group affects all
+// other radios in the group (but not radios not in that group)
+group1 = [ createNode('r', 'g1', false),
+ createNode('r', 'g1', false),
+ createNode('r', 'g1', false) ];
+group2 = [ createNode('r', 'g2', false),
+ createNode('r', 'g2', false),
+ createNode('r', 'g2', false) ];
+group3 = [ createNode('r', 'g1', false),
+ createNode('r', 'g1', false),
+ createNode('r', 'g1', false) ];
+for (let g of group1) {
+ $("f1").appendChild(g);
+}
+for (let g of group2) {
+ $("f1").appendChild(g);
+}
+for (let g of group3) {
+ $("f2").appendChild(g);
+}
+
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, false,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 1");
+ is(g.checked, false,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checkedhecked wrong pass 1");
+ }
+}
+
+group1[1].defaultChecked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 2");
+ is(g.checked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 2");
+ }
+}
+
+group1[0].defaultChecked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 1 ||
+ group1.indexOf(g) == 0),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 3");
+ is(g.checked, n == 1 && group1.indexOf(g) == 0,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 3");
+ }
+}
+
+group1[2].defaultChecked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 4");
+ is(g.checked, n == 1 && group1.indexOf(g) == 2,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 4");
+ }
+}
+
+var next = group1[1].nextSibling;
+var p = group1[1].parentNode;
+p.removeChild(group1[1]);
+group1[1].defaultChecked = false;
+group1[1].defaultChecked = true;
+p.insertBefore(group1[1], next);
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 5");
+ is(g.checked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 5");
+ }
+}
+
+for (let g of group1) {
+ g.defaultChecked = false;
+}
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, false,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 6");
+ is(g.checked, false,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checkedhecked wrong pass 6");
+ }
+}
+
+group1[1].checked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, false,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 7");
+ is(g.checked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 7");
+ }
+}
+
+group1[0].defaultChecked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && group1.indexOf(g) == 0,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 8");
+ is(g.checked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 8");
+ }
+}
+
+group1[2].defaultChecked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 9");
+ is(g.checked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 9");
+ }
+}
+group1[1].remove();
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 10");
+ is(g.checked, n == 1 && group1.indexOf(g) == 1,
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 10");
+ }
+}
+
+group1[2].checked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 11");
+ is(g.checked, n == 1 && (group1.indexOf(g) == 1 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 11");
+ }
+}
+
+group1[0].checked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 12");
+ is(g.checked, n == 1 && (group1.indexOf(g) == 1 ||
+ group1.indexOf(g) == 0),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 12");
+ }
+}
+
+next = group2[1].nextSibling;
+p = group2[1].parentNode;
+p.removeChild(group2[1]);
+p.insertBefore(group2[1], next);
+group2[0].checked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 13");
+ is(g.checked, (n == 1 && (group1.indexOf(g) == 1 ||
+ group1.indexOf(g) == 0)) ||
+ (n == 2 && group2.indexOf(g) == 0),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 13");
+ }
+}
+
+p.insertBefore(group2[1], next);
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 14");
+ is(g.checked, (n == 1 && (group1.indexOf(g) == 1 ||
+ group1.indexOf(g) == 0)) ||
+ (n == 2 && group2.indexOf(g) == 0),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 14");
+ }
+}
+
+group2[1].defaultChecked = true;
+for (let n of [1, 2, 3]) {
+ for (let g of window["group"+n]) {
+ is(g.defaultChecked, (n == 1 && (group1.indexOf(g) == 0 ||
+ group1.indexOf(g) == 2)) ||
+ (n == 2 && group2.indexOf(g) == 1),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] defaultChecked wrong pass 15");
+ is(g.checked, (n == 1 && (group1.indexOf(g) == 1 ||
+ group1.indexOf(g) == 0)) ||
+ (n == 2 && group2.indexOf(g) == 0),
+ "group" + n + "[" + window["group"+n].indexOf(g) +
+ "] checked wrong pass 15");
+ }
+}
+
+cleanup();
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_dir_attributes_reflection.html b/dom/html/test/test_dir_attributes_reflection.html
new file mode 100644
index 0000000000..3aefaef9a5
--- /dev/null
+++ b/dom/html/test/test_dir_attributes_reflection.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLDirectoryElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLDirectoryElement attributes reflection **/
+
+// .name
+reflectBoolean({
+ element: document.createElement("dir"),
+ attribute: "compact",
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_dl_attributes_reflection.html b/dom/html/test/test_dl_attributes_reflection.html
new file mode 100644
index 0000000000..100b28e9fb
--- /dev/null
+++ b/dom/html/test/test_dl_attributes_reflection.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLDListElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLDListElement attributes reflection **/
+
+// .compact
+reflectBoolean({
+ element: document.createElement("dl"),
+ attribute: "compact"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_document-element-inserted.html b/dom/html/test/test_document-element-inserted.html
new file mode 100644
index 0000000000..6d7e8695ce
--- /dev/null
+++ b/dom/html/test/test_document-element-inserted.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Media test: document-element-inserted</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe id = 'media'>
+</iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+if (navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 4);
+}
+
+SimpleTest.waitForExplicitFinish();
+var loc;
+
+var observe = function(doc){
+ if (doc == media.contentDocument) {
+ ok(media.contentDocument.location.toString().includes(loc),
+ "The loaded media should be " + loc);
+ next();
+ }
+}
+
+var media = document.getElementById('media');
+var tests = [
+ "../../../media/test/short-video.ogv",
+ "../../../media/test/sound.ogg",
+ "../../content/test/image.png"
+]
+
+function next() {
+ if (tests.length) {
+ var t = tests.shift();
+ loc = t.substring(t.indexOf("test"));
+ media.setAttribute("src",t);
+ }
+ else {
+ SpecialPowers.removeObserver(observe, "document-element-inserted");
+ SimpleTest.finish();
+ }
+}
+
+SpecialPowers.addObserver(observe, "document-element-inserted")
+next();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_documentAll.html b/dom/html/test/test_documentAll.html
new file mode 100644
index 0000000000..f3bb7e7df6
--- /dev/null
+++ b/dom/html/test/test_documentAll.html
@@ -0,0 +1,167 @@
+<html>
+<!--
+Tests for document.all
+-->
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Tests for document.all</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=259332">Mozilla Bug 259332</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=393629">Mozilla Bug 393629</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448904">Mozilla Bug 448904</a>
+<p id="display">
+</p>
+<div id="content" style="display: none">
+ <a id="id1">A</a>
+ <a id="id2">B</a>
+ <a id="id2">C</a>
+ <a id="id3">D</a>
+ <a id="id3">E</a>
+ <a id="id3">F</a>
+</div>
+<iframe id="subframe" srcdoc="<span id='x'></span>"
+ style="display: none"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+p = document.getElementById("content");
+
+// Test that several elements with the same id or name behave correctly
+function testNumSame() {
+ is(document.all.id0, undefined, "no ids");
+ is(document.all.namedItem("id0"), null, "no ids");
+ is(document.all.id1, p.children[0], "one id");
+ is(document.all.id2[0], p.children[1], "two ids");
+ is(document.all.id2[1], p.children[2], "two ids");
+ is(document.all.id2.length, 2, "two length");
+ is(document.all.id3[0], p.children[3], "three ids");
+ is(document.all.id3[1], p.children[4], "three ids");
+ is(document.all.id3[2], p.children[5], "three ids");
+ is(document.all.id3.length, 3, "three length");
+}
+testNumSame();
+p.innerHTML = p.innerHTML.replace(/id=/g, "name=");
+testNumSame();
+
+
+// Test that dynamic changes behave properly
+
+// Add two elements and check that they are added to the correct lists
+child = Array.prototype.slice.call(p.children);
+child[6] = document.createElement("a");
+child[6].id = "id0";
+p.appendChild(child[6]);
+child[7] = document.createElement("a");
+child[7].id = "id1";
+p.appendChild(child[7]);
+is(document.all.id0, child[6], "now one id");
+is(document.all.id1[0], child[0], "now two ids");
+is(document.all.id1[1], child[7], "now two ids");
+is(document.all.id1.length, 2, "now two length");
+
+// Remove and element and check that the list shrinks
+rC(child[1]);
+is(document.all.id2, child[2], "now just one id");
+
+// Change an id and check that its removed and added to the correct lists
+child[4].name = "id1";
+is(document.all.id1[0], child[0], "now three ids");
+is(document.all.id1[1], child[4], "now three ids");
+is(document.all.id1[2], child[7], "now three ids");
+is(document.all.id1.length, 3, "now three length");
+is(document.all.id3[1], child[5], "now just two ids");
+is(document.all.id3.length, 2, "now two length");
+
+// Remove all elements from a list and check that it goes empty
+id3list = document.all.id3;
+rC(child[3]);
+is(id3list.length, 1, "now one length");
+rC(child[5]);
+is(document.all.id3, undefined, "now none");
+is(document.all.namedItem("id3"), null, "now none (namedItem)");
+is(id3list.length, 0, "now none length");
+
+// Give an element both a name and id and check that it appears in two lists
+p.insertBefore(child[1], child[2]); // restore previously removed
+id1list = document.all.id1;
+id2list = document.all.id2;
+child[1].id = "id1";
+is(id1list[0], child[0], "now four ids");
+is(id1list[1], child[1], "now four ids");
+is(id1list[2], child[4], "now four ids");
+is(id1list[3], child[7], "now four ids");
+is(id1list.length, 4, "now four length");
+is(id2list[0], child[1], "still two ids");
+is(id2list[1], child[2], "still two ids");
+is(id2list.length, 2, "still two length");
+
+
+// Check that document.all behaves list a list of all elements
+allElems = document.getElementsByTagName("*");
+ok(testArraysSame(document.all, allElems), "arrays same");
+length = document.all.length;
+expectedLength = length + p.getElementsByTagName("*").length + 1;
+p.appendChild(p.cloneNode(true));
+ok(testArraysSame(document.all, allElems), "arrays still same");
+is(document.all.length, expectedLength, "grew correctly");
+
+// Check which elements the 'name' attribute works on
+var elementNames =
+ ['abbr','acronym','address','area','a','b','base',
+ 'bgsound','big','blockquote','br','canvas','center','cite','code',
+ 'col','colgroup','dd','del','dfn','dir','div','dir','dl','dt','em','embed',
+ 'fieldset','font','form','frame','frameset','head','i','iframe','img',
+ 'input','ins','isindex','kbd','keygen','label','li','legend','link','menu',
+ 'multicol','noscript','noframes','object','spacer','table','td','td','th',
+ 'thead','tfoot','tr','textarea','select','option','spacer','param',
+ 'marquee','hr','title','hx','tt','u','ul','var','wbr','sub','sup','cite',
+ 'code','q','nobr','ol','p','pre','s','samp','small','body','html','map',
+ 'bdo','legend','listing','style','script','tbody','caption','meta',
+ 'optgroup','button','span','strike','strong','td'].sort();
+var hasName =
+ ['a','embed','form','iframe','img','input','object','textarea',
+ 'select','map','meta','button','frame','frameset'].sort();
+
+elementNames.forEach(function (name) {
+ nameval = 'namefor' + name;
+
+ e = document.createElement(name);
+ p.appendChild(e);
+ e.setAttribute('name', nameval);
+
+ if (name == hasName[0]) {
+ is(document.all[nameval], e, "should have name");
+ hasName.shift();
+ }
+ else {
+ is(document.all[nameval], undefined, "shouldn't have name");
+ is(document.all.namedItem(nameval), null, "shouldn't have name (namedItem)");
+ }
+});
+is(hasName.length, 0, "found all names");
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ var subdoc = $("subframe").contentDocument;
+ is(subdoc.all.x, subdoc.body.firstChild,
+ "document.all should work in a subdocument");
+ SimpleTest.finish();
+});
+
+// Utility functions
+function rC(node) {
+ node.remove();
+}
+function testArraysSame(a1, a2) {
+ return Array.prototype.every.call(a1, function(e, index) {
+ return a2[index] === e;
+ }) && a1.length == a2.length;
+}
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_element_prototype.html b/dom/html/test/test_element_prototype.html
new file mode 100644
index 0000000000..0cb8d9745f
--- /dev/null
+++ b/dom/html/test/test_element_prototype.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=844127
+-->
+<head>
+ <title>Test for Bug 844127</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=844127">Mozilla Bug 844127</a>
+
+<script type="text/javascript">
+
+/** Test for Bug 844127 **/
+
+var a1 = document.createElement('bgsound');
+var a2 = document.createElement('image');
+var a3 = document.createElement('multicol');
+var a4 = document.createElement('spacer');
+var a5 = document.createElement('isindex');
+
+is(Object.getPrototypeOf(a1), HTMLUnknownElement.prototype, "Prototype for bgsound should be correct");
+is(Object.getPrototypeOf(a2), HTMLElement.prototype, "Prototype for image should be correct");
+is(Object.getPrototypeOf(a3), HTMLUnknownElement.prototype, "Prototype for multicol should be correct");
+is(Object.getPrototypeOf(a4), HTMLUnknownElement.prototype, "Prototype for spacer should be correct");
+is(Object.getPrototypeOf(a5), HTMLUnknownElement.prototype, "Prototype for isindex should be correct");
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_embed_attributes_reflection.html b/dom/html/test/test_embed_attributes_reflection.html
new file mode 100644
index 0000000000..44338113a7
--- /dev/null
+++ b/dom/html/test/test_embed_attributes_reflection.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLEmbedElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLEmbedElement attributes reflection **/
+
+// .src (URL)
+reflectURL({
+ element: document.createElement("embed"),
+ attribute: "src",
+});
+
+// .type (String)
+reflectString({
+ element: document.createElement("embed"),
+ attribute: "type",
+});
+
+// .width (String)
+reflectString({
+ element: document.createElement("embed"),
+ attribute: "width",
+});
+
+// .height (String)
+reflectString({
+ element: document.createElement("embed"),
+ attribute: "height",
+});
+
+// .align (String)
+reflectString({
+ element: document.createElement("embed"),
+ attribute: "align",
+});
+
+// .name (String)
+reflectString({
+ element: document.createElement("embed"),
+ attribute: "name",
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_external_protocol_iframe.html b/dom/html/test/test_external_protocol_iframe.html
new file mode 100644
index 0000000000..cb99a19e1a
--- /dev/null
+++ b/dom/html/test/test_external_protocol_iframe.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for external protocol URLs blocked for iframes</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <div id='foo'><a href='#'>Click here to test this issue</a></div>
+ <script>
+
+function test_initialize() {
+ ChromeUtils.resetLastExternalProtocolIframeAllowed();
+ next();
+}
+
+function test_noUserInteraction() {
+ ok(!SpecialPowers.wrap(document).hasValidTransientUserGestureActivation, "No user interaction yet");
+ is(ChromeUtils.lastExternalProtocolIframeAllowed(), 0, "No iframe loaded before this test!");
+
+ for (let i = 0; i < 10; ++i) {
+ let ifr = document.createElement('iframe');
+ ifr.src = "foo+bar:all_good";
+ document.body.appendChild(ifr);
+
+ is(ChromeUtils.getPopupControlState(), "openAbused", "No user-interaction means: abuse state");
+ ok(ChromeUtils.lastExternalProtocolIframeAllowed() != 0, "We have 1 iframe loaded");
+ }
+
+ next();
+}
+
+function test_userInteraction() {
+ let foo = document.getElementById('foo');
+ foo.addEventListener('click', _ => {
+ ok(SpecialPowers.wrap(document).hasValidTransientUserGestureActivation, "User should've interacted");
+
+ for (let i = 0; i < 10; ++i) {
+ let ifr = document.createElement('iframe');
+ ifr.src = "foo+bar:all_good";
+ document.body.appendChild(ifr);
+
+ ok(!SpecialPowers.wrap(document).hasValidTransientUserGestureActivation, "User interaction should've been consumed");
+ }
+
+ next();
+
+ }, {once: true});
+
+ setTimeout(_ => {
+ synthesizeMouseAtCenter(foo, {});
+ }, 0);
+}
+
+let tests = [
+ test_initialize,
+ test_noUserInteraction,
+ test_userInteraction,
+];
+
+function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ let test = tests.shift();
+ SimpleTest.executeSoon(test);
+}
+
+SpecialPowers.pushPrefEnv({'set': [
+ ['dom.block_external_protocol_in_iframes', true],
+ ['dom.delay.block_external_protocol_in_iframes.enabled', true],
+]}, next);
+
+SimpleTest.waitForExplicitFinish();
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/test_fakepath.html b/dom/html/test/test_fakepath.html
new file mode 100644
index 0000000000..f9819e732f
--- /dev/null
+++ b/dom/html/test/test_fakepath.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Fakepath in HTMLInputElement</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="file" type="file"></input>
+<input id="file_wd" type="file" webkitdirectory></input>
+<script type="application/javascript">
+
+var url = SimpleTest.getTestFileURL("script_fakepath.js");
+script = SpecialPowers.loadChromeScript(url);
+
+function onOpened(message) {
+ var e = document.getElementById("file");
+ SpecialPowers.wrap(e).mozSetDndFilesAndDirectories(message.data);
+ ok(e.value, "C:\\fakepath\\prefs.js");
+
+ e = document.getElementById("file_wd");
+ SpecialPowers.wrap(e).mozSetDndFilesAndDirectories(message.data);
+ ok(e.value, "C:\\fakepath\\prefs.js");
+
+ SimpleTest.finish();
+}
+
+function run() {
+ script.addMessageListener("file.opened", onOpened);
+ script.sendAsyncMessage("file.open");
+}
+
+SpecialPowers.pushPrefEnv({"set": [["dom.webkitBlink.dirPicker.enabled", true]]}, run);
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_filepicker_default_directory.html b/dom/html/test/test_filepicker_default_directory.html
new file mode 100644
index 0000000000..2be811655a
--- /dev/null
+++ b/dom/html/test/test_filepicker_default_directory.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1194893
+-->
+<head>
+ <title>Test for filepicker default directory</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1194893">Mozilla Bug 1194893</a>
+<div id="content">
+ <input type="file" id="f">
+</div>
+<pre id="text">
+<script class="testbody" type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+const { Cc: Cc, Ci: Ci } = SpecialPowers;
+
+// Platform-independent directory names are #define'd in xpcom/io/nsDirectoryServiceDefs.h
+
+// When we want to test an upload directory other than the default, we need to
+// get a valid directory in a platform-independent way. Since NS_OS_DESKTOP_DIR
+// may fallback to NS_OS_HOME_DIR, let's use NS_OS_TMP_DIR.
+var customUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIDirectoryService)
+ .QueryInterface(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+
+// Useful for debugging
+//info("customUploadDirectory" + customUploadDirectory.path);
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+// need to show the MockFilePicker so .displayDirectory gets set
+var f = document.getElementById("f");
+f.focus();
+
+var testIndex = 0;
+var tests = [
+ ["", null, "Desk"],
+ [customUploadDirectory.path, customUploadDirectory.path, ""]
+]
+
+MockFilePicker.showCallback = function(filepicker) {
+ if (tests[testIndex][1] === null) {
+ is(SpecialPowers.wrap(MockFilePicker).displayDirectory, null, "DisplayDirectory is null");
+ } else {
+ is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path, tests[testIndex][1], "DisplayDirectory matches the path");
+ }
+
+ is(SpecialPowers.wrap(MockFilePicker).displaySpecialDirectory, tests[testIndex][2], "DisplaySpecialDirectory matches the path");
+
+ if (++testIndex == tests.length) {
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ } else {
+ launchNextTest();
+ }
+}
+
+function launchNextTest() {
+ SpecialPowers.pushPrefEnv(
+ { 'set': [
+ ['dom.input.fallbackUploadDir', tests[testIndex][0]],
+ ]},
+ function () {
+ f.click();
+ });
+}
+
+launchNextTest();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_focusshift_button.html b/dom/html/test/test_focusshift_button.html
new file mode 100644
index 0000000000..90fadd4827
--- /dev/null
+++ b/dom/html/test/test_focusshift_button.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for shifting focus while mouse clicking on button</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+
+<script class="testbody" type="application/javascript">
+
+var result = "";
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ synthesizeMouseAtCenter(document.getElementById("button"), { });
+ is(result, "(focus button)(blur button)(focus input)", "Focus button then input");
+ SimpleTest.finish();
+});
+</script>
+
+
+<button id="button" onfocus="result += '(focus button)'; document.getElementById('input').focus()"
+ onblur="result += '(blur button)'">Focus</button>
+<input id="input" value="Test" onfocus="result += '(focus input)'"
+ onblur="result += '(blur input)'">
+
+</body>
+</html>
diff --git a/dom/html/test/test_form-parsing.html b/dom/html/test/test_form-parsing.html
new file mode 100644
index 0000000000..7c4acca756
--- /dev/null
+++ b/dom/html/test/test_form-parsing.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Form parsing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content" style="display: none">
+ <div>
+ <form name="test" action="test" id="test">
+ <input type="text" name="text1" id="text1" />
+ <input type="text" name="text2" id="text2" />
+ </div>
+ <input type="text" name="text3" id="text3" />
+ <input type="text" name="text4" id="text4" />
+ <input type="text" name="text5" id="text5" />
+ <input type="text" name="text6" id="text6" />
+ </form>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var form1 = document.getElementById("test");
+ var elem1 = form1.getElementsByTagName("*");
+ var elem1Length = elem1.length;
+ var form1ElementsLength = form1.elements.length;
+
+ is(form1ElementsLength, 6, "form.elements must include mis-nested elements");
+ is(elem1Length, 2, "form must not include mis-nested elements");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_formData.html b/dom/html/test/test_formData.html
new file mode 100644
index 0000000000..4518f37cf5
--- /dev/null
+++ b/dom/html/test/test_formData.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=690659
+-->
+<head>
+ <title>Test for Bug 690659 and 739173</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=690659">Mozilla Bug 690659 & 739173</a>
+<script type="text/javascript" src="./formData_test.js"></script>
+<script type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runMainThreadAndWorker() {
+ var mt = new Promise(function(resolve) {
+ runTest(resolve);
+ });
+
+ var worker;
+ var w = new Promise(function(resolve) {
+ worker = new Worker("formData_worker.js");
+ worker.onmessage = function(event) {
+ if (event.data.type == 'finish') {
+ resolve();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == 'todo') {
+ todo(event.data.status, event.data.msg);
+ }
+ }
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
+ resolve();
+ };
+
+ worker.postMessage(true);
+ });
+
+ return Promise.all([mt, w]);
+}
+
+runMainThreadAndWorker().then(SimpleTest.finish);
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_formSubmission.html b/dom/html/test/test_formSubmission.html
new file mode 100644
index 0000000000..952f65e6dd
--- /dev/null
+++ b/dom/html/test/test_formSubmission.html
@@ -0,0 +1,910 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=523771
+-->
+<head>
+ <title>Test for Bug 523771</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body onload="bodyLoaded()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=523771">Mozilla Bug 523771</a>
+<p id="display"></p>
+<iframe name="target_iframe" id="target_iframe"></iframe>
+<form action="form_submit_server.sjs" target="target_iframe" id="form"
+ method="POST" enctype="multipart/form-data">
+ <table>
+ <tr>
+ <td>Control type</td>
+ <td>Name and value</td>
+ <td>Name, empty value</td>
+ <td>Name, no value</td>
+ <td>Empty name, with value</td>
+ <td>No name, with value</td>
+ <td>No name or value</td>
+ <td>Strange name/value</td>
+ </tr>
+ <tr>
+ <td>Default input</td>
+ <td><input name="n1_1" value="v1_1"></td>
+ <td><input name="n1_2" value=""></td>
+ <td><input name="n1_3"></td>
+ <td><input name="" value="v1_4"></td>
+ <td><input value="v1_5"></td>
+ <td><input></td>
+ <td><input name="n1_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v1_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Text input</td>
+ <td><input type=text name="n2_1" value="v2_1"></td>
+ <td><input type=text name="n2_2" value=""></td>
+ <td><input type=text name="n2_3"></td>
+ <td><input type=text name="" value="v2_4"></td>
+ <td><input type=text value="v2_5"></td>
+ <td><input type=text></td>
+ <td><input type=text name="n2_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v2_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Checkbox unchecked</td>
+ <td><input type=checkbox name="n3_1" value="v3_1"></td>
+ <td><input type=checkbox name="n3_2" value=""></td>
+ <td><input type=checkbox name="n3_3"></td>
+ <td><input type=checkbox name="" value="v3_4"></td>
+ <td><input type=checkbox value="v3_5"></td>
+ <td><input type=checkbox></td>
+ <td><input type=checkbox name="n3_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v3_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Checkbox checked</td>
+ <td><input checked type=checkbox name="n4_1" value="v4_1"></td>
+ <td><input checked type=checkbox name="n4_2" value=""></td>
+ <td><input checked type=checkbox name="n4_3"></td>
+ <td><input checked type=checkbox name="" value="v4_4"></td>
+ <td><input checked type=checkbox value="v4_5"></td>
+ <td><input checked type=checkbox></td>
+ <td><input checked type=checkbox
+ name="n4_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v4_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Radio unchecked</td>
+ <td><input type=radio name="n5_1" value="v5_1"></td>
+ <td><input type=radio name="n5_2" value=""></td>
+ <td><input type=radio name="n5_3"></td>
+ <td><input type=radio name="" value="v5_4"></td>
+ <td><input type=radio value="v5_5"></td>
+ <td><input type=radio></td>
+ <td><input type=radio name="n5_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v5_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Radio checked</td>
+ <td><input checked type=radio name="n6_1" value="v6_1"></td>
+ <td><input checked type=radio name="n6_2" value=""></td>
+ <td><input checked type=radio name="n6_3"></td>
+ <td><input checked type=radio name="" value="v6_4"></td>
+ <td><input checked type=radio value="v6_5"></td>
+ <td><input checked type=radio></td>
+ <td><input checked type=radio
+ name="n6_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v6_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Hidden input</td>
+ <td><input type=hidden name="n7_1" value="v7_1"></td>
+ <td><input type=hidden name="n7_2" value=""></td>
+ <td><input type=hidden name="n7_3"></td>
+ <td><input type=hidden nane="" value="v7_4"></td>
+ <td><input type=hidden value="v7_5"></td>
+ <td><input type=hidden></td>
+ <td><input type=hidden name="n7_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v7_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Password input</td>
+ <td><input type=password name="n8_1" value="v8_1"></td>
+ <td><input type=password name="n8_2" value=""></td>
+ <td><input type=password name="n8_3"></td>
+ <td><input type=password name="" value="v8_4"></td>
+ <td><input type=password value="v8_5"></td>
+ <td><input type=password></td>
+ <td><input type=password name="n8_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v8_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Submit input</td>
+ <td><input type=submit name="n9_1" value="v9_1"></td>
+ <td><input type=submit name="n9_2" value=""></td>
+ <td><input type=submit name="n9_3"></td>
+ <td><input type=submit name="" value="v9_4"></td>
+ <td><input type=submit value="v9_5"></td>
+ <td><input type=submit></td>
+ <td><input type=submit name="n9_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v9_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Button input</td>
+ <td><input type=button name="n10_1" value="v10_1"></td>
+ <td><input type=button name="n10_2" value=""></td>
+ <td><input type=button name="n10_3"></td>
+ <td><input type=button name="" value="v10_4"></td>
+ <td><input type=button value="v10_5"></td>
+ <td><input type=button></td>
+ <td><input type=button name="n10_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v10_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Image input</td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="n11_1" value="v11_1"></td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="n11_2" value=""></td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="n11_3"></td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="" value="v11_4"></td>
+ <td><input type=image src="file_formSubmission_img.jpg" value="v11_5"></td>
+ <td><input type=image src="file_formSubmission_img.jpg"></td>
+ <td><input type=image src="file_formSubmission_img.jpg"
+ name="n11_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v11_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Reset input</td>
+ <td><input type=reset name="n12_1" value="v12_1"></td>
+ <td><input type=reset name="n12_2" value=""></td>
+ <td><input type=reset name="n12_3"></td>
+ <td><input type=reset name="" value="v12_4"></td>
+ <td><input type=reset value="v12_5"></td>
+ <td><input type=reset></td>
+ <td><input type=reset name="n12_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v12_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Unknown input</td>
+ <td><input type=foobar name="n13_1" value="v13_1"></td>
+ <td><input type=foobar name="n13_2" value=""></td>
+ <td><input type=foobar name="n13_3"></td>
+ <td><input type=foobar name="" value="v13_4"></td>
+ <td><input type=foobar value="v13_5"></td>
+ <td><input type=foobar></td>
+ <td><input type=foobar name="n13_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v13_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></td>
+ </tr>
+ <tr>
+ <td>Default button</td>
+ <td><button name="n14_1" value="v14_1"></button></td>
+ <td><button name="n14_2" value=""></button></td>
+ <td><button name="n14_3"></button></td>
+ <td><button name="" value="v14_4"></button></td>
+ <td><button value="v14_5"></button></td>
+ <td><button></button></td>
+ <td><button name="n14_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v14_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></button></td>
+ </tr>
+ <tr>
+ <td>Submit button</td>
+ <td><button type=submit name="n15_1" value="v15_1"></button></td>
+ <td><button type=submit name="n15_2" value=""></button></td>
+ <td><button type=submit name="n15_3"></button></td>
+ <td><button type=submit name="" value="v15_4"></button></td>
+ <td><button type=submit value="v15_5"></button></td>
+ <td><button type=submit></button></td>
+ <td><button type=submit name="n15_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v15_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></button></td>
+ </tr>
+ <tr>
+ <td>Button button</td>
+ <td><button type=button name="n16_1" value="v16_1"></button></td>
+ <td><button type=button name="n16_2" value=""></button></td>
+ <td><button type=button name="n16_3"></button></td>
+ <td><button type=button name="" value="v16_4"></button></td>
+ <td><button type=button value="v16_5"></button></td>
+ <td><button type=button></button></td>
+ <td><button type=button name="n16_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v16_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></button></td>
+ </tr>
+ <tr>
+ <td>Reset button</td>
+ <td><button type=reset name="n17_1" value="v17_1"></button></td>
+ <td><button type=reset name="n17_2" value=""></button></td>
+ <td><button type=reset name="n17_3"></button></td>
+ <td><button type=reset name="" value="v17_4"></button></td>
+ <td><button type=reset value="v17_5"></button></td>
+ <td><button type=reset></button></td>
+ <td><button type=reset name="n17_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v17_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></button></td>
+ </tr>
+ <tr>
+ <td>Unknown button</td>
+ <td><button type=foobar name="n18_1" value="v18_1"></button></td>
+ <td><button type=foobar name="n18_2" value=""></button></td>
+ <td><button type=foobar name="n18_3"></button></td>
+ <td><button type=foobar name="" value="v18_4"></button></td>
+ <td><button type=foobar value="v18_5"></button></td>
+ <td><button type=foobar ></button></td>
+ <td><button type=foobar name="n18_7_&#13;_&#10;_&#13;&#10;_ _&quot;"
+ value="v18_7_&#13;_&#10;_&#13;&#10;_ _&quot;"></button></td>
+ </tr>
+ <tr>
+ <td>&lt;input type='url'&gt;</td>
+ <td><input type=url name="n19_1" value="http://v19_1.org"></td>
+ <td><input type=url name="n19_2" value=""></td>
+ <td><input type=url name="n19_3"></td>
+ <td><input type=url name="" value="http://v19_4.org"></td>
+ <td><input type=url value="http://v19_5.org"></td>
+ <td><input type=url ></td>
+ <td><input type=url name="n19_7_&#13;_&#10;_&#13;&#10;__&quot;"
+ value="http://v19_7_&#13;_&#10;_&#13;&#10;__&quot;">
+ <!-- Put UTF-8 value in the "strange" column. -->
+ <input type=url name="n19_8" value="http://m&#xf3;zill&auml;.&#xf3;rg"></td>
+ </tr>
+ <tr>
+ <td>&lt;input type='email'&gt;</td>
+ <td><input type=email name="n20_1" value="v20_1@bar"></td>
+ <td><input type=email name="n20_2" value=""></td>
+ <td><input type=email name="n20_3"></td>
+ <td><input type=email name="" value="v20_4@bar"></td>
+ <td><input type=email value="v20_5@bar"></td>
+ <td><input type=email ></td>
+ <td><input type=email name="n20_7_&#13;_&#10;_&#13;&#10;__&quot;"
+ value="v20_7_&#13;_&#10;_&#13;&#10;__&quot;@bar">
+ <!-- Put UTF-8 value is the "strange" column. -->
+ <input type=email name="n20_8" value="foo@mózillä.órg"></td>
+ </tr>
+ </table>
+
+ <p>
+ File input:
+ <input type=file name="file_1" class="setfile">
+ <input type=file name="file_2">
+ <input type=file name="" class="setfile">
+ <input type=file name="">
+ <input type=file class="setfile">
+ <input type=file>
+ </p>
+ <p>
+ Multifile input:
+ <input multiple type=file name="file_3" class="setfile">
+ <input multiple type=file name="file_4" class="setfile multi">
+ <input multiple type=file name="file_5">
+ <input multiple type=file name="" class="setfile">
+ <input multiple type=file name="" class="setfile multi">
+ <input multiple type=file name="">
+ <input multiple type=file class="setfile">
+ <input multiple type=file class="setfile multi">
+ <input multiple type=file>
+ </p>
+
+ <p>
+ Textarea:
+ <textarea name="t1">t_1_v</textarea>
+ <textarea name="t2"></textarea>
+ <textarea name="">t_3_v</textarea>
+ <textarea>t_4_v</textarea>
+ <textarea></textarea>
+ <textarea name="t6">
+t_6_v</textarea>
+ <textarea name="t7">t_7_v
+</textarea>
+ <textarea name="t8">
+
+ t_8_v&#0032;
+</textarea>
+ <textarea name="t9_&#13;_&#10;_&#13;&#10;_ _&quot;">t_9_&#13;_&#10;_&#13;&#10;_ _&quot;_v</textarea>
+ <textarea name="t10" value="t_10_bogus">t_10_v</textarea>
+ </p>
+
+ <p>
+ Select one:
+
+ <select name="sel_1"></select>
+ <select name="sel_1b"><option></option></select>
+ <select name="sel_1c"><option selected></option></select>
+
+ <select name="sel_2"><option value="sel_2_v"></option></select>
+ <select name="sel_3"><option selected value="sel_3_v"></option></select>
+
+ <select name="sel_4"><option value="sel_4_v1"></option><option value="sel_4_v2"></option></select>
+ <select name="sel_5"><option selected value="sel_5_v1"></option><option value="sel_5_v2"></option></select>
+ <select name="sel_6"><option value="sel_6_v1"></option><option selected value="sel_6_v2"></option></select>
+
+ <select name="sel_7"><option>sel_7_v1</option><option>sel_7_v2</option></select>
+ <select name="sel_8"><option selected>sel_8_v1</option><option>sel_8_v2</option></select>
+ <select name="sel_9"><option>sel_9_v1</option><option selected>sel_9_v2</option></select>
+
+ <select name="sel_10"><option value="sel_10_v1">sel_10_v1_text</option><option value="sel_10_v2">sel_10_v2_text</option></select>
+ <select name="sel_11"><option selected value="sel_11_v1">sel_11_v1_text</option><option value="sel_11_v2">sel_11_v2_text</option></select>
+ <select name="sel_12"><option value="sel_12_v1">sel_12_v1_text</option><option selected value="sel_12_v2">sel_12_v2_text</option></select>
+
+ <select name="sel_13"><option disabled>sel_13_v1</option><option>sel_13_v2</option></select>
+ <select name="sel_14"><option disabled selected>sel_14_v1</option><option>sel_14_v2</option></select>
+ <select name="sel_15"><option disabled>sel_15_v1</option><option selected>sel_15_v2</option></select>
+
+ <select name="sel_16"><option>sel_16_v1</option><option disabled>sel_16_v2</option></select>
+ <select name="sel_17"><option selected>sel_17_v1</option><option disabled>sel_17_v2</option></select>
+ <select name="sel_18"><option>sel_18_v1</option><option disabled selected>sel_18_v2</option></select>
+
+ <select name=""><option selected value="sel_13_v1"></option><option value="sel_13_v2"></option></select>
+ <select name=""><option value="sel_14_v1"></option><option selected value="sel_14_v2"></option></select>
+ <select name=""><option selected>sel_15_v1</option><option>sel_15_v2</option></select>
+ <select name=""><option>sel_16_v1</option><option selected>sel_16_v2</option></select>
+
+ <select><option selected value="sel_17_v1"></option><option value="sel_17_v2"></option></select>
+ <select><option value="sel_18_v1"></option><option selected value="sel_18_v2"></option></select>
+ <select><option selected>sel_19_v1</option><option>sel_19_v2</option></select>
+ <select><option>sel_20_v1</option><option selected>sel_20_v2</option></select>
+ </p>
+
+ <p>
+ Select multiple:
+
+ <select multiple name="msel_1"></select>
+ <select multiple name="msel_1b"><option></option></select>
+ <select multiple name="msel_1c"><option selected></option></select>
+
+ <select multiple name="msel_2"><option value="msel_2_v"></option></select>
+ <select multiple name="msel_3"><option selected value="msel_3_v"></option></select>
+
+ <select multiple name="msel_4"><option value="msel_4_v1"></option><option value="msel_4_v2"></option></select>
+ <select multiple name="msel_5"><option selected value="msel_5_v1"></option><option value="msel_5_v2"></option></select>
+ <select multiple name="msel_6"><option value="msel_6_v1"></option><option selected value="msel_6_v2"></option></select>
+ <select multiple name="msel_7"><option selected value="msel_7_v1"></option><option selected value="msel_7_v2"></option></select>
+
+ <select multiple name="msel_8"><option>msel_8_v1</option><option>msel_8_v2</option></select>
+ <select multiple name="msel_9"><option selected>msel_9_v1</option><option>msel_9_v2</option></select>
+ <select multiple name="msel_10"><option>msel_10_v1</option><option selected>msel_10_v2</option></select>
+ <select multiple name="msel_11"><option selected>msel_11_v1</option><option selected>msel_11_v2</option></select>
+
+ <select multiple name="msel_12"><option value="msel_12_v1">msel_12_v1_text</option><option value="msel_12_v2">msel_12_v2_text</option></select>
+ <select multiple name="msel_13"><option selected value="msel_13_v1">msel_13_v1_text</option><option value="msel_13_v2">msel_13_v2_text</option></select>
+ <select multiple name="msel_14"><option value="msel_14_v1">msel_14_v1_text</option><option selected value="msel_14_v2">msel_14_v2_text</option></select>
+ <select multiple name="msel_15"><option selected value="msel_15_v1">msel_15_v1_text</option><option selected value="msel_15_v2">msel_15_v2_text</option></select>
+
+ <select multiple name="msel_16"><option>msel_16_v1</option><option>msel_16_v2</option><option>msel_16_v3</option></select>
+ <select multiple name="msel_17"><option selected>msel_17_v1</option><option>msel_17_v2</option><option>msel_17_v3</option></select>
+ <select multiple name="msel_18"><option>msel_18_v1</option><option selected>msel_18_v2</option><option>msel_18_v3</option></select>
+ <select multiple name="msel_19"><option selected>msel_19_v1</option><option selected>msel_19_v2</option><option>msel_19_v3</option></select>
+ <select multiple name="msel_20"><option>msel_20_v1</option><option>msel_20_v2</option><option selected>msel_20_v3</option></select>
+ <select multiple name="msel_21"><option selected>msel_21_v1</option><option>msel_21_v2</option><option selected>msel_21_v3</option></select>
+ <select multiple name="msel_22"><option>msel_22_v1</option><option selected>msel_22_v2</option><option selected>msel_22_v3</option></select>
+ <select multiple name="msel_23"><option selected>msel_23_v1</option><option selected>msel_23_v2</option><option selected>msel_23_v3</option></select>
+
+ <select multiple name="msel_24"><option disabled>msel_24_v1</option><option>msel_24_v2</option></select>
+ <select multiple name="msel_25"><option disabled selected>msel_25_v1</option><option>msel_25_v2</option></select>
+ <select multiple name="msel_26"><option disabled>msel_26_v1</option><option selected>msel_26_v2</option></select>
+ <select multiple name="msel_27"><option disabled selected>msel_27_v1</option><option selected>msel_27_v2</option></select>
+
+ <select multiple name="msel_28"><option>msel_28_v1</option><option disabled>msel_28_v2</option></select>
+ <select multiple name="msel_29"><option selected>msel_29_v1</option><option disabled>msel_29_v2</option></select>
+ <select multiple name="msel_30"><option>msel_30_v1</option><option disabled selected>msel_30_v2</option></select>
+ <select multiple name="msel_31"><option selected>msel_31_v1</option><option disabled selected>msel_31_v2</option></select>
+
+ <select multiple name="msel_32"><option disabled selected>msel_32_v1</option><option disabled selected>msel_32_v2</option></select>
+
+ <select multiple name=""><option>msel_33_v1</option><option>msel_33_v2</option></select>
+ <select multiple name=""><option selected>msel_34_v1</option><option>msel_34_v2</option></select>
+ <select multiple name=""><option>msel_35_v1</option><option selected>msel_35_v2</option></select>
+ <select multiple name=""><option selected>msel_36_v1</option><option selected>msel_36_v2</option></select>
+
+ <select multiple><option>msel_37_v1</option><option>msel_37_v2</option></select>
+ <select multiple><option selected>msel_38_v1</option><option>msel_38_v2</option></select>
+ <select multiple><option>msel_39_v1</option><option selected>msel_39_v2</option></select>
+ <select multiple><option selected>msel_40_v1</option><option selected>msel_40_v2</option></select>
+ </p>
+</form>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const placeholder_myFile1 = {};
+const placeholder_myFile2 = {};
+const placeholder_emptyFile = {};
+
+var myFile1, myFile2, emptyFile;
+let openerURL, opener;
+var gen;
+
+function bodyLoaded() {
+ openerURL = SimpleTest.getTestFileURL("formSubmission_chrome.js");
+ opener = SpecialPowers.loadChromeScript(openerURL);
+
+ let xhr = new XMLHttpRequest;
+ xhr.open("GET", "/dynamic/getMyDirectory.sjs", false);
+ xhr.send();
+ let basePath = xhr.responseText;
+
+ opener.addMessageListener("files.opened", onFilesOpened);
+ opener.sendAsyncMessage("files.open", [
+ basePath + "file_formSubmission_text.txt",
+ basePath + "file_formSubmission_img.jpg",
+ ]);
+
+ /*
+ * The below test function uses callbacks that invoke gen.next() rather than
+ * creating and resolving Promises. I'm trying to minimize churn since these
+ * changes want to be uplifted. Some kind soul might want to clean this all up
+ * at some point.
+ */
+
+ $("target_iframe").onload = function() { gen.next(); };
+}
+
+
+function onFilesOpened(files) {
+ let [textFile, imageFile] = files;
+ opener.destroy();
+
+ let singleFile = textFile;
+ let multiFile = [textFile, imageFile];
+
+ var addList = document.getElementsByClassName("setfile");
+ let i = 0;
+ var input;
+ while ((input = addList[i++])) {
+ if (input.classList.contains("multi")) {
+ SpecialPowers.wrap(input).mozSetFileArray(multiFile);
+ } else {
+ SpecialPowers.wrap(input).mozSetFileArray([singleFile]);
+ }
+ }
+
+ input = document.createElement("input");
+ input.type = "file";
+ input.multiple = true;
+ SpecialPowers.wrap(input).mozSetFileArray(multiFile);
+ myFile1 = input.files[0];
+ myFile2 = input.files[1];
+ is(myFile1.size, 20, "File1 size");
+ is(myFile2.size, 2711, "File2 size");
+ emptyFile = { name: "", type: "application/octet-stream" };
+
+ // Now, actually run the tests; see below.
+ runAllTestVariants();
+};
+
+var expectedSub = [
+ // Default input
+ { name: "n1_1", value: "v1_1" },
+ { name: "n1_2", value: "" },
+ { name: "n1_3", value: "" },
+ { name: "n1_7_\r\n_\r\n_\r\n_ _\"", value: "v1_7____ _\"" },
+ // Text input
+ { name: "n2_1", value: "v2_1" },
+ { name: "n2_2", value: "" },
+ { name: "n2_3", value: "" },
+ { name: "n2_7_\r\n_\r\n_\r\n_ _\"", value: "v2_7____ _\"" },
+ // Checkbox unchecked
+ // Checkbox checked
+ { name: "n4_1", value: "v4_1" },
+ { name: "n4_2", value: "" },
+ { name: "n4_3", value: "on" },
+ { name: "n4_7_\r\n_\r\n_\r\n_ _\"", value: "v4_7_\r\n_\r\n_\r\n_ _\"" },
+ // Radio unchecked
+ // Radio checked
+ { name: "n6_1", value: "v6_1" },
+ { name: "n6_2", value: "" },
+ { name: "n6_3", value: "on" },
+ { name: "n6_7_\r\n_\r\n_\r\n_ _\"", value: "v6_7_\r\n_\r\n_\r\n_ _\"" },
+ // Hidden input
+ { name: "n7_1", value: "v7_1" },
+ { name: "n7_2", value: "" },
+ { name: "n7_3", value: "" },
+ { name: "n7_7_\r\n_\r\n_\r\n_ _\"", value: "v7_7_\r\n_\r\n_\r\n_ _\"" },
+ // Password input
+ { name: "n8_1", value: "v8_1" },
+ { name: "n8_2", value: "" },
+ { name: "n8_3", value: "" },
+ { name: "n8_7_\r\n_\r\n_\r\n_ _\"", value: "v8_7____ _\"" },
+ // Submit input
+ // Button input
+ // Image input
+ // Reset input
+ // Unknown input
+ { name: "n13_1", value: "v13_1" },
+ { name: "n13_2", value: "" },
+ { name: "n13_3", value: "" },
+ { name: "n13_7_\r\n_\r\n_\r\n_ _\"", value: "v13_7____ _\"" },
+ // <input type='url'>
+ { name: "n19_1", value: "http://v19_1.org" },
+ { name: "n19_2", value: "" },
+ { name: "n19_3", value: "" },
+ { name: "n19_7_\r\n_\r\n_\r\n__\"", value: "http://v19_7_____\"" },
+ { name: "n19_8", value: "http://m\xf3zill\xe4.\xf3rg" },
+ // <input type='email'>
+ { name: "n20_1", value: "v20_1@bar" },
+ { name: "n20_2", value: "" },
+ { name: "n20_3", value: "" },
+ { name: "n20_7_\r\n_\r\n_\r\n__\"", value: "v20_7_____\"@bar" },
+ { name: "n20_8", value: "foo@mózillä.órg" },
+ // Default button
+ // Submit button
+ // Button button
+ // Reset button
+ // Unknown button
+ // File
+ { name: "file_1", value: placeholder_myFile1 },
+ { name: "file_2", value: placeholder_emptyFile },
+ // Multiple file
+ { name: "file_3", value: placeholder_myFile1 },
+ { name: "file_4", value: placeholder_myFile1 },
+ { name: "file_4", value: placeholder_myFile2 },
+ { name: "file_5", value: placeholder_emptyFile },
+ // Textarea
+ { name: "t1", value: "t_1_v" },
+ { name: "t2", value: "" },
+ { name: "t6", value: "t_6_v" },
+ { name: "t7", value: "t_7_v\r\n" },
+ { name: "t8", value: "\r\n t_8_v \r\n" },
+ { name: "t9_\r\n_\r\n_\r\n_ _\"", value: "t_9_\r\n_\r\n_\r\n_ _\"_v" },
+ { name: "t10", value: "t_10_v" },
+
+ // Select one
+ { name: "sel_1b", value: "" },
+ { name: "sel_1c", value: "" },
+ { name: "sel_2", value: "sel_2_v" },
+ { name: "sel_3", value: "sel_3_v" },
+ { name: "sel_4", value: "sel_4_v1" },
+ { name: "sel_5", value: "sel_5_v1" },
+ { name: "sel_6", value: "sel_6_v2" },
+ { name: "sel_7", value: "sel_7_v1" },
+ { name: "sel_8", value: "sel_8_v1" },
+ { name: "sel_9", value: "sel_9_v2" },
+ { name: "sel_10", value: "sel_10_v1" },
+ { name: "sel_11", value: "sel_11_v1" },
+ { name: "sel_12", value: "sel_12_v2" },
+ { name: "sel_13", value: "sel_13_v2" },
+ { name: "sel_15", value: "sel_15_v2" },
+ { name: "sel_16", value: "sel_16_v1" },
+ { name: "sel_17", value: "sel_17_v1" },
+ // Select three
+ { name: "msel_1c", value: "" },
+ { name: "msel_3", value: "msel_3_v" },
+ { name: "msel_5", value: "msel_5_v1" },
+ { name: "msel_6", value: "msel_6_v2" },
+ { name: "msel_7", value: "msel_7_v1" },
+ { name: "msel_7", value: "msel_7_v2" },
+ { name: "msel_9", value: "msel_9_v1" },
+ { name: "msel_10", value: "msel_10_v2" },
+ { name: "msel_11", value: "msel_11_v1" },
+ { name: "msel_11", value: "msel_11_v2" },
+ { name: "msel_13", value: "msel_13_v1" },
+ { name: "msel_14", value: "msel_14_v2" },
+ { name: "msel_15", value: "msel_15_v1" },
+ { name: "msel_15", value: "msel_15_v2" },
+ { name: "msel_17", value: "msel_17_v1" },
+ { name: "msel_18", value: "msel_18_v2" },
+ { name: "msel_19", value: "msel_19_v1" },
+ { name: "msel_19", value: "msel_19_v2" },
+ { name: "msel_20", value: "msel_20_v3" },
+ { name: "msel_21", value: "msel_21_v1" },
+ { name: "msel_21", value: "msel_21_v3" },
+ { name: "msel_22", value: "msel_22_v2" },
+ { name: "msel_22", value: "msel_22_v3" },
+ { name: "msel_23", value: "msel_23_v1" },
+ { name: "msel_23", value: "msel_23_v2" },
+ { name: "msel_23", value: "msel_23_v3" },
+ { name: "msel_26", value: "msel_26_v2" },
+ { name: "msel_27", value: "msel_27_v2" },
+ { name: "msel_29", value: "msel_29_v1" },
+ { name: "msel_31", value: "msel_31_v1" },
+];
+
+var expectedAugment = [
+ { name: "aName", value: "aValue" },
+ //{ name: "aNameBool", value: "false" },
+ { name: "aNameNum", value: "9.2" },
+ { name: "aNameFile1", value: placeholder_myFile1 },
+ { name: "aNameFile2", value: placeholder_myFile2 },
+ //{ name: "aNameObj", value: "[object XMLHttpRequest]" },
+ //{ name: "aNameNull", value: "null" },
+ //{ name: "aNameUndef", value: "undefined" },
+];
+
+function checkMPSubmission(sub, expected, test) {
+ function getPropCount(o) {
+ var x, l = 0;
+ for (x in o) ++l;
+ return l;
+ }
+ function mpquote_name(s) {
+ return s.replace(/\r?\n|\r/g, "%0D%0A")
+ .replace(/\"/g, "%22");
+ }
+ function mpquote_filename(s) {
+ return s.replace(/\r/g, "%0D")
+ .replace(/\n/g, "%0A")
+ .replace(/\"/g, "%22");
+ }
+
+ is(sub.length, expected.length,
+ "Correct number of multipart items in " + test);
+
+ if (sub.length != expected.length) {
+ alert(JSON.stringify(sub));
+ }
+
+ var i;
+ for (i = 0; i < expected.length; ++i) {
+ if (!("fileName" in expected[i])) {
+ is(sub[i].headers["Content-Disposition"],
+ "form-data; name=\"" + mpquote_name(expected[i].name) + "\"",
+ "Correct name in " + test);
+ is (getPropCount(sub[i].headers), 1,
+ "Wrong number of headers in " + test);
+ is(sub[i].body,
+ expected[i].value.replace(/\r\n|\r|\n/, "\r\n"),
+ "Correct value in " + test);
+ }
+ else {
+ is(sub[i].headers["Content-Disposition"],
+ "form-data; name=\"" + mpquote_name(expected[i].name) + "\"; filename=\"" +
+ mpquote_filename(expected[i].fileName) + "\"",
+ "Correct name in " + test);
+ is(sub[i].headers["Content-Type"],
+ expected[i].contentType,
+ "Correct content type in " + test);
+ is (getPropCount(sub[i].headers), 2,
+ "Wrong number of headers in " + test);
+ is(sub[i].body,
+ expected[i].value,
+ "Correct value in " + test);
+ }
+ }
+}
+
+function utf8encode(s) {
+ return unescape(encodeURIComponent(s));
+}
+
+function checkURLSubmission(sub, expected) {
+ function urlEscape(s) {
+ return escape(utf8encode(s)).replace(/%20/g, "+")
+ .replace(/\//g, "%2F")
+ .replace(/@/g, "%40");
+ }
+
+ subItems = sub.split("&");
+ is(subItems.length, expected.length,
+ "Correct number of url items");
+ var i;
+ for (i = 0; i < expected.length; ++i) {
+ let expect = urlEscape(expected[i].name) + "=" +
+ urlEscape(("fileName" in expected[i]) ? expected[i].fileName : expected[i].value);
+ is (subItems[i], expect, "expected URL part");
+ }
+}
+
+function checkPlainSubmission(sub, expected) {
+
+ is(sub,
+ expected.map(function(v) {
+ return v.name + "=" +
+ (("fileName" in v) ? v.fileName : v.value) +
+ "\r\n";
+ }).join(""),
+ "Correct submission");
+}
+
+function setDisabled(list, state) {
+ Array.prototype.forEach.call(list, function(e) {
+ e.disabled = state;
+ });
+}
+
+// Run the suite of tests for this variant, returning a Promise that will be
+// resolved when the batch completes. Then and only then runTestVariant may
+// be invoked to run a different variation.
+function runTestVariant(variantLabel) {
+ info("starting test variant: " + variantLabel);
+ return new Promise((resolve) => {
+ // Instantiate the generator.
+ gen = runTestVariantUsingWeirdGenDriver(resolve);
+ // Run the generator to the first yield, at which point it is self-driving.
+ gen.next();
+ });
+}
+function* runTestVariantUsingWeirdGenDriver(finishedVariant) {
+ // Set up the expectedSub array
+ fileReader1 = new FileReader;
+ fileReader1.readAsBinaryString(myFile1);
+ fileReader2 = new FileReader;
+ fileReader2.readAsBinaryString(myFile2);
+ fileReader1.onload = fileReader2.onload = function() { gen.next(); };
+ yield undefined; // Wait for both FileReaders. We don't care which order they finish.
+ yield undefined;
+ function fileFixup(o) {
+ if (o.value === placeholder_myFile1) {
+ o.value = fileReader1.result;
+ o.fileName = myFile1.name;
+ o.contentType = myFile1.type;
+ }
+ else if (o.value === placeholder_myFile2) {
+ o.value = fileReader2.result;
+ o.fileName = myFile2.name;
+ o.contentType = myFile2.type;
+ }
+ else if (o.value === placeholder_emptyFile) {
+ o.value = "";
+ o.fileName = emptyFile.name;
+ o.contentType = emptyFile.type;
+ }
+ };
+ expectedSub.forEach(fileFixup);
+ expectedAugment.forEach(fileFixup);
+
+ var form = $("form");
+
+ // multipart/form-data
+ var iframe = $("target_iframe");
+
+ // Make normal submission
+ form.action = "form_submit_server.sjs";
+ form.method = "POST";
+ form.enctype = "multipart/form-data";
+ form.submit();
+ yield undefined; // Wait for iframe to load as a result of the submission
+ var submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkMPSubmission(submission, expectedSub, "normal submission");
+
+ // Disabled controls
+ setDisabled(document.querySelectorAll("input, select, textarea"), true);
+ form.submit();
+ yield undefined;
+ submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkMPSubmission(submission, [], "disabled controls");
+
+ // Reenabled controls
+ setDisabled(document.querySelectorAll("input, select, textarea"), false);
+ form.submit();
+ yield undefined;
+ submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkMPSubmission(submission, expectedSub, "reenabled controls");
+
+ // text/plain
+ form.action = "form_submit_server.sjs?plain";
+ form.enctype = "text/plain";
+ form.submit();
+ yield undefined;
+ submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkPlainSubmission(submission, expectedSub);
+
+ // application/x-www-form-urlencoded
+ form.action = "form_submit_server.sjs?url";
+ form.enctype = "application/x-www-form-urlencoded";
+ form.submit();
+ yield undefined;
+ submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkURLSubmission(submission, expectedSub);
+
+ // application/x-www-form-urlencoded
+ form.action = "form_submit_server.sjs?xxyy";
+ form.method = "GET";
+ form.enctype = "";
+ form.submit();
+ yield undefined;
+ submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkURLSubmission(submission, expectedSub);
+
+ // application/x-www-form-urlencoded
+ form.action = "form_submit_server.sjs";
+ form.method = "";
+ form.enctype = "";
+ form.submit();
+ yield undefined;
+ submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkURLSubmission(submission, expectedSub);
+
+ // Send form using XHR and FormData
+ xhr = new XMLHttpRequest();
+ xhr.onload = function() { gen.next(); };
+ xhr.open("POST", "form_submit_server.sjs");
+ xhr.send(new FormData(form));
+ yield undefined; // Wait for XHR load
+ checkMPSubmission(JSON.parse(xhr.responseText), expectedSub, "send form using XHR and FormData");
+
+ // Send disabled form using XHR and FormData
+ setDisabled(document.querySelectorAll("input, select, textarea"), true);
+ xhr.open("POST", "form_submit_server.sjs");
+ xhr.send(new FormData(form));
+ yield undefined;
+ checkMPSubmission(JSON.parse(xhr.responseText), [], "send disabled form using XHR and FormData");
+ setDisabled(document.querySelectorAll("input, select, textarea"), false);
+
+ // Send FormData
+ function addToFormData(fd) {
+ fd.append("aName", "aValue");
+ fd.append("aNameNum", 9.2);
+ fd.append("aNameFile1", myFile1);
+ fd.append("aNameFile2", myFile2);
+ }
+ var fd = new FormData();
+ addToFormData(fd);
+ xhr.open("POST", "form_submit_server.sjs");
+ xhr.send(fd);
+ yield undefined;
+ checkMPSubmission(JSON.parse(xhr.responseText), expectedAugment, "send FormData");
+
+ // Augment <form> using FormData
+ fd = new FormData(form);
+ addToFormData(fd);
+ xhr.open("POST", "form_submit_server.sjs");
+ xhr.send(fd);
+ yield undefined;
+ checkMPSubmission(JSON.parse(xhr.responseText),
+ expectedSub.concat(expectedAugment), "send augmented FormData");
+
+ finishedVariant();
+}
+
+/**
+ * Install our service-worker (parameterized by appending "?MODE"), which will
+ * invoke skipWaiting() and clients.claim() to begin controlling us ASAP. We
+ * wait on the controllerchange event
+ */
+async function installAndBeControlledByServiceWorker(mode) {
+ const scriptURL = "sw_formSubmission.js?" + mode;
+ const controllerChanged = new Promise((resolve) => {
+ navigator.serviceWorker.addEventListener(
+ "controllerchange", () => { resolve(); }, { once: true });
+ });
+
+ info("installing ServiceWorker: " + scriptURL);
+ const swr = await navigator.serviceWorker.register(scriptURL,
+ { scope: "./" });
+ await controllerChanged;
+ ok(navigator.serviceWorker.controller.scriptURL.endsWith(scriptURL),
+ "controlled by the SW we expected");
+ info("became controlled by ServiceWorker.");
+
+ return swr;
+}
+
+async function runAllTestVariants() {
+ // Run the test as it has historically been run, with no ServiceWorkers
+ // anywhere!
+ await runTestVariant("no ServiceWorker");
+
+ // Uncomment the below if something in the test seems broken and you're not
+ // sure whether it's a side-effect of the multiple passes or not.
+ //await runTestVariant("no ServiceWorker second paranoia time");
+
+ // Ensure ServiceWorkers are enabled and that testing mode (which disables
+ // security checks) is on too.
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]});
+
+ // Now run the test with a ServiceWorker that covers the scope but has no
+ // fetch handler, so the optimization case will not actually dispatch a
+ // "fetch" event, but some stuff will happen that can change things enough
+ // to break them like in https://bugzilla.mozilla.org/show_bug.cgi?id=1383518.
+ await installAndBeControlledByServiceWorker("no-fetch");
+ await runTestVariant("ServiceWorker that does not listen for fetch events");
+
+ // Now the ServiceWorker does have a "fetch" event listener, but it will reset
+ // interception every time. This is similar to the prior case but different
+ // enough that it could break things in a different exciting way.
+ await installAndBeControlledByServiceWorker("reset-fetch");
+ await runTestVariant("ServiceWorker that resets all fetches");
+
+ // Now the ServiceWorker resolves the fetch event with `fetch(event.request)`
+ // which makes little sense but is a thing that can happen.
+ const swr = await installAndBeControlledByServiceWorker("proxy-fetch");
+ await runTestVariant("ServiceWorker that proxies all fetches");
+
+ // cleanup.
+ info("unregistering ServiceWorker");
+ await swr.unregister();
+ info("ServiceWorker uninstalled");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_formSubmission2.html b/dom/html/test/test_formSubmission2.html
new file mode 100644
index 0000000000..fc6b60cdcf
--- /dev/null
+++ b/dom/html/test/test_formSubmission2.html
@@ -0,0 +1,220 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=523771
+-->
+<head>
+ <title>Test for Bug 523771</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=523771">Mozilla Bug 523771</a>
+<p id="display"></p>
+<iframe name="target_iframe" id="target_iframe"></iframe>
+<form action="form_submit_server.sjs" target="target_iframe" id="form"
+ method="POST" enctype="multipart/form-data">
+ <table>
+ <tr>
+ <td>Control type</td>
+ <td>Name and value</td>
+ <td>Name, empty value</td>
+ <td>Name, no value</td>
+ <td>Empty name, with value</td>
+ <td>No name, with value</td>
+ <td>No name or value</td>
+ </tr>
+ <tr>
+ <td>Submit input</td>
+ <td><input type=submit name="n1_1" value="v1_1"></td>
+ <td><input type=submit name="n1_2" value=""></td>
+ <td><input type=submit name="n1_3"></td>
+ <td><input type=submit name="" value="v1_4"></td>
+ <td><input type=submit value="v1_5"></td>
+ <td><input type=submit></td>
+ </tr>
+ <tr>
+ <td>Image input</td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="n2_1" value="v2_1"></td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="n2_2" value=""></td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="n2_3"></td>
+ <td><input type=image src="file_formSubmission_img.jpg" name="" value="v2_4"></td>
+ <td><input type=image src="file_formSubmission_img.jpg" value="v2_5"></td>
+ <td><input type=image src="file_formSubmission_img.jpg"></td>
+ </tr>
+ <tr>
+ <td>Submit button</td>
+ <td><button type=submit name="n3_1" value="v3_1"></button></td>
+ <td><button type=submit name="n3_2" value=""></button></td>
+ <td><button type=submit name="n3_3"></button></td>
+ <td><button type=submit name="" value="v3_4"></button></td>
+ <td><button type=submit value="v3_5"></button></td>
+ <td><button type=submit ></button></td>
+ </tr>
+ <tr>
+ <td>Submit button with text</td>
+ <td><button type=submit name="n4_1" value="v4_1">text here</button></td>
+ <td><button type=submit name="n4_2" value="">text here</button></td>
+ <td><button type=submit name="n4_3">text here</button></td>
+ <td><button type=submit name="" value="v4_4">text here</button></td>
+ <td><button type=submit value="v4_5">text here</button></td>
+ <td><button type=submit>text here</button></td>
+ </tr>
+ </table>
+</form>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var gen = runTest();
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ gen.next();
+});
+
+var expectedSub = [
+ // Submit input
+ [{ name: "n1_1", value: "v1_1" }],
+ [{ name: "n1_2", value: "" }],
+ [{ name: "n1_3", value: "Submit Query" }],
+ [],
+ [],
+ [],
+ // Image input
+ [{ name: "n2_1.x", value: "10" },
+ { name: "n2_1.y", value: "7" }],
+ [{ name: "n2_2.x", value: "10" },
+ { name: "n2_2.y", value: "7" }],
+ [{ name: "n2_3.x", value: "10" },
+ { name: "n2_3.y", value: "7" }],
+ [{ name: "x", value: "10" },
+ { name: "y", value: "7" }],
+ [{ name: "x", value: "10" },
+ { name: "y", value: "7" }],
+ [{ name: "x", value: "10" },
+ { name: "y", value: "7" }],
+ // Submit button
+ [{ name: "n3_1", value: "v3_1" }],
+ [{ name: "n3_2", value: "" }],
+ [{ name: "n3_3", value: "" }],
+ [],
+ [],
+ [],
+ // Submit button with text
+ [{ name: "n4_1", value: "v4_1" }],
+ [{ name: "n4_2", value: "" }],
+ [{ name: "n4_3", value: "" }],
+ [],
+ [],
+ [],
+];
+
+function checkSubmission(sub, expected) {
+ function getPropCount(o) {
+ var x, l = 0;
+ for (x in o) ++l;
+ return l;
+ }
+
+ is(sub.length, expected.length,
+ "Correct number of items");
+ var i;
+ for (i = 0; i < expected.length; ++i) {
+ if (!("fileName" in expected[i])) {
+ is(sub[i].headers["Content-Disposition"],
+ "form-data; name=\"" + expected[i].name + "\"",
+ "Correct name");
+ is (getPropCount(sub[i].headers), 1,
+ "Wrong number of headers");
+ }
+ else {
+ is(sub[i].headers["Content-Disposition"],
+ "form-data; name=\"" + expected[i].name + "\"; filename=\"" +
+ expected[i].fileName + "\"",
+ "Correct name");
+ is(sub[i].headers["Content-Type"],
+ expected[i].contentType,
+ "Correct content type");
+ is (getPropCount(sub[i].headers), 2,
+ "Wrong number of headers");
+ }
+ is(sub[i].body,
+ expected[i].value,
+ "Correct value");
+ }
+}
+
+function clickImage(aTarget, aX, aY)
+{
+ aTarget.style.position = "absolute";
+ aTarget.style.top = "0";
+ aTarget.style.left = "0";
+ aTarget.offsetTop;
+
+ var wu = SpecialPowers.getDOMWindowUtils(aTarget.ownerDocument.defaultView);
+
+ wu.sendMouseEvent('mousedown', aX, aY, 0, 1, 0);
+ wu.sendMouseEvent('mouseup', aX, aY, 0, 0, 0);
+
+ aTarget.style.position = "";
+ aTarget.style.top = "";
+ aTarget.style.left = "";
+}
+
+function* runTest() {
+ // Make normal submission
+ var form = $("form");
+ var iframe = $("target_iframe");
+ iframe.onload = function() { gen.next(); };
+
+ var elements = form.querySelectorAll("input, button");
+
+ is(elements.length, expectedSub.length,
+ "tests vs. expected out of sync");
+
+ var i;
+ for (i = 0; i < elements.length && i < expectedSub.length; ++i) {
+ elem = elements[i];
+ if (elem.localName != "input" || elem.type != "image") {
+ elem.click();
+ }
+ else {
+ clickImage(elem, 10, 7);
+ }
+ yield undefined;
+
+ var submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkSubmission(submission, expectedSub[i]);
+ }
+
+ // Disabled controls
+ var i;
+ for (i = 0; i < elements.length && i < expectedSub.length; ++i) {
+ elem = elements[i];
+ form.onsubmit = function() {
+ elem.disabled = true;
+ }
+ if (elem.localName != "input" || elem.type != "image") {
+ elem.click();
+ }
+ else {
+ clickImage(elem, 10, 7);
+ }
+ yield undefined;
+
+ is(elem.disabled, true, "didn't disable");
+ elem.disabled = false;
+ form.onsubmit = undefined;
+
+ var submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
+ checkSubmission(submission, []);
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_formelements.html b/dom/html/test/test_formelements.html
new file mode 100644
index 0000000000..b753759bcb
--- /dev/null
+++ b/dom/html/test/test_formelements.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=772869
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 772869</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772869">Mozilla Bug 772869</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <form id="f">
+ <input name="x">
+ <input type="image" name="a">
+ <input type="file" name="y">
+ <input type="submit" name="z">
+ <input id="w">
+ <input name="w">
+ </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 772869 **/
+var x = $("f").elements;
+x.something = "another";
+names = [];
+for (var name in x) {
+ names.push(name);
+}
+is(names.length, 9, "Should have 9 enumerated names");
+is(names[0], "0", "Enum entry 1");
+is(names[1], "1", "Enum entry 2");
+is(names[2], "2", "Enum entry 3");
+is(names[3], "3", "Enum entry 4");
+is(names[4], "4", "Enum entry 5");
+is(names[5], "something", "Enum entry 6");
+is(names[6], "namedItem", "Enum entry 7");
+is(names[7], "item", "Enum entry 8");
+is(names[8], "length", "Enum entry 9");
+
+names = Object.getOwnPropertyNames(x);
+is(names.length, 10, "Should have 10 items");
+// Now sort entries 5 through 8, for comparison purposes. We don't sort the
+// whole array, because we want to make sure the ordering between the parts
+// is correct
+temp = names.slice(5, 9);
+temp.sort();
+names.splice.bind(names, 5, 4).apply(null, temp);
+is(names.length, 10, "Should still have 10 items");
+is(names[0], "0", "Entry 1")
+is(names[1], "1", "Entry 2")
+is(names[2], "2", "Entry 3")
+is(names[3], "3", "Entry 4")
+is(names[4], "4", "Entry 5")
+is(names[5], "w", "Entry 6")
+is(names[6], "x", "Entry 7")
+is(names[7], "y", "Entry 8")
+is(names[8], "z", "Entry 9")
+is(names[9], "something", "Entry 10")
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_fragment_form_pointer.html b/dom/html/test/test_fragment_form_pointer.html
new file mode 100644
index 0000000000..ff40385455
--- /dev/null
+++ b/dom/html/test/test_fragment_form_pointer.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=946585
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 946585</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=946585">Mozilla Bug 946585</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<form><div id="formdiv"></div></form>
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+/** Test for Bug 946585 **/
+var formDiv = document.getElementById("formdiv");
+formDiv.innerHTML = '<form>';
+is(formDiv.firstChild, null, "InnerHTML should not produce form element because the div has a form pointer.");
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_frame_count_with_synthetic_doc.html b/dom/html/test/test_frame_count_with_synthetic_doc.html
new file mode 100644
index 0000000000..c282ed09d7
--- /dev/null
+++ b/dom/html/test/test_frame_count_with_synthetic_doc.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function getWindowLength() {
+ setTimeout(function() {
+ if (window.length) {
+ ok(false, "Synthetic document shouldn't be exposed");
+ }
+
+ // Keep running this check until the test stops
+ getWindowLength();
+ });
+ }
+
+ function addObject() {
+ const object = document.createElement("object");
+ object.data = 'file_bug417760.png';
+ document.body.appendChild(object);
+
+ object.onload = function() {
+ ok(true, "Test passes");
+ SimpleTest.finish();
+ }
+ }
+
+ getWindowLength();
+ addObject();
+ </script>
+ </body>
+</html>
diff --git a/dom/html/test/test_getElementsByName_after_mutation.html b/dom/html/test/test_getElementsByName_after_mutation.html
new file mode 100644
index 0000000000..f88b8d579e
--- /dev/null
+++ b/dom/html/test/test_getElementsByName_after_mutation.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1376695
+-->
+<head>
+ <title>Test for Bug 1376695</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1376695">Mozilla Bug 1376695</a>
+<p id="display"></p>
+<div id="originalFoo" name="foo">
+<pre id="test">
+<script type="application/javascript">
+
+/** Test to ensure that the list returned by getElementsByName is updated after
+ * mutations.
+ **/
+
+var fooList = document.getElementsByName("foo");
+var originalDiv = document.getElementById("originalFoo");
+
+is(fooList.length, 1, "Should find one element with name 'foo' initially");
+is(fooList[0], originalDiv, "Element should be the original div");
+
+var newTree = document.createElement("p");
+var child1 = document.createElement("div");
+var child2 = document.createElement("div");
+child2.setAttribute("name", "foo");
+
+newTree.appendChild(child1);
+newTree.appendChild(child2);
+document.body.appendChild(newTree);
+
+is(fooList.length, 2,
+ "Should find two elements with name 'foo' after appending the new tree");
+is(fooList[1], child2, "Element should be the new appended div with name 'foo'");
+
+document.body.removeChild(newTree);
+
+is(fooList.length, 1,
+ "Should find one element with name 'foo' after removing the new tree");
+is(fooList[0], originalDiv,
+ "Element should be the original div after removing the new tree");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_hidden.html b/dom/html/test/test_hidden.html
new file mode 100644
index 0000000000..7b9d488c0e
--- /dev/null
+++ b/dom/html/test/test_hidden.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=567663
+-->
+<head>
+ <title>Test for Bug 567663</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=567663">Mozilla Bug 567663</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <p></p>
+ <p hidden></p>
+</div>
+<pre id="test">
+<script>
+/** Test for Bug 567663 **/
+var ps = document.getElementById("content").getElementsByTagName("p");
+is(ps[0].hidden, false, "First p's IDL attribute was wrong.");
+is(ps[0].hasAttribute("hidden"), false, "First p had a content attribute.");
+is(ps[1].hidden, true, "Second p's IDL attribute was wrong.");
+is(ps[1].hasAttribute("hidden"), true,
+ "Second p didn't have a content attribute.");
+is(ps[1].getAttribute("hidden"), "",
+ "Second p's content attribute was wrong.");
+
+ps[0].hidden = true;
+is(ps[0].getAttribute("hidden"), "",
+ "Content attribute was set to an incorrect value.");
+ps[1].hidden = false;
+is(ps[1].hasAttribute("hidden"), false,
+ "Second p still had a content attribute.");
+
+ps[0].setAttribute("hidden", "banana");
+is(ps[0].hidden, true, "p's IDL attribute was wrong after setting.");
+is(ps[0].getAttribute("hidden"), "banana", "Content attribute changed.");
+
+ps[0].setAttribute("hidden", "false");
+is(ps[0].hidden, true, "p's IDL attribute was wrong after setting.");
+is(ps[0].getAttribute("hidden"), "false", "Content attribute changed.");
+
+ps[0].removeAttribute("hidden");
+is(ps[0].hidden, false,
+ "p's IDL attribute was wrong after removing the content attribute.");
+is(ps[0].hasAttribute("hidden"), false);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_html_attributes_reflection.html b/dom/html/test/test_html_attributes_reflection.html
new file mode 100644
index 0000000000..a3d6c63121
--- /dev/null
+++ b/dom/html/test/test_html_attributes_reflection.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLHtmlElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLHtmlElement attributes reflection **/
+
+// .version
+reflectString({
+ element: document.createElement("html"),
+ attribute: "version",
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_htmlcollection.html b/dom/html/test/test_htmlcollection.html
new file mode 100644
index 0000000000..2d91189f6b
--- /dev/null
+++ b/dom/html/test/test_htmlcollection.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=772869
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 772869</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772869">Mozilla Bug 772869</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <a class="foo" name="x"></a>
+ <span class="foo" id="y"></span>
+ <span class="foo" name="x"></span>
+ <form class="foo" name="z" id="w"></form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 772869 **/
+var x = document.getElementsByClassName("foo");
+x.something = "another";
+var names = [];
+for (var name in x) {
+ names.push(name);
+}
+is(names.length, 8, "Should have 8 enumerated names");
+is(names[0], "0", "Enum entry 1")
+is(names[1], "1", "Enum entry 2")
+is(names[2], "2", "Enum entry 3")
+is(names[3], "3", "Enum entry 4")
+is(names[4], "something", "Enum entry 5")
+is(names[5], "item", "Enum entry 6")
+is(names[6], "namedItem", "Enum entry 7")
+is(names[7], "length", "Enum entry 8");
+
+names = Object.getOwnPropertyNames(x);
+is(names.length, 9, "Should have 9 items");
+is(names[0], "0", "Entry 1")
+is(names[1], "1", "Entry 2")
+is(names[2], "2", "Entry 3")
+is(names[3], "3", "Entry 4")
+is(names[4], "x", "Entry 5")
+is(names[5], "y", "Entry 6")
+is(names[6], "w", "Entry 7")
+is(names[7], "z", "Entry 8")
+is(names[8], "something", "Entry 9")
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_general.html b/dom/html/test/test_iframe_sandbox_general.html
new file mode 100644
index 0000000000..625b7aeeb2
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_general.html
@@ -0,0 +1,283 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs - general tests
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 341604 and Bug 766282</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs - general tests **/
+
+SimpleTest.expectAssertions(0, 1);
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestCompleteLog();
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event)
+{
+ ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+ ok(result, desc);
+
+ completedTests++;
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (completedTests == 32) {
+ is(passedTests, completedTests, "There are " + completedTests + " general tests that should pass");
+ SimpleTest.finish();
+ }
+}
+
+function doTest() {
+ // passes twice if good
+ // 1) test that inline scripts (<script>) can run in an iframe sandboxed with "allow-scripts"
+ // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+ // passes twice if good
+ // 2) test that <script src=...> can run in an iframe sandboxed with "allow-scripts"
+ // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+ // passes twice if good
+ // 3) test that script in an event listener (body onload) can run in an iframe sandboxed with "allow-scripts"
+ // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+ // passes twice if good
+ // 4) test that script in an javascript:url can run in an iframe sandboxed with "allow-scripts"
+ // (done in file_iframe_sandbox_c_if1.html which has 'allow-scripts')
+
+ // fails if bad
+ // 5) test that inline scripts cannot run in an iframe sandboxed without "allow-scripts"
+ // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+ // fails if bad
+ // 6) test that <script src=...> cannot run in an iframe sandboxed without "allow-scripts"
+ // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+ // fails if bad
+ // 7) test that script in an event listener (body onload) cannot run in an iframe sandboxed without "allow-scripts"
+ // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+ // fails if bad
+ // 8) test that script in an event listener (img onerror) cannot run in an iframe sandboxed without "allow-scripts"
+ // (done in file_iframe_sandbox_c_if2.html which has sandbox='')
+
+ // fails if bad
+ // 9) test that script in an javascript:url cannot run in an iframe sandboxed without "allow-scripts"
+ // (done in file_iframe_sandbox_c_if_5.html which has sandbox='allow-same-origin')
+ var if_w = document.getElementById('if_5').contentWindow;
+ sendMouseEvent({type:'click'}, 'a_link', if_w);
+
+ // passes if good
+ // 10) test that a new iframe has sandbox attribute
+ var ifr = document.createElement("iframe");
+ ok_wrapper("sandbox" in ifr, "a new iframe should have a sandbox attribute");
+
+ // passes if good
+ // 11) test that the sandbox attribute's default stringyfied value is an empty string
+ ok_wrapper(ifr.sandbox.length === 0 && ifr.sandbox == "", "default sandbox attribute should be an empty string");
+
+ // passes if good
+ // 12) test that a sandboxed iframe with 'allow-forms' can submit forms
+ // (done in file_iframe_sandbox_c_if3.html which has 'allow-forms' and 'allow-scripts')
+
+ // fails if bad
+ // 13) test that a sandboxed iframe without 'allow-forms' can NOT submit forms
+ // (done in file_iframe_sandbox_c_if1.html which only has 'allow-scripts')
+
+ // fails if bad
+ // 14) test that a sandboxed iframe can't open a new window using the target.attribute
+ // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+ // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+ // function that calls window.parent.ok_wrapper
+
+ // passes if good
+ // 15) test that a sandboxed iframe can't open a new window using window.open
+ // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+ // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+ // function that calls window.parent.ok_wrapper
+
+ // passes if good
+ // 16) test that a sandboxed iframe can't open a new window using window.ShowModalDialog
+ // this is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+ // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+ // function that calls window.parent.ok_wrapper
+
+ // passes twice if good
+ // 17) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+ // is separated with two spaces
+ // done via file_iframe_sandbox_c_if6.html which is sandboxed with " allow-scripts allow-same-origin "
+
+ // passes twice if good
+ // 18) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+ // is separated with tabs
+ // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x09;allow-scripts&#x09;allow-same-origin&#x09;"
+
+ // passes twice if good
+ // 19) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+ // is separated with line feeds
+ // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0a;allow-scripts&#x0a;allow-same-origin&#x0a;"
+
+ // passes twice if good
+ // 20) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+ // is separated with form feeds
+ // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0c;allow-scripts&#x0c;allow-same-origin&#x0c;"
+
+ // passes twice if good
+ // 21) test that a sandboxed iframe can access same-origin documents and run scripts when its sandbox attribute
+ // is separated with carriage returns
+ // done via file_iframe_sandbox_c_if6.html which is sandboxed with "&#x0d;allow-scripts&#x0d;allow-same-origin&#x0d;"
+
+ // fails if bad
+ // 22) test that an iframe with sandbox="" does NOT have script in a src attribute created by a javascript:
+ // URL executed
+ // done by this page, see if_7
+
+ // passes if good
+ // 23) test that an iframe with sandbox="allow-scripts" DOES have script in a src attribute created by a javascript:
+ // URL executed
+ // done by this page, see if_8
+
+ // fails if bad
+ // 24) test that an iframe with sandbox="", starting out with a document already loaded, does NOT have script in a newly
+ // set src attribute created by a javascript: URL executed
+ // done by this page, see if_9
+
+ // passes if good
+ // 25) test that an iframe with sandbox="allow-scripts", starting out with a document already loaded, DOES have script
+ // in a newly set src attribute created by a javascript: URL executed
+ // done by this page, see if_10
+
+ // passes if good or fails if bad
+ // 26) test that an sandboxed document without 'allow-same-origin' can NOT access indexedDB
+ // done via file_iframe_sandbox_c_if7.html, which has sandbox='allow-scripts'
+
+ // passes if good or fails if bad
+ // 27) test that an sandboxed document with 'allow-same-origin' can access indexedDB
+ // done via file_iframe_sandbox_c_if8.html, which has sandbox='allow-scripts allow-same-origin'
+
+ // fails if bad
+ // 28) Test that a sandboxed iframe can't open a new window using the target.attribute for a
+ // non-existing browsing context (BC341604).
+ // This is done via file_iframe_sandbox_c_if4.html which is sandboxed with "allow-scripts" and "allow-same-origin"
+ // the window it attempts to open calls window.opener.ok(false, ...) and file_iframe_c_if4.html has an ok()
+ // function that calls window.parent.ok_wrapper.
+
+ // passes twice if good
+ // 29-32) Test that sandboxFlagsAsString returns the set flags.
+ // see if_14 and if_15
+
+ // passes once if good
+ // 33) Test that sandboxFlagsAsString returns null if iframe does not have sandbox flag set.
+ // see if_16
+}
+
+addLoadEvent(doTest);
+
+var started_if_9 = false;
+var started_if_10 = false;
+
+function start_if_9() {
+ if (started_if_9)
+ return;
+
+ started_if_9 = true;
+ sendMouseEvent({type:'click'}, 'a_button');
+}
+
+function start_if_10() {
+ if (started_if_10)
+ return;
+
+ started_if_10 = true;
+ sendMouseEvent({type:'click'}, 'a_button2');
+}
+
+function do_if_9() {
+ var if_9 = document.getElementById('if_9');
+ if_9.src = 'javascript:"<html><script>window.parent.ok_wrapper(false, \'an iframe sandboxed without allow-scripts should not execute script in a javascript URL in a newly set src attribute\');<\/script><\/html>"';
+}
+
+function do_if_10() {
+ var if_10 = document.getElementById('if_10');
+ if_10.src = 'javascript:"<html><script>window.parent.ok_wrapper(true, \'an iframe sandboxed with allow-scripts should execute script in a javascript URL in a newly set src attribute\');<\/script><\/html>"';
+}
+
+function eqFlags(a, b) {
+ // both a and b should be either null or have the array same flags
+ if (a === null && b === null) { return true; }
+ if (a === null || b === null) { return false; }
+ if (a.length !== b.length) { return false; }
+ var a_sorted = a.sort();
+ var b_sorted = b.sort();
+ for (var i in a_sorted) {
+ if (a_sorted[i] !== b_sorted[i]) { return false; }
+ }
+ return true;
+}
+
+function getSandboxFlags(doc) {
+ var flags = doc.sandboxFlagsAsString;
+ if (flags === null) { return null; }
+ return flags? flags.split(" "):[];
+}
+
+function test_sandboxFlagsAsString(name, expected) {
+ var ifr = document.getElementById(name);
+ try {
+ var flags = getSandboxFlags(SpecialPowers.wrap(ifr).contentDocument);
+ ok_wrapper(eqFlags(flags, expected), name + ' expected: "' + expected + '", got: "' + flags + '"');
+ } catch (e) {
+ ok_wrapper(false, name + ' expected "' + expected + ', but failed with ' + e);
+ }
+}
+
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-same-origin allow-scripts" id="if_1" src="file_iframe_sandbox_c_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="aLlOw-SAME-oRiGin ALLOW-sCrIpTs" id="if_1_case_insensitive" src="file_iframe_sandbox_c_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="" id="if_2" src="file_iframe_sandbox_c_if2.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-forms allow-scripts" id="if_3" src="file_iframe_sandbox_c_if3.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id="if_4" src="file_iframe_sandbox_c_if4.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_5" src="file_iframe_sandbox_c_if5.html" height="10" width="10"></iframe>
+<iframe sandbox=" allow-same-origin allow-scripts " id="if_6_a" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x09;allow-same-origin&#x09;allow-scripts&#x09;" id="if_6_b" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x0a;allow-same-origin&#x0a;allow-scripts&#x0a;" id="if_6_c" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x0c;allow-same-origin&#x0c;allow-scripts&#x0c;" id="if_6_d" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="&#x0d;allow-same-origin&#x0d;allow-scripts&#x0d;" id="if_6_e" src="file_iframe_sandbox_c_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id='if_7' src="javascript:'<html><script>window.parent.ok_wrapper(false, \'an iframe sandboxed without allow-scripts should not execute script in a javascript URL in its src attribute\');<\/script><\/html>';" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id='if_8' src="javascript:'<html><script>window.parent.ok_wrapper(true, \'an iframe sandboxed without allow-scripts should execute script in a javascript URL in its src attribute\');<\/script><\/html>';" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" onload='start_if_9()' id='if_9' src="about:blank" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" onload='start_if_10()' id='if_10' src="about:blank" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id='if_11' src="file_iframe_sandbox_c_if7.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id='if_12' src="file_iframe_sandbox_c_if8.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-top-navigation " id='if_13' src="file_iframe_sandbox_c_if9.html" height="10" width="10" onload='test_sandboxFlagsAsString("if_13",["allow-forms", "allow-pointer-lock", "allow-popups", "allow-same-origin", "allow-scripts", "allow-top-navigation"])'></iframe>
+<iframe sandbox="&#x09;allow-same-origin&#x09;allow-scripts&#x09;" id="if_14" src="file_iframe_sandbox_c_if6.html" height="10" width="10" onload='test_sandboxFlagsAsString("if_14",["allow-same-origin","allow-scripts"])'></iframe>
+<iframe sandbox="" id="if_15" src="file_iframe_sandbox_c_if9.html" height="10" width="10" onload='test_sandboxFlagsAsString("if_15",[])'></iframe>
+<iframe id="if_16" src="file_iframe_sandbox_c_if9.html" height="10" width="10" onload='test_sandboxFlagsAsString("if_16",null)'></iframe>
+<input type='button' id="a_button" onclick='do_if_9()'>
+<input type='button' id="a_button2" onclick='do_if_10()'>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_inheritance.html b/dom/html/test/test_iframe_sandbox_inheritance.html
new file mode 100644
index 0000000000..991e7ef78f
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_inheritance.html
@@ -0,0 +1,202 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs - inheritance tests
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
+/** Inheritance Tests **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+// A postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// It expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok().
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event) {
+ switch (event.data.type) {
+ case "attempted":
+ testAttempted();
+ break;
+ case "ok":
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ break;
+ default:
+ // allow for old style message
+ if (event.data.ok != undefined) {
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ }
+ }
+}
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 15;
+var totalTestsToAttempt = 19;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+ ok(result, desc);
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (addToAttempted) {
+ testAttempted();
+ }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+ attemptedTests++;
+ if (attemptedTests == totalTestsToAttempt) {
+ // Make sure all tests have had a chance to complete.
+ setTimeout(function() {finish();}, 1000);
+ }
+}
+
+var finishCalled = false;
+
+function finish() {
+ if (!finishCalled) {
+ finishCalled = true;
+ is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " inheritance tests that should pass");
+
+ SimpleTest.finish();
+ }
+}
+
+function doTest() {
+ // fails if bad
+ // 1) an iframe with no sandbox attribute inside an iframe that has sandbox = ""
+ // should not be able to execute scripts (cannot ever loosen permissions)
+ // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if1.html)
+ testAttempted();
+
+ // fails if bad
+ // 2) an iframe with sandbox = "allow-scripts" inside an iframe that has sandbox = ""
+ // should not be able to execute scripts (cannot ever loosen permissions)
+ // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if1.html)
+ testAttempted();
+
+ // passes if good and fails if bad
+ // 3) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
+ // should not be same origin with the top window
+ // (done by file_iframe_sandbox_a_if4.html contained within file_iframe_sandbox_a_if3.html)
+
+ // passes if good and fails if bad
+ // 4) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
+ // should not be same origin with its parent
+ // (done by file_iframe_sandbox_a_if4.html contained within file_iframe_sandbox_a_if3.html)
+
+ // passes if good
+ // 5) an iframe with 'allow-same-origin' and 'allow-scripts' inside an iframe with 'allow-same-origin'
+ // and 'allow-scripts' should be same origin with the top window
+ // (done by file_iframe_sandbox_a_if6.html contained within file_iframe_sandbox_a_if5.html)
+
+ // passes if good
+ // 6) an iframe with 'allow-same-origin' and 'allow-scripts' inside an iframe with 'allow-same-origin'
+ // and 'allow-scripts' should be same origin with its parent
+ // (done by file_iframe_sandbox_a_if6.html contained within file_iframe_sandbox_a_if5.html)
+
+ // passes if good
+ // 7) an iframe with no sandbox attribute inside an iframe that has sandbox = "allow-scripts"
+ // should be able to execute scripts
+ // (done by file_iframe_sandbox_a_if7.html contained within file_iframe_sandbox_a_if3.html)
+
+ // fails if bad
+ // 8) an iframe with sandbox="" inside an iframe that has allow-scripts should not be able
+ // to execute scripts
+ // (done by file_iframe_sandbox_a_if2.html contained within file_iframe_sandbox_a_if3.html)
+ testAttempted();
+
+ // passes if good
+ // 9) make sure that changing the sandbox flags on an iframe (if_8) doesn't affect
+ // the sandboxing of subloads of content within that iframe
+ var if_8 = document.getElementById('if_8');
+ if_8.sandbox = 'allow-scripts';
+ if_8.contentWindow.doSubload();
+
+ // passes if good
+ // 10) a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+ // origin with this document
+ // done by file_iframe_sandbox_a_if11.html which is contained with file_iframe_sandbox_a_if10.html
+
+ // passes if good
+ // 11) a <frame> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+ // origin with its parent frame or this document
+ // done by file_iframe_sandbox_a_if12.html which is contained with file_iframe_sandbox_a_if11.html
+
+ // passes if good, fails if bad
+ // 12) An <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+ // origin with this document
+ // Done by file_iframe_sandbox_a_if14.html which is contained within file_iframe_sandbox_a_if13.html
+
+ // passes if good, fails if bad
+ // 13) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+ // origin with its parent frame or this document
+ // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
+
+ // passes if good, fails if bad
+ // 14) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+ // origin with its parent frame or this document
+ // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
+ // which is contained within file_iframe_sandbox_a_if10.html
+
+ // passes if good
+ // 15) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
+ // should be able to submit forms.
+ // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
+
+ // passes if good
+ // 16) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
+ // should be able to submit forms.
+ // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
+ // which is contained within file_iframe_sandbox_a_if10.html
+
+ // fails if bad
+ // 17) An <object> inside an <iframe> sandboxed with 'allow-same-origin'
+ // should not be able to run scripts.
+ // Done by iframe "if_no_scripts", which loads file_iframe_sandbox_srcdoc_no_allow_scripts.html.
+ testAttempted();
+
+ // passes if good
+ // 18) An <object> inside an <iframe> sandboxed with 'allow-scripts allow-same-origin'
+ // should be able to run scripts and be same origin with this document.
+ // Done by iframe "if_scripts", which loads file_iframe_sandbox_srcdoc_allow_scripts.html.
+
+ // passes if good, fails if bad
+ // 19) Make sure that the parent's document's sandboxing flags are copied when
+ // changing the sandbox flags on an iframe inside an iframe.
+ // Done in file_iframe_sandbox_a_if17.html and file_iframe_sandbox_a_if18.html
+}
+
+addLoadEvent(doTest);
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="" id="if_1" src="file_iframe_sandbox_a_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_3" src="file_iframe_sandbox_a_if3.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_5" src="file_iframe_sandbox_a_if5.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_8" src="file_iframe_sandbox_a_if8.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-forms" id="if_10" src="file_iframe_sandbox_a_if10.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-forms" id="if_13" src="file_iframe_sandbox_a_if13.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_no_scripts" srcdoc="<object data='file_iframe_sandbox_srcdoc_no_allow_scripts.html'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_scripts" srcdoc="<object data='file_iframe_sandbox_srcdoc_allow_scripts.html'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_17" src="file_iframe_sandbox_a_if17.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_navigation.html b/dom/html/test/test_iframe_sandbox_navigation.html
new file mode 100644
index 0000000000..caaf4439b8
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_navigation.html
@@ -0,0 +1,285 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604 - navigation</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
+/** Navigation tests Part 1**/
+
+SimpleTest.requestLongerTimeout(2); // slow on Android
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin'/other windows to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+var bc = new BroadcastChannel("test_iframe_sandbox_navigation");
+bc.addEventListener("message", receiveMessage);
+window.addEventListener("message", receiveMessage);
+
+var testPassesReceived = 0;
+
+function receiveMessage(event) {
+ switch (event.data.type) {
+ case "attempted":
+ testAttempted();
+ break;
+ case "ok":
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ break;
+ case "if_10":
+ doIf10TestPart2();
+ break;
+ default:
+ // allow for old style message
+ if (event.data.ok != undefined) {
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ }
+ }
+}
+
+// Open windows for tests to attempt to navigate later.
+var windowsToClose = new Array();
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 7;
+var totalTestsToAttempt = 13;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+ ok(result, desc);
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (addToAttempted) {
+ testAttempted();
+ }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+ attemptedTests++;
+ if (attemptedTests == totalTestsToAttempt) {
+ // Make sure all tests have had a chance to complete.
+ setTimeout(function() {finish();}, 1000);
+ }
+}
+
+var finishCalled = false;
+
+function finish() {
+ if (!finishCalled) {
+ finishCalled = true;
+ is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " navigation tests that should pass");
+
+ closeWindows();
+
+ bc.close();
+
+ SimpleTest.finish();
+ }
+}
+
+function checkTestsFinished() {
+ // If our own finish() has not been called, probably failed due to a timeout, so close remaining windows.
+ if (!finishCalled) {
+ closeWindows();
+ }
+}
+
+function closeWindows() {
+ for (var i = 0; i < windowsToClose.length; i++) {
+ windowsToClose[i].close();
+ }
+}
+
+function doTest() {
+ // passes if good
+ // 1) A sandboxed iframe is allowed to navigate itself
+ // (done by file_iframe_sandbox_d_if1.html which has 'allow-scripts' and navigates to
+ // file_iframe_sandbox_navigation_pass.html).
+
+ // passes if good
+ // 2) A sandboxed iframe is allowed to navigate its children, even if they are sandboxed
+ // (done by file_iframe_sandbox_d_if2.html which has 'allow-scripts', it navigates a child
+ // iframe containing file_iframe_sandbox_navigation_start.html to file_iframe_sandbox_navigation_pass.html).
+
+ // fails if bad
+ // 3) A sandboxed iframe is not allowed to navigate its ancestor
+ // (done by file_iframe_sandbox_d_if4.html contained within file_iframe_sandbox_d_if3.html,
+ // it attempts to navigate file_iframe_sandbox_d_if3.html to file_iframe_sandbox_navigation_fail.html).
+
+ // fails if bad
+ // 4) A sandboxed iframe is not allowed to navigate its sibling
+ // (done by file_iframe_sandbox_d_if5.html which has 'allow scripts allow-same-origin'
+ // and attempts to navigate file_iframe_navigation_start.html contained in if_sibling on this
+ // page to file_iframe_sandbox_navigation_fail.html).
+
+ // passes if good, fails if bad
+ // 5) When a link is clicked in a sandboxed iframe, the document navigated to is sandboxed
+ // the same as the original document and is not same origin with parent document
+ // (done by file_iframe_sandbox_d_if6.html which simulates a link click and navigates
+ // to file_iframe_sandbox_d_if7.html which attempts to call back into its parent).
+
+ // fails if bad
+ // 6) An iframe (if_8) has sandbox="allow-same-origin allow-scripts", the sandboxed document
+ // (file_iframe_sandbox_d_if_8.html) that it contains accesses its parent (this file) and removes
+ // 'allow-same-origin' and then triggers a reload.
+ // The document should not be able to access its parent (this file).
+
+ // fails if bad
+ // 7) An iframe (if_9) has sandbox="allow-same-origin allow-scripts", the sandboxed document
+ // (file_iframe_sandbox_d_if_9.html) that it contains accesses its parent (this file) and removes
+ // 'allow-scripts' and then triggers a reload.
+ // The document should not be able to run a script and access its parent (this file).
+
+ // passes if good
+ // 8) a document in an iframe with sandbox='allow-scripts' should have a different null
+ // principal in its original document than a document to which it navigates itself
+ // file_iframe_sandbox_d_if_10.html does this, co-ordinating with this page via postMessage
+
+ // passes if good
+ // 9) a document (file_iframe_sandbox_d_if11.html in an iframe (if_11) with sandbox='allow-scripts'
+ // is navigated to file_iframe_sandbox_d_if12.html - when that document loads
+ // a message is sent back to this document, which adds 'allow-same-origin' to if_11 and then
+ // calls .back on it - file_iframe_sandbox_if12.html should be able to call back into this
+ // document - this is all contained in file_iframe_sandbox_d_if13.html which is opened in another
+ // tab so it has its own isolated session history
+ window.open("file_iframe_sandbox_d_if13.html");
+
+ // open up the top navigation tests
+
+ // fails if bad
+ // 10) iframe with sandbox='allow-scripts' can NOT navigate top
+ // file_iframe_sandbox_e_if1.html contains file_iframe_sandbox_e_if6.html which
+ // attempts to navigate top
+ windowsToClose.push(window.open("file_iframe_sandbox_e_if1.html"));
+
+ // fails if bad
+ // 11) iframe with sandbox='allow-scripts' nested inside iframe with
+ // 'allow-top-navigation allow-scripts' can NOT navigate top
+ // file_iframe_sandbox_e_if2.html contains file_iframe_sandbox_e_if1.html which
+ // contains file_iframe_sandbox_e_if6.html which attempts to navigate top
+ windowsToClose.push(window.open("file_iframe_sandbox_e_if2.html"));
+
+ // passes if good
+ // 12) iframe with sandbox='allow-top-navigation allow-scripts' can navigate top
+ // file_iframe_sandbox_e_if3.html contains file_iframe_sandbox_e_if5.html which navigates top
+ window.open("file_iframe_sandbox_e_if3.html");
+
+ // passes if good
+ // 13) iframe with sandbox='allow-top-navigation allow-scripts' nested inside an iframe with
+ // 'allow-top-navigation allow-scripts' can navigate top
+ // file_iframe_sandbox_e_if4.html contains file_iframe_sandbox_e_if3.html which contains
+ // file_iframe_sandbox_e_if5.html which navigates top
+ window.open("file_iframe_sandbox_e_if4.html");
+}
+
+addLoadEvent(doTest);
+
+window.modified_if_8 = false;
+
+function reload_if_8() {
+ var if_8 = document.getElementById('if_8');
+ if_8.src = 'file_iframe_sandbox_d_if8.html';
+}
+
+function modify_if_8() {
+ // If this is the second time this has been called
+ // that's a failed test (allow-same-origin was removed
+ // the first time).
+ if (window.modified_if_8) {
+ ok_wrapper(false, "a sandboxed iframe from which 'allow-same-origin' was removed should not be able to access its parent");
+
+ // need to return here since we end up in an infinite loop otherwise
+ return;
+ }
+
+ var if_8 = document.getElementById('if_8');
+ window.modified_if_8 = true;
+
+ if_8.sandbox = 'allow-scripts';
+ testAttempted();
+ sendMouseEvent({type:'click'}, 'a_button');
+}
+
+window.modified_if_9 = false;
+
+function reload_if_9() {
+ var if_9 = document.getElementById('if_9');
+ if_9.src = 'file_iframe_sandbox_d_if9.html';
+}
+
+function modify_if_9() {
+ // If this is the second time this has been called
+ // that's a failed test (allow-scripts was removed
+ // the first time).
+ if (window.modified_if_9) {
+ ok_wrapper(false, "an sandboxed iframe from which 'allow-scripts' should be removed should not be able to access its parent via a script", false);
+
+ // need to return here since we end up in an infinite loop otherwise
+ return;
+ }
+
+ var if_9 = document.getElementById('if_9');
+ window.modified_if_9 = true;
+
+ if_9.sandbox = 'allow-same-origin';
+
+ testAttempted();
+ sendMouseEvent({type:'click'}, 'a_button2');
+}
+
+var firstPrincipal = "";
+var secondPrincipal;
+
+function doIf10TestPart1() {
+ if (firstPrincipal != "")
+ return;
+
+ // use SpecialPowers to get the principal of if_10.
+ // NB: We stringify here and below because special-powers wrapping doesn't
+ // preserve identity.
+ var if_10 = document.getElementById('if_10');
+ firstPrincipal = SpecialPowers.wrap(if_10).contentDocument.nodePrincipal.origin;
+ if_10.src = 'file_iframe_sandbox_d_if10.html';
+}
+
+function doIf10TestPart2() {
+ var if_10 = document.getElementById('if_10');
+ // use SpecialPowers to get the principal of if_10
+ secondPrincipal = SpecialPowers.wrap(if_10).contentDocument.nodePrincipal.origin;
+ ok_wrapper(firstPrincipal != secondPrincipal, "documents should NOT have the same principal if they are sandboxed without" +
+ " allow-same-origin and the first document is navigated to the second");
+}
+</script>
+<body onunload="checkTestsFinished()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-scripts" id="if_1" src="file_iframe_sandbox_d_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_2" src="file_iframe_sandbox_d_if2.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_3" src="file_iframe_sandbox_d_if3.html" height="10" width="10"></iframe>
+<iframe id="if_sibling" name="if_sibling" src="about:blank" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_5" src="file_iframe_sandbox_d_if5.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_6" src="file_iframe_sandbox_d_if6.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id="if_8" src="file_iframe_sandbox_d_if8.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id="if_9" src="file_iframe_sandbox_d_if9.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_10" src="file_iframe_sandbox_navigation_start.html" onload='doIf10TestPart1()' height="10" width="10"></iframe>
+</div>
+<input type='button' id="a_button" onclick='reload_if_8()'>
+<input type='button' id="a_button2" onclick='reload_if_9()'>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_navigation2.html b/dom/html/test/test_iframe_sandbox_navigation2.html
new file mode 100644
index 0000000000..f17c23a458
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_navigation2.html
@@ -0,0 +1,216 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604 - navigation</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
+/** Navigation tests Part 2**/
+
+SimpleTest.expectAssertions(0);
+SimpleTest.requestLongerTimeout(2); // slow on Android
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin'/other windows to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+var bc = new BroadcastChannel("test_iframe_sandbox_navigation");
+bc.addEventListener("message", receiveMessage);
+window.addEventListener("message", receiveMessage);
+
+var testPassesReceived = 0;
+
+function receiveMessage(event) {
+ switch (event.data.type) {
+ case "attempted":
+ testAttempted();
+ break;
+ case "ok":
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ break;
+ default:
+ // allow for old style message
+ if (event.data.ok != undefined) {
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ }
+ }
+}
+
+// Open windows for tests to attempt to navigate later.
+var windowsToClose = new Array();
+windowsToClose.push(window.open("about:blank", "window_to_navigate"));
+windowsToClose.push(window.open("about:blank", "window_to_navigate2"));
+var iframesWithWindowsToClose = new Array();
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 12;
+var totalTestsToAttempt = 15;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+ ok(result, desc);
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (addToAttempted) {
+ testAttempted();
+ }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+ attemptedTests++;
+ if (attemptedTests == totalTestsToAttempt) {
+ // Make sure all tests have had a chance to complete.
+ setTimeout(function() {finish();}, 1000);
+ }
+}
+
+var finishCalled = false;
+
+function finish() {
+ if (!finishCalled) {
+ finishCalled = true;
+ is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " navigation tests that should pass");
+
+ for (var i = 0; i < windowsToClose.length; i++) {
+ windowsToClose[i].close();
+ }
+
+ bc.close();
+
+ SimpleTest.finish();
+ }
+}
+
+function checkTestsFinished() {
+ // If our own finish() has not been called, probably failed due to a timeout, so close remaining windows.
+ if (!finishCalled) {
+ for (var i = 0; i < windowsToClose.length; i++) {
+ windowsToClose[i].close();
+ }
+ }
+}
+
+function doTest() {
+ // fails if bad
+ // 14) iframe with sandbox='allow-same-origin allow-scripts allow-top-navigation' should not
+ // be able to navigate another window (opened by another browsing context) using its name.
+ // file_iframe_sandbox_d_if14.html in if_14 attempts to navigate "window_to_navigate",
+ // which has been opened in preparation.
+
+ // fails if bad
+ // 15) iframe with sandbox='allow-scripts' should not be able to navigate top using its
+ // real name (instead of _top) as allow-top-navigation is not specified.
+ // file_iframe_sandbox_e_if7.html contains file_iframe_sandbox_e_if8.html, which
+ // attempts to navigate top by name.
+ windowsToClose.push(window.open("file_iframe_sandbox_e_if7.html"));
+
+ // fails if bad
+ // 16) iframe with sandbox='allow-same-origin allow-scripts allow-top-navigation' should not
+ // be able to use its parent's name (instead of _parent) to navigate it, when it is not top.
+ // (Note: this would apply to other ancestors that are not top as well.)
+ // file_iframe_sandbox_d_if15.html in if_15 contains file_iframe_sandbox_d_if16.html, which
+ // tries to navigate if_15 by its name (if_parent).
+
+ // passes if good, fails if bad
+ // 17) A sandboxed iframe is allowed to navigate itself using window.open().
+ // (Done by file_iframe_sandbox_d_if17.html which has 'allow-scripts' and navigates to
+ // file_iframe_sandbox_navigation_pass.html).
+
+ // passes if good, fails if bad
+ // 18) A sandboxed iframe is allowed to navigate its children with window.open(), even if
+ // they are sandboxed. (Done by file_iframe_sandbox_d_if18.html which has 'allow-scripts',
+ // it navigates a child iframe to file_iframe_sandbox_navigation_pass.html).
+
+ // passes if good, fails if bad
+ // 19) A sandboxed iframe is not allowed to navigate its ancestor with window.open().
+ // (Done by file_iframe_sandbox_d_if20.html contained within file_iframe_sandbox_d_if19.html,
+ // it attempts to navigate file_iframe_sandbox_d_if19.html to file_iframe_sandbox_navigation_fail.html).
+
+ // passes if good, fails if bad
+ // 20) iframe with sandbox='allow-same-origin allow-scripts allow-top-navigation' should not
+ // be able to navigate another window (opened by another browsing context) using window.open(..., "<name>").
+ // file_iframe_sandbox_d_if14.html in if_14 attempts to navigate "window_to_navigate2",
+ // which has been opened in preparation, using window.open(..., "window_to_navigate2").
+
+ // passes if good, fails if bad
+ // 21) iframe with sandbox='allow-same-origin allow-scripts allow-top-navigation' should not
+ // be able to use its parent's name (not _parent) to navigate it using window.open(), when it is not top.
+ // (Note: this would apply to other ancestors that are not top as well.)
+ // file_iframe_sandbox_d_if21.html in if_21 contains file_iframe_sandbox_d_if22.html, which
+ // tries to navigate if_21 by its name (if_parent2).
+
+ // passes if good, fails if bad
+ // 22) iframe with sandbox='allow-top-navigation allow-scripts' can navigate top with window.open().
+ // file_iframe_sandbox_e_if9.html contains file_iframe_sandbox_e_if11.html which navigates top.
+ window.open("file_iframe_sandbox_e_if9.html");
+
+ // passes if good, fails if bad
+ // 23) iframe with sandbox='allow-top-navigation allow-scripts' nested inside an iframe with
+ // 'allow-top-navigation allow-scripts' can navigate top, with window.open().
+ // file_iframe_sandbox_e_if10.html contains file_iframe_sandbox_e_if9.html which contains
+ // file_iframe_sandbox_e_if11.html which navigates top.
+ window.open("file_iframe_sandbox_e_if10.html");
+
+ // passes if good, fails if bad
+ // 24) iframe with sandbox='allow-scripts' can NOT navigate top with window.open().
+ // file_iframe_sandbox_e_if12.html contains file_iframe_sandbox_e_if14.html which navigates top.
+ window.open("file_iframe_sandbox_e_if12.html");
+
+ // passes if good, fails if bad
+ // 25) iframe with sandbox='allow-scripts' nested inside an iframe with
+ // 'allow-top-navigation allow-scripts' can NOT navigate top, with window.open(..., "_top").
+ // file_iframe_sandbox_e_if13.html contains file_iframe_sandbox_e_if12.html which contains
+ // file_iframe_sandbox_e_if14.html which navigates top.
+ window.open("file_iframe_sandbox_e_if13.html");
+
+ // passes if good, fails if bad
+ // 26) iframe with sandbox='allow-scripts' should not be able to navigate top using its real name
+ // (not with _top e.g. window.open(..., "topname")) as allow-top-navigation is not specified.
+ // file_iframe_sandbox_e_if15.html contains file_iframe_sandbox_e_if16.html, which
+ // attempts to navigate top by name using window.open().
+ window.open("file_iframe_sandbox_e_if15.html");
+
+ // passes if good
+ // 27) iframe with sandbox='allow-scripts allow-popups' should be able to
+ // navigate a window, that it has opened, using it's name.
+ // file_iframe_sandbox_d_if23.html in if_23 opens a window and then attempts
+ // to navigate it using it's name in the target of an anchor.
+ iframesWithWindowsToClose.push("if_23");
+
+ // passes if good, fails if bad
+ // 28) iframe with sandbox='allow-scripts allow-popups' should be able to
+ // navigate a window, that it has opened, using window.open(..., "<name>").
+ // file_iframe_sandbox_d_if23.html in if_23 opens a window and then attempts
+ // to navigate it using it's name in the target of window.open().
+}
+
+addLoadEvent(doTest);
+</script>
+<body onunload="checkTestsFinished()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-same-origin allow-scripts allow-top-navigation" id="if_14" src="file_iframe_sandbox_d_if14.html" height="10" width="10"></iframe>
+<iframe id="if_15" name="if_parent" src="file_iframe_sandbox_d_if15.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_17" src="file_iframe_sandbox_d_if17.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_18" src="file_iframe_sandbox_d_if18.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_19" src="file_iframe_sandbox_d_if19.html" height="10" width="10"></iframe>
+<iframe id="if_21" name="if_parent2" src="file_iframe_sandbox_d_if21.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-popups" id="if_23" src="file_iframe_sandbox_d_if23.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_popups.html b/dom/html/test/test_iframe_sandbox_popups.html
new file mode 100644
index 0000000000..c05b1fc67f
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_popups.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=766282
+implement allow-popups directive for iframe sandbox
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 766282</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event)
+{
+ ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+ ok(result, desc);
+
+ completedTests++;
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (completedTests == 3) {
+ is(passedTests, completedTests, "There are " + completedTests + " popups tests that should pass");
+ SimpleTest.finish();
+ }
+}
+
+function doTest() {
+ // passes if good
+ // 1) Test that a sandboxed iframe with "allow-popups" can open a new window using the target.attribute.
+ // This is done via file_iframe_sandbox_h_if1.html which is sandboxed with "allow-popups allow-scripts allow-same-origin".
+ // The window it attempts to open calls window.opener.ok(true, ...) and file_iframe_h_if1.html has an ok()
+ // function that calls window.parent.ok_wrapper.
+
+ // passes if good
+ // 2) Test that a sandboxed iframe with "allow-popups" can open a new window using window.open.
+ // This is done via file_iframe_sandbox_h_if1.html which is sandboxed with "allow-popups allow-scripts allow-same-origin".
+ // The window it attempts to open calls window.opener.ok(true, ...) and file_iframe_h_if1.html has an ok()
+ // function that calls window.parent.ok_wrapper.
+
+ // passes if good, fails if bad
+ // 3) Test that a sandboxed iframe with "allow-popups" can open a new window using the target.attribute
+ // for a non-existing browsing context (BC766282).
+ // This is done via file_iframe_sandbox_h_if1.html which is sandboxed with "allow-popups allow-scripts allow-same-origin".
+ // The window it attempts to open calls window.opener.ok(true, ...) and file_iframe_h_if1.html has an ok()
+ // function that calls window.parent.ok_wrapper.
+}
+
+addLoadEvent(doTest);
+
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=766282">Mozilla Bug 766282</a> - implement allow-popups directive for iframe sandbox
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-popups allow-same-origin allow-scripts" id="if1" src="file_iframe_sandbox_h_if1.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_popups_inheritance.html b/dom/html/test/test_iframe_sandbox_popups_inheritance.html
new file mode 100644
index 0000000000..af4a03932e
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_popups_inheritance.html
@@ -0,0 +1,157 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=766282
+Implement HTML5 sandbox allow-popuos directive for IFRAMEs - inheritance tests
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 766282</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script type="application/javascript">
+
+SimpleTest.expectAssertions(0, 5);
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+// A postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event) {
+ switch (event.data.type) {
+ case "attempted":
+ testAttempted();
+ break;
+ case "ok":
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ break;
+ default:
+ // allow for old style message
+ if (event.data.ok != undefined) {
+ ok_wrapper(event.data.ok, event.data.desc, event.data.addToAttempted);
+ }
+ }
+}
+
+var iframesWithWindowsToClose = new Array();
+
+var attemptedTests = 0;
+var passedTests = 0;
+var totalTestsToPass = 15;
+var totalTestsToAttempt = 21;
+
+function ok_wrapper(result, desc, addToAttempted = true) {
+ ok(result, desc);
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (addToAttempted) {
+ testAttempted();
+ }
+}
+
+// Added so that tests that don't register unless they fail,
+// can at least notify that they've attempted to run.
+function testAttempted() {
+ attemptedTests++;
+ if (attemptedTests == totalTestsToAttempt) {
+ // Make sure all tests have had a chance to complete.
+ setTimeout(function() {finish();}, 1000);
+ }
+}
+
+var finishCalled = false;
+
+function finish() {
+ if (!finishCalled) {
+ finishCalled = true;
+ is(passedTests, totalTestsToPass, "There are " + totalTestsToPass + " inheritance tests that should pass");
+
+ closeWindows();
+
+ SimpleTest.finish();
+ }
+}
+
+function checkTestsFinished() {
+ // If our own finish() has not been called, probably failed due to a timeout, so close remaining windows.
+ if (!finishCalled) {
+ closeWindows();
+ }
+}
+
+function closeWindows() {
+ for (var i = 0; i < iframesWithWindowsToClose.length; i++) {
+ document.getElementById(iframesWithWindowsToClose[i]).contentWindow.postMessage({type: "closeWindows"}, "*");
+ }
+}
+
+function doTest() {
+ // passes if good and fails if bad
+ // 1,2,3) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+ // allow-same-origin" should not have its origin sandbox flag set and be able to access document.cookie.
+ // (Done by file_iframe_sandbox_k_if5.html opened from file_iframe_sandbox_k_if4.html)
+ // This is repeated for 3 different ways of opening the window,
+ // see file_iframe_sandbox_k_if4.html for details.
+
+ // passes if good
+ // 4,5,6) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+ // allow-top-navigation" should not have its top-level navigation sandbox flag set and be able to
+ // navigate top. (Done by file_iframe_sandbox_k_if5.html (and if6) opened from
+ // file_iframe_sandbox_k_if4.html). This is repeated for 3 different ways of opening the window,
+ // see file_iframe_sandbox_k_if4.html for details.
+
+ // passes if good
+ // 7,8,9) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups
+ // all-forms" should not have its forms sandbox flag set and be able to submit forms.
+ // (Done by file_iframe_sandbox_k_if7.html opened from file_iframe_sandbox_k_if4.html)
+ // This is repeated for 3 different ways of opening the window,
+ // see file_iframe_sandbox_k_if4.html for details.
+
+ // passes if good
+ // 10,11,12) Make sure that the sandbox flags copied to a new browsing context are taken from the
+ // current active document not the browsing context (iframe / docShell).
+ // This is done by removing allow-same-origin and calling doSubOpens from file_iframe_sandbox_k_if8.html,
+ // which opens file_iframe_sandbox_k_if9.html in 3 different ways.
+ // It then navigates to file_iframe_sandbox_k_if1.html to run tests 13 - 21 below.
+ var if_8_1 = document.getElementById('if_8_1');
+ if_8_1.sandbox = 'allow-scripts allow-popups';
+ if_8_1.contentWindow.doSubOpens();
+
+ // passes if good and fails if bad
+ // 13,14,15) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups"
+ // should have its origin sandbox flag set and not be able to access document.cookie.
+ // This is done by file_iframe_sandbox_k_if8.html navigating to file_iframe_sandbox_k_if1.html
+ // after allow-same-origin has been removed from iframe if_8_1. file_iframe_sandbox_k_if1.html
+ // opens file_iframe_sandbox_k_if2.html in 3 different ways to perform the tests.
+ iframesWithWindowsToClose.push("if_8_1");
+
+ // fails if bad
+ // 16,17,18) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups"
+ // should have its forms sandbox flag set and not be able to submit forms.
+ // This is done by file_iframe_sandbox_k_if2.html, see test 10 for details of how this is opened.
+
+ // fails if bad
+ // 19,20,21) A window opened from inside an iframe that has sandbox = "allow-scripts allow-popups"
+ // should have its top-level navigation sandbox flag set and not be able to navigate top.
+ // This is done by file_iframe_sandbox_k_if2.html, see test 10 for details of how this is opened.
+}
+
+addLoadEvent(doTest);
+</script>
+
+<body onunload="checkTestsFinished()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=766282">Mozilla Bug 766282</a> - Implement HTML5 sandbox allow-popups directive for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-scripts allow-popups allow-same-origin allow-forms allow-top-navigation" id="if_4" src="file_iframe_sandbox_k_if4.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-popups allow-same-origin" id="if_8_1" src="file_iframe_sandbox_k_if8.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_redirect.html b/dom/html/test/test_iframe_sandbox_redirect.html
new file mode 100644
index 0000000000..ff13e52487
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_redirect.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=985135
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 985135</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 985135 **/
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ try {
+ var doc = frames[0].document;
+ ok(false, "Should not be able to get the document");
+ isnot(doc.body.textContent.slice(0, -1), "I have been redirected",
+ "Should not happen");
+ SimpleTest.finish();
+ } catch (e) {
+ // Check that we got the right document
+ window.onmessage = function(event) {
+ is(event.data, "who are you? redirect target",
+ "Should get the message we expect");
+ SimpleTest.finish();
+ }
+
+ frames[0].postMessage("who are you?", "*");
+ }
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=985135">Mozilla Bug 985135</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe src="file_iframe_sandbox_redirect.html" sandbox="allow-scripts"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_refresh.html b/dom/html/test/test_iframe_sandbox_refresh.html
new file mode 100644
index 0000000000..81107fe3dc
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_refresh.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1156059
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Bug 1156059</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ // Tests for Bug 1156059
+ // See ok messages in iframes for test cases.
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("We cannot detect when the sandbox blocks the META REFRESH, so we need to allow a reasonable amount of time for them to fail.");
+
+ var testCases = [
+ {
+ desc: "Meta refresh without allow-scripts should be ignored.",
+ numberOfLoads: 0,
+ numberOfLoadsExpected: 1
+ },
+ {
+ desc: "Meta refresh check should be case insensitive.",
+ numberOfLoads: 0,
+ numberOfLoadsExpected: 1
+ },
+ {
+ desc: "Meta refresh with allow-scripts should work.",
+ numberOfLoads: 0,
+ numberOfLoadsExpected: 2
+ },
+ {
+ desc: "Refresh HTTP headers should not be affected by sandbox.",
+ numberOfLoads: 0,
+ numberOfLoadsExpected: 2
+ }
+ ];
+
+ var totalLoads = 0;
+ var totalLoadsExpected = testCases.reduce(function(partialSum, testCase) {
+ return partialSum + testCase.numberOfLoadsExpected;
+ }, 0);
+
+ function processLoad(testCaseIndex) {
+ testCases[testCaseIndex].numberOfLoads++;
+
+ if (++totalLoads == totalLoadsExpected) {
+ // Give the tests that should block the refresh a bit of extra time to
+ // fail. The worst that could happen here is that we get a false pass.
+ window.setTimeout(processResults, 500);
+ }
+ }
+
+ function processResults() {
+ testCases.forEach(function(testCase, index) {
+ var msg = "Test Case " + index + ": " + testCase.desc;
+ is(testCase.numberOfLoads, testCase.numberOfLoadsExpected, msg);
+ });
+
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1156059">Mozilla Bug 1156059</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<iframe
+ onload="processLoad(0)"
+ srcdoc="<meta http-equiv='refresh' content='0; url=data:text/html,Refreshed'>"
+ sandbox="allow-forms allow-pointer-lock allow-popups allow-same-origin allow-top-navigation"
+></iframe>
+
+<iframe
+ onload="processLoad(1)"
+ srcdoc="<meta http-equiv='rEfReSh' content='0; url=data:text/html,Refreshed'>"
+ sandbox="allow-forms allow-pointer-lock allow-popups allow-same-origin allow-top-navigation"
+></iframe>
+
+<iframe
+ onload="processLoad(2)"
+ srcdoc="<meta http-equiv='refresh' content='0; url=data:text/html,Refreshed'>"
+ sandbox="allow-scripts"
+></iframe>
+
+<iframe
+ onload="processLoad(3)"
+ src="file_iframe_sandbox_refresh.html"
+ sandbox
+></iframe>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_iframe_sandbox_same_origin.html b/dom/html/test/test_iframe_sandbox_same_origin.html
new file mode 100644
index 0000000000..b936453bbd
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_same_origin.html
@@ -0,0 +1,108 @@
+\<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs - same origin tests
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
+/** Same Origin Tests **/
+
+SimpleTest.waitForExplicitFinish();
+
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+ ok(result, desc);
+
+ completedTests++;
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (completedTests == 14) {
+ is(passedTests, completedTests, "There are " + completedTests + " same-origin tests that should pass");
+
+ SimpleTest.finish();
+ }
+}
+
+function receiveMessage(event)
+{
+ ok_wrapper(event.data.ok, event.data.desc);
+}
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage);
+
+function doTest() {
+ // 1) test that we can't access an iframe sandboxed without "allow-same-origin"
+ var if_1 = document.getElementById("if_1");
+ try {
+ var b = if_1.contentDocument.body;
+ ok_wrapper(false, "accessing body of a sandboxed document should not be allowed");
+ } catch (err){
+ ok_wrapper(true, "accessing body of a sandboxed document should not be allowed");
+ }
+
+ // 2) test that we can access an iframe sandboxed with "allow-same-origin"
+ var if_2 = document.getElementById("if_2");
+
+ try {
+ var b = if_2.contentDocument.body;
+ ok_wrapper(true, "accessing body of a sandboxed document with allow-same-origin should be allowed");
+ } catch (err) {
+ ok_wrapper(false, "accessing body of a sandboxed document with allow-same-origin should be allowed");
+ }
+
+ // 3) test that a sandboxed iframe without 'allow-same-origin' cannot access its parent
+ // this is done by file_iframe_b_if3.html which has 'allow-scripts' but not 'allow-same-origin'
+
+ // 4) test that a sandboxed iframe with 'allow-same-origin' can access its parent
+ // this is done by file_iframe_b_if2.html which has 'allow-same-origin' and 'allow-scripts'
+
+ // 5) check that a sandboxed iframe with "allow-same-origin" can access document.cookie
+ // this is done by file_iframe_b_if2.html which has 'allow-same-origin' and 'allow-scripts'
+
+ // 6) check that a sandboxed iframe with "allow-same-origin" can access window.localStorage
+ // this is done by file_iframe_b_if2.html which has 'allow-same-origin' and 'allow-scripts'
+
+ // 7) check that a sandboxed iframe with "allow-same-origin" can access window.sessionStorage
+ // this is done by file_iframe_b_if2.html which has 'allow-same-origin' and 'allow-scripts'
+
+ // 8) check that a sandboxed iframe WITHOUT "allow-same-origin" can NOT access document.cookie
+ // this is done by file_iframe_b_if3.html which has 'allow-scripts' but not 'allow-same-origin'
+
+ // 9) check that a sandboxed iframe WITHOUT "allow-same-origin" can NOT access window.localStorage
+ // this is done by file_iframe_b_if3.html which has 'allow-scripts' but not 'allow-same-origin'
+
+ // 10) check that a sandboxed iframe WITHOUT "allow-same-origin" can NOT access window.sessionStorage
+ // this is done by file_iframe_b_if3.html which has 'allow-scripts' but not 'allow-same-origin'
+
+ // 11) check that XHR works normally in a sandboxed iframe with "allow-same-origin" and "allow-scripts"
+ // this is done by file_iframe_b_if2.html which has 'allow-same-origin' and 'allow-scripts'
+
+ // 12) check that XHR is blocked in a sandboxed iframe with "allow-scripts" but WITHOUT "allow-same-origin"
+ // this is done by file_iframe_b_if3.html which has 'allow-scripts' but not 'allow-same-origin'
+}
+addLoadEvent(doTest);
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="" id="if_1" src="file_iframe_sandbox_b_if1.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin allow-scripts" id="if_2" src="file_iframe_sandbox_b_if2.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_3" src="file_iframe_sandbox_b_if3.html" height="10" width="10"></iframe>
+</div>
diff --git a/dom/html/test/test_iframe_sandbox_workers.html b/dom/html/test/test_iframe_sandbox_workers.html
new file mode 100644
index 0000000000..c86f2ab528
--- /dev/null
+++ b/dom/html/test/test_iframe_sandbox_workers.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=341604
+Implement HTML5 sandbox attribute for IFRAMEs - tests for workers
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 341604</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+/** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs - test for workers **/
+
+SimpleTest.waitForExplicitFinish();
+
+// a postMessage handler that is used by sandboxed iframes without
+// 'allow-same-origin' to communicate pass/fail back to this main page.
+// it expects to be called with an object like {ok: true/false, desc:
+// <description of the test> which it then forwards to ok()
+window.addEventListener("message", receiveMessage);
+
+function receiveMessage(event)
+{
+ ok_wrapper(event.data.ok, event.data.desc);
+}
+
+var completedTests = 0;
+var passedTests = 0;
+
+function ok_wrapper(result, desc) {
+ ok(result, desc);
+
+ completedTests++;
+
+ if (result) {
+ passedTests++;
+ }
+
+ if (completedTests == 3) {
+ is(passedTests, 3, "There are 3 worker tests that should pass");
+ SimpleTest.finish();
+ }
+}
+
+function doTest() {
+ // passes if good
+ // 1) test that a worker in a sandboxed iframe with 'allow-scripts' can be loaded
+ // from a data: URI
+ // (done by file_iframe_sandbox_g_if1.html)
+
+ // passes if good
+ // 2) test that a worker in a sandboxed iframe with 'allow-scripts' can be loaded
+ // from a blob URI created by the sandboxed document itself
+ // (done by file_iframe_sandbox_g_if1.html)
+
+ // passes if good
+ // 3) test that a worker in a sandboxed iframe with 'allow-scripts' without
+ // 'allow-same-origin' cannot load a script via a relative URI
+ // (done by file_iframe_sandbox_g_if1.html)
+}
+
+addLoadEvent(doTest);
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-scripts" id="if_1" src="file_iframe_sandbox_g_if1.html" height="10" width="10"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/html/test/test_imageSrcSet.html b/dom/html/test/test_imageSrcSet.html
new file mode 100644
index 0000000000..695d1c2643
--- /dev/null
+++ b/dom/html/test/test_imageSrcSet.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=980243
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 980243</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 980243 **/
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ var img = document.querySelector("img");
+ img.onload = function() {
+ ok(true, "Reached here");
+ SimpleTest.finish();
+ }
+ // If ths spec ever changes to treat .src sets differently from
+ // setAttribute("src"), we'll need some sort of canonicalization step
+ // earlier to make the attr value an absolute URI.
+ img.setAttribute("src", img.getAttribute("src"));
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=980243">Mozilla Bug 980243</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <img src="file_formSubmission_img.jpg">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_image_clone_load.html b/dom/html/test/test_image_clone_load.html
new file mode 100644
index 0000000000..e808c80a53
--- /dev/null
+++ b/dom/html/test/test_image_clone_load.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for image clones doing their load</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var t = async_test("The clone of an image should do the load of the same image, and do it synchronously");
+t.step(function() {
+ var img = new Image();
+ img.onload = t.step_func(function() {
+ var clone = img.cloneNode();
+ assert_not_equals(img.naturalWidth, 0, "Should have a width");
+ assert_equals(clone.naturalWidth, img.naturalWidth,
+ "Clone should have a width too");
+ // And make sure the clone fires onload too, which happens async.
+ clone.onload = function() { t.done() }
+ });
+ img.src = "image.png";
+});
+</script>
diff --git a/dom/html/test/test_img_attributes_reflection.html b/dom/html/test/test_img_attributes_reflection.html
new file mode 100644
index 0000000000..b89b4cec05
--- /dev/null
+++ b/dom/html/test/test_img_attributes_reflection.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLImageElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+/** Test for HTMLImageElement attributes reflection **/
+
+reflectString({
+ element: document.createElement("img"),
+ attribute: "alt",
+})
+
+reflectURL({
+ element: document.createElement("img"),
+ attribute: "src",
+})
+
+reflectString({
+ element: document.createElement("img"),
+ attribute: "srcset",
+})
+
+reflectLimitedEnumerated({
+ element: document.createElement("img"),
+ attribute: "crossOrigin",
+ // "" is a valid value per spec, but gets mapped to the "anonymous" state,
+ // just like invalid values, so just list it under invalidValues
+ validValues: [ "anonymous", "use-credentials" ],
+ invalidValues: [
+ "", " aNOnYmous ", " UsE-CreDEntIALS ", "foobar", "FOOBAR", " fOoBaR "
+ ],
+ defaultValue: { invalid: "anonymous", missing: null },
+ nullable: true,
+})
+
+reflectString({
+ element: document.createElement("img"),
+ attribute: "useMap",
+})
+
+reflectBoolean({
+ element: document.createElement("img"),
+ attribute: "isMap",
+})
+
+ok("width" in document.createElement("img"), "img.width is present")
+ok("height" in document.createElement("img"), "img.height is present")
+ok("naturalWidth" in document.createElement("img"), "img.naturalWidth is present")
+ok("naturalHeight" in document.createElement("img"), "img.naturalHeight is present")
+ok("complete" in document.createElement("img"), "img.complete is present")
+
+reflectString({
+ element: document.createElement("img"),
+ attribute: "name",
+})
+
+reflectString({
+ element: document.createElement("img"),
+ attribute: "align",
+})
+
+reflectUnsignedInt({
+ element: document.createElement("img"),
+ attribute: "hspace",
+})
+
+reflectUnsignedInt({
+ element: document.createElement("img"),
+ attribute: "vspace",
+})
+
+reflectURL({
+ element: document.createElement("img"),
+ attribute: "longDesc",
+})
+
+reflectString({
+ element: document.createElement("img"),
+ attribute: "border",
+ extendedAttributes: { TreatNullAs: "EmptyString" },
+})
+
+reflectURL({
+ element: document.createElement("img"),
+ attribute: "lowsrc",
+})
+
+ok("x" in document.createElement("img"), "img.x is present")
+ok("y" in document.createElement("img"), "img.y is present")
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_input_file_cancel_event.html b/dom/html/test/test_input_file_cancel_event.html
new file mode 100644
index 0000000000..f0fd81c433
--- /dev/null
+++ b/dom/html/test/test_input_file_cancel_event.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for the input type=file cancel event</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<input type=file></input>
+
+<script>
+SimpleTest.waitForExplicitFinish();
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+MockFilePicker.useBlobFile();
+MockFilePicker.returnValue = MockFilePicker.returnCancel;
+
+let input = document.querySelector('input[type=file]');
+input.addEventListener('cancel', event => {
+ ok(true, "cancel event correctly sent");
+
+ is(event.target, input, "Has correct event target");
+ is(event.isTrusted, true, "Event is trusted");
+ is(event.bubbles, true, "Event bubbles");
+ is(event.cancelable, false, "Event is not cancelable");
+ is(event.composed, false, "Event is not composed");
+
+ SimpleTest.executeSoon(function() {
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ });
+});
+input.addEventListener('change' , () => {
+ ok(false, "unexpected change event");
+})
+input.click();
+</script>
+</body>
+</html>
+
diff --git a/dom/html/test/test_input_files_not_nsIFile.html b/dom/html/test/test_input_files_not_nsIFile.html
new file mode 100644
index 0000000000..e70bc093ee
--- /dev/null
+++ b/dom/html/test/test_input_files_not_nsIFile.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for &lt;input type='file'&gt; handling when its "files" do not implement nsIFile</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<div id="content">
+ <input id='a' type='file'>
+</div>
+<button id='b' onclick="document.getElementById('a').click();">Show Filepicker</button>
+
+<input type="file" id="file" />
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+SimpleTest.waitForFocus(function() {
+ MockFilePicker.useBlobFile();
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+
+ var b = document.getElementById('b');
+ b.focus(); // Be sure the element is visible.
+
+ document.getElementById('a').addEventListener("change", function(aEvent) {
+ ok(true, "change event correctly sent");
+
+ SimpleTest.executeSoon(function() {
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ });
+ });
+
+ b.click();
+});
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/html/test/test_input_lastInteractiveValue.html b/dom/html/test/test_input_lastInteractiveValue.html
new file mode 100644
index 0000000000..6ac29edaef
--- /dev/null
+++ b/dom/html/test/test_input_lastInteractiveValue.html
@@ -0,0 +1,134 @@
+<!doctype html>
+<title>Test for HTMLInputElement.lastInteractiveValue</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/NativeKeyCodes.js"></script>
+<link href="/tests/SimpleTest/test.css"/>
+<body>
+<script>
+const kIsMac = navigator.platform.indexOf("Mac") > -1;
+const kIsWin = navigator.platform.indexOf("Win") > -1;
+
+function getFreshInput() {
+ let input = document.body.appendChild(document.createElement("input"));
+ input.focus();
+ return input;
+}
+
+// XXX This should be add_setup, but bug 1776589
+add_task(async function ensure_focus() {
+ await SimpleTest.promiseFocus(window);
+});
+
+add_task(async function simple() {
+ let input = getFreshInput();
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "", "Initial state");
+
+ sendString("abc");
+
+ is(input.value, "abc", ".value after interactive edit");
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue after interactive edit");
+
+ input.value = "muahahaha";
+ is(input.value, "muahahaha", ".value after script edit");
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue after script edit");
+});
+
+add_task(async function test_default_value() {
+ let input = getFreshInput();
+ input.defaultValue = "default value";
+
+ is(input.value, "default value", ".defaultValue affects .value");
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "", "Default value is not interactive");
+
+ sendString("abc");
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "default valueabc", "After interaction with default value");
+});
+
+// This happens in imdb.com login form.
+add_task(async function clone_after_interactive_edit() {
+ let input = getFreshInput();
+
+ sendString("abc");
+
+ is(input.value, "abc", ".value after interactive edit");
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue after interactive edit");
+
+ let clone = input.cloneNode(true);
+ is(clone.value, "abc", ".value after clone");
+ is(SpecialPowers.wrap(clone).lastInteractiveValue, "abc", ".lastInteractiveValue after clone");
+
+ clone.type = "hidden";
+
+ clone.value = "something random";
+ is(SpecialPowers.wrap(clone).lastInteractiveValue, "", ".lastInteractiveValue after clone in non-text-input");
+});
+
+add_task(async function set_user_input() {
+ let input = getFreshInput();
+
+ input.value = "";
+
+ SpecialPowers.wrap(input).setUserInput("abc");
+
+ is(input.value, "abc", ".value after setUserInput edit");
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue after setUserInput");
+
+ input.value = "foobar";
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue after script edit after setUserInput");
+});
+
+
+// TODO(emilio): Maybe execCommand shouldn't be considered interactive, but it
+// matches pre-existing behavior effectively.
+add_task(async function exec_command() {
+ let input = getFreshInput();
+
+ document.execCommand("insertText", false, "a");
+
+ is(input.value, "a", ".value after execCommand edit");
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "a", ".lastInteractiveValue after execCommand");
+
+ input.value = "foobar";
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "a", ".lastInteractiveValue after script edit after execCommand");
+});
+
+add_task(async function cut_paste() {
+ if (true) {
+ // TODO: the above condition should be if (!kIsMac && !kIsWin), but this
+ // fails (intermittently?) in those platforms, see bug 1776838. Disable for
+ // now.
+ todo(false, "synthesizeNativeKey doesn't work elsewhere (yet)");
+ return;
+ }
+
+ function doSynthesizeNativeKey(keyCode, modifiers, chars) {
+ return new Promise((resolve, reject) => {
+ if (!synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, keyCode, modifiers, chars, chars, resolve)) {
+ reject(new Error("Couldn't synthesize native key"));
+ }
+ });
+ }
+
+ let input = getFreshInput();
+
+ sendString("abc");
+
+ input.select();
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue before cut");
+
+ await doSynthesizeNativeKey(kIsMac ? MAC_VK_ANSI_X : WIN_VK_X, { accelKey: true }, "x");
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "", ".lastInteractiveValue after cut");
+
+ await doSynthesizeNativeKey(kIsMac ? MAC_VK_ANSI_V : WIN_VK_V, { accelKey: true }, "v");
+
+ is(SpecialPowers.wrap(input).lastInteractiveValue, "abc", ".lastInteractiveValue after paste");
+});
+
+</script>
diff --git a/dom/html/test/test_inputmode.html b/dom/html/test/test_inputmode.html
new file mode 100644
index 0000000000..56bb101e8a
--- /dev/null
+++ b/dom/html/test/test_inputmode.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Tests for inputmode attribute</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/SpecialPowers.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<div>
+<input id="a1" inputmode="none">
+<input id="a2" inputmode="text">
+<input id="a3" inputmode="tel">
+<input id="a4" inputmode="url">
+<input id="a5" inputmode="email">
+<input id="a6" inputmode="numeric">
+<input id="a7" inputmode="decimal">
+<input id="a8" inputmode="search">
+<input id="a9">
+<input id="a10" type="number" inputmode="numeric">
+<input id="a11" type="date" inputmode="numeric">
+<input id="a12" type="time" inputmode="numeric">
+<textarea id="b1" inputmode="none"></textarea>
+<textarea id="b2" inputmode="text"></textarea>
+<textarea id="b3" inputmode="tel"></textarea>
+<textarea id="b4" inputmode="url"></textarea>
+<textarea id="b5" inputmode="email"></textarea>
+<textarea id="b6" inputmode="numeric"></textarea>
+<textarea id="b7" inputmode="decimal"></textarea>
+<textarea id="b8" inputmode="search"></textarea>
+<textarea id="b9"></textarea>
+<div contenteditable id="c1" inputmode="none"><span>c1</span></div>
+<div contenteditable id="c2" inputmode="text"><span>c2</span></div>
+<div contenteditable id="c3" inputmode="tel"><span>c3</span></div>
+<div contenteditable id="c4" inputmode="url"><span>c4</span></div>
+<div contenteditable id="c5" inputmode="email"><span>c5</span></div>
+<div contenteditable id="c6" inputmode="numeric"><span>c6</span></div>
+<div contenteditable id="c7" inputmode="decimal"><span>c7</span></div>
+<div contenteditable id="c8" inputmode="search"><span>c8</span></div>
+<div contenteditable id="c9"><span>c9</span></div>
+<input id="d1" inputmode="URL"> <!-- no lowercase -->
+</div>
+<pre id="test">
+<script class=testbody" type="application/javascript">
+// eslint-disable-next-line mozilla/no-addtask-setup
+add_task(async function setup() {
+ await new Promise(r => SimpleTest.waitForFocus(r));
+});
+
+add_task(async function basic() {
+ const tests = [
+ { id: "a1", inputmode: "none", desc: "inputmode of input element is none" },
+ { id: "a2", inputmode: "text", desc: "inputmode of input element is text" },
+ { id: "a3", inputmode: "tel", desc: "inputmode of input element is tel" },
+ { id: "a4", inputmode: "url", desc: "inputmode of input element is url" },
+ { id: "a5", inputmode: "email", desc: "inputmode of input element is email" },
+ { id: "a6", inputmode: "numeric", desc: "inputmode of input element is numeric" },
+ { id: "a7", inputmode: "decimal", desc: "inputmode of input element is decimal" },
+ { id: "a8", inputmode: "search", desc: "inputmode of input element is search" },
+ { id: "a9", inputmode: "", desc: "no inputmode of input element" },
+ { id: "a10", inputmode: "numeric", desc: "inputmode of input type=number is numeric" },
+ { id: "a11", inputmode: "", desc: "no inputmode due to type=date" },
+ { id: "a12", inputmode: "", desc: "no inputmode due to type=time" },
+ { id: "b1", inputmode: "none", desc: "inputmode of textarea element is none" },
+ { id: "b2", inputmode: "text", desc: "inputmode of textarea element is text" },
+ { id: "b3", inputmode: "tel", desc: "inputmode of textarea element is tel" },
+ { id: "b4", inputmode: "url", desc: "inputmode of textarea element is url" },
+ { id: "b5", inputmode: "email", desc: "inputmode of textarea element is email" },
+ { id: "b6", inputmode: "numeric", desc: "inputmode of textarea element is numeric" },
+ { id: "b7", inputmode: "decimal", desc: "inputmode of textarea element is decimal" },
+ { id: "b8", inputmode: "search", desc: "inputmode of textarea element is search" },
+ { id: "b9", inputmode: "", desc: "no inputmode of textarea element" },
+ { id: "c1", inputmode: "none", desc: "inputmode of contenteditable is none" },
+ { id: "c2", inputmode: "text", desc: "inputmode of contenteditable is text" },
+ { id: "c3", inputmode: "tel", desc: "inputmode of contentedtiable is tel" },
+ { id: "c4", inputmode: "url", desc: "inputmode of contentedtiable is url" },
+ { id: "c5", inputmode: "email", desc: "inputmode of contentedtable is email" },
+ { id: "c6", inputmode: "numeric", desc: "inputmode of contenteditable is numeric" },
+ { id: "c7", inputmode: "decimal", desc: "inputmode of contenteditable is decimal" },
+ { id: "c8", inputmode: "search", desc: "inputmode of contenteditable is search" },
+ { id: "c9", inputmode: "", desc: "no inputmode of contentedtiable" },
+ { id: "d1", inputmode: "url", desc: "inputmode of input element is URL" },
+ ];
+
+ for (let test of tests) {
+ let element = document.getElementById(test.id);
+ if (element.tagName == "DIV") {
+ // Set caret to text node in contenteditable
+ window.getSelection().removeAllRanges();
+ let range = document.createRange();
+ range.setStart(element.firstChild.firstChild, 1);
+ range.setEnd(element.firstChild.firstChild, 1);
+ window.getSelection().addRange(range);
+ } else {
+ // input and textarea element
+ element.focus();
+ }
+ is(SpecialPowers.DOMWindowUtils.focusedInputMode, test.inputmode, test.desc);
+ }
+});
+
+add_task(async function dynamicChange() {
+ const tests = ["a3", "b3", "c3"];
+ for (let test of tests) {
+ let element = document.getElementById(test);
+ element.focus();
+ is(SpecialPowers.DOMWindowUtils.focusedInputMode, "tel", "Initial inputmode");
+ element.inputMode = "url";
+ is(SpecialPowers.DOMWindowUtils.focusedInputMode, "url",
+ "inputmode in InputContext has to sync with current inputMode property");
+ element.setAttribute("inputmode", "decimal");
+ is(SpecialPowers.DOMWindowUtils.focusedInputMode, "decimal",
+ "inputmode in InputContext has to sync with current inputmode attribute");
+ // Storing the original value may be safer.
+ element.inputMode = "tel";
+ }
+
+ let element = document.getElementById("a3");
+ element.focus();
+ is(SpecialPowers.DOMWindowUtils.focusedInputMode, "tel", "Initial inputmode");
+ document.getElementById("a4").inputMode = "email";
+ is(SpecialPowers.DOMWindowUtils.focusedInputMode, "tel",
+ "inputmode in InputContext keeps focused inputmode value");
+ // Storing the original value may be safer.
+ document.getElementById("a4").inputMode = "url";
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_li_attributes_reflection.html b/dom/html/test/test_li_attributes_reflection.html
new file mode 100644
index 0000000000..fd6795226b
--- /dev/null
+++ b/dom/html/test/test_li_attributes_reflection.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLLIElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLLIElement attributes reflection **/
+
+// .value
+reflectInt({
+ element: document.createElement("li"),
+ attribute: "value",
+ nonNegative: false,
+});
+
+// .type
+reflectString({
+ element: document.createElement("li"),
+ attribute: "type"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_link_attributes_reflection.html b/dom/html/test/test_link_attributes_reflection.html
new file mode 100644
index 0000000000..c75c9e2572
--- /dev/null
+++ b/dom/html/test/test_link_attributes_reflection.html
@@ -0,0 +1,96 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLLinkElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLLinkElement attributes reflection **/
+
+// .href (URL)
+reflectURL({
+ element: document.createElement("link"),
+ attribute: "href",
+});
+
+// .crossOrigin (String or null)
+reflectLimitedEnumerated({
+ element: document.createElement("link"),
+ attribute: "crossOrigin",
+ // "" is a valid value per spec, but gets mapped to the "anonymous" state,
+ // just like invalid values, so just list it under invalidValues
+ validValues: [ "anonymous", "use-credentials" ],
+ invalidValues: [
+ "", " aNOnYmous ", " UsE-CreDEntIALS ", "foobar", "FOOBAR", " fOoBaR "
+ ],
+ defaultValue: { invalid: "anonymous", missing: null },
+ nullable: true,
+})
+
+// .rel (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "rel",
+});
+
+// .media (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "media",
+});
+
+// .hreflang (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "hreflang",
+});
+
+// .type (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "type",
+});
+
+
+// .charset (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "charset",
+});
+
+// .rev (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "rev",
+});
+
+// .target (String)
+reflectString({
+ element: document.createElement("link"),
+ attribute: "target",
+});
+
+// .as (String)
+reflectLimitedEnumerated({
+ element: document.createElement("link"),
+ attribute: "as",
+ validValues: [ "fetch", "audio", "font", "image", "script", "style", "track", "video" ],
+ invalidValues: [
+ "", "audi", "doc", "Emb", "foobar", "FOOBAR", " fOoBaR ", "OBJ", "document", "embed", "manifest", "object", "report", "serviceworker", "sharedworker", "worker", "xslt"
+ ],
+ defaultValue: { invalid: "", missing: "" },
+ nullable: false,
+})
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_link_sizes.html b/dom/html/test/test_link_sizes.html
new file mode 100644
index 0000000000..b242748886
--- /dev/null
+++ b/dom/html/test/test_link_sizes.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+<head>
+<title>Test link.sizes attribute</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" sizes="16x16 24x24 32x32 48x48">
+</head>
+<body>
+
+<pre id="test">
+<script>
+
+ var links = document.getElementsByTagName('link');
+ for (var i = 0; i < links.length; ++i) {
+ var link = links[i];
+ ok("sizes" in link, "link.sizes exists");
+
+ if (link.rel == 'shortcut icon') {
+ is(link.sizes.value, "16x16 24x24 32x32 48x48", 'link.sizes.value correct value');
+ is(link.sizes.length, 4, 'link.sizes.length correct value');
+ ok(link.sizes.contains('32x32'), 'link.sizes.contains() works');
+ link.sizes.add('64x64');
+ is(link.sizes.length, 5, 'link.sizes.length correct value');
+ link.sizes.remove('64x64');
+ is(link.sizes.length, 4, 'link.sizes.length correct value');
+ is(link.sizes + "", "16x16 24x24 32x32 48x48", 'link.sizes stringify correct value');
+ } else {
+ is(link.sizes.value, "", 'link.sizes correct value');
+ }
+ }
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_map_attributes_reflection.html b/dom/html/test/test_map_attributes_reflection.html
new file mode 100644
index 0000000000..8835fb29d2
--- /dev/null
+++ b/dom/html/test/test_map_attributes_reflection.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLMapElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLMapElement attributes reflection **/
+
+// .name (String)
+reflectString({
+ element: document.createElement("map"),
+ attribute: "name",
+})
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_meta_attributes_reflection.html b/dom/html/test/test_meta_attributes_reflection.html
new file mode 100644
index 0000000000..e0cf0c347d
--- /dev/null
+++ b/dom/html/test/test_meta_attributes_reflection.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLMetaElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLMetaElement attributes reflection **/
+
+// .name (String)
+reflectString({
+ element: document.createElement("meta"),
+ attribute: "name",
+})
+
+// .httpEquiv (String)
+reflectString({
+ element: document.createElement("meta"),
+ attribute: { content: "http-equiv", idl: "httpEquiv" },
+})
+
+// .content (String)
+reflectString({
+ element: document.createElement("meta"),
+ attribute: "content",
+})
+
+// .scheme (String)
+reflectString({
+ element: document.createElement("meta"),
+ attribute: "scheme",
+})
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_mod_attributes_reflection.html b/dom/html/test/test_mod_attributes_reflection.html
new file mode 100644
index 0000000000..0efa7c52bf
--- /dev/null
+++ b/dom/html/test/test_mod_attributes_reflection.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLModElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLModElement attributes reflection **/
+
+// .cite (URL)
+reflectURL({
+ element: document.createElement("ins"),
+ attribute: "cite",
+})
+reflectURL({
+ element: document.createElement("del"),
+ attribute: "cite",
+})
+
+// .dateTime (String)
+reflectString({
+ element: document.createElement("ins"),
+ attribute: "dateTime",
+})
+reflectString({
+ element: document.createElement("del"),
+ attribute: "dateTime",
+})
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_multipleFilePicker.html b/dom/html/test/test_multipleFilePicker.html
new file mode 100644
index 0000000000..c4a71151aa
--- /dev/null
+++ b/dom/html/test/test_multipleFilePicker.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for single filepicker per event</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <div id='foo'><a href='#'>Click here to test this issue</a></div>
+ <script>
+
+SimpleTest.requestFlakyTimeout("Timeouts are needed to simulate user-interaction");
+SimpleTest.waitForExplicitFinish();
+
+let clickCount = 0;
+let foo = document.getElementById('foo');
+foo.addEventListener('click', _ => {
+ if (++clickCount < 10) {
+ let input = document.createElement('input');
+ input.type = 'file';
+ foo.appendChild(input);
+ input.click();
+ }
+});
+
+let MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+let pickerCount = 0;
+
+SpecialPowers.pushPrefEnv({
+ set: [["dom.disable_open_during_load", true]],
+})
+// Let's do the first click.
+.then(() => {
+ return new Promise(resolve => {
+ MockFilePicker.showCallback = function(filepicker) {
+ ++pickerCount;
+ resolve();
+ }
+ setTimeout(_ => {
+ is(pickerCount, 0, "No file picker initially");
+ synthesizeMouseAtCenter(foo, {});
+ }, 0);
+ })
+})
+
+// Let's wait a bit more, then let's do a click.
+.then(() => {
+ return new Promise(resolve => {
+ MockFilePicker.showCallback = function(filepicker) {
+ ++pickerCount;
+ resolve();
+ }
+
+ setTimeout(() => {
+ is(pickerCount, 1, "Only 1 file picker");
+ is(clickCount, 10, "10 clicks triggered");
+ clickCount = 0;
+ pickerCount = 0;
+ synthesizeMouseAtCenter(foo, {});
+ }, 1000);
+ });
+})
+
+// Another click...
+.then(_ => {
+ setTimeout(() => {
+ is(pickerCount, 1, "Only 1 file picker");
+ is(clickCount, 10, "10 clicks triggered");
+ MockFilePicker.cleanup();
+ SimpleTest.finish();
+ }, 1000);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_named_options.html b/dom/html/test/test_named_options.html
new file mode 100644
index 0000000000..8c38425240
--- /dev/null
+++ b/dom/html/test/test_named_options.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=772869
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 772869</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772869">Mozilla Bug 772869</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <select id="s">
+ <option name="x"></option>
+ <option name="y" id="z"></option>
+ <option name="z" id="x"></option>
+ <option id="w"></option>
+ </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 772869 **/
+var opt = $("s").options;
+opt.loopy = "something"
+var names = Object.getOwnPropertyNames(opt);
+is(names.length, 9, "Should have nine entries");
+is(names[0], "0", "Entry 1")
+is(names[1], "1", "Entry 2")
+is(names[2], "2", "Entry 3")
+is(names[3], "3", "Entry 4")
+is(names[4], "x", "Entry 5")
+is(names[5], "y", "Entry 6")
+is(names[6], "z", "Entry 7")
+is(names[7], "w", "Entry 8")
+is(names[8], "loopy", "Entry 9")
+
+var names2 = [];
+for (var name in opt) {
+ names2.push(name);
+}
+is(names2.length, 11, "Should have eleven enumerated names");
+is(names2[0], "0", "Enum entry 1")
+is(names2[1], "1", "Enum entry 2")
+is(names2[2], "2", "Enum entry 3")
+is(names2[3], "3", "Enum entry 4")
+is(names2[4], "loopy", "Enum entry 5")
+is(names2[5], "add", "Enum entrry 6")
+is(names2[6], "remove", "Enum entry 7")
+is(names2[7], "length", "Enum entry 8")
+is(names2[8], "selectedIndex", "Enum entry 9")
+is(names2[9], "item", "Enum entry 10")
+is(names2[10], "namedItem", "Enum entry 11")
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_nested_invalid_fieldsets.html b/dom/html/test/test_nested_invalid_fieldsets.html
new file mode 100644
index 0000000000..7c00693697
--- /dev/null
+++ b/dom/html/test/test_nested_invalid_fieldsets.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=914029
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 914029</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 914029 **/
+
+ var innerFieldset = document.createElement("fieldset");
+ var outerFieldset = document.createElement("fieldset");
+ var textarea = document.createElement("textarea");
+ textarea.setAttribute("required", "");
+ innerFieldset.appendChild(textarea);
+ outerFieldset.appendChild(innerFieldset);
+ SpecialPowers.forceGC();
+ ok(true, "This page did not crash - dynamically added nested invalid fieldsets" +
+ " work correctly.");
+ var innerFieldset = document.createElement("fieldset");
+ var outerFieldset = document.createElement("fieldset");
+ var textarea = document.createElement("textarea");
+ var textarea2 = document.createElement("textarea");
+ textarea.setAttribute("required", "");
+ innerFieldset.appendChild(textarea);
+ innerFieldset.appendChild(textarea2);
+ outerFieldset.appendChild(innerFieldset);
+ SpecialPowers.forceGC();
+ ok(true, "This page did not crash - dynamically added nested invalid fieldsets" +
+ " work correctly.");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=914029">Mozilla Bug 914029</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_nestediframe.html b/dom/html/test/test_nestediframe.html
new file mode 100644
index 0000000000..ddbf0ca9dc
--- /dev/null
+++ b/dom/html/test/test_nestediframe.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for same URLs nested iframes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ test_nestediframe body
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function reportState(msg) {
+ if (location.href.includes("#")) {
+ parent.postMessage(msg, "*");
+ return;
+ }
+
+ if (msg == "OK 1") {
+ ok(true, "First frame loaded");
+ } else if (msg == "KO 2") {
+ ok(true, "Second frame load failed");
+ SimpleTest.finish();
+ } else {
+ ok(false, "Unknown message: " + msg);
+ }
+}
+
+addEventListener("message", event => {
+ reportState(event.data);
+});
+
+var recursion;
+if (!location.href.includes("#")) {
+ recursion = 1;
+} else {
+ recursion = parseInt(location.href.split("#")[1]) + 1;
+}
+
+var ifr = document.createElement('iframe');
+ifr.src = location.href.split("#")[0] + "#" + recursion;
+
+ifr.onload = function() {
+ reportState("OK " + recursion);
+}
+ifr.onerror = function() {
+ reportState("KO " + recursion);
+}
+
+document.body.appendChild(ifr);
+
+</script>
+</body>
+</html>
diff --git a/dom/html/test/test_non-ascii-cookie.html b/dom/html/test/test_non-ascii-cookie.html
new file mode 100644
index 0000000000..a15923f39d
--- /dev/null
+++ b/dom/html/test/test_non-ascii-cookie.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=784367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for non-ASCII cookie values</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=784367">Mozilla Bug 784367</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for non-ASCII cookie values **/
+
+SimpleTest.waitForExplicitFinish();
+
+var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL("file_cookiemanager.js"));
+
+function getCookieFromManager() {
+ return new Promise(resolve => {
+ gScript.addMessageListener("getCookieFromManager:return", function gcfm({ cookie }) {
+ gScript.removeMessageListener("getCookieFromManager:return", gcfm);
+ resolve(cookie);
+ });
+ gScript.sendAsyncMessage("getCookieFromManager", { host: location.hostname, path: location.pathname });
+ });
+}
+
+SpecialPowers.pushPrefEnv({
+ "set": [
+ // Bug 1617611: Fix all the tests broken by "cookies SameSite=lax by default"
+ ["network.cookie.sameSite.laxByDefault", false],
+ ]
+}, () => {
+ var c = document.cookie;
+ is(document.cookie, 'abc=012©ABC\ufffdDEF', "document.cookie should be decoded as UTF-8");
+
+ var newCookie;
+
+ getCookieFromManager().then((cookie) => {
+ is(cookie, document.cookie, "nsICookieManager should be consistent with document.cookie");
+ newCookie = 'def=∼≩≭≧∯≳≲≣∽≸≸∺≸∠≯≮≥≲≲≯≲∽≡≬≥≲≴∨∱∩∾';
+ document.cookie = newCookie;
+ is(document.cookie, c + '; ' + newCookie, "document.cookie should be encoded as UTF-8");
+
+ return getCookieFromManager();
+ }).then((cookie) => {
+ is(cookie, document.cookie, "nsICookieManager should be consistent with document.cookie");
+ var date1 = new Date();
+ date1.setTime(0);
+ document.cookie = newCookie + 'def=;expires=' + date1.toGMTString();
+ gScript.destroy();
+ SpecialPowers.clearUserPref("network.cookie.sameSite.laxByDefault");
+ SimpleTest.finish();
+ });
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_non-ascii-cookie.html^headers^ b/dom/html/test/test_non-ascii-cookie.html^headers^
new file mode 100644
index 0000000000..54aa6c3e72
--- /dev/null
+++ b/dom/html/test/test_non-ascii-cookie.html^headers^
@@ -0,0 +1 @@
+Set-Cookie: abc=012©ABC©DEF
diff --git a/dom/html/test/test_object_attributes_reflection.html b/dom/html/test/test_object_attributes_reflection.html
new file mode 100644
index 0000000000..d55183db07
--- /dev/null
+++ b/dom/html/test/test_object_attributes_reflection.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLObjectElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLObjectElement attributes reflection **/
+
+// .data (URL)
+reflectURL({
+ element: document.createElement("object"),
+ attribute: "data",
+});
+
+// .type (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "type",
+});
+
+// .name (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "name",
+});
+
+// .useMap (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "useMap",
+});
+
+// .width (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "width",
+});
+
+// .height (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "height",
+});
+
+// .align (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "align",
+});
+
+// .archive (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "archive",
+});
+
+// .code (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "code",
+});
+
+// .declare (String)
+reflectBoolean({
+ element: document.createElement("object"),
+ attribute: "declare",
+});
+
+// .hspace (unsigned int)
+reflectUnsignedInt({
+ element: document.createElement("object"),
+ attribute: "hspace",
+});
+
+// .standby (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "standby",
+});
+
+// .vspace (unsigned int)
+reflectUnsignedInt({
+ element: document.createElement("object"),
+ attribute: "vspace",
+});
+
+// .codeBase (URL)
+reflectURL({
+ element: document.createElement("object"),
+ attribute: "codeBase",
+});
+
+// .codeType (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "codeType",
+});
+
+// .border (String)
+reflectString({
+ element: document.createElement("object"),
+ attribute: "border",
+ extendedAttributes: { TreatNullAs: "EmptyString" },
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_ol_attributes_reflection.html b/dom/html/test/test_ol_attributes_reflection.html
new file mode 100644
index 0000000000..a941914077
--- /dev/null
+++ b/dom/html/test/test_ol_attributes_reflection.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLOLElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLOLElement attributes reflection **/
+
+// .reversed (boolean)
+reflectBoolean({
+ element: document.createElement("ol"),
+ attribute: "reversed",
+})
+
+// .start
+reflectInt({
+ element: document.createElement("ol"),
+ attribute: "start",
+ nonNegative: false,
+ defaultValue: 1,
+});
+
+// .type
+reflectString({
+ element: document.createElement("ol"),
+ attribute: "type"
+});
+
+// .compact
+reflectBoolean({
+ element: document.createElement("ol"),
+ attribute: "compact",
+})
+
+// Additional tests for ol.start behavior when li elements are added
+var ol = document.createElement("ol");
+var li = document.createElement("li");
+li.value = 42;
+ol.appendChild(li);
+is(ol.start, 1, "ol.start with one li child, li.value = 42:");
+li.value = -42;
+is(ol.start, 1, "ol.start with one li child, li.value = 42:");
+ol.removeAttribute("start");
+li.removeAttribute("value");
+ol.appendChild(document.createElement("li"));
+ol.reversed = true;
+todo_is(ol.start, 2, "ol.start with two li children, ol.reversed == true:");
+li.value = 42;
+todo_is(ol.start, 2, "ol.start with two li childern, ol.reversed == true:");
+ol.start = 42;
+is(ol.start, 42, "ol.start = 42:");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_option_defaultSelected.html b/dom/html/test/test_option_defaultSelected.html
new file mode 100644
index 0000000000..6d999c0b29
--- /dev/null
+++ b/dom/html/test/test_option_defaultSelected.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=927796
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 927796</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=927796">Mozilla Bug 927796</a>
+<p id="display">
+<select id="s1">
+ <option selected>one</option>
+ <option>two</option>
+</select>
+<select id="s2" size="5">
+ <option selected>one</option>
+ <option>two</option>
+</select>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+ <script type="application/javascript">
+
+ /** Test for Bug 927796 **/
+ var s1 = $("s1");
+ s1.options[0].defaultSelected = false;
+ is(s1.options[0].selected, true,
+ "First option in combobox should still be selected");
+ is(s1.options[1].selected, false,
+ "Second option in combobox should not be selected");
+
+ var s2 = $("s2");
+ s2.options[0].defaultSelected = false;
+ is(s2.options[0].selected, false,
+ "First option in listbox should not be selected");
+ is(s2.options[1].selected, false,
+ "Second option in listbox should not be selected");
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/test_option_selected_state.html b/dom/html/test/test_option_selected_state.html
new file mode 100644
index 0000000000..30a634de58
--- /dev/null
+++ b/dom/html/test/test_option_selected_state.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=942648
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 942648</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=942648">Mozilla Bug 942648</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <select>
+ <option value="1">1</option>
+ <option id="e1" value="2">2</option>
+ </select>
+ <select>
+ <option value="1">1</option>
+ <option id="e2" selected value="2">2</option>
+ </select>
+ <select>
+ <option value="1">1</option>
+ <option id="e3" selected="" value="2">2</option>
+ </select>
+ <select>
+ <option value="1">1</option>
+ <option id="e4" selected="selected" value="2">2</option>
+ </select>
+</pre>
+ <script type="application/javascript">
+
+ /** Test for Bug 942648 **/
+SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ var e1 = document.getElementById('e1');
+ var e2 = document.getElementById('e2');
+ var e3 = document.getElementById('e3');
+ var e4 = document.getElementById('e4');
+ ok(!e1.selected, "e1 should not be selected");
+ ok(e2.selected, "e2 should be selected");
+ ok(e3.selected, "e3 should be selected");
+ ok(e4.selected, "e4 should be selected");
+ e1.setAttribute('selected', 'selected');
+ e2.setAttribute('selected', 'selected');
+ e3.setAttribute('selected', 'selected');
+ e4.setAttribute('selected', 'selected');
+ ok(e1.selected, "e1 should now be selected");
+ ok(e2.selected, "e2 should still be selected");
+ ok(e3.selected, "e3 should still be selected");
+ ok(e4.selected, "e4 should still be selected");
+ SimpleTest.finish();
+ };
+ </script>
+</body>
+</html>
diff --git a/dom/html/test/test_param_attributes_reflection.html b/dom/html/test/test_param_attributes_reflection.html
new file mode 100644
index 0000000000..977fb61935
--- /dev/null
+++ b/dom/html/test/test_param_attributes_reflection.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLParamElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLParamElement attributes reflection **/
+
+// .name
+reflectString({
+ element: document.createElement("param"),
+ attribute: "name",
+});
+
+// .value
+reflectString({
+ element: document.createElement("param"),
+ attribute: "value"
+});
+
+// .type
+reflectString({
+ element: document.createElement("param"),
+ attribute: "type"
+});
+
+// .valueType
+reflectString({
+ element: document.createElement("param"),
+ attribute: "valueType"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_plugin.tst b/dom/html/test/test_plugin.tst
new file mode 100644
index 0000000000..323fae03f4
--- /dev/null
+++ b/dom/html/test/test_plugin.tst
@@ -0,0 +1 @@
+foobar
diff --git a/dom/html/test/test_q_attributes_reflection.html b/dom/html/test/test_q_attributes_reflection.html
new file mode 100644
index 0000000000..a840e6f0e5
--- /dev/null
+++ b/dom/html/test/test_q_attributes_reflection.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLQuoteElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLQuoteElement attributes reflection **/
+
+// .cite
+reflectURL({
+ element: document.createElement("q"),
+ attribute: "cite",
+});
+
+reflectURL({
+ element: document.createElement("blockquote"),
+ attribute: "cite",
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_restore_from_parser_fragment.html b/dom/html/test/test_restore_from_parser_fragment.html
new file mode 100644
index 0000000000..7fb3b75e46
--- /dev/null
+++ b/dom/html/test/test_restore_from_parser_fragment.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=644959
+-->
+<head>
+ <title>Test for Bug 644959</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=644959">Mozilla Bug 644959</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 644959 **/
+
+var content = document.getElementById('content');
+
+function appendHTML(aParent, aElementString)
+{
+ aParent.innerHTML = "<form>" + aElementString + "</form>";
+}
+
+function clearHTML(aParent)
+{
+ aParent.innerHTML = "";
+}
+
+var tests = [
+ [ "button", "<button></button>" ],
+ [ "input", "<input>" ],
+ [ "textarea", "<textarea></textarea>" ],
+ [ "select", "<select></select>" ],
+];
+
+var element = null;
+
+for (var test of tests) {
+ appendHTML(content, test[1]);
+ element = content.getElementsByTagName(test[0])[0];
+ is(element.disabled, false, "element shouldn't be disabled");
+ element.disabled = true;
+ is(element.disabled, true, "element should be disabled");
+
+ clearHTML(content);
+
+ appendHTML(content, test[1]);
+ element = content.getElementsByTagName(test[0])[0];
+ is(element.disabled, false, "element shouldn't be disabled");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_rowscollection.html b/dom/html/test/test_rowscollection.html
new file mode 100644
index 0000000000..0e5152e1d5
--- /dev/null
+++ b/dom/html/test/test_rowscollection.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=772869
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 772869</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772869">Mozilla Bug 772869</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <table id="f">
+ <thead>
+ <tr id="x"></tr>
+ </thead>
+ <tfoot>
+ <tr id="z"></tr>
+ <tr id="w"></tr>
+ </tfoot>
+ <tr id="x"></tr>
+ <tr id="y"></tr>
+ <tbody>
+ <tr id="z"></tr>
+ </tbody>
+ </table>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 772869 **/
+var x = $("f").rows;
+x.something = "another";
+var names = [];
+for (var name in x) {
+ names.push(name);
+}
+is(names.length, 10, "Should have 10 enumerated names");
+is(names[0], "0", "Enum entry 1")
+is(names[1], "1", "Enum entry 2")
+is(names[2], "2", "Enum entry 3")
+is(names[3], "3", "Enum entry 4")
+is(names[4], "4", "Enum entry 5")
+is(names[5], "5", "Enum entry 6")
+is(names[6], "something", "Enum entry 7")
+is(names[7], "item", "Enum entry 8")
+is(names[8], "namedItem", "Enum entry 9")
+is(names[9], "length", "Enum entry 10");
+
+names = Object.getOwnPropertyNames(x);
+is(names.length, 11, "Should have 11 items");
+is(names[0], "0", "Entry 1")
+is(names[1], "1", "Entry 2")
+is(names[2], "2", "Entry 3")
+is(names[3], "3", "Entry 4")
+is(names[4], "4", "Entry 5")
+is(names[5], "5", "Entry 6")
+is(names[6], "x", "Entry 7")
+is(names[7], "y", "Entry 8")
+is(names[8], "z", "Entry 9")
+is(names[9], "w", "Entry 10")
+is(names[10], "something", "Entry 11")
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_script_module.html b/dom/html/test/test_script_module.html
new file mode 100644
index 0000000000..26b9cd6f65
--- /dev/null
+++ b/dom/html/test/test_script_module.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLScriptElement with nomodule attribute</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+ <script>
+onmessage = (e) => {
+ if ("done" in e.data) {
+ SimpleTest.finish();
+ } else if ("check" in e.data) {
+ ok(e.data.check, e.data.msg);
+ } else {
+ ok(false, "Unknown message");
+ }
+}
+
+
+var ifr = document.createElement('iframe');
+ifr.src = "file_script_module.html";
+document.body.appendChild(ifr);
+
+
+SimpleTest.waitForExplicitFinish();
+ </script>
+
+</body>
+</html>
diff --git a/dom/html/test/test_set_input_files.html b/dom/html/test/test_set_input_files.html
new file mode 100644
index 0000000000..3b7bf20909
--- /dev/null
+++ b/dom/html/test/test_set_input_files.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1384030
+-->
+<head>
+ <title>Test for Setting &lt;input type=file&gt;.files </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1384030">Mozilla Bug 1384030</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Setting <input type=file>.files **/
+
+function runTest()
+{
+ const form = document.createElement("form");
+ const formInput = document.createElement("input");
+ formInput.type = "file";
+ formInput.name = "inputFile";
+ form.appendChild(formInput);
+
+ const input = document.createElement("input");
+ input.type = "file";
+ SpecialPowers.wrap(input).mozSetFileArray([
+ new File(["foo"], "foo"),
+ new File(["bar"], "bar")
+ ]);
+
+ formInput.files = input.files;
+
+ const inputFiles = (new FormData(form)).getAll("inputFile");
+ is(inputFiles.length, 2, "FormData should contain two input files");
+
+ is(inputFiles[0].name, "foo", "Input file name should be 'foo'");
+ is(inputFiles[1].name, "bar", "Input file name should be 'bar'");
+
+ is(inputFiles[0], input.files[0],
+ "Expect the same File object as input file 'foo'");
+ is(inputFiles[1], input.files[1],
+ "Expect the same File object as input file 'bar'");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+window.addEventListener('load', runTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_srcdoc-2.html b/dom/html/test/test_srcdoc-2.html
new file mode 100644
index 0000000000..5db7d69529
--- /dev/null
+++ b/dom/html/test/test_srcdoc-2.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=802895
+-->
+ <head>
+<title>Test session history for srcdoc iframes introduced in bug 802895</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=802895">Mozilla Bug 802895</a>
+
+<iframe id="pframe" name="pframe" src="file_srcdoc-2.html"></iframe>
+<pre id="test">
+<script>
+
+ SimpleTest.waitForExplicitFinish();
+ var pframe = $("pframe");
+
+ //disable bfcache
+ pframe.contentWindow.addEventListener("unload", function () { });
+
+ var loadState = 0;
+ pframe.onload = function () {
+ SimpleTest.executeSoon(function () {
+
+ var pDoc = pframe.contentDocument;
+
+ if (loadState == 0) {
+ var div = pDoc.createElement("div");
+ div.id = "modifyCheck";
+ div.innerHTML = "hello again";
+ pDoc.body.appendChild(div);
+ ok(pDoc.getElementById("modifyCheck"), "Child element not created");
+ pframe.src = "about:blank";
+ loadState = 1;
+ }
+ else if (loadState == 1) {
+ loadState = 2;
+ window.history.back();
+ }
+ else if (loadState == 2) {
+ ok(!pDoc.getElementById("modifyCheck"), "modifyCheck element shouldn't be present");
+ is(pDoc.getElementById("iframe").contentDocument.body.innerHTML,
+ "Hello World", "srcdoc iframe not present");
+ SimpleTest.finish();
+ }
+
+ })
+ };
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_srcdoc.html b/dom/html/test/test_srcdoc.html
new file mode 100644
index 0000000000..b3137f4e0a
--- /dev/null
+++ b/dom/html/test/test_srcdoc.html
@@ -0,0 +1,118 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=802895
+-->
+ <head>
+<title>Tests for srcdoc iframes introduced in bug 802895</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=802895">Mozilla Bug 802895</a>
+
+<iframe id="pframe" src="file_srcdoc.html"></iframe>
+
+<pre id="test">
+<script>
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ var pframe = $("pframe");
+
+ var loadState = 0;
+ pframe.contentWindow.addEventListener("load", function () {
+
+ var pframeDoc = pframe.contentDocument;
+
+ var iframe = pframeDoc.getElementById("iframe");
+ var innerDoc = iframe.contentDocument;
+ var iframe1 = pframeDoc.getElementById("iframe1");
+ var innerDoc1 = iframe1.contentDocument;
+
+ var finish = false;
+ var finish1 = false;
+ var finish3 = false;
+
+
+
+ is(iframe.srcdoc, "Hello World", "Bad srcdoc attribute contents")
+
+ is(innerDoc.domain, document.domain, "Wrong domain");
+ is(innerDoc.referrer, pframeDoc.referrer, "Wrong referrer");
+ is(innerDoc.body.innerHTML, "Hello World", "Wrong body");
+ is(innerDoc.compatMode, "CSS1Compat", "Not standards compliant");
+
+ is(innerDoc1.domain, document.domain, "Wrong domain with src attribute");
+ is(innerDoc1.referrer, pframeDoc.referrer, "Wrong referrer with src attribute");
+ is(innerDoc1.body.innerHTML, "Goodbye World", "Wrong body with src attribute")
+ is(innerDoc1.compatMode, "CSS1Compat", "Not standards compliant with src attribute");
+
+ var iframe2 = pframeDoc.getElementById("iframe2");
+ var innerDoc2 = iframe2.contentDocument;
+ try {
+ innerDoc2.domain;
+ foundError = false;
+ }
+ catch (error) {
+ foundError = true;
+ }
+ ok(foundError, "srcdoc iframe not sandboxed");
+
+ //Test changed srcdoc attribute
+ iframe.onload = function () {
+
+ iframe = pframeDoc.getElementById("iframe");
+ innerDoc = iframe.contentDocument;
+
+ is(iframe.srcdoc, "Hello again", "Bad srcdoc attribute contents with srcdoc attribute changed");
+ is(innerDoc.domain, document.domain, "Wrong domain with srcdoc attribute changed");
+ is(innerDoc.referrer, pframeDoc.referrer, "Wrong referrer with srcdoc attribute changed");
+ is(innerDoc.body.innerHTML, "Hello again", "Wrong body with srcdoc attribute changed");
+ is(innerDoc.compatMode, "CSS1Compat", "Not standards compliant with srcdoc attribute changed");
+
+ finish = true;
+ if (finish && finish1 && finish3) {
+ SimpleTest.finish();
+ }
+ };
+
+ iframe.srcdoc = "Hello again";
+
+ var iframe3 = pframeDoc.getElementById("iframe3");
+
+ // Test srcdoc attribute removal
+ iframe3.onload = function () {
+ var innerDoc3 = iframe3.contentDocument;
+ is(innerDoc3.body.innerText, "Gone", "Bad srcdoc attribute removal");
+ finish3 = true;
+ if (finish && finish1 && finish3) {
+ SimpleTest.finish();
+ }
+ }
+
+ iframe3.removeAttribute("srcdoc");
+
+
+ var iframe1load = false;
+ iframe1.onload = function () {
+ iframe1load = true;
+ }
+
+ iframe1.src = "data:text/plain;charset=US-ASCII,Goodbyeeee";
+
+ // Need to test that changing the src doesn't change the iframe.
+ setTimeout(function () {
+ ok(!iframe1load, "Changing src attribute shouldn't cause a load when srcdoc is set");
+ finish1 = true;
+ if (finish && finish1 && finish3) {
+ SimpleTest.finish();
+ }
+ }, 2000);
+
+ });
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_style_attributes_reflection.html b/dom/html/test/test_style_attributes_reflection.html
new file mode 100644
index 0000000000..745ed7435f
--- /dev/null
+++ b/dom/html/test/test_style_attributes_reflection.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for HTMLStyleElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLStyleElement attributes reflection **/
+
+var e = document.createElement("style");
+
+// .media
+reflectString({
+ element: e,
+ attribute: "media"
+});
+
+// .type
+reflectString({
+ element: e,
+ attribute: "type"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_track.html b/dom/html/test/test_track.html
new file mode 100644
index 0000000000..50051bf2d6
--- /dev/null
+++ b/dom/html/test/test_track.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=833386
+-->
+<head>
+ <meta charset='utf-8'>
+ <title>Test for Bug 833386 - HTMLTrackElement</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/html/test/reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+reflectLimitedEnumerated({
+ element: document.createElement("track"),
+ attribute: "kind",
+ validValues: ["subtitles", "captions", "descriptions", "chapters",
+ "metadata"],
+ invalidValues: ["foo", "bar", "\u0000", "null", "", "subtitle", "caption",
+ "description", "chapter", "meta"],
+ defaultValue: { missing: "subtitles", invalid: "metadata" },
+});
+
+// Default attribute
+reflectBoolean({
+ element: document.createElement("track"),
+ attribute: "default"
+});
+
+// Label attribute
+reflectString({
+ element: document.createElement("track"),
+ attribute: "label",
+ otherValues: [ "foo", "BAR", "_FoO", "\u0000", "null", "white space" ]
+});
+
+// Source attribute
+reflectURL({
+ element: document.createElement("track"),
+ attribute: "src",
+ otherValues: ["foo", "bar", "\u0000", "null", ""]
+});
+
+// Source Language attribute
+reflectString({
+ element: document.createElement("track"),
+ attribute: "srclang",
+ otherValues: ["foo", "bar", "\u0000", "null", ""]
+});
+
+var track = document.createElement("track");
+is(track.readyState, 0, "Default ready state should be 0 (NONE).");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_ul_attributes_reflection.html b/dom/html/test/test_ul_attributes_reflection.html
new file mode 100644
index 0000000000..cd5f6b1cc2
--- /dev/null
+++ b/dom/html/test/test_ul_attributes_reflection.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for HTMLUListElement attributes reflection</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="reflect.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for HTMLUListElement attributes reflection **/
+
+// .compact
+reflectBoolean({
+ element: document.createElement("ul"),
+ attribute: "compact"
+});
+
+// .type
+reflectString({
+ element: document.createElement("ul"),
+ attribute: "type"
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_viewport_resize.html b/dom/html/test/test_viewport_resize.html
new file mode 100644
index 0000000000..e800aa592a
--- /dev/null
+++ b/dom/html/test/test_viewport_resize.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1135812
+-->
+<head>
+ <title>Test for Bug 1135812</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1135812">Mozilla Bug 1135812</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+
+<iframe style="width: 50px;"
+ srcdoc='<picture><source srcset="data:,a" media="(min-width: 150px)" /><source srcset="data:,b" media="(min-width: 100px)" /><img src="data:,c" /></picture>'></iframe>
+<script>
+ SimpleTest.waitForExplicitFinish();
+ addEventListener('load', function() {
+ var iframe = document.querySelector('iframe');
+ var img = iframe.contentDocument.querySelector('img');
+ is(img.currentSrc, 'data:,c');
+
+ img.onload = function() {
+ is(img.currentSrc, 'data:,a');
+ img.onload = function() {
+ is(img.currentSrc, 'data:,b');
+ SimpleTest.finish();
+ }
+ img.onerror = img.onload;
+ iframe.style.width = '120px';
+ };
+ img.onerror = img.onload;
+
+ iframe.style.width = '200px';
+ }, true);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/html/test/test_window_open_close.html b/dom/html/test/test_window_open_close.html
new file mode 100644
index 0000000000..0869100b4c
--- /dev/null
+++ b/dom/html/test/test_window_open_close.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// Opens a popup. Link should load in main browser window. Popup should be closed when link clicked.
+function openWindow1() {
+ return window.open('file_window_open_close_outer.html','','width=300,height=200');
+}
+
+// Opens a new tab T1. Link opens in another new tab T2. T1 should close when link clicked.
+function openWindow2() {
+ return window.open('file_window_open_close_outer.html');
+}
+
+// Opens a new window. Link should open in a new tab of that window, but then both windows should close.
+function openWindow3() {
+ return window.open('file_window_open_close_outer.html', '', 'toolbar=1');
+}
+
+var TESTS = [openWindow1, openWindow2, openWindow3];
+
+function popupLoad(win)
+{
+ info("Sending click");
+ sendMouseEvent({type: "click"}, "link", win);
+ ok(true, "Didn't crash");
+
+ next();
+}
+
+function next()
+{
+ if (!TESTS.length) {
+ SimpleTest.finish();
+ } else {
+ var test = TESTS.shift();
+ var w = test();
+ w.addEventListener("load", (e) => popupLoad(w));
+ }
+}
+</script>
+
+<body onload="next()">
+</body>
+</html>
diff --git a/dom/html/test/test_window_open_from_closing.html b/dom/html/test/test_window_open_from_closing.html
new file mode 100644
index 0000000000..0d38c88d84
--- /dev/null
+++ b/dom/html/test/test_window_open_from_closing.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>window.open from a window being closed</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <h1>window.open from a window being closed</h1>
+<script>
+add_task(async function() {
+ const RELS = ["", "#noopener", "#opener"];
+ const FEATURES = [
+ "",
+ "noopener",
+ "width=300",
+ "width=300,noopener",
+ ];
+
+ let resolver;
+ let channel = new BroadcastChannel("test");
+ channel.onmessage = function(e) {
+ info("message from broadcastchannel: " + e.data);
+ if (e.data == "load") {
+ resolver();
+ }
+ };
+
+ for (let rel of RELS) {
+ for (let feature of FEATURES) {
+ info(`running test: rel=${rel}, feature=${feature}`);
+
+ let loadPromise = new Promise(r => { resolver = r; });
+ window.open("file_window_close_and_open.html" + rel, "_blank", feature);
+ await loadPromise;
+ ok(true, "popup opened successfully - closing...");
+ channel.postMessage("close");
+ }
+ }
+});
+</script>
+</body>
+</html>