From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../base/content/test/general/alltabslistener.html | 8 + .../base/content/test/general/app_bug575561.html | 18 + .../test/general/app_subframe_bug575561.html | 12 + browser/base/content/test/general/audio.ogg | Bin 0 -> 14293 bytes browser/base/content/test/general/browser.ini | 416 +++++++ .../content/test/general/browser_accesskeys.js | 202 ++++ .../test/general/browser_addCertException.js | 77 ++ .../test/general/browser_alltabslistener.js | 331 ++++++ .../test/general/browser_backButtonFitts.js | 40 + .../browser_beforeunload_duplicate_dialogs.js | 114 ++ .../content/test/general/browser_bug1261299.js | 112 ++ .../content/test/general/browser_bug1297539.js | 122 +++ .../content/test/general/browser_bug1299667.js | 70 ++ .../base/content/test/general/browser_bug321000.js | 91 ++ .../base/content/test/general/browser_bug356571.js | 100 ++ .../base/content/test/general/browser_bug380960.js | 18 + .../base/content/test/general/browser_bug406216.js | 64 ++ .../base/content/test/general/browser_bug417483.js | 50 + .../base/content/test/general/browser_bug424101.js | 72 ++ .../base/content/test/general/browser_bug427559.js | 41 + .../base/content/test/general/browser_bug431826.js | 56 + .../base/content/test/general/browser_bug432599.js | 109 ++ .../base/content/test/general/browser_bug455852.js | 27 + .../base/content/test/general/browser_bug462289.js | 144 +++ .../base/content/test/general/browser_bug462673.js | 66 ++ .../base/content/test/general/browser_bug479408.js | 23 + .../test/general/browser_bug479408_sample.html | 4 + .../base/content/test/general/browser_bug481560.js | 16 + .../base/content/test/general/browser_bug484315.js | 14 + .../base/content/test/general/browser_bug491431.js | 42 + .../base/content/test/general/browser_bug495058.js | 53 + .../base/content/test/general/browser_bug519216.js | 48 + .../base/content/test/general/browser_bug520538.js | 27 + .../base/content/test/general/browser_bug521216.js | 68 ++ .../base/content/test/general/browser_bug533232.js | 56 + .../base/content/test/general/browser_bug537013.js | 168 +++ .../base/content/test/general/browser_bug537474.js | 20 + .../base/content/test/general/browser_bug563588.js | 42 + .../base/content/test/general/browser_bug565575.js | 21 + .../base/content/test/general/browser_bug567306.js | 65 ++ .../base/content/test/general/browser_bug575561.js | 118 ++ .../base/content/test/general/browser_bug577121.js | 27 + .../base/content/test/general/browser_bug578534.js | 31 + .../base/content/test/general/browser_bug579872.js | 26 + .../base/content/test/general/browser_bug581253.js | 74 ++ .../base/content/test/general/browser_bug585785.js | 48 + .../base/content/test/general/browser_bug585830.js | 27 + .../base/content/test/general/browser_bug594131.js | 25 + .../base/content/test/general/browser_bug596687.js | 28 + .../base/content/test/general/browser_bug597218.js | 40 + .../base/content/test/general/browser_bug609700.js | 28 + .../base/content/test/general/browser_bug623893.js | 50 + .../base/content/test/general/browser_bug624734.js | 49 + .../base/content/test/general/browser_bug664672.js | 27 + .../base/content/test/general/browser_bug676619.js | 225 ++++ .../base/content/test/general/browser_bug710878.js | 49 + .../base/content/test/general/browser_bug724239.js | 56 + .../base/content/test/general/browser_bug734076.js | 195 ++++ .../base/content/test/general/browser_bug749738.js | 32 + .../test/general/browser_bug763468_perwindowpb.js | 57 + .../test/general/browser_bug767836_perwindowpb.js | 72 ++ .../base/content/test/general/browser_bug817947.js | 51 + .../base/content/test/general/browser_bug832435.js | 26 + .../base/content/test/general/browser_bug882977.js | 33 + .../base/content/test/general/browser_bug963945.js | 26 + .../base/content/test/general/browser_clipboard.js | 290 +++++ .../test/general/browser_clipboard_pastefile.js | 133 +++ .../test/general/browser_contentAltClick.js | 205 ++++ .../base/content/test/general/browser_ctrlTab.js | 464 ++++++++ .../general/browser_datachoices_notification.js | 287 +++++ .../test/general/browser_documentnavigation.js | 493 +++++++++ .../browser_domFullscreen_fullscreenMode.js | 237 ++++ .../test/general/browser_double_close_tab.js | 120 +++ browser/base/content/test/general/browser_drag.js | 64 ++ .../content/test/general/browser_duplicateIDs.js | 11 + .../content/test/general/browser_findbarClose.js | 47 + .../content/test/general/browser_focusonkeydown.js | 34 + .../test/general/browser_fullscreen-window-open.js | 366 +++++++ .../content/test/general/browser_gestureSupport.js | 1132 ++++++++++++++++++++ .../content/test/general/browser_hide_removing.js | 27 + .../base/content/test/general/browser_homeDrop.js | 117 ++ ...rowser_invalid_uri_back_forward_manipulation.js | 48 + .../test/general/browser_lastAccessedTab.js | 62 ++ .../test/general/browser_menuButtonFitts.js | 69 ++ .../test/general/browser_middleMouse_noJSPaste.js | 49 + .../base/content/test/general/browser_minimize.js | 49 + .../browser_modifiedclick_inherit_principal.js | 42 + .../content/test/general/browser_newTabDrop.js | 221 ++++ .../content/test/general/browser_newWindowDrop.js | 230 ++++ ...browser_new_http_window_opened_from_file_tab.js | 63 ++ .../test/general/browser_newwindow_focus.js | 93 ++ .../content/test/general/browser_plainTextLinks.js | 237 ++++ .../content/test/general/browser_printpreview.js | 43 + .../general/browser_private_browsing_window.js | 133 +++ .../test/general/browser_private_no_prompt.js | 12 + .../content/test/general/browser_refreshBlocker.js | 209 ++++ .../content/test/general/browser_relatedTabs.js | 74 ++ .../test/general/browser_remoteTroubleshoot.js | 130 +++ .../browser_remoteWebNavigation_postdata.js | 53 + .../test/general/browser_restore_isAppTab.js | 87 ++ .../test/general/browser_save_link-perwindowpb.js | 214 ++++ .../browser_save_link_when_window_navigates.js | 197 ++++ .../browser_save_private_link_perwindowpb.js | 127 +++ .../content/test/general/browser_save_video.js | 99 ++ .../test/general/browser_save_video_frame.js | 103 ++ .../test/general/browser_selectTabAtIndex.js | 89 ++ .../base/content/test/general/browser_star_hsts.js | 87 ++ .../content/test/general/browser_star_hsts.sjs | 12 + .../browser_storagePressure_notification.js | 182 ++++ .../base/content/test/general/browser_tabDrop.js | 207 ++++ .../general/browser_tab_close_dependent_window.js | 35 + .../test/general/browser_tab_detach_restore.js | 54 + .../general/browser_tab_drag_drop_perwindow.js | 423 ++++++++ .../content/test/general/browser_tab_dragdrop.js | 257 +++++ .../content/test/general/browser_tab_dragdrop2.js | 65 ++ .../general/browser_tab_dragdrop2_frame1.xhtml | 158 +++ .../test/general/browser_tab_dragdrop_embed.html | 2 + .../base/content/test/general/browser_tabfocus.js | 811 ++++++++++++++ .../general/browser_tabs_close_beforeunload.js | 69 ++ .../content/test/general/browser_tabs_isActive.js | 235 ++++ .../content/test/general/browser_tabs_owner.js | 40 + ...r_testOpenNewRemoteTabsFromNonRemoteBrowsers.js | 144 +++ .../content/test/general/browser_typeAheadFind.js | 31 + .../general/browser_unknownContentType_title.js | 88 ++ .../content/test/general/browser_unloaddialogs.js | 40 + .../general/browser_viewSourceInTabOnViewSource.js | 60 ++ .../test/general/browser_visibleFindSelection.js | 62 ++ .../content/test/general/browser_visibleTabs.js | 125 +++ .../browser_visibleTabs_bookmarkAllPages.js | 35 + .../test/general/browser_visibleTabs_tabPreview.js | 52 + .../test/general/browser_windowactivation.js | 112 ++ .../content/test/general/browser_zbug569342.js | 77 ++ browser/base/content/test/general/bug792517-2.html | 5 + browser/base/content/test/general/bug792517.html | 5 + browser/base/content/test/general/bug792517.sjs | 13 + .../content/test/general/clipboard_pastefile.html | 52 + .../content/test/general/close_beforeunload.html | 8 + .../close_beforeunload_opens_second_tab.html | 3 + .../base/content/test/general/download_page.html | 72 ++ .../base/content/test/general/download_page_1.txt | 1 + .../base/content/test/general/download_page_2.txt | 1 + .../download_with_content_disposition_header.sjs | 19 + browser/base/content/test/general/dummy.ics | 13 + .../base/content/test/general/dummy.ics^headers^ | 1 + browser/base/content/test/general/dummy_page.html | 9 + .../general/file_documentnavigation_frameset.html | 12 + .../test/general/file_double_close_tab.html | 15 + .../test/general/file_fullscreen-window-open.html | 22 + .../test/general/file_window_activation.html | 4 + .../test/general/file_window_activation2.html | 1 + .../test/general/file_with_link_to_http.html | 9 + browser/base/content/test/general/head.js | 347 ++++++ browser/base/content/test/general/moz.png | Bin 0 -> 580 bytes .../general/navigating_window_with_download.html | 7 + .../base/content/test/general/print_postdata.sjs | 25 + .../content/test/general/redirect_download.sjs | 11 + .../base/content/test/general/refresh_header.sjs | 24 + browser/base/content/test/general/refresh_meta.sjs | 36 + .../base/content/test/general/test_bug462673.html | 18 + .../base/content/test/general/test_bug628179.html | 9 + .../test/general/test_remoteTroubleshoot.html | 50 + browser/base/content/test/general/title_test.svg | 59 + .../test/general/unknownContentType_file.pif | 1 + .../general/unknownContentType_file.pif^headers^ | 1 + browser/base/content/test/general/video.ogg | Bin 0 -> 285310 bytes browser/base/content/test/general/web_video.html | 10 + browser/base/content/test/general/web_video1.ogv | Bin 0 -> 28942 bytes .../content/test/general/web_video1.ogv^headers^ | 3 + 168 files changed, 15536 insertions(+) create mode 100644 browser/base/content/test/general/alltabslistener.html create mode 100644 browser/base/content/test/general/app_bug575561.html create mode 100644 browser/base/content/test/general/app_subframe_bug575561.html create mode 100644 browser/base/content/test/general/audio.ogg create mode 100644 browser/base/content/test/general/browser.ini create mode 100644 browser/base/content/test/general/browser_accesskeys.js create mode 100644 browser/base/content/test/general/browser_addCertException.js create mode 100644 browser/base/content/test/general/browser_alltabslistener.js create mode 100644 browser/base/content/test/general/browser_backButtonFitts.js create mode 100644 browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js create mode 100644 browser/base/content/test/general/browser_bug1261299.js create mode 100644 browser/base/content/test/general/browser_bug1297539.js create mode 100644 browser/base/content/test/general/browser_bug1299667.js create mode 100644 browser/base/content/test/general/browser_bug321000.js create mode 100644 browser/base/content/test/general/browser_bug356571.js create mode 100644 browser/base/content/test/general/browser_bug380960.js create mode 100644 browser/base/content/test/general/browser_bug406216.js create mode 100644 browser/base/content/test/general/browser_bug417483.js create mode 100644 browser/base/content/test/general/browser_bug424101.js create mode 100644 browser/base/content/test/general/browser_bug427559.js create mode 100644 browser/base/content/test/general/browser_bug431826.js create mode 100644 browser/base/content/test/general/browser_bug432599.js create mode 100644 browser/base/content/test/general/browser_bug455852.js create mode 100644 browser/base/content/test/general/browser_bug462289.js create mode 100644 browser/base/content/test/general/browser_bug462673.js create mode 100644 browser/base/content/test/general/browser_bug479408.js create mode 100644 browser/base/content/test/general/browser_bug479408_sample.html create mode 100644 browser/base/content/test/general/browser_bug481560.js create mode 100644 browser/base/content/test/general/browser_bug484315.js create mode 100644 browser/base/content/test/general/browser_bug491431.js create mode 100644 browser/base/content/test/general/browser_bug495058.js create mode 100644 browser/base/content/test/general/browser_bug519216.js create mode 100644 browser/base/content/test/general/browser_bug520538.js create mode 100644 browser/base/content/test/general/browser_bug521216.js create mode 100644 browser/base/content/test/general/browser_bug533232.js create mode 100644 browser/base/content/test/general/browser_bug537013.js create mode 100644 browser/base/content/test/general/browser_bug537474.js create mode 100644 browser/base/content/test/general/browser_bug563588.js create mode 100644 browser/base/content/test/general/browser_bug565575.js create mode 100644 browser/base/content/test/general/browser_bug567306.js create mode 100644 browser/base/content/test/general/browser_bug575561.js create mode 100644 browser/base/content/test/general/browser_bug577121.js create mode 100644 browser/base/content/test/general/browser_bug578534.js create mode 100644 browser/base/content/test/general/browser_bug579872.js create mode 100644 browser/base/content/test/general/browser_bug581253.js create mode 100644 browser/base/content/test/general/browser_bug585785.js create mode 100644 browser/base/content/test/general/browser_bug585830.js create mode 100644 browser/base/content/test/general/browser_bug594131.js create mode 100644 browser/base/content/test/general/browser_bug596687.js create mode 100644 browser/base/content/test/general/browser_bug597218.js create mode 100644 browser/base/content/test/general/browser_bug609700.js create mode 100644 browser/base/content/test/general/browser_bug623893.js create mode 100644 browser/base/content/test/general/browser_bug624734.js create mode 100644 browser/base/content/test/general/browser_bug664672.js create mode 100644 browser/base/content/test/general/browser_bug676619.js create mode 100644 browser/base/content/test/general/browser_bug710878.js create mode 100644 browser/base/content/test/general/browser_bug724239.js create mode 100644 browser/base/content/test/general/browser_bug734076.js create mode 100644 browser/base/content/test/general/browser_bug749738.js create mode 100644 browser/base/content/test/general/browser_bug763468_perwindowpb.js create mode 100644 browser/base/content/test/general/browser_bug767836_perwindowpb.js create mode 100644 browser/base/content/test/general/browser_bug817947.js create mode 100644 browser/base/content/test/general/browser_bug832435.js create mode 100644 browser/base/content/test/general/browser_bug882977.js create mode 100644 browser/base/content/test/general/browser_bug963945.js create mode 100644 browser/base/content/test/general/browser_clipboard.js create mode 100644 browser/base/content/test/general/browser_clipboard_pastefile.js create mode 100644 browser/base/content/test/general/browser_contentAltClick.js create mode 100644 browser/base/content/test/general/browser_ctrlTab.js create mode 100644 browser/base/content/test/general/browser_datachoices_notification.js create mode 100644 browser/base/content/test/general/browser_documentnavigation.js create mode 100644 browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js create mode 100644 browser/base/content/test/general/browser_double_close_tab.js create mode 100644 browser/base/content/test/general/browser_drag.js create mode 100644 browser/base/content/test/general/browser_duplicateIDs.js create mode 100644 browser/base/content/test/general/browser_findbarClose.js create mode 100644 browser/base/content/test/general/browser_focusonkeydown.js create mode 100644 browser/base/content/test/general/browser_fullscreen-window-open.js create mode 100644 browser/base/content/test/general/browser_gestureSupport.js create mode 100644 browser/base/content/test/general/browser_hide_removing.js create mode 100644 browser/base/content/test/general/browser_homeDrop.js create mode 100644 browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js create mode 100644 browser/base/content/test/general/browser_lastAccessedTab.js create mode 100644 browser/base/content/test/general/browser_menuButtonFitts.js create mode 100644 browser/base/content/test/general/browser_middleMouse_noJSPaste.js create mode 100644 browser/base/content/test/general/browser_minimize.js create mode 100644 browser/base/content/test/general/browser_modifiedclick_inherit_principal.js create mode 100644 browser/base/content/test/general/browser_newTabDrop.js create mode 100644 browser/base/content/test/general/browser_newWindowDrop.js create mode 100644 browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js create mode 100644 browser/base/content/test/general/browser_newwindow_focus.js create mode 100644 browser/base/content/test/general/browser_plainTextLinks.js create mode 100644 browser/base/content/test/general/browser_printpreview.js create mode 100644 browser/base/content/test/general/browser_private_browsing_window.js create mode 100644 browser/base/content/test/general/browser_private_no_prompt.js create mode 100644 browser/base/content/test/general/browser_refreshBlocker.js create mode 100644 browser/base/content/test/general/browser_relatedTabs.js create mode 100644 browser/base/content/test/general/browser_remoteTroubleshoot.js create mode 100644 browser/base/content/test/general/browser_remoteWebNavigation_postdata.js create mode 100644 browser/base/content/test/general/browser_restore_isAppTab.js create mode 100644 browser/base/content/test/general/browser_save_link-perwindowpb.js create mode 100644 browser/base/content/test/general/browser_save_link_when_window_navigates.js create mode 100644 browser/base/content/test/general/browser_save_private_link_perwindowpb.js create mode 100644 browser/base/content/test/general/browser_save_video.js create mode 100644 browser/base/content/test/general/browser_save_video_frame.js create mode 100644 browser/base/content/test/general/browser_selectTabAtIndex.js create mode 100644 browser/base/content/test/general/browser_star_hsts.js create mode 100644 browser/base/content/test/general/browser_star_hsts.sjs create mode 100644 browser/base/content/test/general/browser_storagePressure_notification.js create mode 100644 browser/base/content/test/general/browser_tabDrop.js create mode 100644 browser/base/content/test/general/browser_tab_close_dependent_window.js create mode 100644 browser/base/content/test/general/browser_tab_detach_restore.js create mode 100644 browser/base/content/test/general/browser_tab_drag_drop_perwindow.js create mode 100644 browser/base/content/test/general/browser_tab_dragdrop.js create mode 100644 browser/base/content/test/general/browser_tab_dragdrop2.js create mode 100644 browser/base/content/test/general/browser_tab_dragdrop2_frame1.xhtml create mode 100644 browser/base/content/test/general/browser_tab_dragdrop_embed.html create mode 100644 browser/base/content/test/general/browser_tabfocus.js create mode 100644 browser/base/content/test/general/browser_tabs_close_beforeunload.js create mode 100644 browser/base/content/test/general/browser_tabs_isActive.js create mode 100644 browser/base/content/test/general/browser_tabs_owner.js create mode 100644 browser/base/content/test/general/browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js create mode 100644 browser/base/content/test/general/browser_typeAheadFind.js create mode 100644 browser/base/content/test/general/browser_unknownContentType_title.js create mode 100644 browser/base/content/test/general/browser_unloaddialogs.js create mode 100644 browser/base/content/test/general/browser_viewSourceInTabOnViewSource.js create mode 100644 browser/base/content/test/general/browser_visibleFindSelection.js create mode 100644 browser/base/content/test/general/browser_visibleTabs.js create mode 100644 browser/base/content/test/general/browser_visibleTabs_bookmarkAllPages.js create mode 100644 browser/base/content/test/general/browser_visibleTabs_tabPreview.js create mode 100644 browser/base/content/test/general/browser_windowactivation.js create mode 100644 browser/base/content/test/general/browser_zbug569342.js create mode 100644 browser/base/content/test/general/bug792517-2.html create mode 100644 browser/base/content/test/general/bug792517.html create mode 100644 browser/base/content/test/general/bug792517.sjs create mode 100644 browser/base/content/test/general/clipboard_pastefile.html create mode 100644 browser/base/content/test/general/close_beforeunload.html create mode 100644 browser/base/content/test/general/close_beforeunload_opens_second_tab.html create mode 100644 browser/base/content/test/general/download_page.html create mode 100644 browser/base/content/test/general/download_page_1.txt create mode 100644 browser/base/content/test/general/download_page_2.txt create mode 100644 browser/base/content/test/general/download_with_content_disposition_header.sjs create mode 100644 browser/base/content/test/general/dummy.ics create mode 100644 browser/base/content/test/general/dummy.ics^headers^ create mode 100644 browser/base/content/test/general/dummy_page.html create mode 100644 browser/base/content/test/general/file_documentnavigation_frameset.html create mode 100644 browser/base/content/test/general/file_double_close_tab.html create mode 100644 browser/base/content/test/general/file_fullscreen-window-open.html create mode 100644 browser/base/content/test/general/file_window_activation.html create mode 100644 browser/base/content/test/general/file_window_activation2.html create mode 100644 browser/base/content/test/general/file_with_link_to_http.html create mode 100644 browser/base/content/test/general/head.js create mode 100644 browser/base/content/test/general/moz.png create mode 100644 browser/base/content/test/general/navigating_window_with_download.html create mode 100644 browser/base/content/test/general/print_postdata.sjs create mode 100644 browser/base/content/test/general/redirect_download.sjs create mode 100644 browser/base/content/test/general/refresh_header.sjs create mode 100644 browser/base/content/test/general/refresh_meta.sjs create mode 100644 browser/base/content/test/general/test_bug462673.html create mode 100644 browser/base/content/test/general/test_bug628179.html create mode 100644 browser/base/content/test/general/test_remoteTroubleshoot.html create mode 100644 browser/base/content/test/general/title_test.svg create mode 100644 browser/base/content/test/general/unknownContentType_file.pif create mode 100644 browser/base/content/test/general/unknownContentType_file.pif^headers^ create mode 100644 browser/base/content/test/general/video.ogg create mode 100644 browser/base/content/test/general/web_video.html create mode 100644 browser/base/content/test/general/web_video1.ogv create mode 100644 browser/base/content/test/general/web_video1.ogv^headers^ (limited to 'browser/base/content/test/general') diff --git a/browser/base/content/test/general/alltabslistener.html b/browser/base/content/test/general/alltabslistener.html new file mode 100644 index 0000000000..166c31037a --- /dev/null +++ b/browser/base/content/test/general/alltabslistener.html @@ -0,0 +1,8 @@ + + +Test page for bug 463387 + + +

Test page for bug 463387

+ + diff --git a/browser/base/content/test/general/app_bug575561.html b/browser/base/content/test/general/app_bug575561.html new file mode 100644 index 0000000000..13c525487e --- /dev/null +++ b/browser/base/content/test/general/app_bug575561.html @@ -0,0 +1,18 @@ + + + + + Test for links in app tabs + + + same domain + same domain (different subdomain) + different domain + different domain (with target) + same domain (www prefix) + data: URI + + + diff --git a/browser/base/content/test/general/app_subframe_bug575561.html b/browser/base/content/test/general/app_subframe_bug575561.html new file mode 100644 index 0000000000..8690497ffb --- /dev/null +++ b/browser/base/content/test/general/app_subframe_bug575561.html @@ -0,0 +1,12 @@ + + + + + Test for links in app tab subframes + + + different domain + + diff --git a/browser/base/content/test/general/audio.ogg b/browser/base/content/test/general/audio.ogg new file mode 100644 index 0000000000..477544875d Binary files /dev/null and b/browser/base/content/test/general/audio.ogg differ diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini new file mode 100644 index 0000000000..2e9438135a --- /dev/null +++ b/browser/base/content/test/general/browser.ini @@ -0,0 +1,416 @@ +############################################################################### +# DO NOT ADD MORE TESTS HERE. # +# TRY ONE OF THE MORE TOPICAL SIBLING DIRECTORIES. # +# THIS DIRECTORY HAS 200+ TESTS AND TAKES AGES TO RUN ON A DEBUG BUILD. # +# PLEASE, FOR THE LOVE OF WHATEVER YOU HOLD DEAR, DO NOT ADD MORE TESTS HERE. # +############################################################################### + +[DEFAULT] +support-files = + alltabslistener.html + app_bug575561.html + app_subframe_bug575561.html + audio.ogg + browser_bug479408_sample.html + browser_star_hsts.sjs + browser_tab_dragdrop2_frame1.xhtml + browser_tab_dragdrop_embed.html + bug792517-2.html + bug792517.html + bug792517.sjs + clipboard_pastefile.html + download_page.html + download_page_1.txt + download_page_2.txt + download_with_content_disposition_header.sjs + dummy_page.html + file_documentnavigation_frameset.html + file_double_close_tab.html + file_fullscreen-window-open.html + file_with_link_to_http.html + head.js + moz.png + navigating_window_with_download.html + print_postdata.sjs + test_bug462673.html + test_bug628179.html + title_test.svg + unknownContentType_file.pif + unknownContentType_file.pif^headers^ + video.ogg + web_video.html + web_video1.ogv + web_video1.ogv^headers^ + !/image/test/mochitest/blue.png + !/toolkit/content/tests/browser/common/mockTransfer.js + +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_accesskeys.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_addCertException.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_alltabslistener.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_backButtonFitts.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_beforeunload_duplicate_dialogs.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug1261299.js] +skip-if = + os != "mac" # Because of tests for supporting Service Menu of macOS, bug 1261299 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug1297539.js] +skip-if = + os != "mac" # Because of tests for supporting pasting from Service Menu of macOS, bug 1297539 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug1299667.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug321000.js] +skip-if = true # browser_bug321000.js is disabled because newline handling is shaky (bug 592528) +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug356571.js] +skip-if = + verify && !debug && os == 'win' +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug380960.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug406216.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug417483.js] +skip-if = + verify && debug && os == 'mac' + os == 'mac' + os == 'linux' #Bug 1444703 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug424101.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug427559.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug431826.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug432599.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug455852.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug462289.js] +skip-if = + os == "mac" +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug462673.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug477014.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug479408.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug481560.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug484315.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug491431.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug495058.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug519216.js] +skip-if = true # Bug 1478159 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug520538.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug521216.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug533232.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug537013.js] +skip-if = true # bug 1393813 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug537474.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug563588.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug565575.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug567306.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug575561.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug577121.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug578534.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug579872.js] +skip-if = + verify && debug && os == 'linux' + os == 'mac' + os == 'linux' && !debug #Bug 1448915 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug581253.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug585785.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug585830.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug594131.js] +skip-if = + verify && debug && os == 'linux' +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug596687.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug597218.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug609700.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug623893.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug624734.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug664672.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug676619.js] +support-files = + dummy.ics + dummy.ics^headers^ + redirect_download.sjs +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug710878.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug724239.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug734076.js] +skip-if = + verify && debug && os == 'linux' +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug749738.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug763468_perwindowpb.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug767836_perwindowpb.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug817947.js] +skip-if = + os == 'linux' && !debug # Bug 1556066 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug832435.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug882977.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_bug963945.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_clipboard.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_clipboard_pastefile.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_contentAltClick.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_contentAreaClick.js] +skip-if = true # Clicks in content don't go through contentAreaClick. +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_ctrlTab.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_datachoices_notification.js] +skip-if = + !datareporting + verify && !debug && os == 'win' +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_documentnavigation.js] +skip-if = + verify && !debug && os == 'linux' +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_domFullscreen_fullscreenMode.js] +tags = fullscreen +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_double_close_tab.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_drag.js] +skip-if = true # browser_drag.js is disabled, as it needs to be updated for the new behavior from bug 320638. +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_duplicateIDs.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_findbarClose.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_focusonkeydown.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_fullscreen-window-open.js] +tags = fullscreen +skip-if = + os == "linux" # Linux: Intermittent failures - bug 941575. +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_gestureSupport.js] +support-files = + !/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js + !/gfx/layers/apz/test/mochitest/apz_test_utils.js +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_hide_removing.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_homeDrop.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_invalid_uri_back_forward_manipulation.js] +skip-if = + os == 'mac' && socketprocess_networking +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_lastAccessedTab.js] +skip-if = + os == "windows" # Disabled on Windows due to frequent failures (bug 969405) +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_menuButtonFitts.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_middleMouse_noJSPaste.js] +https_first_disabled = true +skip-if = + apple_silicon && !debug # Bug 1724711 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_minimize.js] +skip-if = + apple_silicon && !debug # Bug 1725756 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_modifiedclick_inherit_principal.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_newTabDrop.js] +https_first_disabled = true +skip-if = + os == "linux" && fission && tsan # high frequency intermittent +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_newWindowDrop.js] +https_first_disabled = true +skip-if = + os == "win" && os_version == "6.1" # bug 1715862 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_new_http_window_opened_from_file_tab.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_newwindow_focus.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_plainTextLinks.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_printpreview.js] +skip-if = + os == 'win' + os == 'linux' && os_version == '18.04' # Bug 1384127 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_private_browsing_window.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_private_no_prompt.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_refreshBlocker.js] +skip-if = + os == "mac" + os == "linux" && !debug + os == "win" && bits == 32 # Bug 1559410 for all instances +support-files = + refresh_header.sjs + refresh_meta.sjs +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_relatedTabs.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_remoteTroubleshoot.js] +https_first_disabled = true +skip-if = + !updater + os == 'linux' && asan # Bug 1711507 +reason = depends on UpdateUtils .Locale +support-files = + test_remoteTroubleshoot.html +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_remoteWebNavigation_postdata.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_restore_isAppTab.js] +skip-if = + !crashreporter # test requires crashreporter due to 1536221 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_save_link-perwindowpb.js] +skip-if = + debug && os == "win" + verify # Bug 1280505 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_save_link_when_window_navigates.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_save_private_link_perwindowpb.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_save_video.js] +skip-if = + os == 'mac' + verify && os == 'mac' + os == 'win' && debug + os =='linux' #Bug 1212419 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_save_video_frame.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_selectTabAtIndex.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_star_hsts.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_storagePressure_notification.js] +skip-if = verify +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tabDrop.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tab_close_dependent_window.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tab_detach_restore.js] +https_first_disabled = true +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tab_drag_drop_perwindow.js] +skip-if = + os == "win" && os_version == "6.1" && bits == 32 # bug 1717587 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tab_dragdrop.js] +skip-if = true # Bug 1312436, Bug 1388973 +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tab_dragdrop2.js] +skip-if = + os == "win" && bits == 32 && !debug # high frequency win7 intermittent: crash +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tabfocus.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tabkeynavigation.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tabs_close_beforeunload.js] +support-files = + close_beforeunload_opens_second_tab.html + close_beforeunload.html +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tabs_isActive.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_tabs_owner.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_typeAheadFind.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_unknownContentType_title.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_unloaddialogs.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_viewSourceInTabOnViewSource.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_visibleFindSelection.js] +skip-if = true # Bug 1409184 disabled because interactive find next is not automating properly +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_visibleTabs.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_visibleTabs_bookmarkAllPages.js] +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_visibleTabs_tabPreview.js] +skip-if = + os == "win" && !debug +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_windowactivation.js] +skip-if = + verify + os == "linux" && debug # Bug 1678774 +support-files = + file_window_activation.html + file_window_activation2.html +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. +[browser_zbug569342.js] +skip-if = true # Bug 1094240 - has findbar-related failures +# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. diff --git a/browser/base/content/test/general/browser_accesskeys.js b/browser/base/content/test/general/browser_accesskeys.js new file mode 100644 index 0000000000..c8b27d6307 --- /dev/null +++ b/browser/base/content/test/general/browser_accesskeys.js @@ -0,0 +1,202 @@ +add_task(async function () { + await pushPrefs(["ui.key.contentAccess", 5], ["ui.key.chromeAccess", 5]); + + const gPageURL1 = + "data:text/html,

" + + "" + + "Checkbox" + + "

"; + let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, gPageURL1); + + Services.focus.clearFocus(window); + + // Press an accesskey in the child document while the chrome is focused. + let focusedId = await performAccessKey(tab1.linkedBrowser, "y"); + is(focusedId, "button", "button accesskey"); + + // Press an accesskey in the child document while the content document is focused. + focusedId = await performAccessKey(tab1.linkedBrowser, "z"); + is(focusedId, "checkbox", "checkbox accesskey"); + + // Add an element with an accesskey to the chrome and press its accesskey while the chrome is focused. + let newButton = document.createXULElement("button"); + newButton.id = "chromebutton"; + newButton.setAttribute("accesskey", "z"); + document.documentElement.appendChild(newButton); + + Services.focus.clearFocus(window); + + newButton.getBoundingClientRect(); // Accesskey registration happens during frame construction. + + focusedId = await performAccessKeyForChrome("z"); + is(focusedId, "chromebutton", "chromebutton accesskey"); + + // Add a second tab and ensure that accesskey from the first tab is not used. + const gPageURL2 = + "data:text/html," + + "" + + ""; + let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, gPageURL2); + + Services.focus.clearFocus(window); + + focusedId = await performAccessKey(tab2.linkedBrowser, "y"); + is(focusedId, "tab2button", "button accesskey in tab2"); + + // Press the accesskey for the chrome element while the content document is focused. + focusedId = await performAccessKeyForChrome("z"); + is(focusedId, "chromebutton", "chromebutton accesskey"); + + gBrowser.removeTab(tab1); + gBrowser.removeTab(tab2); + + // Test whether access key for the newButton isn't available when content + // consumes the key event. + + // When content in the tab3 consumes all keydown events. + const gPageURL3 = + "data:text/html," + + "" + + ""; + let tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, gPageURL3); + + Services.focus.clearFocus(window); + + focusedId = await performAccessKey(tab3.linkedBrowser, "y"); + is(focusedId, "tab3button", "button accesskey in tab3 should be focused"); + + newButton.onfocus = () => { + ok(false, "chromebutton shouldn't get focus during testing with tab3"); + }; + + // Press the accesskey for the chrome element while the content document is focused. + focusedId = await performAccessKey(tab3.linkedBrowser, "z"); + is( + focusedId, + "tab3body", + "button accesskey in tab3 should keep having focus" + ); + + newButton.onfocus = null; + + gBrowser.removeTab(tab3); + + // When content in the tab4 consumes all keypress events. + const gPageURL4 = + "data:text/html," + + "" + + ""; + let tab4 = await BrowserTestUtils.openNewForegroundTab(gBrowser, gPageURL4); + + Services.focus.clearFocus(window); + + focusedId = await performAccessKey(tab4.linkedBrowser, "y"); + is(focusedId, "tab4button", "button accesskey in tab4 should be focused"); + + newButton.onfocus = () => { + // EventStateManager handles accesskey before dispatching keypress event + // into the DOM tree, therefore, chrome accesskey always wins focus from + // content. However, this is different from shortcut keys. + todo(false, "chromebutton shouldn't get focus during testing with tab4"); + }; + + // Press the accesskey for the chrome element while the content document is focused. + focusedId = await performAccessKey(tab4.linkedBrowser, "z"); + is( + focusedId, + "tab4body", + "button accesskey in tab4 should keep having focus" + ); + + newButton.onfocus = null; + + gBrowser.removeTab(tab4); + + newButton.remove(); +}); + +function performAccessKey(browser, key) { + return new Promise(resolve => { + let removeFocus, removeKeyDown, removeKeyUp; + function callback(eventName, result) { + removeFocus(); + removeKeyUp(); + removeKeyDown(); + + SpecialPowers.spawn(browser, [], () => { + let oldFocusedElement = content._oldFocusedElement; + delete content._oldFocusedElement; + return oldFocusedElement.id; + }).then(oldFocus => resolve(oldFocus)); + } + + removeFocus = BrowserTestUtils.addContentEventListener( + browser, + "focus", + callback, + { capture: true }, + event => { + if (!HTMLElement.isInstance(event.target)) { + return false; // ignore window and document focus events + } + + event.target.ownerGlobal._sent = true; + let focusedElement = event.target.ownerGlobal.document.activeElement; + event.target.ownerGlobal._oldFocusedElement = focusedElement; + focusedElement.blur(); + return true; + } + ); + + removeKeyDown = BrowserTestUtils.addContentEventListener( + browser, + "keydown", + () => {}, + { capture: true }, + event => { + event.target.ownerGlobal._sent = false; + return true; + } + ); + + removeKeyUp = BrowserTestUtils.addContentEventListener( + browser, + "keyup", + callback, + {}, + event => { + if (!event.target.ownerGlobal._sent) { + event.target.ownerGlobal._sent = true; + let focusedElement = event.target.ownerGlobal.document.activeElement; + event.target.ownerGlobal._oldFocusedElement = focusedElement; + focusedElement.blur(); + return true; + } + + return false; + } + ); + + // Spawn an no-op content task to better ensure that the messages + // for adding the event listeners above get handled. + SpecialPowers.spawn(browser, [], () => {}).then(() => { + EventUtils.synthesizeKey(key, { altKey: true, shiftKey: true }); + }); + }); +} + +// This version is used when a chrome element is expected to be found for an accesskey. +async function performAccessKeyForChrome(key, inChild) { + let waitFocusChangePromise = BrowserTestUtils.waitForEvent( + document, + "focus", + true + ); + EventUtils.synthesizeKey(key, { altKey: true, shiftKey: true }); + await waitFocusChangePromise; + return document.activeElement.id; +} diff --git a/browser/base/content/test/general/browser_addCertException.js b/browser/base/content/test/general/browser_addCertException.js new file mode 100644 index 0000000000..d3d1ac1ce4 --- /dev/null +++ b/browser/base/content/test/general/browser_addCertException.js @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Test adding a certificate exception by attempting to browse to a site with +// a bad certificate, being redirected to the internal about:certerror page, +// using the button contained therein to load the certificate exception +// dialog, using that to add an exception, and finally successfully visiting +// the site, including showing the right identity box and control center icons. +add_task(async function () { + await BrowserTestUtils.openNewForegroundTab(gBrowser); + await loadBadCertPage("https://expired.example.com"); + + let { gIdentityHandler } = gBrowser.ownerGlobal; + let promisePanelOpen = BrowserTestUtils.waitForEvent( + gBrowser.ownerGlobal, + "popupshown", + true, + event => event.target == gIdentityHandler._identityPopup + ); + gIdentityHandler._identityIconBox.click(); + await promisePanelOpen; + + let promiseViewShown = BrowserTestUtils.waitForEvent( + gIdentityHandler._identityPopup, + "ViewShown" + ); + document.getElementById("identity-popup-security-button").click(); + await promiseViewShown; + + is_element_visible( + document.getElementById("identity-icon"), + "Should see identity icon" + ); + let identityIconImage = gBrowser.ownerGlobal + .getComputedStyle(document.getElementById("identity-icon")) + .getPropertyValue("list-style-image"); + let securityViewBG = gBrowser.ownerGlobal + .getComputedStyle( + document + .getElementById("identity-popup-securityView") + .getElementsByClassName("identity-popup-security-connection")[0] + ) + .getPropertyValue("list-style-image"); + let securityContentBG = gBrowser.ownerGlobal + .getComputedStyle( + document + .getElementById("identity-popup-mainView") + .getElementsByClassName("identity-popup-security-connection")[0] + ) + .getPropertyValue("list-style-image"); + is( + identityIconImage, + 'url("chrome://global/skin/icons/security-warning.svg")', + "Using expected icon image in the identity block" + ); + is( + securityViewBG, + 'url("chrome://global/skin/icons/security-warning.svg")', + "Using expected icon image in the Control Center main view" + ); + is( + securityContentBG, + 'url("chrome://global/skin/icons/security-warning.svg")', + "Using expected icon image in the Control Center subview" + ); + + gIdentityHandler._identityPopup.hidePopup(); + + let certOverrideService = Cc[ + "@mozilla.org/security/certoverride;1" + ].getService(Ci.nsICertOverrideService); + certOverrideService.clearValidityOverride("expired.example.com", -1, {}); + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); diff --git a/browser/base/content/test/general/browser_alltabslistener.js b/browser/base/content/test/general/browser_alltabslistener.js new file mode 100644 index 0000000000..0c9677306d --- /dev/null +++ b/browser/base/content/test/general/browser_alltabslistener.js @@ -0,0 +1,331 @@ +const gCompleteState = + Ci.nsIWebProgressListener.STATE_STOP + + Ci.nsIWebProgressListener.STATE_IS_NETWORK; + +function getOriginalURL(request) { + return request && request.QueryInterface(Ci.nsIChannel).originalURI.spec; +} + +var gFrontProgressListener = { + onProgressChange( + aWebProgress, + aRequest, + aCurSelfProgress, + aMaxSelfProgress, + aCurTotalProgress, + aMaxTotalProgress + ) {}, + + onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + var url = getOriginalURL(aRequest); + if (url == "about:blank") { + return; + } + var state = "onStateChange"; + info( + "FrontProgress (" + url + "): " + state + " 0x" + aStateFlags.toString(16) + ); + assertCorrectBrowserAndEventOrderForFront(state); + }, + + onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) { + var url = getOriginalURL(aRequest); + if (url == "about:blank") { + return; + } + var state = "onLocationChange"; + info("FrontProgress: " + state + " " + aLocationURI.spec); + assertCorrectBrowserAndEventOrderForFront(state); + }, + + onSecurityChange(aWebProgress, aRequest, aState) { + var url = getOriginalURL(aRequest); + if (url == "about:blank") { + return; + } + var state = "onSecurityChange"; + info("FrontProgress (" + url + "): " + state + " 0x" + aState.toString(16)); + assertCorrectBrowserAndEventOrderForFront(state); + }, +}; + +function assertCorrectBrowserAndEventOrderForFront(aEventName) { + Assert.less( + gFrontNotificationsPos, + gFrontNotifications.length, + "Got an expected notification for the front notifications listener" + ); + is( + aEventName, + gFrontNotifications[gFrontNotificationsPos], + "Got a notification for the front notifications listener" + ); + gFrontNotificationsPos++; +} + +var gAllProgressListener = { + onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { + var url = getOriginalURL(aRequest); + if (url == "about:blank") { + // ignore initial about blank + return; + } + var state = "onStateChange"; + info( + "AllProgress (" + url + "): " + state + " 0x" + aStateFlags.toString(16) + ); + assertCorrectBrowserAndEventOrderForAll(state, aBrowser); + assertReceivedFlags( + state, + gAllNotifications[gAllNotificationsPos], + aStateFlags + ); + gAllNotificationsPos++; + + if ((aStateFlags & gCompleteState) == gCompleteState) { + is( + gAllNotificationsPos, + gAllNotifications.length, + "Saw the expected number of notifications" + ); + is( + gFrontNotificationsPos, + gFrontNotifications.length, + "Saw the expected number of frontnotifications" + ); + executeSoon(gNextTest); + } + }, + + onLocationChange(aBrowser, aWebProgress, aRequest, aLocationURI, aFlags) { + var url = getOriginalURL(aRequest); + if (url == "about:blank") { + // ignore initial about blank + return; + } + var state = "onLocationChange"; + info("AllProgress: " + state + " " + aLocationURI.spec); + assertCorrectBrowserAndEventOrderForAll(state, aBrowser); + assertReceivedFlags( + "onLocationChange", + gAllNotifications[gAllNotificationsPos], + aFlags + ); + gAllNotificationsPos++; + }, + + onSecurityChange(aBrowser, aWebProgress, aRequest, aState) { + var url = getOriginalURL(aRequest); + if (url == "about:blank") { + // ignore initial about blank + return; + } + var state = "onSecurityChange"; + info("AllProgress (" + url + "): " + state + " 0x" + aState.toString(16)); + assertCorrectBrowserAndEventOrderForAll(state, aBrowser); + is( + state, + gAllNotifications[gAllNotificationsPos], + "Got a notification for the all notifications listener" + ); + gAllNotificationsPos++; + }, +}; + +function assertCorrectBrowserAndEventOrderForAll(aState, aBrowser) { + ok( + aBrowser == gTestBrowser, + aState + " notification came from the correct browser" + ); + Assert.less( + gAllNotificationsPos, + gAllNotifications.length, + "Got an expected notification for the all notifications listener" + ); +} + +function assertReceivedFlags(aState, aObjOrEvent, aFlags) { + if (aObjOrEvent !== null && typeof aObjOrEvent === "object") { + is( + aState, + aObjOrEvent.state, + "Got a notification for the all notifications listener" + ); + is(aFlags, aFlags & aObjOrEvent.flags, `Got correct flags for ${aState}`); + } else { + is( + aState, + aObjOrEvent, + "Got a notification for the all notifications listener" + ); + } +} + +var gFrontNotifications, + gAllNotifications, + gFrontNotificationsPos, + gAllNotificationsPos; +var gBackgroundTab, + gForegroundTab, + gBackgroundBrowser, + gForegroundBrowser, + gTestBrowser; +var gTestPage = + "/browser/browser/base/content/test/general/alltabslistener.html"; +const kBasePage = + "http://mochi.test:8888/browser/browser/base/content/test/general/dummy_page.html"; +var gNextTest; + +async function test() { + waitForExplicitFinish(); + + gBackgroundTab = BrowserTestUtils.addTab(gBrowser); + gForegroundTab = BrowserTestUtils.addTab(gBrowser); + gBackgroundBrowser = gBrowser.getBrowserForTab(gBackgroundTab); + gForegroundBrowser = gBrowser.getBrowserForTab(gForegroundTab); + gBrowser.selectedTab = gForegroundTab; + + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange", + ]; + + // We must wait until a page has completed loading before + // starting tests or we get notifications from that + let promises = [ + BrowserTestUtils.browserStopped(gBackgroundBrowser, kBasePage), + BrowserTestUtils.browserStopped(gForegroundBrowser, kBasePage), + ]; + BrowserTestUtils.loadURIString(gBackgroundBrowser, kBasePage); + BrowserTestUtils.loadURIString(gForegroundBrowser, kBasePage); + await Promise.all(promises); + // If we process switched, the tabbrowser may still be processing the state_stop + // notification here because of how microtasks work. Ensure that that has + // happened before starting to test (which would add listeners to the tabbrowser + // which would get confused by being called about kBasePage loading). + await new Promise(executeSoon); + startTest1(); +} + +function runTest(browser, url, next) { + gFrontNotificationsPos = 0; + gAllNotificationsPos = 0; + gNextTest = next; + gTestBrowser = browser; + BrowserTestUtils.loadURIString(browser, url); +} + +function startTest1() { + info("\nTest 1"); + gBrowser.addProgressListener(gFrontProgressListener); + gBrowser.addTabsProgressListener(gAllProgressListener); + + gFrontNotifications = gAllNotifications; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest2); +} + +function startTest2() { + info("\nTest 2"); + gFrontNotifications = gAllNotifications; + runTest(gForegroundBrowser, "https://example.com" + gTestPage, startTest3); +} + +function startTest3() { + info("\nTest 3"); + gFrontNotifications = []; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + runTest(gBackgroundBrowser, "http://example.org" + gTestPage, startTest4); +} + +function startTest4() { + info("\nTest 4"); + gFrontNotifications = []; + runTest(gBackgroundBrowser, "https://example.com" + gTestPage, startTest5); +} + +function startTest5() { + info("\nTest 5"); + // Switch the foreground browser + [gForegroundBrowser, gBackgroundBrowser] = [ + gBackgroundBrowser, + gForegroundBrowser, + ]; + [gForegroundTab, gBackgroundTab] = [gBackgroundTab, gForegroundTab]; + // Avoid the onLocationChange this will fire + gBrowser.removeProgressListener(gFrontProgressListener); + gBrowser.selectedTab = gForegroundTab; + gBrowser.addProgressListener(gFrontProgressListener); + + gFrontNotifications = gAllNotifications; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest6); +} + +function startTest6() { + info("\nTest 6"); + gFrontNotifications = []; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + runTest(gBackgroundBrowser, "http://example.org" + gTestPage, startTest7); +} + +// Navigate from remote to non-remote +function startTest7() { + info("\nTest 7"); + gFrontNotifications = []; + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + { + state: "onLocationChange", + flags: Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT, + }, // dummy onLocationChange event + "onStateChange", + ]; + runTest(gBackgroundBrowser, "about:preferences", startTest8); +} + +// Navigate from non-remote to non-remote +function startTest8() { + info("\nTest 8"); + gFrontNotifications = []; + gAllNotifications = [ + "onStateChange", + { + state: "onStateChange", + flags: + Ci.nsIWebProgressListener.STATE_IS_REDIRECTED_DOCUMENT | + Ci.nsIWebProgressListener.STATE_IS_REQUEST | + Ci.nsIWebProgressListener.STATE_START, + }, + "onLocationChange", + "onSecurityChange", + "onStateChange", + ]; + runTest(gBackgroundBrowser, "about:config", startTest9); +} + +// Navigate from non-remote to remote +function startTest9() { + info("\nTest 9"); + gFrontNotifications = []; + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange", + ]; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + runTest(gBackgroundBrowser, "http://example.org" + gTestPage, finishTest); +} + +function finishTest() { + gBrowser.removeProgressListener(gFrontProgressListener); + gBrowser.removeTabsProgressListener(gAllProgressListener); + gBrowser.removeTab(gBackgroundTab); + gBrowser.removeTab(gForegroundTab); + finish(); +} diff --git a/browser/base/content/test/general/browser_backButtonFitts.js b/browser/base/content/test/general/browser_backButtonFitts.js new file mode 100644 index 0000000000..8ef3006a2c --- /dev/null +++ b/browser/base/content/test/general/browser_backButtonFitts.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +add_task(async function () { + let firstLocation = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/general/dummy_page.html"; + await BrowserTestUtils.openNewForegroundTab(gBrowser, firstLocation); + + await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function () { + // Push the state before maximizing the window and clicking below. + content.history.pushState("page2", "page2", "page2"); + }); + + window.maximize(); + + // Find where the nav-bar is vertically. + var navBar = document.getElementById("nav-bar"); + var boundingRect = navBar.getBoundingClientRect(); + var yPixel = boundingRect.top + Math.floor(boundingRect.height / 2); + var xPixel = 0; // Use the first pixel of the screen since it is maximized. + + let popStatePromise = BrowserTestUtils.waitForContentEvent( + gBrowser.selectedBrowser, + "popstate", + true + ); + EventUtils.synthesizeMouseAtPoint(xPixel, yPixel, {}, window); + await popStatePromise; + + is( + gBrowser.selectedBrowser.currentURI.spec, + firstLocation, + "Clicking the first pixel should have navigated back." + ); + window.restore(); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js new file mode 100644 index 0000000000..8a77f01ce4 --- /dev/null +++ b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js @@ -0,0 +1,114 @@ +const TEST_PAGE = + "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html"; + +const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( + "prompts.contentPromptSubDialog", + false +); + +var expectingDialog = false; +var wantToClose = true; +var resolveDialogPromise; + +function onTabModalDialogLoaded(node) { + ok( + !CONTENT_PROMPT_SUBDIALOG, + "Should not be using content prompt subdialogs." + ); + ok(expectingDialog, "Should be expecting this dialog."); + expectingDialog = false; + if (wantToClose) { + // This accepts the dialog, closing it + node.querySelector(".tabmodalprompt-button0").click(); + } else { + // This keeps the page open + node.querySelector(".tabmodalprompt-button1").click(); + } + if (resolveDialogPromise) { + resolveDialogPromise(); + } +} + +function onCommonDialogLoaded(promptWindow) { + ok(CONTENT_PROMPT_SUBDIALOG, "Should be using content prompt subdialogs."); + ok(expectingDialog, "Should be expecting this dialog."); + expectingDialog = false; + let dialog = promptWindow.Dialog; + if (wantToClose) { + // This accepts the dialog, closing it. + dialog.ui.button0.click(); + } else { + // This keeps the page open + dialog.ui.button1.click(); + } + if (resolveDialogPromise) { + resolveDialogPromise(); + } +} + +SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], +}); + +// Listen for the dialog being created +Services.obs.addObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded"); +Services.obs.addObserver(onCommonDialogLoaded, "common-dialog-loaded"); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("browser.tabs.warnOnClose"); + Services.obs.removeObserver(onTabModalDialogLoaded, "tabmodal-dialog-loaded"); + Services.obs.removeObserver(onCommonDialogLoaded, "common-dialog-loaded"); +}); + +add_task(async function closeLastTabInWindow() { + let newWin = await promiseOpenAndLoadWindow({}, true); + let firstTab = newWin.gBrowser.selectedTab; + await promiseTabLoadEvent(firstTab, TEST_PAGE); + let windowClosedPromise = BrowserTestUtils.domWindowClosed(newWin); + expectingDialog = true; + // close tab: + firstTab.closeButton.click(); + await windowClosedPromise; + ok(!expectingDialog, "There should have been a dialog."); + ok(newWin.closed, "Window should be closed."); +}); + +add_task(async function closeWindowWithMultipleTabsIncludingOneBeforeUnload() { + Services.prefs.setBoolPref("browser.tabs.warnOnClose", false); + let newWin = await promiseOpenAndLoadWindow({}, true); + let firstTab = newWin.gBrowser.selectedTab; + await promiseTabLoadEvent(firstTab, TEST_PAGE); + await promiseTabLoadEvent( + BrowserTestUtils.addTab(newWin.gBrowser), + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/" + ); + let windowClosedPromise = BrowserTestUtils.domWindowClosed(newWin); + expectingDialog = true; + newWin.BrowserTryToCloseWindow(); + await windowClosedPromise; + ok(!expectingDialog, "There should have been a dialog."); + ok(newWin.closed, "Window should be closed."); + Services.prefs.clearUserPref("browser.tabs.warnOnClose"); +}); + +add_task(async function closeWindoWithSingleTabTwice() { + let newWin = await promiseOpenAndLoadWindow({}, true); + let firstTab = newWin.gBrowser.selectedTab; + await promiseTabLoadEvent(firstTab, TEST_PAGE); + let windowClosedPromise = BrowserTestUtils.domWindowClosed(newWin); + expectingDialog = true; + wantToClose = false; + let firstDialogShownPromise = new Promise((resolve, reject) => { + resolveDialogPromise = resolve; + }); + firstTab.closeButton.click(); + await firstDialogShownPromise; + info("Got initial dialog, now trying again"); + expectingDialog = true; + wantToClose = true; + resolveDialogPromise = null; + firstTab.closeButton.click(); + await windowClosedPromise; + ok(!expectingDialog, "There should have been a dialog."); + ok(newWin.closed, "Window should be closed."); +}); diff --git a/browser/base/content/test/general/browser_bug1261299.js b/browser/base/content/test/general/browser_bug1261299.js new file mode 100644 index 0000000000..47b82a5da0 --- /dev/null +++ b/browser/base/content/test/general/browser_bug1261299.js @@ -0,0 +1,112 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Tests for Bug 1261299 + * Test that the service menu code path is called properly and the + * current selection (transferable) is cached properly on the parent process. + */ + +add_task(async function test_content_and_chrome_selection() { + let testPage = + "data:text/html," + + ''; + let DOMWindowUtils = EventUtils._getDOMWindowUtils(window); + let selectedText; + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage); + await BrowserTestUtils.synthesizeMouse( + "#textarea", + 0, + 0, + {}, + gBrowser.selectedBrowser + ); + await BrowserTestUtils.synthesizeKey( + "KEY_ArrowRight", + { shiftKey: true, ctrlKey: true }, + gBrowser.selectedBrowser + ); + selectedText = DOMWindowUtils.GetSelectionAsPlaintext(); + is( + selectedText, + "Write something here", + "The macOS services got the selected content text" + ); + gURLBar.value = "test.mozilla.org"; + await gURLBar.editor.selectAll(); + selectedText = DOMWindowUtils.GetSelectionAsPlaintext(); + is( + selectedText, + "test.mozilla.org", + "The macOS services got the selected chrome text" + ); + + BrowserTestUtils.removeTab(tab); +}); + +// Test switching active selection. +// Each tab has a content selection and when you switch to that tab, its selection becomes +// active aka the current selection. +// Expect: The active selection is what is being sent to OSX service menu. + +add_task(async function test_active_selection_switches_properly() { + let testPage1 = + // eslint-disable-next-line no-useless-concat + "data:text/html," + + ''; + let testPage2 = + // eslint-disable-next-line no-useless-concat + "data:text/html," + ''; + let DOMWindowUtils = EventUtils._getDOMWindowUtils(window); + let selectedText; + + let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage1); + await BrowserTestUtils.synthesizeMouse( + "#textarea", + 0, + 0, + {}, + gBrowser.selectedBrowser + ); + await BrowserTestUtils.synthesizeKey( + "KEY_ArrowRight", + { shiftKey: true, ctrlKey: true }, + gBrowser.selectedBrowser + ); + + let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage2); + await BrowserTestUtils.synthesizeMouse( + "#textarea", + 0, + 0, + {}, + gBrowser.selectedBrowser + ); + await BrowserTestUtils.synthesizeKey( + "KEY_ArrowRight", + { shiftKey: true, ctrlKey: true }, + gBrowser.selectedBrowser + ); + + await BrowserTestUtils.switchTab(gBrowser, tab1); + selectedText = DOMWindowUtils.GetSelectionAsPlaintext(); + is( + selectedText, + "Write something here", + "The macOS services got the selected content text" + ); + + await BrowserTestUtils.switchTab(gBrowser, tab2); + selectedText = DOMWindowUtils.GetSelectionAsPlaintext(); + is( + selectedText, + "Nothing available", + "The macOS services got the selected content text" + ); + + BrowserTestUtils.removeTab(tab1); + BrowserTestUtils.removeTab(tab2); +}); diff --git a/browser/base/content/test/general/browser_bug1297539.js b/browser/base/content/test/general/browser_bug1297539.js new file mode 100644 index 0000000000..7572d85eaf --- /dev/null +++ b/browser/base/content/test/general/browser_bug1297539.js @@ -0,0 +1,122 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Test for Bug 1297539 + * Test that the content event "pasteTransferable" + * (mozilla::EventMessage::eContentCommandPasteTransferable) + * is handled correctly for plain text and html in the remote case. + * + * Original test test_bug525389.html for command content event + * "pasteTransferable" runs only in the content process. + * This doesn't test the remote case. + * + */ + +"use strict"; + +function getLoadContext() { + return window.docShell.QueryInterface(Ci.nsILoadContext); +} + +function getTransferableFromClipboard(asHTML) { + let trans = Cc["@mozilla.org/widget/transferable;1"].createInstance( + Ci.nsITransferable + ); + trans.init(getLoadContext()); + if (asHTML) { + trans.addDataFlavor("text/html"); + } else { + trans.addDataFlavor("text/plain"); + } + Services.clipboard.getData(trans, Ci.nsIClipboard.kGlobalClipboard); + return trans; +} + +async function cutCurrentSelection(elementQueryString, property, browser) { + // Cut the current selection. + await BrowserTestUtils.synthesizeKey("x", { accelKey: true }, browser); + + // The editor should be empty after cut. + await SpecialPowers.spawn( + browser, + [[elementQueryString, property]], + async function ([contentElementQueryString, contentProperty]) { + let element = content.document.querySelector(contentElementQueryString); + is( + element[contentProperty], + "", + `${contentElementQueryString} should be empty after cut (superkey + x)` + ); + } + ); +} + +// Test that you are able to pasteTransferable for plain text +// which is handled by TextEditor::PasteTransferable to paste into the editor. +add_task(async function test_paste_transferable_plain_text() { + let testPage = + "data:text/html," + + ''; + + await BrowserTestUtils.withNewTab(testPage, async function (browser) { + // Select all the content in your editor element. + await BrowserTestUtils.synthesizeMouse("#textarea", 0, 0, {}, browser); + await BrowserTestUtils.synthesizeKey("a", { accelKey: true }, browser); + + await cutCurrentSelection("#textarea", "value", browser); + + let trans = getTransferableFromClipboard(false); + let DOMWindowUtils = EventUtils._getDOMWindowUtils(window); + DOMWindowUtils.sendContentCommandEvent("pasteTransferable", trans); + + await SpecialPowers.spawn(browser, [], async function () { + let textArea = content.document.querySelector("#textarea"); + is( + textArea.value, + "Write something here", + "Send content command pasteTransferable successful" + ); + }); + }); +}); + +// Test that you are able to pasteTransferable for html +// which is handled by HTMLEditor::PasteTransferable to paste into the editor. +// +// On Linux, +// BrowserTestUtils.synthesizeKey("a", {accelKey: true}, browser); +// doesn't seem to trigger for contenteditable which is why we use +// Selection to select the contenteditable contents. +add_task(async function test_paste_transferable_html() { + let testPage = + "data:text/html," + + '
Bold Textitalics
'; + + await BrowserTestUtils.withNewTab(testPage, async function (browser) { + // Select all the content in your editor element. + await BrowserTestUtils.synthesizeMouse("div", 0, 0, {}, browser); + await SpecialPowers.spawn(browser, [], async function () { + let element = content.document.querySelector("div"); + let selection = content.window.getSelection(); + selection.selectAllChildren(element); + }); + + await cutCurrentSelection("div", "textContent", browser); + + let trans = getTransferableFromClipboard(true); + let DOMWindowUtils = EventUtils._getDOMWindowUtils(window); + DOMWindowUtils.sendContentCommandEvent("pasteTransferable", trans); + + await SpecialPowers.spawn(browser, [], async function () { + let textArea = content.document.querySelector("div"); + is( + textArea.innerHTML, + "Bold Textitalics", + "Send content command pasteTransferable successful" + ); + }); + }); +}); diff --git a/browser/base/content/test/general/browser_bug1299667.js b/browser/base/content/test/general/browser_bug1299667.js new file mode 100644 index 0000000000..d281652c44 --- /dev/null +++ b/browser/base/content/test/general/browser_bug1299667.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function () { + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com"); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + content.history.pushState({}, "2", "2.html"); + }); + + await TestUtils.topicObserved("sessionstore-state-write-complete"); + + // Wait for the session data to be flushed before continuing the test + await new Promise(resolve => + SessionStore.getSessionHistory(gBrowser.selectedTab, resolve) + ); + + let backButton = document.getElementById("back-button"); + let contextMenu = document.getElementById("backForwardMenu"); + + info("waiting for the history menu to open"); + + let popupShownPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popupshown" + ); + EventUtils.synthesizeMouseAtCenter(backButton, { + type: "contextmenu", + button: 2, + }); + let event = await popupShownPromise; + + ok(true, "history menu opened"); + + // Wait for the session data to be flushed before continuing the test + await new Promise(resolve => + SessionStore.getSessionHistory(gBrowser.selectedTab, resolve) + ); + + is(event.target.children.length, 2, "Two history items"); + + let node = event.target.firstElementChild; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + is(node.getAttribute("uri"), "http://example.com/2.html", "first item uri"); + is(node.getAttribute("index"), "1", "first item index"); + is(node.getAttribute("historyindex"), "0", "first item historyindex"); + + node = event.target.lastElementChild; + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + is(node.getAttribute("uri"), "http://example.com/", "second item uri"); + is(node.getAttribute("index"), "0", "second item index"); + is(node.getAttribute("historyindex"), "-1", "second item historyindex"); + + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + event.target.hidePopup(); + await popupHiddenPromise; + info("Hidden popup"); + + let onClose = BrowserTestUtils.waitForEvent( + gBrowser.tabContainer, + "TabClose" + ); + BrowserTestUtils.removeTab(gBrowser.selectedTab); + await onClose; + info("Tab closed"); +}); diff --git a/browser/base/content/test/general/browser_bug321000.js b/browser/base/content/test/general/browser_bug321000.js new file mode 100644 index 0000000000..78ab74e543 --- /dev/null +++ b/browser/base/content/test/general/browser_bug321000.js @@ -0,0 +1,91 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const kTestString = " hello hello \n world\nworld "; + +var gTests = [ + { + desc: "Urlbar strips newlines and surrounding whitespace", + element: gURLBar, + expected: kTestString.replace(/\s*\n\s*/g, ""), + }, + + { + desc: "Searchbar replaces newlines with spaces", + element: document.getElementById("searchbar"), + expected: kTestString.replace(/\n/g, " "), + }, +]; + +// Test for bug 23485 and bug 321000. +// Urlbar should strip newlines, +// search bar should replace newlines with spaces. +function test() { + waitForExplicitFinish(); + + let cbHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService( + Ci.nsIClipboardHelper + ); + + // Put a multi-line string in the clipboard. + // Setting the clipboard value is an async OS operation, so we need to poll + // the clipboard for valid data before going on. + waitForClipboard( + kTestString, + function () { + cbHelper.copyString(kTestString); + }, + next_test, + finish + ); +} + +function next_test() { + if (gTests.length) { + test_paste(gTests.shift()); + } else { + finish(); + } +} + +function test_paste(aCurrentTest) { + var element = aCurrentTest.element; + + // Register input listener. + var inputListener = { + test: aCurrentTest, + handleEvent(event) { + element.removeEventListener(event.type, this); + + is(element.value, this.test.expected, this.test.desc); + + // Clear the field and go to next test. + element.value = ""; + setTimeout(next_test, 0); + }, + }; + element.addEventListener("input", inputListener); + + // Focus the window. + window.focus(); + gBrowser.selectedBrowser.focus(); + + // Focus the element and wait for focus event. + info("About to focus " + element.id); + element.addEventListener( + "focus", + function () { + executeSoon(function () { + // Pasting is async because the Accel+V codepath ends up going through + // nsDocumentViewer::FireClipboardEvent. + info("Pasting into " + element.id); + EventUtils.synthesizeKey("v", { accelKey: true }); + }); + }, + { once: true } + ); + element.focus(); +} diff --git a/browser/base/content/test/general/browser_bug356571.js b/browser/base/content/test/general/browser_bug356571.js new file mode 100644 index 0000000000..185d59d8fd --- /dev/null +++ b/browser/base/content/test/general/browser_bug356571.js @@ -0,0 +1,100 @@ +// Bug 356571 - loadOneOrMoreURIs gives up if one of the URLs has an unknown protocol + +var Cm = Components.manager; + +// Set to true when docShell alerts for unknown protocol error +var didFail = false; + +// Override Alert to avoid blocking the test due to unknown protocol error +const kPromptServiceUUID = "{6cc9c9fe-bc0b-432b-a410-253ef8bcc699}"; +const kPromptServiceContractID = "@mozilla.org/prompter;1"; + +// Save original prompt service factory +const kPromptServiceFactory = Cm.getClassObject( + Cc[kPromptServiceContractID], + Ci.nsIFactory +); + +var fakePromptServiceFactory = { + createInstance(aIid) { + return promptService.QueryInterface(aIid); + }, +}; + +var promptService = { + QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]), + alert() { + didFail = true; + }, +}; + +/* FIXME +Cm.QueryInterface(Ci.nsIComponentRegistrar) + .registerFactory(Components.ID(kPromptServiceUUID), "Prompt Service", + kPromptServiceContractID, fakePromptServiceFactory); +*/ + +const kCompleteState = + Ci.nsIWebProgressListener.STATE_STOP + + Ci.nsIWebProgressListener.STATE_IS_NETWORK; + +const kDummyPage = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/general/dummy_page.html"; +const kURIs = ["bad://www.mozilla.org/", kDummyPage, kDummyPage]; + +var gProgressListener = { + _runCount: 0, + onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { + if ((aStateFlags & kCompleteState) == kCompleteState) { + if (++this._runCount != kURIs.length) { + return; + } + // Check we failed on unknown protocol (received an alert from docShell) + ok(didFail, "Correctly failed on unknown protocol"); + // Check we opened all tabs + ok( + gBrowser.tabs.length == kURIs.length, + "Correctly opened all expected tabs" + ); + finishTest(); + } + }, +}; + +function test() { + todo(false, "temp. disabled"); + /* FIXME */ + /* + waitForExplicitFinish(); + // Wait for all tabs to finish loading + gBrowser.addTabsProgressListener(gProgressListener); + loadOneOrMoreURIs(kURIs.join("|")); + */ +} + +function finishTest() { + // Unregister the factory so we do not leak + Cm.QueryInterface(Ci.nsIComponentRegistrar).unregisterFactory( + Components.ID(kPromptServiceUUID), + fakePromptServiceFactory + ); + + // Restore the original factory + Cm.QueryInterface(Ci.nsIComponentRegistrar).registerFactory( + Components.ID(kPromptServiceUUID), + "Prompt Service", + kPromptServiceContractID, + kPromptServiceFactory + ); + + // Remove the listener + gBrowser.removeTabsProgressListener(gProgressListener); + + // Close opened tabs + for (var i = gBrowser.tabs.length - 1; i > 0; i--) { + gBrowser.removeTab(gBrowser.tabs[i]); + } + + finish(); +} diff --git a/browser/base/content/test/general/browser_bug380960.js b/browser/base/content/test/general/browser_bug380960.js new file mode 100644 index 0000000000..5571d8f08e --- /dev/null +++ b/browser/base/content/test/general/browser_bug380960.js @@ -0,0 +1,18 @@ +function test() { + var tab = BrowserTestUtils.addTab(gBrowser, "about:blank", { + skipAnimation: true, + }); + gBrowser.removeTab(tab); + is(tab.parentNode, null, "tab removed immediately"); + + tab = BrowserTestUtils.addTab(gBrowser, "about:blank", { + skipAnimation: true, + }); + gBrowser.removeTab(tab, { animate: true }); + gBrowser.removeTab(tab); + is( + tab.parentNode, + null, + "tab removed immediately when calling removeTab again after the animation was kicked off" + ); +} diff --git a/browser/base/content/test/general/browser_bug406216.js b/browser/base/content/test/general/browser_bug406216.js new file mode 100644 index 0000000000..bee262e4f8 --- /dev/null +++ b/browser/base/content/test/general/browser_bug406216.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* + * "TabClose" event is possibly used for closing related tabs of the current. + * "removeTab" method should work correctly even if the number of tabs are + * changed while "TabClose" event. + */ + +var count = 0; +const URIS = [ + "about:config", + "about:plugins", + "about:buildconfig", + "data:text/html,OK", +]; + +function test() { + waitForExplicitFinish(); + URIS.forEach(addTab); +} + +function addTab(aURI, aIndex) { + var tab = BrowserTestUtils.addTab(gBrowser, aURI); + if (aIndex == 0) { + gBrowser.removeTab(gBrowser.tabs[0], { skipPermitUnload: true }); + } + + BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => { + if (++count == URIS.length) { + executeSoon(doTabsTest); + } + }); +} + +function doTabsTest() { + is(gBrowser.tabs.length, URIS.length, "Correctly opened all expected tabs"); + + // sample of "close related tabs" feature + gBrowser.tabContainer.addEventListener( + "TabClose", + function (event) { + var closedTab = event.originalTarget; + var scheme = closedTab.linkedBrowser.currentURI.scheme; + Array.from(gBrowser.tabs).forEach(function (aTab) { + if ( + aTab != closedTab && + aTab.linkedBrowser.currentURI.scheme == scheme + ) { + gBrowser.removeTab(aTab, { skipPermitUnload: true }); + } + }); + }, + { capture: true, once: true } + ); + + gBrowser.removeTab(gBrowser.tabs[0], { skipPermitUnload: true }); + is(gBrowser.tabs.length, 1, "Related tabs are not closed unexpectedly"); + + BrowserTestUtils.addTab(gBrowser, "about:blank"); + gBrowser.removeTab(gBrowser.tabs[0], { skipPermitUnload: true }); + finish(); +} diff --git a/browser/base/content/test/general/browser_bug417483.js b/browser/base/content/test/general/browser_bug417483.js new file mode 100644 index 0000000000..6c8619b532 --- /dev/null +++ b/browser/base/content/test/general/browser_bug417483.js @@ -0,0 +1,50 @@ +add_task(async function () { + let loadedPromise = BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + true + ); + const htmlContent = + "data:text/html, "; + BrowserTestUtils.loadURIString(gBrowser, htmlContent); + await loadedPromise; + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function (arg) { + let frame = content.frames[0]; + let sel = frame.getSelection(); + let range = frame.document.createRange(); + let tn = frame.document.body.childNodes[0]; + range.setStart(tn, 4); + range.setEnd(tn, 5); + sel.addRange(range); + frame.focus(); + }); + + let contentAreaContextMenu = document.getElementById( + "contentAreaContextMenu" + ); + + let popupShownPromise = BrowserTestUtils.waitForEvent( + contentAreaContextMenu, + "popupshown" + ); + await BrowserTestUtils.synthesizeMouse( + "frame", + 5, + 5, + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + await popupShownPromise; + + ok( + document.getElementById("frame-sep").hidden, + "'frame-sep' should be hidden if the selection contains only spaces" + ); + + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + contentAreaContextMenu, + "popuphidden" + ); + contentAreaContextMenu.hidePopup(); + await popupHiddenPromise; +}); diff --git a/browser/base/content/test/general/browser_bug424101.js b/browser/base/content/test/general/browser_bug424101.js new file mode 100644 index 0000000000..ecaf7064ab --- /dev/null +++ b/browser/base/content/test/general/browser_bug424101.js @@ -0,0 +1,72 @@ +/* Make sure that the context menu appears on form elements */ + +add_task(async function () { + await BrowserTestUtils.openNewForegroundTab(gBrowser, "data:text/html,test"); + + let contentAreaContextMenu = document.getElementById( + "contentAreaContextMenu" + ); + + let tests = [ + { element: "input", type: "text" }, + { element: "input", type: "password" }, + { element: "input", type: "image" }, + { element: "input", type: "button" }, + { element: "input", type: "submit" }, + { element: "input", type: "reset" }, + { element: "input", type: "checkbox" }, + { element: "input", type: "radio" }, + { element: "button" }, + { element: "select" }, + { element: "option" }, + { element: "optgroup" }, + ]; + + for (let index = 0; index < tests.length; index++) { + let test = tests[index]; + + await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [{ element: test.element, type: test.type, index }], + async function (arg) { + let element = content.document.createElement(arg.element); + element.id = "element" + arg.index; + if (arg.type) { + element.setAttribute("type", arg.type); + } + content.document.body.appendChild(element); + } + ); + + let popupShownPromise = BrowserTestUtils.waitForEvent( + contentAreaContextMenu, + "popupshown" + ); + await BrowserTestUtils.synthesizeMouseAtCenter( + "#element" + index, + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + await popupShownPromise; + + let typeAttr = test.type ? "type=" + test.type + " " : ""; + is( + gContextMenu.shouldDisplay, + true, + "context menu behavior for <" + + test.element + + " " + + typeAttr + + "> is wrong" + ); + + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + contentAreaContextMenu, + "popuphidden" + ); + contentAreaContextMenu.hidePopup(); + await popupHiddenPromise; + } + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/general/browser_bug427559.js b/browser/base/content/test/general/browser_bug427559.js new file mode 100644 index 0000000000..29acf0862b --- /dev/null +++ b/browser/base/content/test/general/browser_bug427559.js @@ -0,0 +1,41 @@ +"use strict"; + +/* + * Test bug 427559 to make sure focused elements that are no longer on the page + * will have focus transferred to the window when changing tabs back to that + * tab with the now-gone element. + */ + +// Default focus on a button and have it kill itself on blur. +const URL = + "data:text/html;charset=utf-8," + + '"; +var testPage2 = + "data:text/html,"; +var testPage3 = + "data:text/html,"; + +var fm = Services.focus; + +async function expectFocusOnF6( + backward, + expectedDocument, + expectedElement, + onContent, + desc +) { + if (onContent) { + let success = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [expectedElement], + async function (expectedElementId) { + content.lastResult = ""; + let contentExpectedElement = + content.document.getElementById(expectedElementId); + if (!contentExpectedElement) { + // Element not found, so look in the child frames. + for (let f = 0; f < content.frames.length; f++) { + if (content.frames[f].document.getElementById(expectedElementId)) { + contentExpectedElement = content.frames[f].document; + break; + } + } + } else if (contentExpectedElement.localName == "html") { + contentExpectedElement = contentExpectedElement.ownerDocument; + } + + if (!contentExpectedElement) { + return null; + } + + contentExpectedElement.addEventListener( + "focus", + function () { + let details = + Services.focus.focusedWindow.document.documentElement.id; + if (Services.focus.focusedElement) { + details += "," + Services.focus.focusedElement.id; + } + + // Assign the result to a temporary place, to be used + // by the next spawn call. + content.lastResult = details; + }, + { capture: true, once: true } + ); + + return !!contentExpectedElement; + } + ); + + ok(success, "expected element " + expectedElement + " was found"); + + EventUtils.synthesizeKey("VK_F6", { shiftKey: backward }); + + let expected = expectedDocument; + if (!expectedElement.startsWith("html")) { + expected += "," + expectedElement; + } + + let result = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [], + async () => { + await ContentTaskUtils.waitForCondition(() => content.lastResult); + return content.lastResult; + } + ); + is(result, expected, desc + " child focus matches"); + } else { + let focusPromise = BrowserTestUtils.waitForEvent(window, "focus", true); + EventUtils.synthesizeKey("VK_F6", { shiftKey: backward }); + await focusPromise; + } + + if (typeof expectedElement == "string") { + expectedElement = fm.focusedWindow.document.getElementById(expectedElement); + } + + if (gMultiProcessBrowser && onContent) { + expectedDocument = "main-window"; + expectedElement = gBrowser.selectedBrowser; + } + + is( + fm.focusedWindow.document.documentElement.id, + expectedDocument, + desc + " document matches" + ); + is( + fm.focusedElement, + expectedElement, + desc + + " element matches (wanted: " + + expectedElement.id + + " got: " + + fm.focusedElement.id + + ")" + ); +} + +// Load a page and navigate between it and the chrome window. +add_task(async function () { + let page1Promise = BrowserTestUtils.browserLoaded( + gBrowser.selectedBrowser, + false, + testPage1 + ); + BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, testPage1); + await page1Promise; + + // When the urlbar is focused, pressing F6 should focus the root of the content page. + gURLBar.focus(); + await expectFocusOnF6( + false, + "html1", + "html1", + true, + "basic focus content page" + ); + + // When the content is focused, pressing F6 should focus the urlbar. + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "basic focus content page urlbar" + ); + + // When a button in content is focused, pressing F6 should focus the urlbar. + await expectFocusOnF6( + false, + "html1", + "html1", + true, + "basic focus content page with button focused" + ); + + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + return content.document.getElementById("button1").focus(); + }); + + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "basic focus content page with button focused urlbar" + ); + + // The document root should be focused, not the button + await expectFocusOnF6( + false, + "html1", + "html1", + true, + "basic focus again content page with button focused" + ); + + // Check to ensure that the root element is focused + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { + Assert.ok( + content.document.activeElement == content.document.documentElement, + "basic focus again content page with button focused child root is focused" + ); + }); +}); + +// Open a second tab. Document focus should skip the background tab. +add_task(async function () { + await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage2); + + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "basic focus content page and second tab urlbar" + ); + await expectFocusOnF6( + false, + "html2", + "html2", + true, + "basic focus content page with second tab" + ); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +// Shift+F6 should navigate backwards. There's only one document here so the effect +// is the same. +add_task(async function () { + gURLBar.focus(); + await expectFocusOnF6( + true, + "html1", + "html1", + true, + "back focus content page" + ); + await expectFocusOnF6( + true, + "main-window", + gURLBar.inputField, + false, + "back focus content page urlbar" + ); +}); + +// Open the sidebar and navigate between the sidebar, content and top-level window +add_task(async function () { + let sidebar = document.getElementById("sidebar"); + + let loadPromise = BrowserTestUtils.waitForEvent(sidebar, "load", true); + SidebarUI.toggle("viewBookmarksSidebar"); + await loadPromise; + + gURLBar.focus(); + await expectFocusOnF6( + false, + "bookmarksPanel", + sidebar.contentDocument.getElementById("search-box").inputField, + false, + "focus with sidebar open sidebar" + ); + await expectFocusOnF6( + false, + "html1", + "html1", + true, + "focus with sidebar open content" + ); + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "focus with sidebar urlbar" + ); + + // Now go backwards + await expectFocusOnF6( + true, + "html1", + "html1", + true, + "back focus with sidebar open content" + ); + await expectFocusOnF6( + true, + "bookmarksPanel", + sidebar.contentDocument.getElementById("search-box").inputField, + false, + "back focus with sidebar open sidebar" + ); + await expectFocusOnF6( + true, + "main-window", + gURLBar.inputField, + false, + "back focus with sidebar urlbar" + ); + + SidebarUI.toggle("viewBookmarksSidebar"); +}); + +// Navigate when the downloads panel is open +add_task(async function test_download_focus() { + await pushPrefs( + ["accessibility.tabfocus", 7], + ["browser.download.autohideButton", false], + ["security.dialog_enable_delay", 0] + ); + await promiseButtonShown("downloads-button"); + + let popupShownPromise = BrowserTestUtils.waitForEvent( + document, + "popupshown", + true + ); + EventUtils.synthesizeMouseAtCenter( + document.getElementById("downloads-button"), + {} + ); + await popupShownPromise; + + gURLBar.focus(); + await expectFocusOnF6( + false, + "main-window", + document.getElementById("downloadsHistory"), + false, + "focus with downloads panel open panel" + ); + await expectFocusOnF6( + false, + "html1", + "html1", + true, + "focus with downloads panel open" + ); + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "focus downloads panel open urlbar" + ); + + // Now go backwards + await expectFocusOnF6( + true, + "html1", + "html1", + true, + "back focus with downloads panel open" + ); + await expectFocusOnF6( + true, + "main-window", + document.getElementById("downloadsHistory"), + false, + "back focus with downloads panel open" + ); + await expectFocusOnF6( + true, + "main-window", + gURLBar.inputField, + false, + "back focus downloads panel open urlbar" + ); + + let downloadsPopup = document.getElementById("downloadsPanel"); + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + downloadsPopup, + "popuphidden", + true + ); + downloadsPopup.hidePopup(); + await popupHiddenPromise; +}); + +// Navigation with a contenteditable body +add_task(async function () { + await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage3); + + // The body should be focused when it is editable, not the root. + gURLBar.focus(); + await expectFocusOnF6( + false, + "html3", + "body3", + true, + "focus with contenteditable body" + ); + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "focus with contenteditable body urlbar" + ); + + // Now go backwards + + await expectFocusOnF6( + false, + "html3", + "body3", + true, + "back focus with contenteditable body" + ); + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "back focus with contenteditable body urlbar" + ); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +// Navigation with a frameset loaded +add_task(async function () { + await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "http://mochi.test:8888/browser/browser/base/content/test/general/file_documentnavigation_frameset.html" + ); + + gURLBar.focus(); + await expectFocusOnF6( + false, + "htmlframe1", + "htmlframe1", + true, + "focus on frameset frame 0" + ); + await expectFocusOnF6( + false, + "htmlframe2", + "htmlframe2", + true, + "focus on frameset frame 1" + ); + await expectFocusOnF6( + false, + "htmlframe3", + "htmlframe3", + true, + "focus on frameset frame 2" + ); + await expectFocusOnF6( + false, + "htmlframe4", + "htmlframe4", + true, + "focus on frameset frame 3" + ); + await expectFocusOnF6( + false, + "main-window", + gURLBar.inputField, + false, + "focus on frameset frame urlbar" + ); + + await expectFocusOnF6( + true, + "htmlframe4", + "htmlframe4", + true, + "back focus on frameset frame 3" + ); + await expectFocusOnF6( + true, + "htmlframe3", + "htmlframe3", + true, + "back focus on frameset frame 2" + ); + await expectFocusOnF6( + true, + "htmlframe2", + "htmlframe2", + true, + "back focus on frameset frame 1" + ); + await expectFocusOnF6( + true, + "htmlframe1", + "htmlframe1", + true, + "back focus on frameset frame 0" + ); + await expectFocusOnF6( + true, + "main-window", + gURLBar.inputField, + false, + "back focus on frameset frame urlbar" + ); + + BrowserTestUtils.removeTab(gBrowser.selectedTab); +}); + +// XXXndeakin add tests for browsers inside of panels + +function promiseButtonShown(id) { + let dwu = window.windowUtils; + return TestUtils.waitForCondition(() => { + let target = document.getElementById(id); + let bounds = dwu.getBoundsWithoutFlushing(target); + return bounds.width > 0 && bounds.height > 0; + }, `Waiting for button ${id} to have non-0 size`); +} diff --git a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js new file mode 100644 index 0000000000..c96fa6cf7b --- /dev/null +++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js @@ -0,0 +1,237 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +"use strict"; + +// This test tends to trigger a race in the fullscreen time telemetry, +// where the fullscreen enter and fullscreen exit events (which use the +// same histogram ID) overlap. That causes TelemetryStopwatch to log an +// error. +SimpleTest.ignoreAllUncaughtExceptions(true); + +function listenOneEvent(aEvent, aListener) { + function listener(evt) { + removeEventListener(aEvent, listener); + aListener(evt); + } + addEventListener(aEvent, listener); +} + +function queryFullscreenState(browser) { + return SpecialPowers.spawn(browser, [], () => { + return { + inDOMFullscreen: !!content.document.fullscreenElement, + inFullscreen: content.fullScreen, + }; + }); +} + +function captureUnexpectedFullscreenChange() { + ok(false, "catched an unexpected fullscreen change"); +} + +const FS_CHANGE_DOM = 1 << 0; +const FS_CHANGE_SIZE = 1 << 1; +const FS_CHANGE_BOTH = FS_CHANGE_DOM | FS_CHANGE_SIZE; + +function waitForDocActivated(aBrowser) { + return SpecialPowers.spawn(aBrowser, [], () => { + return ContentTaskUtils.waitForCondition( + () => content.browsingContext.isActive && content.document.hasFocus() + ); + }); +} + +function waitForFullscreenChanges(aBrowser, aFlags) { + return new Promise(resolve => { + let fullscreenData = null; + let sizemodeChanged = false; + function tryResolve() { + if ( + (!(aFlags & FS_CHANGE_DOM) || fullscreenData) && + (!(aFlags & FS_CHANGE_SIZE) || sizemodeChanged) + ) { + // In the platforms that support reporting occlusion state (e.g. Mac), + // enter/exit fullscreen mode will trigger docshell being set to + // non-activate and then set to activate back again. + // For those platform, we should wait until the docshell has been + // activated again, otherwise, the fullscreen request might be denied. + waitForDocActivated(aBrowser).then(() => { + if (!fullscreenData) { + queryFullscreenState(aBrowser).then(resolve); + } else { + resolve(fullscreenData); + } + }); + } + } + if (aFlags & FS_CHANGE_SIZE) { + listenOneEvent("sizemodechange", () => { + sizemodeChanged = true; + tryResolve(); + }); + } + if (aFlags & FS_CHANGE_DOM) { + BrowserTestUtils.waitForContentEvent(aBrowser, "fullscreenchange").then( + async () => { + fullscreenData = await queryFullscreenState(aBrowser); + tryResolve(); + } + ); + } + }); +} + +var gTests = [ + { + desc: "document method", + affectsFullscreenMode: false, + exitFunc: browser => { + SpecialPowers.spawn(browser, [], () => { + content.document.exitFullscreen(); + }); + }, + }, + { + desc: "escape key", + affectsFullscreenMode: false, + exitFunc: () => { + executeSoon(() => EventUtils.synthesizeKey("KEY_Escape")); + }, + }, + { + desc: "F11 key", + affectsFullscreenMode: true, + exitFunc() { + executeSoon(() => EventUtils.synthesizeKey("KEY_F11")); + }, + }, +]; + +function checkState(expectedStates, contentStates) { + is( + contentStates.inDOMFullscreen, + expectedStates.inDOMFullscreen, + "The DOM fullscreen state of the content should match" + ); + // TODO window.fullScreen is not updated as soon as the fullscreen + // state flips in child process, hence checking it could cause + // anonying intermittent failure. As we just want to confirm the + // fullscreen state of the browser window, we can just check the + // that on the chrome window below. + // is(contentStates.inFullscreen, expectedStates.inFullscreen, + // "The fullscreen state of the content should match"); + is( + !!document.fullscreenElement, + expectedStates.inDOMFullscreen, + "The DOM fullscreen state of the chrome should match" + ); + is( + window.fullScreen, + expectedStates.inFullscreen, + "The fullscreen state of the chrome should match" + ); +} + +const kPage = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/" + + "base/content/test/general/dummy_page.html"; + +add_task(async function () { + await pushPrefs( + ["full-screen-api.transition-duration.enter", "0 0"], + ["full-screen-api.transition-duration.leave", "0 0"] + ); + + registerCleanupFunction(async function () { + if (window.fullScreen) { + let fullscreenPromise = waitForFullscreenChanges( + gBrowser.selectedBrowser, + FS_CHANGE_SIZE + ); + executeSoon(() => BrowserFullScreen()); + await fullscreenPromise; + } + }); + + let tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: kPage, + }); + let browser = tab.linkedBrowser; + + // As requestFullscreen checks the active state of the docshell, + // wait for the document to be activated, just to be sure that + // the fullscreen request won't be denied. + await waitForDocActivated(browser); + + for (let test of gTests) { + let contentStates; + info("Testing exit DOM fullscreen via " + test.desc); + + contentStates = await queryFullscreenState(browser); + checkState({ inDOMFullscreen: false, inFullscreen: false }, contentStates); + + /* DOM fullscreen without fullscreen mode */ + + info("> Enter DOM fullscreen"); + let fullscreenPromise = waitForFullscreenChanges(browser, FS_CHANGE_BOTH); + await SpecialPowers.spawn(browser, [], () => { + content.document.body.requestFullscreen(); + }); + contentStates = await fullscreenPromise; + checkState({ inDOMFullscreen: true, inFullscreen: true }, contentStates); + + info("> Exit DOM fullscreen"); + fullscreenPromise = waitForFullscreenChanges(browser, FS_CHANGE_BOTH); + test.exitFunc(browser); + contentStates = await fullscreenPromise; + checkState({ inDOMFullscreen: false, inFullscreen: false }, contentStates); + + /* DOM fullscreen with fullscreen mode */ + + info("> Enter fullscreen mode"); + // Need to be asynchronous because sizemodechange event could be + // dispatched synchronously, which would cause the event listener + // miss that event and wait infinitely. + fullscreenPromise = waitForFullscreenChanges(browser, FS_CHANGE_SIZE); + executeSoon(() => BrowserFullScreen()); + contentStates = await fullscreenPromise; + checkState({ inDOMFullscreen: false, inFullscreen: true }, contentStates); + + info("> Enter DOM fullscreen in fullscreen mode"); + fullscreenPromise = waitForFullscreenChanges(browser, FS_CHANGE_DOM); + await SpecialPowers.spawn(browser, [], () => { + content.document.body.requestFullscreen(); + }); + contentStates = await fullscreenPromise; + checkState({ inDOMFullscreen: true, inFullscreen: true }, contentStates); + + info("> Exit DOM fullscreen in fullscreen mode"); + fullscreenPromise = waitForFullscreenChanges( + browser, + test.affectsFullscreenMode ? FS_CHANGE_BOTH : FS_CHANGE_DOM + ); + test.exitFunc(browser); + contentStates = await fullscreenPromise; + checkState( + { + inDOMFullscreen: false, + inFullscreen: !test.affectsFullscreenMode, + }, + contentStates + ); + + /* Cleanup */ + + // Exit fullscreen mode if we are still in + if (window.fullScreen) { + info("> Cleanup"); + fullscreenPromise = waitForFullscreenChanges(browser, FS_CHANGE_SIZE); + executeSoon(() => BrowserFullScreen()); + await fullscreenPromise; + } + } + + BrowserTestUtils.removeTab(tab); +}); diff --git a/browser/base/content/test/general/browser_double_close_tab.js b/browser/base/content/test/general/browser_double_close_tab.js new file mode 100644 index 0000000000..554aeb8077 --- /dev/null +++ b/browser/base/content/test/general/browser_double_close_tab.js @@ -0,0 +1,120 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +"use strict"; +const TEST_PAGE = + "http://mochi.test:8888/browser/browser/base/content/test/general/file_double_close_tab.html"; +var testTab; + +const CONTENT_PROMPT_SUBDIALOG = Services.prefs.getBoolPref( + "prompts.contentPromptSubDialog", + false +); + +function waitForDialog(callback) { + function onDialogLoaded(nodeOrDialogWindow) { + let node = CONTENT_PROMPT_SUBDIALOG + ? nodeOrDialogWindow.document.querySelector("dialog") + : nodeOrDialogWindow; + Services.obs.removeObserver(onDialogLoaded, "tabmodal-dialog-loaded"); + Services.obs.removeObserver(onDialogLoaded, "common-dialog-loaded"); + // Allow dialog's onLoad call to run to completion + Promise.resolve().then(() => callback(node)); + } + + // Listen for the dialog being created + Services.obs.addObserver(onDialogLoaded, "tabmodal-dialog-loaded"); + Services.obs.addObserver(onDialogLoaded, "common-dialog-loaded"); +} + +function waitForDialogDestroyed(node, callback) { + // Now listen for the dialog going away again... + let observer = new MutationObserver(function (muts) { + if (!node.parentNode) { + ok(true, "Dialog is gone"); + done(); + } + }); + observer.observe(node.parentNode, { childList: true }); + + if (CONTENT_PROMPT_SUBDIALOG) { + node.ownerGlobal.addEventListener("unload", done); + } + + let failureTimeout = setTimeout(function () { + ok(false, "Dialog should have been destroyed"); + done(); + }, 10000); + + function done() { + clearTimeout(failureTimeout); + observer.disconnect(); + observer = null; + + if (CONTENT_PROMPT_SUBDIALOG) { + node.ownerGlobal.removeEventListener("unload", done); + SimpleTest.executeSoon(callback); + } else { + callback(); + } + } +} + +add_task(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["dom.require_user_interaction_for_beforeunload", false]], + }); + + testTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE); + + // XXXgijs the reason this has nesting and callbacks rather than promises is + // that DOM promises resolve on the next tick. So they're scheduled + // in an event queue. So when we spin a new event queue for a modal dialog... + // everything gets messed up and the promise's .then callbacks never get + // called, despite resolve() being called just fine. + await new Promise(resolveOuter => { + waitForDialog(dialogNode => { + waitForDialogDestroyed(dialogNode, () => { + let doCompletion = () => setTimeout(resolveOuter, 0); + info("Now checking if dialog is destroyed"); + + if (CONTENT_PROMPT_SUBDIALOG) { + ok( + !dialogNode.ownerGlobal || dialogNode.ownerGlobal.closed, + "onbeforeunload dialog should be gone." + ); + if (dialogNode.ownerGlobal && !dialogNode.ownerGlobal.closed) { + dialogNode.acceptDialog(); + } + } else { + ok(!dialogNode.parentNode, "onbeforeunload dialog should be gone."); + if (dialogNode.parentNode) { + // Failed to remove onbeforeunload dialog, so do it ourselves: + let leaveBtn = dialogNode.querySelector(".tabmodalprompt-button0"); + waitForDialogDestroyed(dialogNode, doCompletion); + EventUtils.synthesizeMouseAtCenter(leaveBtn, {}); + return; + } + } + + doCompletion(); + }); + // Click again: + testTab.closeButton.click(); + }); + // Click once: + testTab.closeButton.click(); + }); + await TestUtils.waitForCondition(() => !testTab.parentNode); + ok(!testTab.parentNode, "Tab should be closed completely"); +}); + +registerCleanupFunction(async function () { + if (testTab.parentNode) { + // Remove the handler, or closing this tab will prove tricky: + try { + await SpecialPowers.spawn(testTab.linkedBrowser, [], function () { + content.window.onbeforeunload = null; + }); + } catch (ex) {} + gBrowser.removeTab(testTab); + } +}); diff --git a/browser/base/content/test/general/browser_drag.js b/browser/base/content/test/general/browser_drag.js new file mode 100644 index 0000000000..04373e7ce2 --- /dev/null +++ b/browser/base/content/test/general/browser_drag.js @@ -0,0 +1,64 @@ +async function test() { + waitForExplicitFinish(); + + let EventUtils = {}; + Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", + EventUtils + ); + + // ---- Test dragging the proxy icon --- + var value = content.location.href; + var urlString = value + "\n" + content.document.title; + var htmlString = '' + value + ""; + var expected = [ + [ + { type: "text/x-moz-url", data: urlString }, + { type: "text/uri-list", data: value }, + { type: "text/plain", data: value }, + { type: "text/html", data: htmlString }, + ], + ]; + // set the valid attribute so dropping is allowed + var oldstate = gURLBar.getAttribute("pageproxystate"); + gURLBar.setPageProxyState("valid"); + let result = await EventUtils.synthesizePlainDragAndCancel( + { + srcElement: document.getElementById("identity-icon-box"), + }, + expected + ); + ok(result === true, "dragging dataTransfer should be expected"); + gURLBar.setPageProxyState(oldstate); + // Now, the identity information panel is opened by the proxy icon click. + // We need to close it for next tests. + EventUtils.synthesizeKey("VK_ESCAPE", {}, window); + + // now test dragging onto a tab + var tab = BrowserTestUtils.addTab(gBrowser, "about:blank", { + skipAnimation: true, + }); + var browser = gBrowser.getBrowserForTab(tab); + + browser.addEventListener( + "load", + function () { + is( + browser.contentWindow.location, + "http://mochi.test:8888/", + "drop on tab" + ); + gBrowser.removeTab(tab); + finish(); + }, + true + ); + + EventUtils.synthesizeDrop( + tab, + tab, + [[{ type: "text/uri-list", data: "http://mochi.test:8888/" }]], + "copy", + window + ); +} diff --git a/browser/base/content/test/general/browser_duplicateIDs.js b/browser/base/content/test/general/browser_duplicateIDs.js new file mode 100644 index 0000000000..b0c65c6af6 --- /dev/null +++ b/browser/base/content/test/general/browser_duplicateIDs.js @@ -0,0 +1,11 @@ +function test() { + var ids = {}; + Array.prototype.forEach.call( + document.querySelectorAll("[id]"), + function (node) { + var id = node.id; + ok(!(id in ids), id + " should be unique"); + ids[id] = null; + } + ); +} diff --git a/browser/base/content/test/general/browser_findbarClose.js b/browser/base/content/test/general/browser_findbarClose.js new file mode 100644 index 0000000000..e0fe2fcb98 --- /dev/null +++ b/browser/base/content/test/general/browser_findbarClose.js @@ -0,0 +1,47 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests find bar auto-close behavior + +const TEST_PATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content", + "https://example.com" +); + +add_task(async function findbar_test() { + let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank"); + gBrowser.selectedTab = newTab; + + let url = TEST_PATH + "test_bug628179.html"; + let promise = BrowserTestUtils.browserLoaded( + newTab.linkedBrowser, + false, + url + ); + BrowserTestUtils.loadURIString(newTab.linkedBrowser, url); + await promise; + + await gFindBarPromise; + gFindBar.open(); + + await new ContentTask.spawn(newTab.linkedBrowser, null, async function () { + let iframe = content.document.getElementById("iframe"); + let awaitLoad = ContentTaskUtils.waitForEvent(iframe, "load", false); + iframe.src = "https://example.org/"; + await awaitLoad; + }); + + ok( + !gFindBar.hidden, + "the Find bar isn't hidden after the location of a subdocument changes" + ); + + let findBarClosePromise = BrowserTestUtils.waitForEvent( + gBrowser, + "findbarclose" + ); + gFindBar.close(); + await findBarClosePromise; + + gBrowser.removeTab(newTab); +}); diff --git a/browser/base/content/test/general/browser_focusonkeydown.js b/browser/base/content/test/general/browser_focusonkeydown.js new file mode 100644 index 0000000000..9cf1f113f5 --- /dev/null +++ b/browser/base/content/test/general/browser_focusonkeydown.js @@ -0,0 +1,34 @@ +add_task(async function () { + let keyUps = 0; + + await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "data:text/html," + ); + + gURLBar.focus(); + + window.addEventListener( + "keyup", + function (event) { + if (event.originalTarget == gURLBar.inputField) { + keyUps++; + } + }, + { capture: true, once: true } + ); + + gURLBar.addEventListener( + "keydown", + function (event) { + gBrowser.selectedBrowser.focus(); + }, + { capture: true, once: true } + ); + + EventUtils.sendString("v"); + + is(keyUps, 1, "Key up fired at url bar"); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/general/browser_fullscreen-window-open.js b/browser/base/content/test/general/browser_fullscreen-window-open.js new file mode 100644 index 0000000000..2b21e34e92 --- /dev/null +++ b/browser/base/content/test/general/browser_fullscreen-window-open.js @@ -0,0 +1,366 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +const PREF_DISABLE_OPEN_NEW_WINDOW = + "browser.link.open_newwindow.disabled_in_fullscreen"; +const PREF_BLOCK_TOPLEVEL_DATA = + "security.data_uri.block_toplevel_data_uri_navigations"; +const isOSX = Services.appinfo.OS === "Darwin"; + +const TEST_FILE = "file_fullscreen-window-open.html"; +const gHttpTestRoot = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "http://127.0.0.1:8888/" +); + +var newWin; +var newBrowser; + +async function test() { + waitForExplicitFinish(); + + Services.prefs.setBoolPref(PREF_DISABLE_OPEN_NEW_WINDOW, true); + Services.prefs.setBoolPref(PREF_BLOCK_TOPLEVEL_DATA, false); + + newWin = await BrowserTestUtils.openNewBrowserWindow(); + newBrowser = newWin.gBrowser; + await promiseTabLoadEvent(newBrowser.selectedTab, gHttpTestRoot + TEST_FILE); + + // Enter browser fullscreen mode. + newWin.BrowserFullScreen(); + + runNextTest(); +} + +registerCleanupFunction(async function () { + // Exit browser fullscreen mode. + newWin.BrowserFullScreen(); + + await BrowserTestUtils.closeWindow(newWin); + + Services.prefs.clearUserPref(PREF_DISABLE_OPEN_NEW_WINDOW); + Services.prefs.clearUserPref(PREF_BLOCK_TOPLEVEL_DATA); +}); + +var gTests = [ + test_open, + test_open_with_size, + test_open_with_pos, + test_open_with_outerSize, + test_open_with_innerSize, + test_open_with_dialog, + test_open_when_open_new_window_by_pref, + test_open_with_pref_to_disable_in_fullscreen, + test_open_from_chrome, +]; + +function runNextTest() { + let testCase = gTests.shift(); + if (testCase) { + executeSoon(testCase); + } else { + finish(); + } +} + +// Test for window.open() with no feature. +function test_open() { + waitForTabOpen({ + message: { + title: "test_open", + param: "", + }, + finalizeFn() {}, + }); +} + +// Test for window.open() with width/height. +function test_open_with_size() { + waitForTabOpen({ + message: { + title: "test_open_with_size", + param: "width=400,height=400", + }, + finalizeFn() {}, + }); +} + +// Test for window.open() with top/left. +function test_open_with_pos() { + waitForTabOpen({ + message: { + title: "test_open_with_pos", + param: "top=200,left=200", + }, + finalizeFn() {}, + }); +} + +// Test for window.open() with outerWidth/Height. +function test_open_with_outerSize() { + let [outerWidth, outerHeight] = [newWin.outerWidth, newWin.outerHeight]; + waitForTabOpen({ + message: { + title: "test_open_with_outerSize", + param: "outerWidth=200,outerHeight=200", + }, + successFn() { + is(newWin.outerWidth, outerWidth, "Don't change window.outerWidth."); + is(newWin.outerHeight, outerHeight, "Don't change window.outerHeight."); + }, + finalizeFn() {}, + }); +} + +// Test for window.open() with innerWidth/Height. +function test_open_with_innerSize() { + let [innerWidth, innerHeight] = [newWin.innerWidth, newWin.innerHeight]; + waitForTabOpen({ + message: { + title: "test_open_with_innerSize", + param: "innerWidth=200,innerHeight=200", + }, + successFn() { + is(newWin.innerWidth, innerWidth, "Don't change window.innerWidth."); + is(newWin.innerHeight, innerHeight, "Don't change window.innerHeight."); + }, + finalizeFn() {}, + }); +} + +// Test for window.open() with dialog. +function test_open_with_dialog() { + waitForTabOpen({ + message: { + title: "test_open_with_dialog", + param: "dialog=yes", + }, + finalizeFn() {}, + }); +} + +// Test for window.open() +// when "browser.link.open_newwindow" is nsIBrowserDOMWindow.OPEN_NEWWINDOW +function test_open_when_open_new_window_by_pref() { + const PREF_NAME = "browser.link.open_newwindow"; + Services.prefs.setIntPref(PREF_NAME, Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW); + is( + Services.prefs.getIntPref(PREF_NAME), + Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW, + PREF_NAME + " is nsIBrowserDOMWindow.OPEN_NEWWINDOW at this time" + ); + + waitForTabOpen({ + message: { + title: "test_open_when_open_new_window_by_pref", + param: "width=400,height=400", + }, + finalizeFn() { + Services.prefs.clearUserPref(PREF_NAME); + }, + }); +} + +// Test for the pref, "browser.link.open_newwindow.disabled_in_fullscreen" +function test_open_with_pref_to_disable_in_fullscreen() { + Services.prefs.setBoolPref(PREF_DISABLE_OPEN_NEW_WINDOW, false); + + waitForWindowOpen({ + message: { + title: "test_open_with_pref_disabled_in_fullscreen", + param: "width=400,height=400", + }, + finalizeFn() { + Services.prefs.setBoolPref(PREF_DISABLE_OPEN_NEW_WINDOW, true); + }, + }); +} + +// Test for window.open() called from chrome context. +function test_open_from_chrome() { + waitForWindowOpenFromChrome({ + message: { + title: "test_open_from_chrome", + param: "", + option: "noopener", + }, + finalizeFn() {}, + }); +} + +function waitForTabOpen(aOptions) { + let message = aOptions.message; + + if (!message.title) { + ok(false, "Can't get message.title."); + aOptions.finalizeFn(); + runNextTest(); + return; + } + + info("Running test: " + message.title); + + let onTabOpen = function onTabOpen(aEvent) { + newBrowser.tabContainer.removeEventListener("TabOpen", onTabOpen, true); + + let tab = aEvent.target; + whenTabLoaded(tab, function () { + is( + tab.linkedBrowser.contentTitle, + message.title, + "Opened Tab is expected: " + message.title + ); + + if (aOptions.successFn) { + aOptions.successFn(); + } + + newBrowser.removeTab(tab); + finalize(); + }); + }; + newBrowser.tabContainer.addEventListener("TabOpen", onTabOpen, true); + + let finalize = function () { + aOptions.finalizeFn(); + info("Finished: " + message.title); + runNextTest(); + }; + + const URI = + "data:text/html;charset=utf-8," + + message.title + + "<%2Ftitle><%2Fhead><body><%2Fbody><%2Fhtml>"; + + executeWindowOpenInContent({ + uri: URI, + title: message.title, + option: message.param, + }); +} + +function waitForWindowOpen(aOptions) { + let message = aOptions.message; + let url = aOptions.url || "about:blank"; + + if (!message.title) { + ok(false, "Can't get message.title"); + aOptions.finalizeFn(); + runNextTest(); + return; + } + + info("Running test: " + message.title); + + let onFinalize = function () { + aOptions.finalizeFn(); + + info("Finished: " + message.title); + runNextTest(); + }; + + let listener = new WindowListener( + message.title, + AppConstants.BROWSER_CHROME_URL, + { + onSuccess: aOptions.successFn, + onFinalize, + } + ); + Services.wm.addListener(listener); + + executeWindowOpenInContent({ + uri: url, + title: message.title, + option: message.param, + }); +} + +function executeWindowOpenInContent(aParam) { + SpecialPowers.spawn( + newBrowser.selectedBrowser, + [JSON.stringify(aParam)], + async function (dataTestParam) { + let testElm = content.document.getElementById("test"); + testElm.setAttribute("data-test-param", dataTestParam); + testElm.click(); + } + ); +} + +function waitForWindowOpenFromChrome(aOptions) { + let message = aOptions.message; + let url = aOptions.url || "about:blank"; + + if (!message.title) { + ok(false, "Can't get message.title"); + aOptions.finalizeFn(); + runNextTest(); + return; + } + + info("Running test: " + message.title); + + let onFinalize = function () { + aOptions.finalizeFn(); + + info("Finished: " + message.title); + runNextTest(); + }; + + let listener = new WindowListener( + message.title, + AppConstants.BROWSER_CHROME_URL, + { + onSuccess: aOptions.successFn, + onFinalize, + } + ); + Services.wm.addListener(listener); + + newWin.open(url, message.title, message.option); +} + +function WindowListener(aTitle, aUrl, aCallBackObj) { + this.test_title = aTitle; + this.test_url = aUrl; + this.callback_onSuccess = aCallBackObj.onSuccess; + this.callBack_onFinalize = aCallBackObj.onFinalize; +} +WindowListener.prototype = { + test_title: null, + test_url: null, + callback_onSuccess: null, + callBack_onFinalize: null, + + onOpenWindow(aXULWindow) { + Services.wm.removeListener(this); + + let domwindow = aXULWindow.docShell.domWindow; + let onLoad = aEvent => { + is( + domwindow.document.location.href, + this.test_url, + "Opened Window is expected: " + this.test_title + ); + if (this.callback_onSuccess) { + this.callback_onSuccess(); + } + + domwindow.removeEventListener("load", onLoad, true); + + // wait for trasition to fullscreen on OSX Lion later + if (isOSX) { + setTimeout(() => { + domwindow.close(); + executeSoon(this.callBack_onFinalize); + }, 3000); + } else { + domwindow.close(); + executeSoon(this.callBack_onFinalize); + } + }; + domwindow.addEventListener("load", onLoad, true); + }, + onCloseWindow(aXULWindow) {}, + QueryInterface: ChromeUtils.generateQI(["nsIWindowMediatorListener"]), +}; diff --git a/browser/base/content/test/general/browser_gestureSupport.js b/browser/base/content/test/general/browser_gestureSupport.js new file mode 100644 index 0000000000..d8f0331268 --- /dev/null +++ b/browser/base/content/test/general/browser_gestureSupport.js @@ -0,0 +1,1132 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_utils.js", + this +); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js", + this +); + +// Simple gestures tests +// +// Some of these tests require the ability to disable the fact that the +// Firefox chrome intentionally prevents "simple gesture" events from +// reaching web content. + +var test_utils; +var test_commandset; +var test_prefBranch = "browser.gesture."; +var test_normalTab; + +async function test() { + waitForExplicitFinish(); + + // Disable the default gestures support during this part of the test + gGestureSupport.init(false); + + test_utils = window.windowUtils; + + // Run the tests of "simple gesture" events generally + test_EnsureConstantsAreDisjoint(); + test_TestEventListeners(); + test_TestEventCreation(); + + // Reenable the default gestures support. The remaining tests target + // the Firefox gesture functionality. + gGestureSupport.init(true); + + const aPage = "about:about"; + test_normalTab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + aPage, + true /* waitForLoad */ + ); + + // Test Firefox's gestures support. + test_commandset = document.getElementById("mainCommandSet"); + await test_swipeGestures(); + await test_latchedGesture("pinch", "out", "in", "MozMagnifyGesture"); + await test_thresholdGesture("pinch", "out", "in", "MozMagnifyGesture"); + test_rotateGestures(); +} + +var test_eventCount = 0; +var test_expectedType; +var test_expectedDirection; +var test_expectedDelta; +var test_expectedModifiers; +var test_expectedClickCount; +var test_imageTab; + +function test_gestureListener(evt) { + is( + evt.type, + test_expectedType, + "evt.type (" + evt.type + ") does not match expected value" + ); + is( + evt.target, + test_utils.elementFromPoint(60, 60, false, false), + "evt.target (" + evt.target + ") does not match expected value" + ); + is( + evt.clientX, + 60, + "evt.clientX (" + evt.clientX + ") does not match expected value" + ); + is( + evt.clientY, + 60, + "evt.clientY (" + evt.clientY + ") does not match expected value" + ); + isnot( + evt.screenX, + 0, + "evt.screenX (" + evt.screenX + ") does not match expected value" + ); + isnot( + evt.screenY, + 0, + "evt.screenY (" + evt.screenY + ") does not match expected value" + ); + + is( + evt.direction, + test_expectedDirection, + "evt.direction (" + evt.direction + ") does not match expected value" + ); + is( + evt.delta, + test_expectedDelta, + "evt.delta (" + evt.delta + ") does not match expected value" + ); + + is( + evt.shiftKey, + (test_expectedModifiers & Event.SHIFT_MASK) != 0, + "evt.shiftKey did not match expected value" + ); + is( + evt.ctrlKey, + (test_expectedModifiers & Event.CONTROL_MASK) != 0, + "evt.ctrlKey did not match expected value" + ); + is( + evt.altKey, + (test_expectedModifiers & Event.ALT_MASK) != 0, + "evt.altKey did not match expected value" + ); + is( + evt.metaKey, + (test_expectedModifiers & Event.META_MASK) != 0, + "evt.metaKey did not match expected value" + ); + + if (evt.type == "MozTapGesture") { + is( + evt.clickCount, + test_expectedClickCount, + "evt.clickCount does not match" + ); + } + + test_eventCount++; +} + +function test_helper1(type, direction, delta, modifiers) { + // Setup the expected values + test_expectedType = type; + test_expectedDirection = direction; + test_expectedDelta = delta; + test_expectedModifiers = modifiers; + + let expectedEventCount = test_eventCount + 1; + + document.addEventListener(type, test_gestureListener, true); + test_utils.sendSimpleGestureEvent(type, 60, 60, direction, delta, modifiers); + document.removeEventListener(type, test_gestureListener, true); + + is( + expectedEventCount, + test_eventCount, + "Event (" + type + ") was never received by event listener" + ); +} + +function test_clicks(type, clicks) { + // Setup the expected values + test_expectedType = type; + test_expectedDirection = 0; + test_expectedDelta = 0; + test_expectedModifiers = 0; + test_expectedClickCount = clicks; + + let expectedEventCount = test_eventCount + 1; + + document.addEventListener(type, test_gestureListener, true); + test_utils.sendSimpleGestureEvent(type, 60, 60, 0, 0, 0, clicks); + document.removeEventListener(type, test_gestureListener, true); + + is( + expectedEventCount, + test_eventCount, + "Event (" + type + ") was never received by event listener" + ); +} + +function test_TestEventListeners() { + let e = test_helper1; // easier to type this name + + // Swipe gesture animation events + e("MozSwipeGestureStart", 0, -0.7, 0); + e("MozSwipeGestureUpdate", 0, -0.4, 0); + e("MozSwipeGestureEnd", 0, 0, 0); + e("MozSwipeGestureStart", 0, 0.6, 0); + e("MozSwipeGestureUpdate", 0, 0.3, 0); + e("MozSwipeGestureEnd", 0, 1, 0); + + // Swipe gesture event + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_LEFT, 0.0, 0); + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0.0, 0); + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_UP, 0.0, 0); + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_DOWN, 0.0, 0); + e( + "MozSwipeGesture", + SimpleGestureEvent.DIRECTION_UP | SimpleGestureEvent.DIRECTION_LEFT, + 0.0, + 0 + ); + e( + "MozSwipeGesture", + SimpleGestureEvent.DIRECTION_DOWN | SimpleGestureEvent.DIRECTION_RIGHT, + 0.0, + 0 + ); + e( + "MozSwipeGesture", + SimpleGestureEvent.DIRECTION_UP | SimpleGestureEvent.DIRECTION_RIGHT, + 0.0, + 0 + ); + e( + "MozSwipeGesture", + SimpleGestureEvent.DIRECTION_DOWN | SimpleGestureEvent.DIRECTION_LEFT, + 0.0, + 0 + ); + + // magnify gesture events + e("MozMagnifyGestureStart", 0, 50.0, 0); + e("MozMagnifyGestureUpdate", 0, -25.0, 0); + e("MozMagnifyGestureUpdate", 0, 5.0, 0); + e("MozMagnifyGesture", 0, 30.0, 0); + + // rotate gesture events + e("MozRotateGestureStart", SimpleGestureEvent.ROTATION_CLOCKWISE, 33.0, 0); + e( + "MozRotateGestureUpdate", + SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE, + -13.0, + 0 + ); + e("MozRotateGestureUpdate", SimpleGestureEvent.ROTATION_CLOCKWISE, 13.0, 0); + e("MozRotateGesture", SimpleGestureEvent.ROTATION_CLOCKWISE, 33.0, 0); + + // Tap and presstap gesture events + test_clicks("MozTapGesture", 1); + test_clicks("MozTapGesture", 2); + test_clicks("MozTapGesture", 3); + test_clicks("MozPressTapGesture", 1); + + // simple delivery test for edgeui gestures + e("MozEdgeUIStarted", 0, 0, 0); + e("MozEdgeUICanceled", 0, 0, 0); + e("MozEdgeUICompleted", 0, 0, 0); + + // event.shiftKey + let modifier = Event.SHIFT_MASK; + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier); + + // event.metaKey + modifier = Event.META_MASK; + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier); + + // event.altKey + modifier = Event.ALT_MASK; + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier); + + // event.ctrlKey + modifier = Event.CONTROL_MASK; + e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0, modifier); +} + +function test_eventDispatchListener(evt) { + test_eventCount++; + evt.stopPropagation(); +} + +function test_helper2( + type, + direction, + delta, + altKey, + ctrlKey, + shiftKey, + metaKey +) { + let event = null; + let successful; + + try { + event = document.createEvent("SimpleGestureEvent"); + successful = true; + } catch (ex) { + successful = false; + } + ok(successful, "Unable to create SimpleGestureEvent"); + + try { + event.initSimpleGestureEvent( + type, + true, + true, + window, + 1, + 10, + 10, + 10, + 10, + ctrlKey, + altKey, + shiftKey, + metaKey, + 1, + window, + 0, + direction, + delta, + 0 + ); + successful = true; + } catch (ex) { + successful = false; + } + ok(successful, "event.initSimpleGestureEvent should not fail"); + + // Make sure the event fields match the expected values + is(event.type, type, "Mismatch on evt.type"); + is(event.direction, direction, "Mismatch on evt.direction"); + is(event.delta, delta, "Mismatch on evt.delta"); + is(event.altKey, altKey, "Mismatch on evt.altKey"); + is(event.ctrlKey, ctrlKey, "Mismatch on evt.ctrlKey"); + is(event.shiftKey, shiftKey, "Mismatch on evt.shiftKey"); + is(event.metaKey, metaKey, "Mismatch on evt.metaKey"); + is(event.view, window, "Mismatch on evt.view"); + is(event.detail, 1, "Mismatch on evt.detail"); + is(event.clientX, 10, "Mismatch on evt.clientX"); + is(event.clientY, 10, "Mismatch on evt.clientY"); + is(event.screenX, 10, "Mismatch on evt.screenX"); + is(event.screenY, 10, "Mismatch on evt.screenY"); + is(event.button, 1, "Mismatch on evt.button"); + is(event.relatedTarget, window, "Mismatch on evt.relatedTarget"); + + // Test event dispatch + let expectedEventCount = test_eventCount + 1; + document.addEventListener(type, test_eventDispatchListener, true); + document.dispatchEvent(event); + document.removeEventListener(type, test_eventDispatchListener, true); + is( + expectedEventCount, + test_eventCount, + "Dispatched event was never received by listener" + ); +} + +function test_TestEventCreation() { + // Event creation + test_helper2( + "MozMagnifyGesture", + SimpleGestureEvent.DIRECTION_RIGHT, + 20.0, + true, + false, + true, + false + ); + test_helper2( + "MozMagnifyGesture", + SimpleGestureEvent.DIRECTION_LEFT, + -20.0, + false, + true, + false, + true + ); +} + +function test_EnsureConstantsAreDisjoint() { + let up = SimpleGestureEvent.DIRECTION_UP; + let down = SimpleGestureEvent.DIRECTION_DOWN; + let left = SimpleGestureEvent.DIRECTION_LEFT; + let right = SimpleGestureEvent.DIRECTION_RIGHT; + + let clockwise = SimpleGestureEvent.ROTATION_CLOCKWISE; + let cclockwise = SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE; + + ok(up ^ down, "DIRECTION_UP and DIRECTION_DOWN are not bitwise disjoint"); + ok(up ^ left, "DIRECTION_UP and DIRECTION_LEFT are not bitwise disjoint"); + ok(up ^ right, "DIRECTION_UP and DIRECTION_RIGHT are not bitwise disjoint"); + ok(down ^ left, "DIRECTION_DOWN and DIRECTION_LEFT are not bitwise disjoint"); + ok( + down ^ right, + "DIRECTION_DOWN and DIRECTION_RIGHT are not bitwise disjoint" + ); + ok( + left ^ right, + "DIRECTION_LEFT and DIRECTION_RIGHT are not bitwise disjoint" + ); + ok( + clockwise ^ cclockwise, + "ROTATION_CLOCKWISE and ROTATION_COUNTERCLOCKWISE are not bitwise disjoint" + ); +} + +// Helper for test of latched event processing. Emits the actual +// gesture events to test whether the commands associated with the +// gesture will only trigger once for each direction of movement. +async function test_emitLatchedEvents(eventPrefix, initialDelta, cmd) { + let cumulativeDelta = 0; + let isIncreasing = initialDelta > 0; + + let expect = {}; + // Reset the call counters and initialize expected values + for (let dir in cmd) { + cmd[dir].callCount = expect[dir] = 0; + } + + let check = (aDir, aMsg) => ok(cmd[aDir].callCount == expect[aDir], aMsg); + let checkBoth = function (aNum, aInc, aDec) { + let prefix = "Step " + aNum + ": "; + check("inc", prefix + aInc); + check("dec", prefix + aDec); + }; + + // Send the "Start" event. + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Start", + 10, + 10, + 0, + initialDelta, + 0, + 0 + ); + cumulativeDelta += initialDelta; + if (isIncreasing) { + expect.inc++; + checkBoth( + 1, + "Increasing command was not triggered", + "Decreasing command was triggered" + ); + } else { + expect.dec++; + checkBoth( + 1, + "Increasing command was triggered", + "Decreasing command was not triggered" + ); + } + + // Send random values in the same direction and ensure neither + // command triggers. + for (let i = 0; i < 5; i++) { + let delta = Math.random() * (isIncreasing ? 100 : -100); + + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + delta, + 0, + 0 + ); + cumulativeDelta += delta; + checkBoth( + 2, + "Increasing command was triggered", + "Decreasing command was triggered" + ); + } + + // Now go back in the opposite direction. + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + -initialDelta, + 0, + 0 + ); + cumulativeDelta += -initialDelta; + if (isIncreasing) { + expect.dec++; + checkBoth( + 3, + "Increasing command was triggered", + "Decreasing command was not triggered" + ); + } else { + expect.inc++; + checkBoth( + 3, + "Increasing command was not triggered", + "Decreasing command was triggered" + ); + } + + // Send random values in the opposite direction and ensure neither + // command triggers. + for (let i = 0; i < 5; i++) { + let delta = Math.random() * (isIncreasing ? -100 : 100); + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + delta, + 0, + 0 + ); + cumulativeDelta += delta; + checkBoth( + 4, + "Increasing command was triggered", + "Decreasing command was triggered" + ); + } + + // Go back to the original direction. The original command should trigger. + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + initialDelta, + 0, + 0 + ); + cumulativeDelta += initialDelta; + if (isIncreasing) { + expect.inc++; + checkBoth( + 5, + "Increasing command was not triggered", + "Decreasing command was triggered" + ); + } else { + expect.dec++; + checkBoth( + 5, + "Increasing command was triggered", + "Decreasing command was not triggered" + ); + } + + // Send the wrap-up event. No commands should be triggered. + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix, + 10, + 10, + 0, + cumulativeDelta, + 0, + 0 + ); + checkBoth( + 6, + "Increasing command was triggered", + "Decreasing command was triggered" + ); +} + +function test_addCommand(prefName, id) { + let cmd = test_commandset.appendChild(document.createXULElement("command")); + cmd.setAttribute("id", id); + cmd.setAttribute("oncommand", "this.callCount++;"); + + cmd.origPrefName = prefName; + cmd.origPrefValue = Services.prefs.getCharPref(prefName); + Services.prefs.setCharPref(prefName, id); + + return cmd; +} + +function test_removeCommand(cmd) { + Services.prefs.setCharPref(cmd.origPrefName, cmd.origPrefValue); + test_commandset.removeChild(cmd); +} + +// Test whether latched events are only called once per direction of motion. +async function test_latchedGesture(gesture, inc, dec, eventPrefix) { + let branch = test_prefBranch + gesture + "."; + + // Put the gesture into latched mode. + let oldLatchedValue = Services.prefs.getBoolPref(branch + "latched"); + Services.prefs.setBoolPref(branch + "latched", true); + + // Install the test commands for increasing and decreasing motion. + let cmd = { + inc: test_addCommand(branch + inc, "test:incMotion"), + dec: test_addCommand(branch + dec, "test:decMotion"), + }; + + // Test the gestures in each direction. + await test_emitLatchedEvents(eventPrefix, 500, cmd); + await test_emitLatchedEvents(eventPrefix, -500, cmd); + + // Restore the gesture to its original configuration. + Services.prefs.setBoolPref(branch + "latched", oldLatchedValue); + for (let dir in cmd) { + test_removeCommand(cmd[dir]); + } +} + +// Test whether non-latched events are triggered upon sufficient motion. +async function test_thresholdGesture(gesture, inc, dec, eventPrefix) { + let branch = test_prefBranch + gesture + "."; + + // Disable latched mode for this gesture. + let oldLatchedValue = Services.prefs.getBoolPref(branch + "latched"); + Services.prefs.setBoolPref(branch + "latched", false); + + // Set the triggering threshold value to 50. + let oldThresholdValue = Services.prefs.getIntPref(branch + "threshold"); + Services.prefs.setIntPref(branch + "threshold", 50); + + // Install the test commands for increasing and decreasing motion. + let cmdInc = test_addCommand(branch + inc, "test:incMotion"); + let cmdDec = test_addCommand(branch + dec, "test:decMotion"); + + // Send the start event but stop short of triggering threshold. + cmdInc.callCount = cmdDec.callCount = 0; + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Start", + 10, + 10, + 0, + 49.5, + 0, + 0 + ); + ok(cmdInc.callCount == 0, "Increasing command was triggered"); + ok(cmdDec.callCount == 0, "Decreasing command was triggered"); + + // Now trigger the threshold. + cmdInc.callCount = cmdDec.callCount = 0; + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + 1, + 0, + 0 + ); + ok(cmdInc.callCount == 1, "Increasing command was not triggered"); + ok(cmdDec.callCount == 0, "Decreasing command was triggered"); + + // The tracking counter should go to zero. Go back the other way and + // stop short of triggering the threshold. + cmdInc.callCount = cmdDec.callCount = 0; + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + -49.5, + 0, + 0 + ); + ok(cmdInc.callCount == 0, "Increasing command was triggered"); + ok(cmdDec.callCount == 0, "Decreasing command was triggered"); + + // Now cross the threshold and trigger the decreasing command. + cmdInc.callCount = cmdDec.callCount = 0; + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix + "Update", + 10, + 10, + 0, + -1.5, + 0, + 0 + ); + ok(cmdInc.callCount == 0, "Increasing command was triggered"); + ok(cmdDec.callCount == 1, "Decreasing command was not triggered"); + + // Send the wrap-up event. No commands should trigger. + cmdInc.callCount = cmdDec.callCount = 0; + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + eventPrefix, + 0, + 0, + 0, + -0.5, + 0, + 0 + ); + ok(cmdInc.callCount == 0, "Increasing command was triggered"); + ok(cmdDec.callCount == 0, "Decreasing command was triggered"); + + // Restore the gesture to its original configuration. + Services.prefs.setBoolPref(branch + "latched", oldLatchedValue); + Services.prefs.setIntPref(branch + "threshold", oldThresholdValue); + test_removeCommand(cmdInc); + test_removeCommand(cmdDec); +} + +async function test_swipeGestures() { + // easier to type names for the direction constants + let up = SimpleGestureEvent.DIRECTION_UP; + let down = SimpleGestureEvent.DIRECTION_DOWN; + let left = SimpleGestureEvent.DIRECTION_LEFT; + let right = SimpleGestureEvent.DIRECTION_RIGHT; + + let branch = test_prefBranch + "swipe."; + + // Install the test commands for the swipe gestures. + let cmdUp = test_addCommand(branch + "up", "test:swipeUp"); + let cmdDown = test_addCommand(branch + "down", "test:swipeDown"); + let cmdLeft = test_addCommand(branch + "left", "test:swipeLeft"); + let cmdRight = test_addCommand(branch + "right", "test:swipeRight"); + + function resetCounts() { + cmdUp.callCount = 0; + cmdDown.callCount = 0; + cmdLeft.callCount = 0; + cmdRight.callCount = 0; + } + + // UP + resetCounts(); + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + "MozSwipeGesture", + 10, + 10, + up, + 0, + 0, + 0 + ); + ok(cmdUp.callCount == 1, "Step 1: Up command was not triggered"); + ok(cmdDown.callCount == 0, "Step 1: Down command was triggered"); + ok(cmdLeft.callCount == 0, "Step 1: Left command was triggered"); + ok(cmdRight.callCount == 0, "Step 1: Right command was triggered"); + + // DOWN + resetCounts(); + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + "MozSwipeGesture", + 10, + 10, + down, + 0, + 0, + 0 + ); + ok(cmdUp.callCount == 0, "Step 2: Up command was triggered"); + ok(cmdDown.callCount == 1, "Step 2: Down command was not triggered"); + ok(cmdLeft.callCount == 0, "Step 2: Left command was triggered"); + ok(cmdRight.callCount == 0, "Step 2: Right command was triggered"); + + // LEFT + resetCounts(); + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + "MozSwipeGesture", + 10, + 10, + left, + 0, + 0, + 0 + ); + ok(cmdUp.callCount == 0, "Step 3: Up command was triggered"); + ok(cmdDown.callCount == 0, "Step 3: Down command was triggered"); + ok(cmdLeft.callCount == 1, "Step 3: Left command was not triggered"); + ok(cmdRight.callCount == 0, "Step 3: Right command was triggered"); + + // RIGHT + resetCounts(); + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + "MozSwipeGesture", + 10, + 10, + right, + 0, + 0, + 0 + ); + ok(cmdUp.callCount == 0, "Step 4: Up command was triggered"); + ok(cmdDown.callCount == 0, "Step 4: Down command was triggered"); + ok(cmdLeft.callCount == 0, "Step 4: Left command was triggered"); + ok(cmdRight.callCount == 1, "Step 4: Right command was not triggered"); + + // Make sure combinations do not trigger events. + let combos = [up | left, up | right, down | left, down | right]; + for (let i = 0; i < combos.length; i++) { + resetCounts(); + await synthesizeSimpleGestureEvent( + test_normalTab.linkedBrowser, + "MozSwipeGesture", + 10, + 10, + combos[i], + 0, + 0, + 0 + ); + ok(cmdUp.callCount == 0, "Step 5-" + i + ": Up command was triggered"); + ok(cmdDown.callCount == 0, "Step 5-" + i + ": Down command was triggered"); + ok(cmdLeft.callCount == 0, "Step 5-" + i + ": Left command was triggered"); + ok( + cmdRight.callCount == 0, + "Step 5-" + i + ": Right command was triggered" + ); + } + + // Remove the test commands. + test_removeCommand(cmdUp); + test_removeCommand(cmdDown); + test_removeCommand(cmdLeft); + test_removeCommand(cmdRight); +} + +function test_rotateHelperGetImageRotation(aImageElement) { + // Get the true image rotation from the transform matrix, bounded + // to 0 <= result < 360 + let transformValue = content.window.getComputedStyle(aImageElement).transform; + if (transformValue == "none") { + return 0; + } + + transformValue = transformValue.split("(")[1].split(")")[0].split(","); + var rotation = Math.round( + Math.atan2(transformValue[1], transformValue[0]) * (180 / Math.PI) + ); + return rotation < 0 ? rotation + 360 : rotation; +} + +async function test_rotateHelperOneGesture( + aImageElement, + aCurrentRotation, + aDirection, + aAmount, + aStop +) { + if (aAmount <= 0 || aAmount > 90) { + // Bound to 0 < aAmount <= 90 + return; + } + + // easier to type names for the direction constants + let clockwise = SimpleGestureEvent.ROTATION_CLOCKWISE; + + let delta = aAmount * (aDirection == clockwise ? 1 : -1); + + // Kill transition time on image so test isn't wrong and doesn't take 10 seconds + aImageElement.style.transitionDuration = "0s"; + + // Start the gesture, perform an update, and force flush + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGestureStart", + 10, + 10, + aDirection, + 0.001, + 0, + 0 + ); + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGestureUpdate", + 10, + 10, + aDirection, + delta, + 0, + 0 + ); + aImageElement.clientTop; + + // If stop, check intermediate + if (aStop) { + // Send near-zero-delta to stop, and force flush + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGestureUpdate", + 10, + 10, + aDirection, + 0.001, + 0, + 0 + ); + aImageElement.clientTop; + + let stopExpectedRotation = (aCurrentRotation + delta) % 360; + if (stopExpectedRotation < 0) { + stopExpectedRotation += 360; + } + + is( + stopExpectedRotation, + test_rotateHelperGetImageRotation(aImageElement), + "Image rotation at gesture stop/hold: expected=" + + stopExpectedRotation + + ", observed=" + + test_rotateHelperGetImageRotation(aImageElement) + + ", init=" + + aCurrentRotation + + ", amt=" + + aAmount + + ", dir=" + + (aDirection == clockwise ? "cl" : "ccl") + ); + } + // End it and force flush + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGesture", + 10, + 10, + aDirection, + 0, + 0, + 0 + ); + aImageElement.clientTop; + + let finalExpectedRotation; + + if (aAmount < 45 && aStop) { + // Rotate a bit, then stop. Expect no change at end of gesture. + finalExpectedRotation = aCurrentRotation; + } else { + // Either not stopping (expect 90 degree change in aDirection), OR + // stopping but after 45, (expect 90 degree change in aDirection) + finalExpectedRotation = + (aCurrentRotation + (aDirection == clockwise ? 1 : -1) * 90) % 360; + if (finalExpectedRotation < 0) { + finalExpectedRotation += 360; + } + } + + is( + finalExpectedRotation, + test_rotateHelperGetImageRotation(aImageElement), + "Image rotation gesture end: expected=" + + finalExpectedRotation + + ", observed=" + + test_rotateHelperGetImageRotation(aImageElement) + + ", init=" + + aCurrentRotation + + ", amt=" + + aAmount + + ", dir=" + + (aDirection == clockwise ? "cl" : "ccl") + ); +} + +async function test_rotateGesturesOnTab() { + gBrowser.selectedBrowser.removeEventListener( + "load", + test_rotateGesturesOnTab, + true + ); + + if (!ImageDocument.isInstance(content.document)) { + ok(false, "Image document failed to open for rotation testing"); + gBrowser.removeTab(test_imageTab); + BrowserTestUtils.removeTab(test_normalTab); + test_imageTab = null; + test_normalTab = null; + finish(); + return; + } + + // easier to type names for the direction constants + let cl = SimpleGestureEvent.ROTATION_CLOCKWISE; + let ccl = SimpleGestureEvent.ROTATION_COUNTERCLOCKWISE; + + let imgElem = + content.document.body && content.document.body.firstElementChild; + + if (!imgElem) { + ok(false, "Could not get image element on ImageDocument for rotation!"); + gBrowser.removeTab(test_imageTab); + BrowserTestUtils.removeTab(test_normalTab); + test_imageTab = null; + test_normalTab = null; + finish(); + return; + } + + // Quick function to normalize rotation to 0 <= r < 360 + var normRot = function (rotation) { + rotation = rotation % 360; + if (rotation < 0) { + rotation += 360; + } + return rotation; + }; + + for (var initRot = 0; initRot < 360; initRot += 90) { + // Test each case: at each 90 degree snap; cl/ccl; + // amount more or less than 45; stop and hold or don't (32 total tests) + // The amount added to the initRot is where it is expected to be + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 0), + cl, + 35, + true + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 0), + cl, + 35, + false + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 90), + cl, + 55, + true + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 180), + cl, + 55, + false + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 270), + ccl, + 35, + true + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 270), + ccl, + 35, + false + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 180), + ccl, + 55, + true + ); + await test_rotateHelperOneGesture( + imgElem, + normRot(initRot + 90), + ccl, + 55, + false + ); + + // Manually rotate it 90 degrees clockwise to prepare for next iteration, + // and force flush + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGestureStart", + 10, + 10, + cl, + 0.001, + 0, + 0 + ); + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGestureUpdate", + 10, + 10, + cl, + 90, + 0, + 0 + ); + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGestureUpdate", + 10, + 10, + cl, + 0.001, + 0, + 0 + ); + await synthesizeSimpleGestureEvent( + test_imageTab.linkedBrowser, + "MozRotateGesture", + 10, + 10, + cl, + 0, + 0, + 0 + ); + imgElem.clientTop; + } + + gBrowser.removeTab(test_imageTab); + BrowserTestUtils.removeTab(test_normalTab); + test_imageTab = null; + test_normalTab = null; + finish(); +} + +function test_rotateGestures() { + test_imageTab = BrowserTestUtils.addTab( + gBrowser, + "chrome://branding/content/about-logo.png" + ); + gBrowser.selectedTab = test_imageTab; + + gBrowser.selectedBrowser.addEventListener( + "load", + test_rotateGesturesOnTab, + true + ); +} diff --git a/browser/base/content/test/general/browser_hide_removing.js b/browser/base/content/test/general/browser_hide_removing.js new file mode 100644 index 0000000000..24079c22e6 --- /dev/null +++ b/browser/base/content/test/general/browser_hide_removing.js @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Bug 587922: tabs don't get removed if they're hidden + +add_task(async function () { + // Add a tab that will get removed and hidden + let testTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { + skipAnimation: true, + }); + is(gBrowser.visibleTabs.length, 2, "just added a tab, so 2 tabs"); + await BrowserTestUtils.switchTab(gBrowser, testTab); + + let numVisBeforeHide, numVisAfterHide; + + // We have to animate the tab removal in order to get an async + // tab close. + BrowserTestUtils.removeTab(testTab, { animate: true }); + + numVisBeforeHide = gBrowser.visibleTabs.length; + gBrowser.hideTab(testTab); + numVisAfterHide = gBrowser.visibleTabs.length; + + is(numVisBeforeHide, 1, "animated remove has in 1 tab left"); + is(numVisAfterHide, 1, "hiding a removing tab also has 1 tab"); +}); diff --git a/browser/base/content/test/general/browser_homeDrop.js b/browser/base/content/test/general/browser_homeDrop.js new file mode 100644 index 0000000000..81dc48d3e4 --- /dev/null +++ b/browser/base/content/test/general/browser_homeDrop.js @@ -0,0 +1,117 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function setupHomeButton() { + // Put the home button in the pre-proton placement to test focus states. + CustomizableUI.addWidgetToArea( + "home-button", + "nav-bar", + CustomizableUI.getPlacementOfWidget("stop-reload-button").position + 1 + ); + CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar"); + registerCleanupFunction(async function resetToolbar() { + await CustomizableUI.reset(); + }); +}); + +add_task(async function () { + let HOMEPAGE_PREF = "browser.startup.homepage"; + + await pushPrefs([HOMEPAGE_PREF, "about:mozilla"]); + + let EventUtils = {}; + Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", + EventUtils + ); + + // Since synthesizeDrop triggers the srcElement, need to use another button + // that should be visible. + let dragSrcElement = document.getElementById("sidebar-button"); + ok(dragSrcElement, "Sidebar button exists"); + let homeButton = document.getElementById("home-button"); + ok(homeButton, "home button present"); + + async function drop(dragData, homepage) { + let setHomepageDialogPromise = + BrowserTestUtils.promiseAlertDialogOpen("accept"); + let setHomepagePromise = TestUtils.waitForPrefChange( + HOMEPAGE_PREF, + newVal => newVal == homepage + ); + + EventUtils.synthesizeDrop( + dragSrcElement, + homeButton, + dragData, + "copy", + window + ); + + // Ensure dnd suppression is cleared. + EventUtils.synthesizeMouseAtCenter(homeButton, { type: "mouseup" }, window); + + await setHomepageDialogPromise; + ok(true, "dialog appeared in response to home button drop"); + + await setHomepagePromise; + + let modified = Services.prefs.getStringPref(HOMEPAGE_PREF); + is(modified, homepage, "homepage is set correctly"); + Services.prefs.setStringPref(HOMEPAGE_PREF, "about:mozilla;"); + } + + function dropInvalidURI() { + return new Promise(resolve => { + let consoleListener = { + observe(m) { + if (m.message.includes("NS_ERROR_DOM_BAD_URI")) { + ok(true, "drop was blocked"); + resolve(); + } + }, + }; + Services.console.registerListener(consoleListener); + registerCleanupFunction(function () { + Services.console.unregisterListener(consoleListener); + }); + + executeSoon(function () { + info("Attempting second drop, of a javascript: URI"); + // The drop handler throws an exception when dragging URIs that inherit + // principal, e.g. javascript: + expectUncaughtException(); + EventUtils.synthesizeDrop( + dragSrcElement, + homeButton, + [[{ type: "text/plain", data: "javascript:8888" }]], + "copy", + window + ); + // Ensure dnd suppression is cleared. + EventUtils.synthesizeMouseAtCenter( + homeButton, + { type: "mouseup" }, + window + ); + }); + }); + } + + await drop( + [[{ type: "text/plain", data: "http://mochi.test:8888/" }]], + "http://mochi.test:8888/" + ); + await drop( + [ + [ + { + type: "text/plain", + data: "http://mochi.test:8888/\nhttp://mochi.test:8888/b\nhttp://mochi.test:8888/c", + }, + ], + ], + "http://mochi.test:8888/|http://mochi.test:8888/b|http://mochi.test:8888/c" + ); + await dropInvalidURI(); +}); diff --git a/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js b/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js new file mode 100644 index 0000000000..1624a1514d --- /dev/null +++ b/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js @@ -0,0 +1,48 @@ +"use strict"; + +/** + * Verify that loading an invalid URI does not clobber a previously-loaded page's history + * entry, but that the invalid URI gets its own history entry instead. We're checking this + * using nsIWebNavigation's canGoBack, as well as actually going back and then checking + * canGoForward. + */ +add_task(async function checkBackFromInvalidURI() { + await pushPrefs(["keyword.enabled", false]); + let tab = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + "about:robots", + true + ); + info("Loaded about:robots"); + + gURLBar.value = "::2600"; + + let promiseErrorPageLoaded = BrowserTestUtils.waitForErrorPage( + tab.linkedBrowser + ); + gURLBar.handleCommand(); + await promiseErrorPageLoaded; + + ok(gBrowser.webNavigation.canGoBack, "Should be able to go back"); + if (gBrowser.webNavigation.canGoBack) { + // Can't use DOMContentLoaded here because the page is bfcached. Can't use pageshow for + // the error page because it doesn't seem to fire for those. + let promiseOtherPageLoaded = BrowserTestUtils.waitForEvent( + tab.linkedBrowser, + "pageshow", + false, + // Be paranoid we *are* actually seeing this other page load, not some kind of race + // for if/when we do start firing pageshow for the error page... + function (e) { + return gBrowser.currentURI.spec == "about:robots"; + } + ); + gBrowser.goBack(); + await promiseOtherPageLoaded; + ok( + gBrowser.webNavigation.canGoForward, + "Should be able to go forward from previous page." + ); + } + BrowserTestUtils.removeTab(tab); +}); diff --git a/browser/base/content/test/general/browser_lastAccessedTab.js b/browser/base/content/test/general/browser_lastAccessedTab.js new file mode 100644 index 0000000000..631fcb3bfe --- /dev/null +++ b/browser/base/content/test/general/browser_lastAccessedTab.js @@ -0,0 +1,62 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +// gBrowser.selectedTab.lastAccessed and Date.now() called from this test can't +// run concurrently, and therefore don't always match exactly. +const CURRENT_TIME_TOLERANCE_MS = 15; + +function isCurrent(tab, msg) { + const DIFF = Math.abs(Date.now() - tab.lastAccessed); + ok(DIFF <= CURRENT_TIME_TOLERANCE_MS, msg + " (difference: " + DIFF + ")"); +} + +function nextStep(fn) { + setTimeout(fn, CURRENT_TIME_TOLERANCE_MS + 10); +} + +var originalTab; +var newTab; + +function test() { + waitForExplicitFinish(); + // This test assumes that time passes between operations. But if the precision + // is low enough, and the test fast enough, an operation, and a successive call + // to Date.now() will have the same time value. + SpecialPowers.pushPrefEnv( + { set: [["privacy.reduceTimerPrecision", false]] }, + function () { + originalTab = gBrowser.selectedTab; + nextStep(step2); + } + ); +} + +function step2() { + isCurrent(originalTab, "selected tab has the current timestamp"); + newTab = BrowserTestUtils.addTab(gBrowser, "about:blank", { + skipAnimation: true, + }); + nextStep(step3); +} + +function step3() { + ok(newTab.lastAccessed < Date.now(), "new tab hasn't been selected so far"); + gBrowser.selectedTab = newTab; + isCurrent(newTab, "new tab has the current timestamp after being selected"); + nextStep(step4); +} + +function step4() { + ok( + originalTab.lastAccessed < Date.now(), + "original tab has old timestamp after being deselected" + ); + isCurrent( + newTab, + "new tab has the current timestamp since it's still selected" + ); + + gBrowser.removeTab(newTab); + finish(); +} diff --git a/browser/base/content/test/general/browser_menuButtonFitts.js b/browser/base/content/test/general/browser_menuButtonFitts.js new file mode 100644 index 0000000000..f56f46eb6c --- /dev/null +++ b/browser/base/content/test/general/browser_menuButtonFitts.js @@ -0,0 +1,69 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +"use strict"; + +function getNavBarEndPosition() { + let navBar = document.getElementById("nav-bar"); + let boundingRect = navBar.getBoundingClientRect(); + + // Find where the nav-bar is vertically. + let y = boundingRect.top + Math.floor(boundingRect.height / 2); + // Use the last pixel of the screen since it is maximized. + let x = boundingRect.width - 1; + return { x, y }; +} + +/** + * Clicking the right end of a maximized window should open the hamburger menu. + */ +add_task(async function test_clicking_hamburger_edge_fitts() { + if (window.windowState != window.STATE_MAXIMIZED) { + info(`Waiting for maximize, current state: ${window.windowState}`); + let resizeDone = BrowserTestUtils.waitForEvent( + window, + "resize", + false, + () => window.outerWidth >= screen.width - 1 + ); + let maximizeDone = BrowserTestUtils.waitForEvent(window, "sizemodechange"); + window.maximize(); + await maximizeDone; + await resizeDone; + } + + is(window.windowState, window.STATE_MAXIMIZED, "should be maximized"); + + let { x, y } = getNavBarEndPosition(); + info(`Clicking in ${x}, ${y}`); + + let popupHiddenResolve; + let popupHiddenPromise = new Promise(resolve => { + popupHiddenResolve = resolve; + }); + async function onPopupHidden() { + PanelUI.panel.removeEventListener("popuphidden", onPopupHidden); + + info("Waiting for restore"); + + let restoreDone = BrowserTestUtils.waitForEvent(window, "sizemodechange"); + window.restore(); + await restoreDone; + + popupHiddenResolve(); + } + function onPopupShown() { + PanelUI.panel.removeEventListener("popupshown", onPopupShown); + ok(true, "Clicking at the far edge of the window opened the menu popup."); + PanelUI.panel.addEventListener("popuphidden", onPopupHidden); + PanelUI.hide(); + } + registerCleanupFunction(function () { + PanelUI.panel.removeEventListener("popupshown", onPopupShown); + PanelUI.panel.removeEventListener("popuphidden", onPopupHidden); + }); + PanelUI.panel.addEventListener("popupshown", onPopupShown); + EventUtils.synthesizeMouseAtPoint(x, y, {}, window); + await popupHiddenPromise; +}); diff --git a/browser/base/content/test/general/browser_middleMouse_noJSPaste.js b/browser/base/content/test/general/browser_middleMouse_noJSPaste.js new file mode 100644 index 0000000000..f023b78909 --- /dev/null +++ b/browser/base/content/test/general/browser_middleMouse_noJSPaste.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const middleMousePastePref = "middlemouse.contentLoadURL"; +const autoScrollPref = "general.autoScroll"; + +add_task(async function () { + await pushPrefs([middleMousePastePref, true], [autoScrollPref, false]); + + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser); + + let url = "javascript:http://www.example.com/"; + await new Promise((resolve, reject) => { + SimpleTest.waitForClipboard( + url, + () => { + Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(Ci.nsIClipboardHelper) + .copyString(url); + }, + resolve, + () => { + ok(false, "Clipboard copy failed"); + reject(); + } + ); + }); + + let middlePagePromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); + + // Middle click on the content area + info("Middle clicking"); + await BrowserTestUtils.synthesizeMouse( + null, + 10, + 10, + { button: 1 }, + gBrowser.selectedBrowser + ); + await middlePagePromise; + + is( + gBrowser.currentURI.spec, + url.replace(/^javascript:/, ""), + "url loaded by middle click doesn't include JS" + ); + + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/general/browser_minimize.js b/browser/base/content/test/general/browser_minimize.js new file mode 100644 index 0000000000..a57fea079c --- /dev/null +++ b/browser/base/content/test/general/browser_minimize.js @@ -0,0 +1,49 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +add_task(async function () { + registerCleanupFunction(function () { + window.restore(); + }); + function isActive() { + return gBrowser.selectedTab.linkedBrowser.docShellIsActive; + } + + ok(isActive(), "Docshell should be active when starting the test"); + ok(!document.hidden, "Top level window should be visible"); + + info("Calling window.minimize"); + let promiseSizeModeChange = BrowserTestUtils.waitForEvent( + window, + "sizemodechange" + ); + window.minimize(); + await promiseSizeModeChange; + ok(!isActive(), "Docshell should be Inactive"); + ok(document.hidden, "Top level window should be hidden"); + + info("Calling window.restore"); + promiseSizeModeChange = BrowserTestUtils.waitForEvent( + window, + "sizemodechange" + ); + window.restore(); + // On Ubuntu `window.restore` doesn't seem to work, use a timer to make the + // test fail faster and more cleanly than with a test timeout. + await Promise.race([ + promiseSizeModeChange, + new Promise((resolve, reject) => + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(() => { + reject("timed out waiting for sizemodechange event"); + }, 5000) + ), + ]); + // The sizemodechange event can sometimes be fired before the + // occlusionstatechange event, especially in chaos mode. + if (window.isFullyOccluded) { + await BrowserTestUtils.waitForEvent(window, "occlusionstatechange"); + } + ok(isActive(), "Docshell should be active again"); + ok(!document.hidden, "Top level window should be visible"); +}); diff --git a/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js b/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js new file mode 100644 index 0000000000..be3de519d6 --- /dev/null +++ b/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js @@ -0,0 +1,42 @@ +"use strict"; + +const kURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/browser/browser/base/content/test/general/dummy_page.html"; +("data:text/html,<a href=''>Middle-click me</a>"); + +/* + * Check that when manually opening content JS links in new tabs/windows, + * we use the correct principal, and we don't clear the URL bar. + */ +add_task(async function () { + await BrowserTestUtils.withNewTab(kURL, async function (browser) { + let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser); + await SpecialPowers.spawn(browser, [], async function () { + let a = content.document.createElement("a"); + // newTabPromise won't resolve until it has a URL that's not "about:blank". + // But doing document.open() from inside that same document does not change + // the URL of the docshell. So we need to do some URL change to cause + // newTabPromise to resolve, since the document is at about:blank the whole + // time, URL-wise. Navigating to '#' should do the trick without changing + // anything else about the document involved. + a.href = + "javascript:document.write('spoof'); location.href='#'; void(0);"; + a.textContent = "Some link"; + content.document.body.appendChild(a); + }); + info("Added element"); + await BrowserTestUtils.synthesizeMouseAtCenter("a", { button: 1 }, browser); + let newTab = await newTabPromise; + is( + newTab.linkedBrowser.contentPrincipal.origin, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com", + "Principal should be for example.com" + ); + await BrowserTestUtils.switchTab(gBrowser, newTab); + info(gURLBar.value); + isnot(gURLBar.value, "", "URL bar should not be empty."); + BrowserTestUtils.removeTab(newTab); + }); +}); diff --git a/browser/base/content/test/general/browser_newTabDrop.js b/browser/base/content/test/general/browser_newTabDrop.js new file mode 100644 index 0000000000..d0e7f35c1f --- /dev/null +++ b/browser/base/content/test/general/browser_newTabDrop.js @@ -0,0 +1,221 @@ +const ANY_URL = undefined; + +const { SearchTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/SearchTestUtils.sys.mjs" +); + +SearchTestUtils.init(this); + +registerCleanupFunction(async function cleanup() { + while (gBrowser.tabs.length > 1) { + BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]); + } +}); + +add_task(async function test_setup() { + // This test opens multiple tabs and some confirm dialogs, that takes long. + requestLongerTimeout(2); + + // Stop search-engine loads from hitting the network + await SearchTestUtils.installSearchExtension( + { + name: "MozSearch", + search_url: "https://example.com/", + search_url_get_params: "q={searchTerms}", + }, + { setAsDefault: true } + ); +}); + +// New Tab Button opens any link. +add_task(async function single_url() { + await dropText("mochi.test/first", ["http://mochi.test/first"]); +}); +add_task(async function single_url2() { + await dropText("mochi.test/second", ["http://mochi.test/second"]); +}); +add_task(async function single_url3() { + await dropText("mochi.test/third", ["http://mochi.test/third"]); +}); + +// Single text/plain item, with multiple links. +add_task(async function multiple_urls() { + await dropText("www.mochi.test/1\nmochi.test/2", [ + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://www.mochi.test/1", + "http://mochi.test/2", + ]); +}); + +// Multiple text/plain items, with single and multiple links. +add_task(async function multiple_items_single_and_multiple_links() { + await drop( + [ + [{ type: "text/plain", data: "mochi.test/5" }], + [{ type: "text/plain", data: "mochi.test/6\nmochi.test/7" }], + ], + ["http://mochi.test/5", "http://mochi.test/6", "http://mochi.test/7"] + ); +}); + +// Single text/x-moz-url item, with multiple links. +// "text/x-moz-url" has titles in even-numbered lines. +add_task(async function single_moz_url_multiple_links() { + await drop( + [ + [ + { + type: "text/x-moz-url", + data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9", + }, + ], + ], + ["http://mochi.test/8", "http://mochi.test/9"] + ); +}); + +// Single item with multiple types. +add_task(async function single_item_multiple_types() { + await drop( + [ + [ + { type: "text/plain", data: "mochi.test/10" }, + { type: "text/x-moz-url", data: "mochi.test/11\nTITLE11" }, + ], + ], + ["http://mochi.test/11"] + ); +}); + +// Warn when too many URLs are dropped. +add_task(async function multiple_tabs_under_max() { + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/multi" + i); + } + await dropText(urls.join("\n"), [ + "http://mochi.test/multi0", + "http://mochi.test/multi1", + "http://mochi.test/multi2", + "http://mochi.test/multi3", + "http://mochi.test/multi4", + ]); +}); +add_task(async function multiple_tabs_over_max_accept() { + await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]); + + let confirmPromise = BrowserTestUtils.promiseAlertDialog("accept"); + + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/accept" + i); + } + await dropText(urls.join("\n"), [ + "http://mochi.test/accept0", + "http://mochi.test/accept1", + "http://mochi.test/accept2", + "http://mochi.test/accept3", + "http://mochi.test/accept4", + ]); + + await confirmPromise; + + await popPrefs(); +}); +add_task(async function multiple_tabs_over_max_cancel() { + await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]); + + let confirmPromise = BrowserTestUtils.promiseAlertDialog("cancel"); + + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/cancel" + i); + } + await dropText(urls.join("\n"), []); + + await confirmPromise; + + await popPrefs(); +}); + +// Open URLs ignoring non-URL. +add_task(async function multiple_urls() { + await dropText( + ` + mochi.test/urls0 + mochi.test/urls1 + mochi.test/urls2 + non url0 + mochi.test/urls3 + non url1 + non url2 +`, + [ + "http://mochi.test/urls0", + "http://mochi.test/urls1", + "http://mochi.test/urls2", + "http://mochi.test/urls3", + ] + ); +}); + +// Open single search if there's no URL. +add_task(async function multiple_text() { + await dropText( + ` + non url0 + non url1 + non url2 +`, + [ANY_URL] + ); +}); + +function dropText(text, expectedURLs) { + return drop([[{ type: "text/plain", data: text }]], expectedURLs); +} + +async function drop(dragData, expectedURLs) { + let dragDataString = JSON.stringify(dragData); + info( + `Starting test for dragData:${dragDataString}; expectedURLs.length:${expectedURLs.length}` + ); + let EventUtils = {}; + Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", + EventUtils + ); + + // Since synthesizeDrop triggers the srcElement, need to use another button + // that should be visible. + let dragSrcElement = document.getElementById("back-button"); + ok(dragSrcElement, "Back button exists"); + let newTabButton = document.getElementById( + gBrowser.tabContainer.hasAttribute("overflow") + ? "new-tab-button" + : "tabs-newtab-button" + ); + ok(newTabButton, "New Tab button exists"); + + let awaitDrop = BrowserTestUtils.waitForEvent(newTabButton, "drop"); + + let loadedPromises = expectedURLs.map(url => + BrowserTestUtils.waitForNewTab(gBrowser, url, false, true) + ); + + EventUtils.synthesizeDrop( + dragSrcElement, + newTabButton, + dragData, + "link", + window + ); + + let tabs = await Promise.all(loadedPromises); + for (let tab of tabs) { + BrowserTestUtils.removeTab(tab); + } + + await awaitDrop; + ok(true, "Got drop event"); +} diff --git a/browser/base/content/test/general/browser_newWindowDrop.js b/browser/base/content/test/general/browser_newWindowDrop.js new file mode 100644 index 0000000000..243b691873 --- /dev/null +++ b/browser/base/content/test/general/browser_newWindowDrop.js @@ -0,0 +1,230 @@ +const { SearchTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/SearchTestUtils.sys.mjs" +); + +SearchTestUtils.init(this); + +add_task(async function test_setup() { + // Opening multiple windows on debug build takes too long time. + requestLongerTimeout(10); + + // Stop search-engine loads from hitting the network + await SearchTestUtils.installSearchExtension( + { + name: "MozSearch", + search_url: "https://example.com/", + search_url_get_params: "q={searchTerms}", + }, + { setAsDefault: true } + ); + + // Move New Window button to nav bar, to make it possible to drag and drop. + let { CustomizableUI } = ChromeUtils.importESModule( + "resource:///modules/CustomizableUI.sys.mjs" + ); + let origPlacement = CustomizableUI.getPlacementOfWidget("new-window-button"); + if (!origPlacement || origPlacement.area != CustomizableUI.AREA_NAVBAR) { + CustomizableUI.addWidgetToArea( + "new-window-button", + CustomizableUI.AREA_NAVBAR, + 0 + ); + CustomizableUI.ensureWidgetPlacedInWindow("new-window-button", window); + registerCleanupFunction(function () { + CustomizableUI.removeWidgetFromArea("new-window-button"); + }); + } + + CustomizableUI.addWidgetToArea("sidebar-button", "nav-bar"); + registerCleanupFunction(() => + CustomizableUI.removeWidgetFromArea("sidebar-button") + ); +}); + +// New Window Button opens any link. +add_task(async function single_url() { + await dropText("mochi.test/first", ["http://mochi.test/first"]); +}); +add_task(async function single_javascript() { + await dropText("javascript:'bad'", ["about:blank"]); +}); +add_task(async function single_javascript_capital() { + await dropText("jAvascript:'bad'", ["about:blank"]); +}); +add_task(async function single_url2() { + await dropText("mochi.test/second", ["http://mochi.test/second"]); +}); +add_task(async function single_data_url() { + await dropText("data:text/html,bad", ["data:text/html,bad"]); +}); +add_task(async function single_url3() { + await dropText("mochi.test/third", ["http://mochi.test/third"]); +}); + +// Single text/plain item, with multiple links. +add_task(async function multiple_urls() { + await dropText("mochi.test/1\nmochi.test/2", [ + "http://mochi.test/1", + "http://mochi.test/2", + ]); +}); +add_task(async function multiple_urls_javascript() { + await dropText("javascript:'bad1'\nmochi.test/3", [ + "about:blank", + "http://mochi.test/3", + ]); +}); +add_task(async function multiple_urls_data() { + await dropText("mochi.test/4\ndata:text/html,bad1", [ + "http://mochi.test/4", + "data:text/html,bad1", + ]); +}); + +// Multiple text/plain items, with single and multiple links. +add_task(async function multiple_items_single_and_multiple_links() { + await drop( + [ + [{ type: "text/plain", data: "mochi.test/5" }], + [{ type: "text/plain", data: "mochi.test/6\nmochi.test/7" }], + ], + ["http://mochi.test/5", "http://mochi.test/6", "http://mochi.test/7"] + ); +}); + +// Single text/x-moz-url item, with multiple links. +// "text/x-moz-url" has titles in even-numbered lines. +add_task(async function single_moz_url_multiple_links() { + await drop( + [ + [ + { + type: "text/x-moz-url", + data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9", + }, + ], + ], + ["http://mochi.test/8", "http://mochi.test/9"] + ); +}); + +// Single item with multiple types. +add_task(async function single_item_multiple_types() { + await drop( + [ + [ + { type: "text/plain", data: "mochi.test/10" }, + { type: "text/x-moz-url", data: "mochi.test/11\nTITLE11" }, + ], + ], + ["http://mochi.test/11"] + ); +}); + +// Warn when too many URLs are dropped. +add_task(async function multiple_tabs_under_max() { + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/multi" + i); + } + await dropText(urls.join("\n"), [ + "http://mochi.test/multi0", + "http://mochi.test/multi1", + "http://mochi.test/multi2", + "http://mochi.test/multi3", + "http://mochi.test/multi4", + ]); +}); +add_task(async function multiple_tabs_over_max_accept() { + await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]); + + let confirmPromise = BrowserTestUtils.promiseAlertDialog("accept"); + + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/accept" + i); + } + await dropText( + urls.join("\n"), + [ + "http://mochi.test/accept0", + "http://mochi.test/accept1", + "http://mochi.test/accept2", + "http://mochi.test/accept3", + "http://mochi.test/accept4", + ], + true + ); + + await confirmPromise; + + await popPrefs(); +}); +add_task(async function multiple_tabs_over_max_cancel() { + await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]); + + let confirmPromise = BrowserTestUtils.promiseAlertDialog("cancel"); + + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/cancel" + i); + } + await dropText(urls.join("\n"), [], true); + + await confirmPromise; + + await popPrefs(); +}); + +function dropText(text, expectedURLs, ignoreFirstWindow = false) { + return drop( + [[{ type: "text/plain", data: text }]], + expectedURLs, + ignoreFirstWindow + ); +} + +async function drop(dragData, expectedURLs, ignoreFirstWindow = false) { + let dragDataString = JSON.stringify(dragData); + info( + `Starting test for dragData:${dragDataString}; expectedURLs.length:${expectedURLs.length}` + ); + let EventUtils = {}; + Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", + EventUtils + ); + + // Since synthesizeDrop triggers the srcElement, need to use another button + // that should be visible. + let dragSrcElement = document.getElementById("sidebar-button"); + ok(dragSrcElement, "Sidebar button exists"); + let newWindowButton = document.getElementById("new-window-button"); + ok(newWindowButton, "New Window button exists"); + + let awaitDrop = BrowserTestUtils.waitForEvent(newWindowButton, "drop"); + + let loadedPromises = expectedURLs.map(url => + BrowserTestUtils.waitForNewWindow({ + url, + anyWindow: true, + maybeErrorPage: true, + }) + ); + + EventUtils.synthesizeDrop( + dragSrcElement, + newWindowButton, + dragData, + "link", + window + ); + + let windows = await Promise.all(loadedPromises); + for (let window of windows) { + await BrowserTestUtils.closeWindow(window); + } + + await awaitDrop; + ok(true, "Got drop event"); +} diff --git a/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js new file mode 100644 index 0000000000..8e9f458073 --- /dev/null +++ b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js @@ -0,0 +1,63 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ + +const TEST_FILE = "file_with_link_to_http.html"; +// eslint-disable-next-line @microsoft/sdl/no-insecure-url +const TEST_HTTP = "http://example.org/"; + +// Test for bug 1338375. +add_task(async function () { + // Open file:// page. + let dir = getChromeDir(getResolvedURI(gTestPath)); + dir.append(TEST_FILE); + const uriString = Services.io.newFileURI(dir).spec; + let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uriString); + registerCleanupFunction(async function () { + BrowserTestUtils.removeTab(tab); + }); + let browser = tab.linkedBrowser; + + // Set pref to open in new window. + Services.prefs.setIntPref("browser.link.open_newwindow", 2); + registerCleanupFunction(function () { + Services.prefs.clearUserPref("browser.link.open_newwindow"); + }); + + // Open new http window from JavaScript in file:// page and check that we get + // a new window with the correct page and features. + let promiseNewWindow = BrowserTestUtils.waitForNewWindow({ url: TEST_HTTP }); + await SpecialPowers.spawn(browser, [TEST_HTTP], uri => { + content.open(uri, "_blank"); + }); + let win = await promiseNewWindow; + registerCleanupFunction(async function () { + await BrowserTestUtils.closeWindow(win); + }); + ok(win, "Check that an http window loaded when using window.open."); + ok( + win.menubar.visible, + "Check that the menu bar on the new window is visible." + ); + ok( + win.toolbar.visible, + "Check that the tool bar on the new window is visible." + ); + + // Open new http window from a link in file:// page and check that we get a + // new window with the correct page and features. + promiseNewWindow = BrowserTestUtils.waitForNewWindow({ url: TEST_HTTP }); + await BrowserTestUtils.synthesizeMouseAtCenter("#linkToExample", {}, browser); + let win2 = await promiseNewWindow; + registerCleanupFunction(async function () { + await BrowserTestUtils.closeWindow(win2); + }); + ok(win2, "Check that an http window loaded when using link."); + ok( + win2.menubar.visible, + "Check that the menu bar on the new window is visible." + ); + ok( + win2.toolbar.visible, + "Check that the tool bar on the new window is visible." + ); +}); diff --git a/browser/base/content/test/general/browser_newwindow_focus.js b/browser/base/content/test/general/browser_newwindow_focus.js new file mode 100644 index 0000000000..dbf99f1233 --- /dev/null +++ b/browser/base/content/test/general/browser_newwindow_focus.js @@ -0,0 +1,93 @@ +"use strict"; + +/** + * These tests are for the auto-focus behaviour on the initial browser + * when a window is opened from content. + */ + +const PAGE = `data:text/html,<a id="target" href="%23" onclick="window.open('http://www.example.com', '_blank', 'width=100,height=100');">Click me</a>`; + +/** + * Test that when a new window is opened from content, focus moves + * to the initial browser in that window once the window has finished + * painting. + */ +add_task(async function test_focus_browser() { + await BrowserTestUtils.withNewTab( + { + url: PAGE, + gBrowser, + }, + async function (browser) { + let newWinPromise = BrowserTestUtils.domWindowOpenedAndLoaded(null); + let delayedStartupPromise = BrowserTestUtils.waitForNewWindow(); + + await BrowserTestUtils.synthesizeMouseAtCenter("#target", {}, browser); + let newWin = await newWinPromise; + await BrowserTestUtils.waitForContentEvent( + newWin.gBrowser.selectedBrowser, + "MozAfterPaint" + ); + await delayedStartupPromise; + + let focusedElement = Services.focus.getFocusedElementForWindow( + newWin, + false, + {} + ); + + Assert.equal( + focusedElement, + newWin.gBrowser.selectedBrowser, + "Initial browser should be focused" + ); + + await BrowserTestUtils.closeWindow(newWin); + } + ); +}); + +/** + * Test that when a new window is opened from content and focus + * shifts in that window before the content has a chance to paint + * that we _don't_ steal focus once content has painted. + */ +add_task(async function test_no_steal_focus() { + await BrowserTestUtils.withNewTab( + { + url: PAGE, + gBrowser, + }, + async function (browser) { + let newWinPromise = BrowserTestUtils.domWindowOpenedAndLoaded(null); + let delayedStartupPromise = BrowserTestUtils.waitForNewWindow(); + + await BrowserTestUtils.synthesizeMouseAtCenter("#target", {}, browser); + let newWin = await newWinPromise; + + // Because we're switching focus, we shouldn't steal it once + // content paints. + newWin.gURLBar.focus(); + + await BrowserTestUtils.waitForContentEvent( + newWin.gBrowser.selectedBrowser, + "MozAfterPaint" + ); + await delayedStartupPromise; + + let focusedElement = Services.focus.getFocusedElementForWindow( + newWin, + false, + {} + ); + + Assert.equal( + focusedElement, + newWin.gURLBar.inputField, + "URLBar should be focused" + ); + + await BrowserTestUtils.closeWindow(newWin); + } + ); +}); diff --git a/browser/base/content/test/general/browser_plainTextLinks.js b/browser/base/content/test/general/browser_plainTextLinks.js new file mode 100644 index 0000000000..706f21387c --- /dev/null +++ b/browser/base/content/test/general/browser_plainTextLinks.js @@ -0,0 +1,237 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +function testExpected(expected, msg) { + is( + document.getElementById("context-openlinkincurrent").hidden, + expected, + msg + ); +} + +function testLinkExpected(expected, msg) { + is(gContextMenu.linkURL, expected, msg); +} + +add_task(async function () { + const url = + "data:text/html;charset=UTF-8,Test For Non-Hyperlinked url selection"; + await BrowserTestUtils.openNewForegroundTab(gBrowser, url); + + await SimpleTest.promiseFocus(gBrowser.selectedBrowser); + + // Initial setup of the content area. + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function (arg) { + let doc = content.document; + let range = doc.createRange(); + let selection = content.getSelection(); + + let mainDiv = doc.createElement("div"); + let div = doc.createElement("div"); + let div2 = doc.createElement("div"); + let span1 = doc.createElement("span"); + let span2 = doc.createElement("span"); + let span3 = doc.createElement("span"); + let span4 = doc.createElement("span"); + let p1 = doc.createElement("p"); + let p2 = doc.createElement("p"); + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + span1.textContent = "http://index."; + span2.textContent = "example.com example.com"; + span3.textContent = " - Test"; + span4.innerHTML = + "<a href='http://www.example.com'>http://www.example.com/example</a>"; + p1.textContent = "mailto:test.com ftp.example.com"; + p2.textContent = "example.com -"; + div.appendChild(span1); + div.appendChild(span2); + div.appendChild(span3); + div.appendChild(span4); + div.appendChild(p1); + div.appendChild(p2); + let p3 = doc.createElement("p"); + p3.textContent = "main.example.com"; + div2.appendChild(p3); + mainDiv.appendChild(div); + mainDiv.appendChild(div2); + doc.body.appendChild(mainDiv); + + function setSelection(el1, el2, index1, index2) { + while (el1.nodeType != el1.TEXT_NODE) { + el1 = el1.firstChild; + } + while (el2.nodeType != el1.TEXT_NODE) { + el2 = el2.firstChild; + } + + selection.removeAllRanges(); + range.setStart(el1, index1); + range.setEnd(el2, index2); + selection.addRange(range); + + return range; + } + + // Each of these tests creates a selection and returns a range within it. + content.tests = [ + () => setSelection(span1.firstChild, span2.firstChild, 0, 11), + () => setSelection(span1.firstChild, span2.firstChild, 7, 11), + () => setSelection(span1.firstChild, span2.firstChild, 8, 11), + () => setSelection(span2.firstChild, span2.firstChild, 0, 11), + () => setSelection(span2.firstChild, span2.firstChild, 11, 23), + () => setSelection(span2.firstChild, span2.firstChild, 0, 10), + () => setSelection(span2.firstChild, span3.firstChild, 12, 7), + () => setSelection(span2.firstChild, span2.firstChild, 12, 19), + () => setSelection(p1.firstChild, p1.firstChild, 0, 15), + () => setSelection(p1.firstChild, p1.firstChild, 16, 31), + () => setSelection(p2.firstChild, p2.firstChild, 0, 14), + () => { + selection.selectAllChildren(div2); + return selection.getRangeAt(0); + }, + () => { + selection.selectAllChildren(span4); + return selection.getRangeAt(0); + }, + () => { + mainDiv.innerHTML = "(open-suse.ru)"; + return setSelection(mainDiv, mainDiv, 1, 13); + }, + () => setSelection(mainDiv, mainDiv, 1, 14), + ]; + }); + + let checks = [ + () => + testExpected( + false, + "The link context menu should show for http://www.example.com" + ), + () => + testExpected( + false, + "The link context menu should show for www.example.com" + ), + () => + testExpected( + true, + "The link context menu should not show for ww.example.com" + ), + () => { + testExpected(false, "The link context menu should show for example.com"); + testLinkExpected( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/", + "url for example.com selection should not prepend www" + ); + }, + () => + testExpected(false, "The link context menu should show for example.com"), + () => + testExpected( + true, + "Link options should not show for selection that's not at a word boundary" + ), + () => + testExpected( + true, + "Link options should not show for selection that has whitespace" + ), + () => + testExpected( + true, + "Link options should not show unless a url is selected" + ), + () => testExpected(true, "Link options should not show for mailto: links"), + () => { + testExpected(false, "Link options should show for ftp.example.com"); + testLinkExpected( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://ftp.example.com/", + "ftp.example.com should be preceeded with http://" + ); + }, + () => testExpected(false, "Link options should show for www.example.com "), + () => + testExpected( + false, + "Link options should show for triple-click selections" + ), + () => + testLinkExpected( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://www.example.com/", + "Linkified text should open the correct link" + ), + () => { + testExpected(false, "Link options should show for open-suse.ru"); + testLinkExpected( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://open-suse.ru/", + "Linkified text should open the correct link" + ); + }, + () => + testExpected(true, "Link options should not show for 'open-suse.ru)'"), + ]; + + let contentAreaContextMenu = document.getElementById( + "contentAreaContextMenu" + ); + + for (let testid = 0; testid < checks.length; testid++) { + let menuPosition = await SpecialPowers.spawn( + gBrowser.selectedBrowser, + [{ testid }], + async function (arg) { + let range = content.tests[arg.testid](); + + // Get the range of the selection and determine its coordinates. These + // coordinates will be returned to the parent process and the context menu + // will be opened at that location. + let rangeRect = range.getBoundingClientRect(); + return [rangeRect.x + 3, rangeRect.y + 3]; + } + ); + + // Trigger a mouse event until we receive the popupshown event. + let sawPopup = false; + let popupShownPromise = BrowserTestUtils.waitForEvent( + contentAreaContextMenu, + "popupshown", + false, + () => { + sawPopup = true; + return true; + } + ); + while (!sawPopup) { + await BrowserTestUtils.synthesizeMouseAtPoint( + menuPosition[0], + menuPosition[1], + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + if (!sawPopup) { + await new Promise(r => setTimeout(r, 100)); + } + } + await popupShownPromise; + + checks[testid](); + + // On Linux non-e10s it's possible the menu was closed by a focus-out event + // on the window. Work around this by calling hidePopup only if the menu + // hasn't been closed yet. See bug 1352709 comment 36. + if (contentAreaContextMenu.state === "closed") { + continue; + } + + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + contentAreaContextMenu, + "popuphidden" + ); + contentAreaContextMenu.hidePopup(); + await popupHiddenPromise; + } + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/general/browser_printpreview.js b/browser/base/content/test/general/browser_printpreview.js new file mode 100644 index 0000000000..945e2bbd3a --- /dev/null +++ b/browser/base/content/test/general/browser_printpreview.js @@ -0,0 +1,43 @@ +let ourTab; + +async function test() { + waitForExplicitFinish(); + + BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home", true).then( + function (tab) { + ourTab = tab; + ok( + !document.querySelector(".printPreviewBrowser"), + "Should NOT be in print preview mode at starting this tests" + ); + testClosePrintPreviewWithEscKey(); + } + ); +} + +function tidyUp() { + BrowserTestUtils.removeTab(ourTab); + finish(); +} + +async function testClosePrintPreviewWithEscKey() { + await openPrintPreview(); + EventUtils.synthesizeKey("KEY_Escape"); + await checkPrintPreviewClosed(); + ok(true, "print preview mode should be finished by Esc key press"); + tidyUp(); +} + +async function openPrintPreview() { + document.getElementById("cmd_print").doCommand(); + await BrowserTestUtils.waitForCondition(() => { + let preview = document.querySelector(".printPreviewBrowser"); + return preview && BrowserTestUtils.is_visible(preview); + }); +} + +async function checkPrintPreviewClosed() { + await BrowserTestUtils.waitForCondition( + () => !document.querySelector(".printPreviewBrowser") + ); +} diff --git a/browser/base/content/test/general/browser_private_browsing_window.js b/browser/base/content/test/general/browser_private_browsing_window.js new file mode 100644 index 0000000000..34a4c8bbf0 --- /dev/null +++ b/browser/base/content/test/general/browser_private_browsing_window.js @@ -0,0 +1,133 @@ +// Make sure that we can open private browsing windows + +function test() { + waitForExplicitFinish(); + var nonPrivateWin = OpenBrowserWindow(); + ok( + !PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), + "OpenBrowserWindow() should open a normal window" + ); + nonPrivateWin.close(); + + var privateWin = OpenBrowserWindow({ private: true }); + ok( + PrivateBrowsingUtils.isWindowPrivate(privateWin), + "OpenBrowserWindow({private: true}) should open a private window" + ); + + nonPrivateWin = OpenBrowserWindow({ private: false }); + ok( + !PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), + "OpenBrowserWindow({private: false}) should open a normal window" + ); + nonPrivateWin.close(); + + whenDelayedStartupFinished(privateWin, function () { + nonPrivateWin = privateWin.OpenBrowserWindow({ private: false }); + ok( + !PrivateBrowsingUtils.isWindowPrivate(nonPrivateWin), + "privateWin.OpenBrowserWindow({private: false}) should open a normal window" + ); + + nonPrivateWin.close(); + + [ + { + normal: "menu_newNavigator", + private: "menu_newPrivateWindow", + accesskey: true, + }, + { + normal: "appmenu_newNavigator", + private: "appmenu_newPrivateWindow", + accesskey: false, + }, + ].forEach(function (menu) { + let newWindow = privateWin.document.getElementById(menu.normal); + let newPrivateWindow = privateWin.document.getElementById(menu.private); + if (newWindow && newPrivateWindow) { + ok( + !newPrivateWindow.hidden, + "New Private Window menu item should be hidden" + ); + isnot( + newWindow.label, + newPrivateWindow.label, + "New Window's label shouldn't be overwritten" + ); + if (menu.accesskey) { + isnot( + newWindow.accessKey, + newPrivateWindow.accessKey, + "New Window's accessKey shouldn't be overwritten" + ); + } + isnot( + newWindow.command, + newPrivateWindow.command, + "New Window's command shouldn't be overwritten" + ); + } + }); + + is( + privateWin.gBrowser.tabs[0].label, + "New Private Tab", + "New tabs in the private browsing windows should have 'New Private Tab' as the title." + ); + + privateWin.close(); + + Services.prefs.setBoolPref("browser.privatebrowsing.autostart", true); + privateWin = OpenBrowserWindow({ private: true }); + whenDelayedStartupFinished(privateWin, function () { + [ + { + normal: "menu_newNavigator", + private: "menu_newPrivateWindow", + accessKey: true, + }, + { + normal: "appmenu_newNavigator", + private: "appmenu_newPrivateWindow", + accessKey: false, + }, + ].forEach(function (menu) { + let newWindow = privateWin.document.getElementById(menu.normal); + let newPrivateWindow = privateWin.document.getElementById(menu.private); + if (newWindow && newPrivateWindow) { + ok( + newPrivateWindow.hidden, + "New Private Window menu item should be hidden" + ); + is( + newWindow.label, + newPrivateWindow.label, + "New Window's label should be overwritten" + ); + if (menu.accesskey) { + is( + newWindow.accessKey, + newPrivateWindow.accessKey, + "New Window's accessKey should be overwritten" + ); + } + is( + newWindow.command, + newPrivateWindow.command, + "New Window's command should be overwritten" + ); + } + }); + + is( + privateWin.gBrowser.tabs[0].label, + "New Tab", + "Normal tab title is used also in the permanent private browsing mode." + ); + privateWin.close(); + Services.prefs.clearUserPref("browser.privatebrowsing.autostart"); + finish(); + }); + }); +} diff --git a/browser/base/content/test/general/browser_private_no_prompt.js b/browser/base/content/test/general/browser_private_no_prompt.js new file mode 100644 index 0000000000..d8c9f8e7b5 --- /dev/null +++ b/browser/base/content/test/general/browser_private_no_prompt.js @@ -0,0 +1,12 @@ +function test() { + waitForExplicitFinish(); + var privateWin = OpenBrowserWindow({ private: true }); + + whenDelayedStartupFinished(privateWin, function () { + privateWin.BrowserOpenTab(); + privateWin.BrowserTryToCloseWindow(); + ok(true, "didn't prompt"); + + executeSoon(finish); + }); +} diff --git a/browser/base/content/test/general/browser_refreshBlocker.js b/browser/base/content/test/general/browser_refreshBlocker.js new file mode 100644 index 0000000000..0052282257 --- /dev/null +++ b/browser/base/content/test/general/browser_refreshBlocker.js @@ -0,0 +1,209 @@ +"use strict"; + +const META_PAGE = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/general/refresh_meta.sjs"; +const HEADER_PAGE = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/general/refresh_header.sjs"; +const TARGET_PAGE = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.org/browser/browser/base/content/test/general/dummy_page.html"; +const PREF = "accessibility.blockautorefresh"; + +/** + * Goes into the content, and simulates a meta-refresh header at a very + * low level, and checks to see if it was blocked. This will always cancel + * the refresh, regardless of whether or not the refresh was blocked. + * + * @param browser (<xul:browser>) + * The browser to test for refreshing. + * @param expectRefresh (bool) + * Whether or not we expect the refresh attempt to succeed. + * @returns Promise + */ +async function attemptFakeRefresh(browser, expectRefresh) { + await SpecialPowers.spawn( + browser, + [expectRefresh], + async function (contentExpectRefresh) { + let URI = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI; + let refresher = docShell.QueryInterface(Ci.nsIRefreshURI); + refresher.refreshURI(URI, null, 0); + + Assert.equal( + refresher.refreshPending, + contentExpectRefresh, + "Got the right refreshPending state" + ); + + if (refresher.refreshPending) { + // Cancel the pending refresh + refresher.cancelRefreshURITimers(); + } + + // The RefreshBlocker will wait until onLocationChange has + // been fired before it will show any notifications (see bug + // 1246291), so we cause this to occur manually here. + content.location = URI.spec + "#foo"; + } + ); +} + +/** + * Tests that we can enable the blocking pref and block a refresh + * from occurring while showing a notification bar. Also tests that + * when we disable the pref, that refreshes can go through again. + */ +add_task(async function test_can_enable_and_block() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: TARGET_PAGE, + }, + async function (browser) { + // By default, we should be able to reload the page. + await attemptFakeRefresh(browser, true); + + await pushPrefs(["accessibility.blockautorefresh", true]); + + let notificationPromise = BrowserTestUtils.waitForNotificationBar( + gBrowser, + browser, + "refresh-blocked" + ); + + await attemptFakeRefresh(browser, false); + + await notificationPromise; + + await pushPrefs(["accessibility.blockautorefresh", false]); + + // Page reloads should go through again. + await attemptFakeRefresh(browser, true); + } + ); +}); + +/** + * Attempts a "real" refresh by opening a tab, and then sending it to + * an SJS page that will attempt to cause a refresh. This will also pass + * a delay amount to the SJS page. The refresh should be blocked, and + * the notification should be shown. Once shown, the "Allow" button will + * be clicked, and the refresh will go through. Finally, the helper will + * close the tab and resolve the Promise. + * + * @param refreshPage (string) + * The SJS page to use. Use META_PAGE for the <meta> tag refresh + * case. Use HEADER_PAGE for the HTTP header case. + * @param delay (int) + * The amount, in ms, for the page to wait before attempting the + * refresh. + * + * @returns Promise + */ +async function testRealRefresh(refreshPage, delay) { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "about:blank", + }, + async function (browser) { + await pushPrefs(["accessibility.blockautorefresh", true]); + + BrowserTestUtils.loadURIString( + browser, + refreshPage + "?p=" + TARGET_PAGE + "&d=" + delay + ); + await BrowserTestUtils.browserLoaded(browser); + + // Once browserLoaded resolves, all nsIWebProgressListener callbacks + // should have fired, so the notification should be visible. + let notificationBox = gBrowser.getNotificationBox(browser); + let notification = notificationBox.currentNotification; + + ok(notification, "Notification should be visible"); + is( + notification.getAttribute("value"), + "refresh-blocked", + "Should be showing the right notification" + ); + + // Then click the button to allow the refresh. + let buttons = notification.buttonContainer.querySelectorAll( + ".notification-button" + ); + is(buttons.length, 1, "Should have one button."); + + // Prepare a Promise that should resolve when the refresh goes through + let refreshPromise = BrowserTestUtils.browserLoaded(browser); + buttons[0].click(); + + await refreshPromise; + } + ); +} + +/** + * Tests the meta-tag case for both short and longer delay times. + */ +add_task(async function test_can_allow_refresh() { + await testRealRefresh(META_PAGE, 0); + await testRealRefresh(META_PAGE, 100); + await testRealRefresh(META_PAGE, 500); +}); + +/** + * Tests that when a HTTP header case for both short and longer + * delay times. + */ +add_task(async function test_can_block_refresh_from_header() { + await testRealRefresh(HEADER_PAGE, 0); + await testRealRefresh(HEADER_PAGE, 100); + await testRealRefresh(HEADER_PAGE, 500); +}); + +/** + * Tests that we can update a notification when multiple reload/redirect + * attempts happen. + */ +add_task(async function test_can_update_notification() { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "about:blank", + }, + async function (browser) { + await pushPrefs(["accessibility.blockautorefresh", true]); + + // First, attempt a redirect + BrowserTestUtils.loadURIString( + browser, + META_PAGE + "?d=0&p=" + TARGET_PAGE + ); + await BrowserTestUtils.browserLoaded(browser); + + // Once browserLoaded resolves, all nsIWebProgressListener callbacks + // should have fired, so the notification should be visible. + let notificationBox = gBrowser.getNotificationBox(browser); + let notification = notificationBox.currentNotification; + + let message = notification.messageText.querySelector("span"); + is( + message.dataset.l10nId, + "refresh-blocked-redirect-label", + "Should be showing the redirect message" + ); + + // Next, attempt a refresh + await attemptFakeRefresh(browser, false); + + message = notification.messageText.querySelector("span"); + is( + message.dataset.l10nId, + "refresh-blocked-refresh-label", + "Should be showing the refresh message" + ); + } + ); +}); diff --git a/browser/base/content/test/general/browser_relatedTabs.js b/browser/base/content/test/general/browser_relatedTabs.js new file mode 100644 index 0000000000..22ed8fbb1b --- /dev/null +++ b/browser/base/content/test/general/browser_relatedTabs.js @@ -0,0 +1,74 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +add_task(async function () { + is(gBrowser.tabs.length, 1, "one tab is open initially"); + + // Add several new tabs in sequence, interrupted by selecting a + // different tab, moving a tab around and closing a tab, + // returning a list of opened tabs for verifying the expected order. + // The new tab behaviour is documented in bug 465673 + let tabs = []; + let ReferrerInfo = Components.Constructor( + "@mozilla.org/referrer-info;1", + "nsIReferrerInfo", + "init" + ); + + function addTab(aURL, aReferrer) { + let referrerInfo = new ReferrerInfo( + Ci.nsIReferrerInfo.EMPTY, + true, + aReferrer + ); + let tab = BrowserTestUtils.addTab(gBrowser, aURL, { referrerInfo }); + tabs.push(tab); + return BrowserTestUtils.browserLoaded(tab.linkedBrowser); + } + + await addTab("http://mochi.test:8888/#0"); + gBrowser.selectedTab = tabs[0]; + await addTab("http://mochi.test:8888/#1"); + await addTab("http://mochi.test:8888/#2", gBrowser.currentURI); + await addTab("http://mochi.test:8888/#3", gBrowser.currentURI); + gBrowser.selectedTab = tabs[tabs.length - 1]; + gBrowser.selectedTab = tabs[0]; + await addTab("http://mochi.test:8888/#4", gBrowser.currentURI); + gBrowser.selectedTab = tabs[3]; + await addTab("http://mochi.test:8888/#5", gBrowser.currentURI); + gBrowser.removeTab(tabs.pop()); + await addTab("about:blank", gBrowser.currentURI); + gBrowser.moveTabTo(gBrowser.selectedTab, 1); + await addTab("http://mochi.test:8888/#6", gBrowser.currentURI); + await addTab(); + await addTab("http://mochi.test:8888/#7"); + + function testPosition(tabNum, expectedPosition, msg) { + is( + Array.prototype.indexOf.call(gBrowser.tabs, tabs[tabNum]), + expectedPosition, + msg + ); + } + + testPosition(0, 3, "tab without referrer was opened to the far right"); + testPosition(1, 7, "tab without referrer was opened to the far right"); + testPosition(2, 5, "tab with referrer opened immediately to the right"); + testPosition(3, 1, "next tab with referrer opened further to the right"); + testPosition( + 4, + 4, + "tab selection changed, tab opens immediately to the right" + ); + testPosition( + 5, + 6, + "blank tab with referrer opens to the right of 3rd original tab where removed tab was" + ); + testPosition(6, 2, "tab has moved, new tab opens immediately to the right"); + testPosition(7, 8, "blank tab without referrer opens at the end"); + testPosition(8, 9, "tab without referrer opens at the end"); + + tabs.forEach(gBrowser.removeTab, gBrowser); +}); diff --git a/browser/base/content/test/general/browser_remoteTroubleshoot.js b/browser/base/content/test/general/browser_remoteTroubleshoot.js new file mode 100644 index 0000000000..84722b2603 --- /dev/null +++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js @@ -0,0 +1,130 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var { WebChannel } = ChromeUtils.importESModule( + "resource://gre/modules/WebChannel.sys.mjs" +); +const { PermissionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/PermissionTestUtils.sys.mjs" +); + +const TEST_URL_TAIL = + "example.com/browser/browser/base/content/test/general/test_remoteTroubleshoot.html"; +const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URL_TAIL); +const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URL_TAIL); +const TEST_URI_GOOD_OBJECT = Services.io.newURI( + "https://" + TEST_URL_TAIL + "?object" +); + +// Creates a one-shot web-channel for the test data to be sent back from the test page. +function promiseChannelResponse(channelID, originOrPermission) { + return new Promise((resolve, reject) => { + let channel = new WebChannel(channelID, originOrPermission); + channel.listen((id, data, target) => { + channel.stopListening(); + resolve(data); + }); + }); +} + +// Loads the specified URI in a new tab and waits for it to send us data on our +// test web-channel and resolves with that data. +function promiseNewChannelResponse(uri) { + let channelPromise = promiseChannelResponse( + "test-remote-troubleshooting-backchannel", + uri + ); + let tab = gBrowser.addTab(uri.spec, { + inBackground: false, + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), + }); + return promiseTabLoaded(tab) + .then(() => channelPromise) + .then(data => { + gBrowser.removeTab(tab); + return data; + }); +} + +add_task(async function () { + // We haven't set a permission yet - so even the "good" URI should fail. + let got = await promiseNewChannelResponse(TEST_URI_GOOD); + // Should return an error. + Assert.ok( + got.message.errno === 2, + "should have failed with errno 2, no such channel" + ); + + // Add a permission manager entry for our URI. + PermissionTestUtils.add( + TEST_URI_GOOD, + "remote-troubleshooting", + Services.perms.ALLOW_ACTION + ); + registerCleanupFunction(() => { + PermissionTestUtils.remove(TEST_URI_GOOD, "remote-troubleshooting"); + }); + + // Try again - now we are expecting a response with the actual data. + got = await promiseNewChannelResponse(TEST_URI_GOOD); + + // Check some keys we expect to always get. + Assert.ok(got.message.addons, "should have addons"); + Assert.ok(got.message.graphics, "should have graphics"); + + // Check we have channel and build ID info: + Assert.equal( + got.message.application.buildID, + Services.appinfo.appBuildID, + "should have correct build ID" + ); + + let updateChannel = null; + try { + updateChannel = ChromeUtils.importESModule( + "resource://gre/modules/UpdateUtils.sys.mjs" + ).UpdateUtils.UpdateChannel; + } catch (ex) {} + if (!updateChannel) { + Assert.ok( + !("updateChannel" in got.message.application), + "should not have update channel where not available." + ); + } else { + Assert.equal( + got.message.application.updateChannel, + updateChannel, + "should have correct update channel." + ); + } + + // And check some keys we know we decline to return. + Assert.ok( + !got.message.modifiedPreferences, + "should not have a modifiedPreferences key" + ); + Assert.ok( + !got.message.printingPreferences, + "should not have a printingPreferences key" + ); + Assert.ok(!got.message.crashes, "should not have crash info"); + + // Now a http:// URI - should receive an error + got = await promiseNewChannelResponse(TEST_URI_BAD); + Assert.ok( + got.message.errno === 2, + "should have failed with errno 2, no such channel" + ); + + // Check that the page can send an object as well if it's in the whitelist + let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist"; + let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref); + let newWhitelist = origWhitelist + " https://example.com"; + Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist); + registerCleanupFunction(() => { + Services.prefs.clearUserPref(webchannelWhitelistPref); + }); + got = await promiseNewChannelResponse(TEST_URI_GOOD_OBJECT); + Assert.ok(got.message, "should have gotten some data back"); +}); diff --git a/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js b/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js new file mode 100644 index 0000000000..3ae7c62105 --- /dev/null +++ b/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function makeInputStream(aString) { + let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( + Ci.nsIStringInputStream + ); + stream.data = aString; + return stream; // XPConnect will QI this to nsIInputStream for us. +} + +add_task(async function test_remoteWebNavigation_postdata() { + let { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js"); + let { CommonUtils } = ChromeUtils.importESModule( + "resource://services-common/utils.sys.mjs" + ); + + let server = new HttpServer(); + server.start(-1); + + await new Promise(resolve => { + server.registerPathHandler("/test", (request, response) => { + let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream); + is(body, "success", "request body is correct"); + is(request.method, "POST", "request was a post"); + response.write("Received from POST: " + body); + resolve(); + }); + + let i = server.identity; + let path = + i.primaryScheme + "://" + i.primaryHost + ":" + i.primaryPort + "/test"; + + let postdata = + "Content-Length: 7\r\n" + + "Content-Type: application/x-www-form-urlencoded\r\n" + + "\r\n" + + "success"; + + openTrustedLinkIn(path, "tab", { + allowThirdPartyFixup: null, + postData: makeInputStream(postdata), + }); + }); + BrowserTestUtils.removeTab(gBrowser.selectedTab); + + await new Promise(resolve => { + server.stop(function () { + resolve(); + }); + }); +}); diff --git a/browser/base/content/test/general/browser_restore_isAppTab.js b/browser/base/content/test/general/browser_restore_isAppTab.js new file mode 100644 index 0000000000..ab26342692 --- /dev/null +++ b/browser/base/content/test/general/browser_restore_isAppTab.js @@ -0,0 +1,87 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +const { TabStateFlusher } = ChromeUtils.importESModule( + "resource:///modules/sessionstore/TabStateFlusher.sys.mjs" +); + +const DUMMY = + "https://example.com/browser/browser/base/content/test/general/dummy_page.html"; + +function isBrowserAppTab(browser) { + return browser.browsingContext.isAppTab; +} + +// Restarts the child process by crashing it then reloading the tab +var restart = async function (browser) { + // If the tab isn't remote this would crash the main process so skip it + if (!browser.isRemoteBrowser) { + return; + } + + // Make sure the main process has all of the current tab state before crashing + await TabStateFlusher.flush(browser); + + await BrowserTestUtils.crashFrame(browser); + + let tab = gBrowser.getTabForBrowser(browser); + SessionStore.reviveCrashedTab(tab); + + await promiseTabLoaded(tab); +}; + +add_task(async function navigate() { + let tab = BrowserTestUtils.addTab(gBrowser, "about:robots"); + let browser = tab.linkedBrowser; + gBrowser.selectedTab = tab; + await BrowserTestUtils.browserStopped(gBrowser); + let isAppTab = isBrowserAppTab(browser); + ok(!isAppTab, "Docshell shouldn't think it is an app tab"); + + gBrowser.pinTab(tab); + isAppTab = isBrowserAppTab(browser); + ok(isAppTab, "Docshell should think it is an app tab"); + + BrowserTestUtils.loadURIString(gBrowser, DUMMY); + await BrowserTestUtils.browserStopped(gBrowser); + isAppTab = isBrowserAppTab(browser); + ok(isAppTab, "Docshell should think it is an app tab"); + + gBrowser.unpinTab(tab); + isAppTab = isBrowserAppTab(browser); + ok(!isAppTab, "Docshell shouldn't think it is an app tab"); + + gBrowser.pinTab(tab); + isAppTab = isBrowserAppTab(browser); + ok(isAppTab, "Docshell should think it is an app tab"); + + BrowserTestUtils.loadURIString(gBrowser, "about:robots"); + await BrowserTestUtils.browserStopped(gBrowser); + isAppTab = isBrowserAppTab(browser); + ok(isAppTab, "Docshell should think it is an app tab"); + + gBrowser.removeCurrentTab(); +}); + +add_task(async function crash() { + if (!gMultiProcessBrowser || !AppConstants.MOZ_CRASHREPORTER) { + return; + } + + let tab = BrowserTestUtils.addTab(gBrowser, DUMMY); + let browser = tab.linkedBrowser; + gBrowser.selectedTab = tab; + await BrowserTestUtils.browserStopped(gBrowser); + let isAppTab = isBrowserAppTab(browser); + ok(!isAppTab, "Docshell shouldn't think it is an app tab"); + + gBrowser.pinTab(tab); + isAppTab = isBrowserAppTab(browser); + ok(isAppTab, "Docshell should think it is an app tab"); + + await restart(browser); + isAppTab = isBrowserAppTab(browser); + ok(isAppTab, "Docshell should think it is an app tab"); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/base/content/test/general/browser_save_link-perwindowpb.js b/browser/base/content/test/general/browser_save_link-perwindowpb.js new file mode 100644 index 0000000000..4800c813b3 --- /dev/null +++ b/browser/base/content/test/general/browser_save_link-perwindowpb.js @@ -0,0 +1,214 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var MockFilePicker = SpecialPowers.MockFilePicker; +MockFilePicker.init(window); + +// Trigger a save of a link in public mode, then trigger an identical save +// in private mode and ensure that the second request is differentiated from +// the first by checking that cookies set by the first response are not sent +// during the second request. +function triggerSave(aWindow, aCallback) { + info("started triggerSave"); + var fileName; + let testBrowser = aWindow.gBrowser.selectedBrowser; + // This page sets a cookie if and only if a cookie does not exist yet + let testURI = + "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517-2.html"; + BrowserTestUtils.loadURIString(testBrowser, testURI); + BrowserTestUtils.browserLoaded(testBrowser, false, testURI).then(() => { + waitForFocus(function () { + info("register to handle popupshown"); + aWindow.document.addEventListener("popupshown", contextMenuOpened); + + BrowserTestUtils.synthesizeMouseAtCenter( + "#fff", + { type: "contextmenu", button: 2 }, + testBrowser + ); + info("right clicked!"); + }, aWindow); + }); + + function contextMenuOpened(event) { + info("contextMenuOpened"); + aWindow.document.removeEventListener("popupshown", contextMenuOpened); + + // Create the folder the link will be saved into. + var destDir = createTemporarySaveDirectory(); + var destFile = destDir.clone(); + + MockFilePicker.displayDirectory = destDir; + MockFilePicker.showCallback = function (fp) { + info("showCallback"); + fileName = fp.defaultString; + info("fileName: " + fileName); + destFile.append(fileName); + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 1; // kSaveAsType_URL + info("done showCallback"); + }; + + mockTransferCallback = function (downloadSuccess) { + info("mockTransferCallback"); + onTransferComplete(aWindow, downloadSuccess, destDir); + destDir.remove(true); + ok(!destDir.exists(), "Destination dir should be removed"); + ok(!destFile.exists(), "Destination file should be removed"); + mockTransferCallback = null; + info("done mockTransferCallback"); + }; + + // Select "Save Link As" option from context menu + var saveLinkCommand = aWindow.document.getElementById("context-savelink"); + info("saveLinkCommand: " + saveLinkCommand); + saveLinkCommand.doCommand(); + + event.target.hidePopup(); + info("popup hidden"); + } + + function onTransferComplete(aWindow2, downloadSuccess, destDir) { + ok(downloadSuccess, "Link should have been downloaded successfully"); + aWindow2.close(); + + executeSoon(() => aCallback()); + } +} + +function test() { + info("Start the test"); + waitForExplicitFinish(); + + var gNumSet = 0; + function testOnWindow(options, callback) { + info("testOnWindow(" + options + ")"); + var win = OpenBrowserWindow(options); + info("got " + win); + whenDelayedStartupFinished(win, () => callback(win)); + } + + function whenDelayedStartupFinished(aWindow, aCallback) { + info("whenDelayedStartupFinished"); + Services.obs.addObserver(function obs(aSubject, aTopic) { + info( + "whenDelayedStartupFinished, got topic: " + + aTopic + + ", got subject: " + + aSubject + + ", waiting for " + + aWindow + ); + if (aWindow == aSubject) { + Services.obs.removeObserver(obs, aTopic); + executeSoon(aCallback); + info("whenDelayedStartupFinished found our window"); + } + }, "browser-delayed-startup-finished"); + } + + mockTransferRegisterer.register(); + + registerCleanupFunction(function () { + info("Running the cleanup code"); + mockTransferRegisterer.unregister(); + MockFilePicker.cleanup(); + Services.obs.removeObserver(observer, "http-on-modify-request"); + Services.obs.removeObserver(observer, "http-on-examine-response"); + info("Finished running the cleanup code"); + }); + + function observer(subject, topic, state) { + info("observer called with " + topic); + if (topic == "http-on-modify-request") { + onModifyRequest(subject); + } else if (topic == "http-on-examine-response") { + onExamineResponse(subject); + } + } + + function onExamineResponse(subject) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + info("onExamineResponse with " + channel.URI.spec); + if ( + channel.URI.spec != + "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs" + ) { + info("returning"); + return; + } + try { + let cookies = channel.getResponseHeader("set-cookie"); + // From browser/base/content/test/general/bug792715.sjs, we receive a Set-Cookie + // header with foopy=1 when there are no cookies for that domain. + is(cookies, "foopy=1", "Cookie should be foopy=1"); + gNumSet += 1; + info("gNumSet = " + gNumSet); + } catch (ex) { + if (ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { + info("onExamineResponse caught NOTAVAIL" + ex); + } else { + info("ionExamineResponse caught " + ex); + } + } + } + + function onModifyRequest(subject) { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + info("onModifyRequest with " + channel.URI.spec); + if ( + channel.URI.spec != + "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.sjs" + ) { + return; + } + try { + let cookies = channel.getRequestHeader("cookie"); + info("cookies: " + cookies); + // From browser/base/content/test/general/bug792715.sjs, we should never send a + // cookie because we are making only 2 requests: one in public mode, and + // one in private mode. + throw new Error("We should never send a cookie in this test"); + } catch (ex) { + if (ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { + info("onModifyRequest caught NOTAVAIL" + ex); + } else { + info("ionModifyRequest caught " + ex); + } + } + } + + Services.obs.addObserver(observer, "http-on-modify-request"); + Services.obs.addObserver(observer, "http-on-examine-response"); + + testOnWindow(undefined, function (win) { + // The first save from a regular window sets a cookie. + triggerSave(win, function () { + is(gNumSet, 1, "1 cookie should be set"); + + // The second save from a private window also sets a cookie. + testOnWindow({ private: true }, function (win2) { + triggerSave(win2, function () { + is(gNumSet, 2, "2 cookies should be set"); + finish(); + }); + }); + }); + }); +} + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js", + this +); + +function createTemporarySaveDirectory() { + var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); + saveDir.append("testsavedir"); + if (!saveDir.exists()) { + info("create testsavedir!"); + saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + info("return from createTempSaveDir: " + saveDir.path); + return saveDir; +} diff --git a/browser/base/content/test/general/browser_save_link_when_window_navigates.js b/browser/base/content/test/general/browser_save_link_when_window_navigates.js new file mode 100644 index 0000000000..49901e8bfa --- /dev/null +++ b/browser/base/content/test/general/browser_save_link_when_window_navigates.js @@ -0,0 +1,197 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var MockFilePicker = SpecialPowers.MockFilePicker; +MockFilePicker.init(window); + +const SAVE_PER_SITE_PREF = "browser.download.lastDir.savePerSite"; +const ALWAYS_DOWNLOAD_DIR_PREF = "browser.download.useDownloadDir"; +const ALWAYS_ASK_PREF = "browser.download.always_ask_before_handling_new_types"; +const UCT_URI = "chrome://mozapps/content/downloads/unknownContentType.xhtml"; + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js", + this +); + +function createTemporarySaveDirectory() { + var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); + saveDir.append("testsavedir"); + if (!saveDir.exists()) { + info("create testsavedir!"); + saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + + Services.prefs.setIntPref("browser.download.folderList", 2); + Services.prefs.setCharPref("browser.download.dir", saveDir); + info("return from createTempSaveDir: " + saveDir.path); + return saveDir; +} + +function triggerSave(aWindow, aCallback) { + info( + "started triggerSave, persite downloads: " + + (Services.prefs.getBoolPref(SAVE_PER_SITE_PREF) ? "on" : "off") + ); + var fileName; + let testBrowser = aWindow.gBrowser.selectedBrowser; + let testURI = + "http://mochi.test:8888/browser/browser/base/content/test/general/navigating_window_with_download.html"; + + // Only observe the UTC dialog if it's enabled by pref + if (Services.prefs.getBoolPref(ALWAYS_ASK_PREF)) { + windowObserver.setCallback(onUCTDialog); + } + + BrowserTestUtils.loadURIString(testBrowser, testURI); + + // Create the folder the link will be saved into. + var destDir = createTemporarySaveDirectory(); + var destFile = destDir.clone(); + + MockFilePicker.displayDirectory = destDir; + MockFilePicker.showCallback = function (fp) { + info("showCallback"); + fileName = fp.defaultString; + info("fileName: " + fileName); + destFile.append(fileName); + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 1; // kSaveAsType_URL + info("done showCallback"); + }; + + mockTransferCallback = function (downloadSuccess) { + info("mockTransferCallback"); + onTransferComplete(aWindow, downloadSuccess, destDir); + destDir.remove(true); + ok(!destDir.exists(), "Destination dir should be removed"); + ok(!destFile.exists(), "Destination file should be removed"); + mockTransferCallback = null; + info("done mockTransferCallback"); + }; + + function onUCTDialog(dialog) { + SpecialPowers.spawn(testBrowser, [], async () => { + content.document.querySelector("iframe").remove(); + }).then(() => executeSoon(continueDownloading)); + } + + function continueDownloading() { + for (let win of Services.wm.getEnumerator("")) { + if (win.location && win.location.href == UCT_URI) { + win.document + .getElementById("unknownContentType") + ._fireButtonEvent("accept"); + win.close(); + return; + } + } + ok(false, "No Unknown Content Type dialog yet?"); + } + + function onTransferComplete(aWindow2, downloadSuccess) { + ok(downloadSuccess, "Link should have been downloaded successfully"); + aWindow2.close(); + + executeSoon(aCallback); + } +} + +var windowObserver = { + setCallback(aCallback) { + if (this._callback) { + ok(false, "Should only be dealing with one callback at a time."); + } + this._callback = aCallback; + }, + observe(aSubject, aTopic, aData) { + if (aTopic != "domwindowopened") { + return; + } + + let win = aSubject; + + win.addEventListener( + "load", + function (event) { + if (win.location == UCT_URI) { + SimpleTest.executeSoon(function () { + if (windowObserver._callback) { + windowObserver._callback(win); + delete windowObserver._callback; + } else { + ok(false, "Unexpected UCT dialog!"); + } + }); + } + }, + { once: true } + ); + }, +}; + +Services.ww.registerNotification(windowObserver); + +function test() { + waitForExplicitFinish(); + Services.prefs.setBoolPref(ALWAYS_ASK_PREF, false); + + function testOnWindow(options, callback) { + info("testOnWindow(" + options + ")"); + var win = OpenBrowserWindow(options); + info("got " + win); + whenDelayedStartupFinished(win, () => callback(win)); + } + + function whenDelayedStartupFinished(aWindow, aCallback) { + info("whenDelayedStartupFinished"); + Services.obs.addObserver(function observer(aSubject, aTopic) { + info( + "whenDelayedStartupFinished, got topic: " + + aTopic + + ", got subject: " + + aSubject + + ", waiting for " + + aWindow + ); + if (aWindow == aSubject) { + Services.obs.removeObserver(observer, aTopic); + executeSoon(aCallback); + info("whenDelayedStartupFinished found our window"); + } + }, "browser-delayed-startup-finished"); + } + + mockTransferRegisterer.register(); + + registerCleanupFunction(function () { + info("Running the cleanup code"); + mockTransferRegisterer.unregister(); + MockFilePicker.cleanup(); + Services.ww.unregisterNotification(windowObserver); + Services.prefs.clearUserPref(ALWAYS_DOWNLOAD_DIR_PREF); + Services.prefs.clearUserPref(SAVE_PER_SITE_PREF); + Services.prefs.clearUserPref(ALWAYS_ASK_PREF); + Services.prefs.clearUserPref("browser.download.folderList"); + Services.prefs.clearUserPref("browser.download.dir"); + info("Finished running the cleanup code"); + }); + + info( + `Running test with ${ALWAYS_ASK_PREF} set to ${Services.prefs.getBoolPref( + ALWAYS_ASK_PREF, + false + )}` + ); + testOnWindow(undefined, function (win) { + let windowGonePromise = BrowserTestUtils.domWindowClosed(win); + Services.prefs.setBoolPref(SAVE_PER_SITE_PREF, true); + triggerSave(win, async function () { + await windowGonePromise; + Services.prefs.setBoolPref(SAVE_PER_SITE_PREF, false); + testOnWindow(undefined, function (win2) { + triggerSave(win2, finish); + }); + }); + }); +} diff --git a/browser/base/content/test/general/browser_save_private_link_perwindowpb.js b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js new file mode 100644 index 0000000000..8ede97e640 --- /dev/null +++ b/browser/base/content/test/general/browser_save_private_link_perwindowpb.js @@ -0,0 +1,127 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function createTemporarySaveDirectory() { + var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); + saveDir.append("testsavedir"); + if (!saveDir.exists()) { + saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + return saveDir; +} + +function promiseNoCacheEntry(filename) { + return new Promise((resolve, reject) => { + Visitor.prototype = { + onCacheStorageInfo(num, consumption) { + info("disk storage contains " + num + " entries"); + }, + onCacheEntryInfo(uri) { + let urispec = uri.asciiSpec; + info(urispec); + is( + urispec.includes(filename), + false, + "web content present in disk cache" + ); + }, + onCacheEntryVisitCompleted() { + resolve(); + }, + }; + function Visitor() {} + + let storage = Services.cache2.diskCacheStorage( + Services.loadContextInfo.default + ); + storage.asyncVisitStorage(new Visitor(), true /* Do walk entries */); + }); +} + +function promiseImageDownloaded() { + return new Promise((resolve, reject) => { + let fileName; + let MockFilePicker = SpecialPowers.MockFilePicker; + MockFilePicker.init(window); + + function onTransferComplete(downloadSuccess) { + ok( + downloadSuccess, + "Image file should have been downloaded successfully " + fileName + ); + + // Give the request a chance to finish and create a cache entry + resolve(fileName); + } + + // Create the folder the image will be saved into. + var destDir = createTemporarySaveDirectory(); + var destFile = destDir.clone(); + + MockFilePicker.displayDirectory = destDir; + MockFilePicker.showCallback = function (fp) { + fileName = fp.defaultString; + destFile.append(fileName); + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 1; // kSaveAsType_URL + }; + + mockTransferCallback = onTransferComplete; + mockTransferRegisterer.register(); + + registerCleanupFunction(function () { + mockTransferCallback = null; + mockTransferRegisterer.unregister(); + MockFilePicker.cleanup(); + destDir.remove(true); + }); + }); +} + +add_task(async function () { + let testURI = + "http://mochi.test:8888/browser/browser/base/content/test/general/bug792517.html"; + let privateWindow = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + }); + let tab = await BrowserTestUtils.openNewForegroundTab( + privateWindow.gBrowser, + testURI + ); + + let contextMenu = privateWindow.document.getElementById( + "contentAreaContextMenu" + ); + let popupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + let popupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden"); + await BrowserTestUtils.synthesizeMouseAtCenter( + "#img", + { + type: "contextmenu", + button: 2, + }, + tab.linkedBrowser + ); + await popupShown; + + Services.cache2.clear(); + + let imageDownloaded = promiseImageDownloaded(); + // Select "Save Image As" option from context menu + privateWindow.document.getElementById("context-saveimage").doCommand(); + + contextMenu.hidePopup(); + await popupHidden; + + // wait for image download + let fileName = await imageDownloaded; + await promiseNoCacheEntry(fileName); + + await BrowserTestUtils.closeWindow(privateWindow); +}); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js", + this +); diff --git a/browser/base/content/test/general/browser_save_video.js b/browser/base/content/test/general/browser_save_video.js new file mode 100644 index 0000000000..5456ac240f --- /dev/null +++ b/browser/base/content/test/general/browser_save_video.js @@ -0,0 +1,99 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +var MockFilePicker = SpecialPowers.MockFilePicker; +MockFilePicker.init(window); + +/** + * TestCase for bug 564387 + * <https://bugzilla.mozilla.org/show_bug.cgi?id=564387> + */ +add_task(async function () { + var fileName; + + let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + BrowserTestUtils.loadURIString( + gBrowser, + "http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html" + ); + await loadPromise; + + let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown"); + + await BrowserTestUtils.synthesizeMouseAtCenter( + "#video1", + { type: "contextmenu", button: 2 }, + gBrowser.selectedBrowser + ); + info("context menu click on video1"); + + await popupShownPromise; + + info("context menu opened on video1"); + + // Create the folder the video will be saved into. + var destDir = createTemporarySaveDirectory(); + var destFile = destDir.clone(); + + MockFilePicker.displayDirectory = destDir; + MockFilePicker.showCallback = function (fp) { + fileName = fp.defaultString; + destFile.append(fileName); + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 1; // kSaveAsType_URL + }; + + let transferCompletePromise = new Promise(resolve => { + function onTransferComplete(downloadSuccess) { + ok( + downloadSuccess, + "Video file should have been downloaded successfully" + ); + + is( + fileName, + "web-video1-expectedName.ogv", + "Video file name is correctly retrieved from Content-Disposition http header" + ); + resolve(); + } + + mockTransferCallback = onTransferComplete; + mockTransferRegisterer.register(); + }); + + registerCleanupFunction(function () { + mockTransferRegisterer.unregister(); + MockFilePicker.cleanup(); + destDir.remove(true); + }); + + // Select "Save Video As" option from context menu + var saveVideoCommand = document.getElementById("context-savevideo"); + saveVideoCommand.doCommand(); + info("context-savevideo command executed"); + + let contextMenu = document.getElementById("contentAreaContextMenu"); + let popupHiddenPromise = BrowserTestUtils.waitForEvent( + contextMenu, + "popuphidden" + ); + contextMenu.hidePopup(); + await popupHiddenPromise; + + await transferCompletePromise; +}); + +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js", + this +); + +function createTemporarySaveDirectory() { + var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); + saveDir.append("testsavedir"); + if (!saveDir.exists()) { + saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + return saveDir; +} diff --git a/browser/base/content/test/general/browser_save_video_frame.js b/browser/base/content/test/general/browser_save_video_frame.js new file mode 100644 index 0000000000..877c33bcd3 --- /dev/null +++ b/browser/base/content/test/general/browser_save_video_frame.js @@ -0,0 +1,103 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const VIDEO_URL = + "http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html"; + +/** + * mockTransfer.js provides a utility that lets us mock out + * the "Save File" dialog. + */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js", + this +); + +/** + * Creates and returns an nsIFile for a new temporary save + * directory. + * + * @return nsIFile + */ +function createTemporarySaveDirectory() { + let saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile); + saveDir.append("testsavedir"); + if (!saveDir.exists()) { + saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + } + return saveDir; +} +/** + * MockTransfer exposes a "mockTransferCallback" global which + * allows us to define a callback to be called once the mock file + * selector has selected where to save the file. + */ +function waitForTransferComplete() { + return new Promise(resolve => { + mockTransferCallback = () => { + ok(true, "Transfer completed"); + mockTransferCallback = () => {}; + resolve(); + }; + }); +} + +/** + * Loads a page with a <video> element, right-clicks it and chooses + * to save a frame screenshot to the disk. Completes once we've + * verified that the frame has been saved to disk. + */ +add_task(async function () { + let MockFilePicker = SpecialPowers.MockFilePicker; + MockFilePicker.init(window); + + // Create the folder the video will be saved into. + let destDir = createTemporarySaveDirectory(); + let destFile = destDir.clone(); + + MockFilePicker.displayDirectory = destDir; + MockFilePicker.showCallback = function (fp) { + destFile.append(fp.defaultString); + MockFilePicker.setFiles([destFile]); + MockFilePicker.filterIndex = 1; // kSaveAsType_URL + }; + + mockTransferRegisterer.register(); + + // Make sure that we clean these things up when we're done. + registerCleanupFunction(function () { + mockTransferRegisterer.unregister(); + MockFilePicker.cleanup(); + destDir.remove(true); + }); + + let tab = BrowserTestUtils.addTab(gBrowser); + gBrowser.selectedTab = tab; + let browser = tab.linkedBrowser; + info("Loading video tab"); + await promiseTabLoadEvent(tab, VIDEO_URL); + info("Video tab loaded."); + + let context = document.getElementById("contentAreaContextMenu"); + let popupPromise = promisePopupShown(context); + + info("Synthesizing right-click on video element"); + await BrowserTestUtils.synthesizeMouseAtCenter( + "#video1", + { type: "contextmenu", button: 2 }, + browser + ); + info("Waiting for popup to fire popupshown."); + await popupPromise; + info("Popup fired popupshown"); + + let saveSnapshotCommand = document.getElementById("context-video-saveimage"); + let promiseTransfer = waitForTransferComplete(); + info("Firing save snapshot command"); + saveSnapshotCommand.doCommand(); + context.hidePopup(); + info("Waiting for transfer completion"); + await promiseTransfer; + info("Transfer complete"); + gBrowser.removeTab(tab); +}); diff --git a/browser/base/content/test/general/browser_selectTabAtIndex.js b/browser/base/content/test/general/browser_selectTabAtIndex.js new file mode 100644 index 0000000000..5d2e8c739e --- /dev/null +++ b/browser/base/content/test/general/browser_selectTabAtIndex.js @@ -0,0 +1,89 @@ +"use strict"; + +function test() { + const isLinux = navigator.platform.indexOf("Linux") == 0; + + function assertTab(expectedTab) { + is( + gBrowser.tabContainer.selectedIndex, + expectedTab, + `tab index ${expectedTab} should be selected` + ); + } + + function sendAccelKey(key) { + // Make sure the keystroke goes to chrome. + document.activeElement.blur(); + EventUtils.synthesizeKey(key.toString(), { + altKey: isLinux, + accelKey: !isLinux, + }); + } + + function createTabs(count) { + for (let n = 0; n < count; n++) { + BrowserTestUtils.addTab(gBrowser); + } + } + + function testKey(key, expectedTab) { + sendAccelKey(key); + assertTab(expectedTab); + } + + function testIndex(index, expectedTab) { + gBrowser.selectTabAtIndex(index); + assertTab(expectedTab); + } + + // Create fewer tabs than our 9 number keys. + is(gBrowser.tabs.length, 1, "should have 1 tab"); + createTabs(4); + is(gBrowser.tabs.length, 5, "should have 5 tabs"); + + // Test keyboard shortcuts. Order tests so that no two test cases have the + // same expected tab in a row. This ensures that tab selection actually + // changed the selected tab. + testKey(8, 4); + testKey(1, 0); + testKey(2, 1); + testKey(4, 3); + testKey(9, 4); + + // Test index selection. + testIndex(0, 0); + testIndex(4, 4); + testIndex(-5, 0); + testIndex(5, 4); + testIndex(-4, 1); + testIndex(1, 1); + testIndex(-1, 4); + testIndex(9, 4); + + // Create more tabs than our 9 number keys. + createTabs(10); + is(gBrowser.tabs.length, 15, "should have 15 tabs"); + + // Test keyboard shortcuts. + testKey(2, 1); + testKey(1, 0); + testKey(4, 3); + testKey(8, 7); + testKey(9, 14); + + // Test index selection. + testIndex(-15, 0); + testIndex(14, 14); + testIndex(-14, 1); + testIndex(15, 14); + testIndex(-1, 14); + testIndex(0, 0); + testIndex(1, 1); + testIndex(9, 9); + + // Clean up tabs. + for (let n = 15; n > 1; n--) { + gBrowser.removeTab(gBrowser.selectedTab, { skipPermitUnload: true }); + } + is(gBrowser.tabs.length, 1, "should have 1 tab"); +} diff --git a/browser/base/content/test/general/browser_star_hsts.js b/browser/base/content/test/general/browser_star_hsts.js new file mode 100644 index 0000000000..9452c61beb --- /dev/null +++ b/browser/base/content/test/general/browser_star_hsts.js @@ -0,0 +1,87 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +var secureURL = + "https://example.com/browser/browser/base/content/test/general/browser_star_hsts.sjs"; +var unsecureURL = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/browser/browser/base/content/test/general/browser_star_hsts.sjs"; + +add_task(async function test_star_redirect() { + registerCleanupFunction(async () => { + // Ensure to remove example.com from the HSTS list. + let sss = Cc["@mozilla.org/ssservice;1"].getService( + Ci.nsISiteSecurityService + ); + sss.resetState( + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + NetUtil.newURI("http://example.com/"), + Services.prefs.getBoolPref("privacy.partition.network_state") + ? { partitionKey: "(http,example.com)" } + : {} + ); + await PlacesUtils.bookmarks.eraseEverything(); + gBrowser.removeCurrentTab(); + }); + + let tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser)); + // This will add the page to the HSTS cache. + await promiseTabLoadEvent(tab, secureURL, secureURL); + // This should transparently be redirected to the secure page. + await promiseTabLoadEvent(tab, unsecureURL, secureURL); + + await promiseStarState(BookmarkingUI.STATUS_UNSTARRED); + + StarUI._createPanelIfNeeded(); + let bookmarkPanel = document.getElementById("editBookmarkPanel"); + let shownPromise = promisePopupShown(bookmarkPanel); + BookmarkingUI.star.click(); + await shownPromise; + + is(BookmarkingUI.status, BookmarkingUI.STATUS_STARRED, "The star is starred"); +}); + +/** + * Waits for the star to reflect the expected state. + */ +function promiseStarState(aValue) { + return new Promise(resolve => { + let expectedStatus = aValue + ? BookmarkingUI.STATUS_STARRED + : BookmarkingUI.STATUS_UNSTARRED; + (function checkState() { + if ( + BookmarkingUI.status == BookmarkingUI.STATUS_UPDATING || + BookmarkingUI.status != expectedStatus + ) { + info("Waiting for star button change."); + setTimeout(checkState, 1000); + } else { + resolve(); + } + })(); + }); +} + +/** + * Starts a load in an existing tab and waits for it to finish (via some event). + * + * @param aTab + * The tab to load into. + * @param aUrl + * The url to load. + * @param [optional] aFinalURL + * The url to wait for, same as aURL if not defined. + * @return {Promise} resolved when the event is handled. + */ +function promiseTabLoadEvent(aTab, aURL, aFinalURL) { + if (!aFinalURL) { + aFinalURL = aURL; + } + + info("Wait for load tab event"); + BrowserTestUtils.loadURIString(aTab.linkedBrowser, aURL); + return BrowserTestUtils.browserLoaded(aTab.linkedBrowser, false, aFinalURL); +} diff --git a/browser/base/content/test/general/browser_star_hsts.sjs b/browser/base/content/test/general/browser_star_hsts.sjs new file mode 100644 index 0000000000..64c4235288 --- /dev/null +++ b/browser/base/content/test/general/browser_star_hsts.sjs @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function handleRequest(request, response) { + let page = "<!DOCTYPE html><html><body><p>HSTS page</p></body></html>"; + response.setStatusLine(request.httpVersion, "200", "OK"); + response.setHeader("Strict-Transport-Security", "max-age=60"); + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Content-Length", page.length + "", false); + response.write(page); +} diff --git a/browser/base/content/test/general/browser_storagePressure_notification.js b/browser/base/content/test/general/browser_storagePressure_notification.js new file mode 100644 index 0000000000..dcafbe8bf9 --- /dev/null +++ b/browser/base/content/test/general/browser_storagePressure_notification.js @@ -0,0 +1,182 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ + +async function notifyStoragePressure(usage = 100) { + let notifyPromise = TestUtils.topicObserved( + "QuotaManager::StoragePressure", + () => true + ); + let usageWrapper = Cc["@mozilla.org/supports-PRUint64;1"].createInstance( + Ci.nsISupportsPRUint64 + ); + usageWrapper.data = usage; + Services.obs.notifyObservers(usageWrapper, "QuotaManager::StoragePressure"); + return notifyPromise; +} + +function openAboutPrefPromise(win) { + let promises = [ + BrowserTestUtils.waitForLocationChange( + win.gBrowser, + "about:preferences#privacy" + ), + TestUtils.topicObserved("privacy-pane-loaded", () => true), + TestUtils.topicObserved("sync-pane-loaded", () => true), + ]; + return Promise.all(promises); +} +add_setup(async function () { + let win = await BrowserTestUtils.openNewBrowserWindow(); + // Open a new tab to keep the window open. + await BrowserTestUtils.openNewForegroundTab( + win.gBrowser, + "https://example.com" + ); +}); + +// Test only displaying notification once within the given interval +add_task(async function () { + const win = Services.wm.getMostRecentWindow("navigator:browser"); + const TEST_NOTIFICATION_INTERVAL_MS = 2000; + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "browser.storageManager.pressureNotification.minIntervalMS", + TEST_NOTIFICATION_INTERVAL_MS, + ], + ], + }); + // Commenting this to see if we really need it + // await SpecialPowers.pushPrefEnv({set: [["privacy.reduceTimerPrecision", false]]}); + + await notifyStoragePressure(); + await TestUtils.waitForCondition(() => + win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ) + ); + let notification = win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ); + is( + notification.localName, + "notification-message", + "Should display storage pressure notification" + ); + notification.close(); + + await notifyStoragePressure(); + notification = win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ); + is( + notification, + null, + "Should not display storage pressure notification more than once within the given interval" + ); + + await new Promise(resolve => + setTimeout(resolve, TEST_NOTIFICATION_INTERVAL_MS + 1) + ); + await notifyStoragePressure(); + await TestUtils.waitForCondition(() => + win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ) + ); + notification = win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ); + is( + notification.localName, + "notification-message", + "Should display storage pressure notification after the given interval" + ); + notification.close(); +}); + +// Test guiding user to the about:preferences when usage exceeds the given threshold +add_task(async function () { + const win = Services.wm.getMostRecentWindow("navigator:browser"); + await SpecialPowers.pushPrefEnv({ + set: [["browser.storageManager.pressureNotification.minIntervalMS", 0]], + }); + let tab = await BrowserTestUtils.openNewForegroundTab( + win.gBrowser, + "https://example.com" + ); + + const BYTES_IN_GIGABYTE = 1073741824; + const USAGE_THRESHOLD_BYTES = + BYTES_IN_GIGABYTE * + Services.prefs.getIntPref( + "browser.storageManager.pressureNotification.usageThresholdGB" + ); + await notifyStoragePressure(USAGE_THRESHOLD_BYTES); + await TestUtils.waitForCondition(() => + win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ) + ); + let notification = win.gNotificationBox.getNotificationWithValue( + "storage-pressure-notification" + ); + is( + notification.localName, + "notification-message", + "Should display storage pressure notification" + ); + await new Promise(r => setTimeout(r, 1000)); + + let prefBtn = notification.buttonContainer.getElementsByTagName("button")[0]; + ok(prefBtn, "Should have an open preferences button"); + let aboutPrefPromise = openAboutPrefPromise(win); + EventUtils.synthesizeMouseAtCenter(prefBtn, {}, win); + await aboutPrefPromise; + let aboutPrefTab = win.gBrowser.selectedTab; + let prefDoc = win.gBrowser.selectedBrowser.contentDocument; + let siteDataGroup = prefDoc.getElementById("siteDataGroup"); + is_element_visible( + siteDataGroup, + "Should open to the siteDataGroup section in about:preferences" + ); + BrowserTestUtils.removeTab(aboutPrefTab); + BrowserTestUtils.removeTab(tab); +}); + +// Test not displaying the 2nd notification if one is already being displayed +add_task(async function () { + const win = Services.wm.getMostRecentWindow("navigator:browser"); + const TEST_NOTIFICATION_INTERVAL_MS = 0; + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "browser.storageManager.pressureNotification.minIntervalMS", + TEST_NOTIFICATION_INTERVAL_MS, + ], + ], + }); + + await notifyStoragePressure(); + await notifyStoragePressure(); + let allNotifications = win.gNotificationBox.allNotifications; + let pressureNotificationCount = 0; + allNotifications.forEach(notification => { + if (notification.getAttribute("value") == "storage-pressure-notification") { + pressureNotificationCount++; + } + }); + is( + pressureNotificationCount, + 1, + "Should not display the 2nd notification when there is already one" + ); + win.gNotificationBox.removeAllNotifications(); +}); + +add_task(async function cleanup() { + const win = Services.wm.getMostRecentWindow("navigator:browser"); + await BrowserTestUtils.closeWindow(win); +}); diff --git a/browser/base/content/test/general/browser_tabDrop.js b/browser/base/content/test/general/browser_tabDrop.js new file mode 100644 index 0000000000..eddb405f46 --- /dev/null +++ b/browser/base/content/test/general/browser_tabDrop.js @@ -0,0 +1,207 @@ +// TODO (Bug 1680996): Investigate why this test takes a long time. +requestLongerTimeout(2); + +const ANY_URL = undefined; + +const { SearchTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/SearchTestUtils.sys.mjs" +); + +SearchTestUtils.init(this); + +registerCleanupFunction(async function cleanup() { + while (gBrowser.tabs.length > 1) { + BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]); + } +}); + +add_task(async function test_setup() { + // Stop search-engine loads from hitting the network + await SearchTestUtils.installSearchExtension( + { + name: "MozSearch", + search_url: "https://example.com/", + search_url_get_params: "q={searchTerms}", + }, + { setAsDefault: true } + ); +}); + +add_task(async function single_url() { + await dropText("mochi.test/first", ["http://mochi.test/first"]); +}); +add_task(async function single_javascript() { + await dropText("javascript:'bad'", []); +}); +add_task(async function single_javascript_capital() { + await dropText("jAvascript:'bad'", []); +}); +add_task(async function single_search() { + await dropText("search this", [ANY_URL]); +}); +add_task(async function single_url2() { + await dropText("mochi.test/second", ["http://mochi.test/second"]); +}); +add_task(async function single_data_url() { + await dropText("data:text/html,bad", []); +}); +add_task(async function single_url3() { + await dropText("mochi.test/third", ["http://mochi.test/third"]); +}); + +// Single text/plain item, with multiple links. +add_task(async function multiple_urls() { + await dropText("mochi.test/1\nmochi.test/2", [ + "http://mochi.test/1", + "http://mochi.test/2", + ]); +}); +add_task(async function multiple_urls_javascript() { + await dropText("javascript:'bad1'\nmochi.test/3", []); +}); +add_task(async function multiple_urls_data() { + await dropText("mochi.test/4\ndata:text/html,bad1", []); +}); + +// Multiple text/plain items, with single and multiple links. +add_task(async function multiple_items_single_and_multiple_links() { + await drop( + [ + [{ type: "text/plain", data: "mochi.test/5" }], + [{ type: "text/plain", data: "mochi.test/6\nmochi.test/7" }], + ], + ["http://mochi.test/5", "http://mochi.test/6", "http://mochi.test/7"] + ); +}); + +// Single text/x-moz-url item, with multiple links. +// "text/x-moz-url" has titles in even-numbered lines. +add_task(async function single_moz_url_multiple_links() { + await drop( + [ + [ + { + type: "text/x-moz-url", + data: "mochi.test/8\nTITLE8\nmochi.test/9\nTITLE9", + }, + ], + ], + ["http://mochi.test/8", "http://mochi.test/9"] + ); +}); + +// Single item with multiple types. +add_task(async function single_item_multiple_types() { + await drop( + [ + [ + { type: "text/plain", data: "mochi.test/10" }, + { type: "text/x-moz-url", data: "mochi.test/11\nTITLE11" }, + ], + ], + ["http://mochi.test/11"] + ); +}); + +// Warn when too many URLs are dropped. +add_task(async function multiple_tabs_under_max() { + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/multi" + i); + } + await dropText(urls.join("\n"), [ + "http://mochi.test/multi0", + "http://mochi.test/multi1", + "http://mochi.test/multi2", + "http://mochi.test/multi3", + "http://mochi.test/multi4", + ]); +}); +add_task(async function multiple_tabs_over_max_accept() { + await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]); + + let confirmPromise = BrowserTestUtils.promiseAlertDialog("accept"); + + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/accept" + i); + } + await dropText(urls.join("\n"), [ + "http://mochi.test/accept0", + "http://mochi.test/accept1", + "http://mochi.test/accept2", + "http://mochi.test/accept3", + "http://mochi.test/accept4", + ]); + + await confirmPromise; + + await popPrefs(); +}); +add_task(async function multiple_tabs_over_max_cancel() { + await pushPrefs(["browser.tabs.maxOpenBeforeWarn", 4]); + + let confirmPromise = BrowserTestUtils.promiseAlertDialog("cancel"); + + let urls = []; + for (let i = 0; i < 5; i++) { + urls.push("mochi.test/cancel" + i); + } + await dropText(urls.join("\n"), []); + + await confirmPromise; + + await popPrefs(); +}); + +function dropText(text, expectedURLs) { + return drop([[{ type: "text/plain", data: text }]], expectedURLs); +} + +async function drop(dragData, expectedURLs) { + let dragDataString = JSON.stringify(dragData); + info( + `Starting test for dragData:${dragDataString}; expectedURLs.length:${expectedURLs.length}` + ); + let EventUtils = {}; + Services.scriptloader.loadSubScript( + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", + EventUtils + ); + + let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop"); + + let loadedPromises = expectedURLs.map(url => + BrowserTestUtils.waitForNewTab(gBrowser, url, false, true) + ); + + // A drop type of "link" onto an existing tab would normally trigger a + // load in that same tab, but tabbrowser code in _getDragTargetTab treats + // drops on the outer edges of a tab differently (loading a new tab + // instead). Make events created by synthesizeDrop have all of their + // coordinates set to 0 (screenX/screenY), so they're treated as drops + // on the outer edge of the tab, thus they open new tabs. + var event = { + clientX: 0, + clientY: 0, + screenX: 0, + screenY: 0, + }; + EventUtils.synthesizeDrop( + gBrowser.selectedTab, + gBrowser.selectedTab, + dragData, + "link", + window, + undefined, + event + ); + + let tabs = await Promise.all(loadedPromises); + for (let tab of tabs) { + BrowserTestUtils.removeTab(tab); + } + + await awaitDrop; + ok(true, "Got drop event"); +} diff --git a/browser/base/content/test/general/browser_tab_close_dependent_window.js b/browser/base/content/test/general/browser_tab_close_dependent_window.js new file mode 100644 index 0000000000..a9b9c1d999 --- /dev/null +++ b/browser/base/content/test/general/browser_tab_close_dependent_window.js @@ -0,0 +1,35 @@ +"use strict"; + +add_task(async function closing_tab_with_dependents_should_close_window() { + info("Opening window"); + let win = await BrowserTestUtils.openNewBrowserWindow(); + + info("Opening tab with data URI"); + let tab = await BrowserTestUtils.openNewForegroundTab( + win.gBrowser, + `data:text/html,<html%20onclick="W=window.open()"><body%20onbeforeunload="W.close()">` + ); + info("Closing original tab in this window."); + BrowserTestUtils.removeTab(win.gBrowser.tabs[0]); + info("Clicking into the window"); + let depTabOpened = BrowserTestUtils.waitForEvent( + win.gBrowser.tabContainer, + "TabOpen" + ); + await BrowserTestUtils.synthesizeMouse("html", 0, 0, {}, tab.linkedBrowser); + + let openedTab = (await depTabOpened).target; + info("Got opened tab"); + + let windowClosedPromise = BrowserTestUtils.windowClosed(win); + BrowserTestUtils.removeTab(tab); + is( + Cu.isDeadWrapper(openedTab) || openedTab.linkedBrowser == null, + true, + "Opened tab should also have closed" + ); + info( + "If we timeout now, the window failed to close - that shouldn't happen!" + ); + await windowClosedPromise; +}); diff --git a/browser/base/content/test/general/browser_tab_detach_restore.js b/browser/base/content/test/general/browser_tab_detach_restore.js new file mode 100644 index 0000000000..d3f6a58aaa --- /dev/null +++ b/browser/base/content/test/general/browser_tab_detach_restore.js @@ -0,0 +1,54 @@ +"use strict"; + +const { TabStateFlusher } = ChromeUtils.importESModule( + "resource:///modules/sessionstore/TabStateFlusher.sys.mjs" +); + +add_task(async function () { + let uri = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/browser/browser/base/content/test/general/dummy_page.html"; + + // Clear out the closed windows set to start + while (SessionStore.getClosedWindowCount() > 0) { + SessionStore.forgetClosedWindow(0); + } + + let tab = BrowserTestUtils.addTab(gBrowser); + BrowserTestUtils.loadURIString(tab.linkedBrowser, uri); + await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, uri); + await TabStateFlusher.flush(tab.linkedBrowser); + + let key = tab.linkedBrowser.permanentKey; + let win = gBrowser.replaceTabWithWindow(tab); + await new Promise(resolve => whenDelayedStartupFinished(win, resolve)); + + is( + win.gBrowser.selectedBrowser.permanentKey, + key, + "Should have properly copied the permanentKey" + ); + await BrowserTestUtils.closeWindow(win); + + is( + SessionStore.getClosedWindowCount(), + 1, + "Should have restore data for the closed window" + ); + + win = SessionStore.undoCloseWindow(0); + await BrowserTestUtils.waitForEvent(win, "load"); + await BrowserTestUtils.waitForEvent( + win.gBrowser.tabContainer, + "SSTabRestored" + ); + + is(win.gBrowser.tabs.length, 1, "Should have restored one tab"); + is( + win.gBrowser.selectedBrowser.currentURI.spec, + uri, + "Should have restored the right page" + ); + + await promiseWindowClosed(win); +}); diff --git a/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js b/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js new file mode 100644 index 0000000000..de4e17b97d --- /dev/null +++ b/browser/base/content/test/general/browser_tab_drag_drop_perwindow.js @@ -0,0 +1,423 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +requestLongerTimeout(2); + +const EVENTUTILS_URL = + "chrome://mochikit/content/tests/SimpleTest/EventUtils.js"; +var EventUtils = {}; + +Services.scriptloader.loadSubScript(EVENTUTILS_URL, EventUtils); + +/** + * Tests that tabs from Private Browsing windows cannot be dragged + * into non-private windows, and vice-versa. + */ +add_task(async function test_dragging_private_windows() { + let normalWin = await BrowserTestUtils.openNewBrowserWindow(); + let privateWin = await BrowserTestUtils.openNewBrowserWindow({ + private: true, + }); + + let normalTab = await BrowserTestUtils.openNewForegroundTab( + normalWin.gBrowser + ); + let privateTab = await BrowserTestUtils.openNewForegroundTab( + privateWin.gBrowser + ); + + let effect = EventUtils.synthesizeDrop( + normalTab, + privateTab, + [[{ type: TAB_DROP_TYPE, data: normalTab }]], + null, + normalWin, + privateWin + ); + is( + effect, + "none", + "Should not be able to drag a normal tab to a private window" + ); + + effect = EventUtils.synthesizeDrop( + privateTab, + normalTab, + [[{ type: TAB_DROP_TYPE, data: privateTab }]], + null, + privateWin, + normalWin + ); + is( + effect, + "none", + "Should not be able to drag a private tab to a normal window" + ); + + normalWin.gBrowser.swapBrowsersAndCloseOther(normalTab, privateTab); + is( + normalWin.gBrowser.tabs.length, + 2, + "Prevent moving a normal tab to a private tabbrowser" + ); + is( + privateWin.gBrowser.tabs.length, + 2, + "Prevent accepting a normal tab in a private tabbrowser" + ); + + privateWin.gBrowser.swapBrowsersAndCloseOther(privateTab, normalTab); + is( + privateWin.gBrowser.tabs.length, + 2, + "Prevent moving a private tab to a normal tabbrowser" + ); + is( + normalWin.gBrowser.tabs.length, + 2, + "Prevent accepting a private tab in a normal tabbrowser" + ); + + await BrowserTestUtils.closeWindow(normalWin); + await BrowserTestUtils.closeWindow(privateWin); +}); + +/** + * Tests that tabs from e10s windows cannot be dragged into non-e10s + * windows, and vice-versa. + */ +add_task(async function test_dragging_e10s_windows() { + if (!gMultiProcessBrowser) { + return; + } + + let remoteWin = await BrowserTestUtils.openNewBrowserWindow({ remote: true }); + let nonRemoteWin = await BrowserTestUtils.openNewBrowserWindow({ + remote: false, + fission: false, + }); + + let remoteTab = await BrowserTestUtils.openNewForegroundTab( + remoteWin.gBrowser + ); + let nonRemoteTab = await BrowserTestUtils.openNewForegroundTab( + nonRemoteWin.gBrowser + ); + + let effect = EventUtils.synthesizeDrop( + remoteTab, + nonRemoteTab, + [[{ type: TAB_DROP_TYPE, data: remoteTab }]], + null, + remoteWin, + nonRemoteWin + ); + is( + effect, + "none", + "Should not be able to drag a remote tab to a non-e10s window" + ); + + effect = EventUtils.synthesizeDrop( + nonRemoteTab, + remoteTab, + [[{ type: TAB_DROP_TYPE, data: nonRemoteTab }]], + null, + nonRemoteWin, + remoteWin + ); + is( + effect, + "none", + "Should not be able to drag a non-remote tab to an e10s window" + ); + + remoteWin.gBrowser.swapBrowsersAndCloseOther(remoteTab, nonRemoteTab); + is( + remoteWin.gBrowser.tabs.length, + 2, + "Prevent moving a normal tab to a private tabbrowser" + ); + is( + nonRemoteWin.gBrowser.tabs.length, + 2, + "Prevent accepting a normal tab in a private tabbrowser" + ); + + nonRemoteWin.gBrowser.swapBrowsersAndCloseOther(nonRemoteTab, remoteTab); + is( + nonRemoteWin.gBrowser.tabs.length, + 2, + "Prevent moving a private tab to a normal tabbrowser" + ); + is( + remoteWin.gBrowser.tabs.length, + 2, + "Prevent accepting a private tab in a normal tabbrowser" + ); + + await BrowserTestUtils.closeWindow(remoteWin); + await BrowserTestUtils.closeWindow(nonRemoteWin); +}); + +/** + * Tests that tabs from fission windows cannot be dragged into non-fission + * windows, and vice-versa. + */ +add_task(async function test_dragging_fission_windows() { + let fissionWin = await BrowserTestUtils.openNewBrowserWindow({ + remote: true, + fission: true, + }); + let nonFissionWin = await BrowserTestUtils.openNewBrowserWindow({ + remote: true, + fission: false, + }); + + let fissionTab = await BrowserTestUtils.openNewForegroundTab( + fissionWin.gBrowser + ); + let nonFissionTab = await BrowserTestUtils.openNewForegroundTab( + nonFissionWin.gBrowser + ); + + let effect = EventUtils.synthesizeDrop( + fissionTab, + nonFissionTab, + [[{ type: TAB_DROP_TYPE, data: fissionTab }]], + null, + fissionWin, + nonFissionWin + ); + is( + effect, + "none", + "Should not be able to drag a fission tab to a non-fission window" + ); + + effect = EventUtils.synthesizeDrop( + nonFissionTab, + fissionTab, + [[{ type: TAB_DROP_TYPE, data: nonFissionTab }]], + null, + nonFissionWin, + fissionWin + ); + is( + effect, + "none", + "Should not be able to drag a non-fission tab to an fission window" + ); + + let swapOk = fissionWin.gBrowser.swapBrowsersAndCloseOther( + fissionTab, + nonFissionTab + ); + is( + swapOk, + false, + "Returns false swapping fission tab to a non-fission tabbrowser" + ); + is( + fissionWin.gBrowser.tabs.length, + 2, + "Prevent moving a fission tab to a non-fission tabbrowser" + ); + is( + nonFissionWin.gBrowser.tabs.length, + 2, + "Prevent accepting a fission tab in a non-fission tabbrowser" + ); + + swapOk = nonFissionWin.gBrowser.swapBrowsersAndCloseOther( + nonFissionTab, + fissionTab + ); + is( + swapOk, + false, + "Returns false swapping non-fission tab to a fission tabbrowser" + ); + is( + nonFissionWin.gBrowser.tabs.length, + 2, + "Prevent moving a non-fission tab to a fission tabbrowser" + ); + is( + fissionWin.gBrowser.tabs.length, + 2, + "Prevent accepting a non-fission tab in a fission tabbrowser" + ); + + await BrowserTestUtils.closeWindow(fissionWin); + await BrowserTestUtils.closeWindow(nonFissionWin); +}); + +/** + * Tests that remoteness-blacklisted tabs from e10s windows can + * be dragged between e10s windows. + */ +add_task(async function test_dragging_blacklisted() { + if (!gMultiProcessBrowser) { + return; + } + + let remoteWin1 = await BrowserTestUtils.openNewBrowserWindow({ + remote: true, + }); + remoteWin1.gBrowser.myID = "remoteWin1"; + let remoteWin2 = await BrowserTestUtils.openNewBrowserWindow({ + remote: true, + }); + remoteWin2.gBrowser.myID = "remoteWin2"; + + // Anything under chrome://mochitests/content/ will be blacklisted, and + // open in the parent process. + const BLACKLISTED_URL = + getRootDirectory(gTestPath) + "browser_tab_drag_drop_perwindow.js"; + let blacklistedTab = await BrowserTestUtils.openNewForegroundTab( + remoteWin1.gBrowser, + BLACKLISTED_URL + ); + + ok(blacklistedTab.linkedBrowser, "Newly created tab should have a browser."); + + ok( + !blacklistedTab.linkedBrowser.isRemoteBrowser, + `Expected a non-remote browser for URL: ${BLACKLISTED_URL}` + ); + + let otherTab = await BrowserTestUtils.openNewForegroundTab( + remoteWin2.gBrowser + ); + + let effect = EventUtils.synthesizeDrop( + blacklistedTab, + otherTab, + [[{ type: TAB_DROP_TYPE, data: blacklistedTab }]], + null, + remoteWin1, + remoteWin2 + ); + is(effect, "move", "Should be able to drag the blacklisted tab."); + + // The synthesized drop should also do the work of swapping the + // browsers, so no need to call swapBrowsersAndCloseOther manually. + + is( + remoteWin1.gBrowser.tabs.length, + 1, + "Should have moved the blacklisted tab out of this window." + ); + is( + remoteWin2.gBrowser.tabs.length, + 3, + "Should have inserted the blacklisted tab into the other window." + ); + + // The currently selected tab in the second window should be the + // one we just dragged in. + let draggedBrowser = remoteWin2.gBrowser.selectedBrowser; + ok( + !draggedBrowser.isRemoteBrowser, + "The browser we just dragged in should not be remote." + ); + + is( + draggedBrowser.currentURI.spec, + BLACKLISTED_URL, + `Expected the URL of the dragged in tab to be ${BLACKLISTED_URL}` + ); + + await BrowserTestUtils.closeWindow(remoteWin1); + await BrowserTestUtils.closeWindow(remoteWin2); +}); + +/** + * Tests that tabs dragged between windows dispatch TabOpen and TabClose + * events with the appropriate adoption details. + */ +add_task(async function test_dragging_adoption_events() { + let win1 = await BrowserTestUtils.openNewBrowserWindow(); + let win2 = await BrowserTestUtils.openNewBrowserWindow(); + + let tab1 = await BrowserTestUtils.openNewForegroundTab(win1.gBrowser); + let tab2 = await BrowserTestUtils.openNewForegroundTab(win2.gBrowser); + + let awaitCloseEvent = BrowserTestUtils.waitForEvent(tab1, "TabClose"); + let awaitOpenEvent = BrowserTestUtils.waitForEvent(win2, "TabOpen"); + + let effect = EventUtils.synthesizeDrop( + tab1, + tab2, + [[{ type: TAB_DROP_TYPE, data: tab1 }]], + null, + win1, + win2 + ); + is(effect, "move", "Tab should be moved from win1 to win2."); + + let closeEvent = await awaitCloseEvent; + let openEvent = await awaitOpenEvent; + + is(openEvent.detail.adoptedTab, tab1, "New tab adopted old tab"); + is( + closeEvent.detail.adoptedBy, + openEvent.target, + "Old tab adopted by new tab" + ); + + await BrowserTestUtils.closeWindow(win1); + await BrowserTestUtils.closeWindow(win2); +}); + +/** + * Tests that per-site zoom settings remain active after a tab is + * dragged between windows. + */ +add_task(async function test_dragging_zoom_handling() { + const ZOOM_FACTOR = 1.62; + + let win1 = await BrowserTestUtils.openNewBrowserWindow(); + let win2 = await BrowserTestUtils.openNewBrowserWindow(); + + let tab1 = await BrowserTestUtils.openNewForegroundTab(win1.gBrowser); + let tab2 = await BrowserTestUtils.openNewForegroundTab( + win2.gBrowser, + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.com/" + ); + + win2.FullZoom.setZoom(ZOOM_FACTOR); + is( + ZoomManager.getZoomForBrowser(tab2.linkedBrowser), + ZOOM_FACTOR, + "Original tab should have correct zoom factor" + ); + + let effect = EventUtils.synthesizeDrop( + tab2, + tab1, + [[{ type: TAB_DROP_TYPE, data: tab2 }]], + null, + win2, + win1 + ); + is(effect, "move", "Tab should be moved from win2 to win1."); + + // Delay slightly to make sure we've finished executing any promise + // chains in the zoom code. + await new Promise(resolve => setTimeout(resolve, 0)); + + is( + ZoomManager.getZoomForBrowser(win1.gBrowser.selectedBrowser), + ZOOM_FACTOR, + "Dragged tab should have correct zoom factor" + ); + + win1.FullZoom.reset(); + + await BrowserTestUtils.closeWindow(win1); + await BrowserTestUtils.closeWindow(win2); +}); diff --git a/browser/base/content/test/general/browser_tab_dragdrop.js b/browser/base/content/test/general/browser_tab_dragdrop.js new file mode 100644 index 0000000000..9ea05842f2 --- /dev/null +++ b/browser/base/content/test/general/browser_tab_dragdrop.js @@ -0,0 +1,257 @@ +// Swaps the content of tab a into tab b and then closes tab a. +function swapTabsAndCloseOther(a, b) { + gBrowser.swapBrowsersAndCloseOther(gBrowser.tabs[b], gBrowser.tabs[a]); +} + +// Mirrors the effect of the above function on an array. +function swapArrayContentsAndRemoveOther(arr, a, b) { + arr[b] = arr[a]; + arr.splice(a, 1); +} + +function checkBrowserIds(expected) { + is( + gBrowser.tabs.length, + expected.length, + "Should have the right number of tabs." + ); + + for (let [i, tab] of gBrowser.tabs.entries()) { + is( + tab.linkedBrowser.browserId, + expected[i], + `Tab ${i} should have the right browser ID.` + ); + is( + tab.linkedBrowser.browserId, + tab.linkedBrowser.browsingContext.browserId, + `Browser for tab ${i} has the same browserId as its BrowsingContext` + ); + } +} + +var getClicks = function (tab) { + return SpecialPowers.spawn(tab.linkedBrowser, [], function () { + return content.wrappedJSObject.clicks; + }); +}; + +var clickTest = async function (tab) { + let clicks = await getClicks(tab); + + await SpecialPowers.spawn(tab.linkedBrowser, [], function () { + let target = content.document.body; + let rect = target.getBoundingClientRect(); + let left = (rect.left + rect.right) / 2; + let top = (rect.top + rect.bottom) / 2; + + let utils = content.windowUtils; + utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); + utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); + }); + + let newClicks = await getClicks(tab); + is(newClicks, clicks + 1, "adding 1 more click on BODY"); +}; + +function loadURI(tab, url) { + BrowserTestUtils.loadURIString(tab.linkedBrowser, url); + return BrowserTestUtils.browserLoaded(tab.linkedBrowser); +} + +// Creates a framescript which caches the current object value from the plugin +// in the page. checkObjectValue below verifies that the framescript is still +// active for the browser and that the cached value matches that from the plugin +// in the page which tells us the plugin hasn't been reinitialized. +async function cacheObjectValue(browser) { + await SpecialPowers.spawn(browser, [], () => { + let plugin = content.document.getElementById("p").wrappedJSObject; + info(`plugin is ${plugin}`); + let win = content.document.defaultView; + info(`win is ${win}`); + win.objectValue = plugin.getObjectValue(); + info(`got objectValue: ${win.objectValue}`); + }); +} + +// Note, can't run this via registerCleanupFunction because it needs the +// browser to still be alive and have a messageManager. +async function cleanupObjectValue(browser) { + info("entered cleanupObjectValue"); + await SpecialPowers.spawn(browser, [], () => { + info("in cleanup function"); + let win = content.document.defaultView; + info(`about to delete objectValue: ${win.objectValue}`); + delete win.objectValue; + }); + info("exiting cleanupObjectValue"); +} + +// See the notes for cacheObjectValue above. +async function checkObjectValue(browser) { + let data = await SpecialPowers.spawn(browser, [], () => { + let plugin = content.document.getElementById("p").wrappedJSObject; + let win = content.document.defaultView; + let result, exception; + try { + result = plugin.checkObjectValue(win.objectValue); + } catch (e) { + exception = e.toString(); + } + return { + result, + exception, + }; + }); + + if (data.result === null) { + ok(false, "checkObjectValue threw an exception: " + data.exception); + throw new Error(data.exception); + } else { + return data.result; + } +} + +add_task(async function () { + // create a few tabs + let tabs = [ + gBrowser.tabs[0], + BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true }), + BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true }), + BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true }), + BrowserTestUtils.addTab(gBrowser, "about:blank", { skipAnimation: true }), + ]; + + // Initially 0 1 2 3 4 + await loadURI( + tabs[1], + "data:text/html;charset=utf-8,<title>tab1tab1 + + diff --git a/browser/base/content/test/general/print_postdata.sjs b/browser/base/content/test/general/print_postdata.sjs new file mode 100644 index 0000000000..0e3ef38419 --- /dev/null +++ b/browser/base/content/test/general/print_postdata.sjs @@ -0,0 +1,25 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function handleRequest(request, response) { + response.setHeader("Content-Type", "text/plain", false); + if (request.method == "GET") { + response.write(request.queryString); + } else { + var body = new BinaryInputStream(request.bodyInputStream); + + var avail; + var bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + + var data = String.fromCharCode.apply(null, bytes); + response.bodyOutputStream.write(data, data.length); + } +} diff --git a/browser/base/content/test/general/redirect_download.sjs b/browser/base/content/test/general/redirect_download.sjs new file mode 100644 index 0000000000..c2857b9338 --- /dev/null +++ b/browser/base/content/test/general/redirect_download.sjs @@ -0,0 +1,11 @@ +function handleRequest(request, response) { + response.setHeader("Cache-Control", "no-cache", false); + let queryStr = request.queryString; + + response.setStatusLine("1.1", 302, "Found"); + response.setHeader( + "Location", + `download_with_content_disposition_header.sjs?${queryStr}`, + false + ); +} diff --git a/browser/base/content/test/general/refresh_header.sjs b/browser/base/content/test/general/refresh_header.sjs new file mode 100644 index 0000000000..7d66e0a429 --- /dev/null +++ b/browser/base/content/test/general/refresh_header.sjs @@ -0,0 +1,24 @@ +/** + * Will cause an auto-refresh to the URL provided in the query string + * after some delay using the refresh HTTP header. + * + * Expects the query string to be in the format: + * + * ?p=[URL of the page to redirect to]&d=[delay] + * + * Example: + * + * ?p=http%3A%2F%2Fexample.org%2Fbrowser%2Fbrowser%2Fbase%2Fcontent%2Ftest%2Fgeneral%2Frefresh_meta.sjs&d=200 + */ +function handleRequest(request, response) { + Cu.importGlobalProperties(["URLSearchParams"]); + let query = new URLSearchParams(request.queryString); + + let page = query.get("p"); + let delay = query.get("d"); + + response.setHeader("Content-Type", "text/html", false); + response.setStatusLine(request.httpVersion, "200", "Found"); + response.setHeader("refresh", `${delay}; url=${page}`); + response.write("OK"); +} diff --git a/browser/base/content/test/general/refresh_meta.sjs b/browser/base/content/test/general/refresh_meta.sjs new file mode 100644 index 0000000000..0f91507c18 --- /dev/null +++ b/browser/base/content/test/general/refresh_meta.sjs @@ -0,0 +1,36 @@ +/** + * Will cause an auto-refresh to the URL provided in the query string + * after some delay using a tag. + * + * Expects the query string to be in the format: + * + * ?p=[URL of the page to redirect to]&d=[delay] + * + * Example: + * + * ?p=http%3A%2F%2Fexample.org%2Fbrowser%2Fbrowser%2Fbase%2Fcontent%2Ftest%2Fgeneral%2Frefresh_meta.sjs&d=200 + */ +function handleRequest(request, response) { + Cu.importGlobalProperties(["URLSearchParams"]); + let query = new URLSearchParams(request.queryString); + + let page = query.get("p"); + let delay = query.get("d"); + + let html = ` + + + + + Gonna refresh you, folks. + + +

Wait for it...

+ + `; + + response.setHeader("Content-Type", "text/html", false); + response.setStatusLine(request.httpVersion, "200", "Found"); + response.setHeader("Cache-Control", "no-cache", false); + response.write(html); +} diff --git a/browser/base/content/test/general/test_bug462673.html b/browser/base/content/test/general/test_bug462673.html new file mode 100644 index 0000000000..d864990e4f --- /dev/null +++ b/browser/base/content/test/general/test_bug462673.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/browser/base/content/test/general/test_bug628179.html b/browser/base/content/test/general/test_bug628179.html new file mode 100644 index 0000000000..1136048d36 --- /dev/null +++ b/browser/base/content/test/general/test_bug628179.html @@ -0,0 +1,9 @@ + + + + Test for closing the Find bar in subdocuments + + + + + diff --git a/browser/base/content/test/general/test_remoteTroubleshoot.html b/browser/base/content/test/general/test_remoteTroubleshoot.html new file mode 100644 index 0000000000..c0c3f5e604 --- /dev/null +++ b/browser/base/content/test/general/test_remoteTroubleshoot.html @@ -0,0 +1,50 @@ + + + + + +
+
+
+
diff --git a/browser/base/content/test/general/title_test.svg b/browser/base/content/test/general/title_test.svg
new file mode 100644
index 0000000000..80390a3cca
--- /dev/null
+++ b/browser/base/content/test/general/title_test.svg
@@ -0,0 +1,59 @@
+
+  This is a root SVG element's title
+  
+    
+      
+        
+          This is a non-root SVG element title
+        
+      
+    
+  
+  
+    This contains only <title>
+    
+
+
+    This            is a title
+
+    
+  
+  
+    This contains only <desc>
+    This is a desc
+  
+  
+    This contains nothing.
+  
+  
+    This link contains <title>
+    
+      This is a title
+    
+    
+    
+  
+  
+    
+      This text contains <title>
+      
+      This is a title
+      
+    
+  
+  
+    
+      This link contains <title> & xlink:title attr.
+      This is a title
+    
+  
+  
+    
+      This link contains xlink:title attr.
+    
+  
+  
+    This contains nothing.
+  
+
diff --git a/browser/base/content/test/general/unknownContentType_file.pif b/browser/base/content/test/general/unknownContentType_file.pif
new file mode 100644
index 0000000000..9353d13126
--- /dev/null
+++ b/browser/base/content/test/general/unknownContentType_file.pif
@@ -0,0 +1 @@
+Dummy content for unknownContentType_dialog_layout_data.pif
diff --git a/browser/base/content/test/general/unknownContentType_file.pif^headers^ b/browser/base/content/test/general/unknownContentType_file.pif^headers^
new file mode 100644
index 0000000000..09b22facc0
--- /dev/null
+++ b/browser/base/content/test/general/unknownContentType_file.pif^headers^
@@ -0,0 +1 @@
+Content-Type: application/octet-stream
diff --git a/browser/base/content/test/general/video.ogg b/browser/base/content/test/general/video.ogg
new file mode 100644
index 0000000000..ac7ece3519
Binary files /dev/null and b/browser/base/content/test/general/video.ogg differ
diff --git a/browser/base/content/test/general/web_video.html b/browser/base/content/test/general/web_video.html
new file mode 100644
index 0000000000..467fb0ce1c
--- /dev/null
+++ b/browser/base/content/test/general/web_video.html
@@ -0,0 +1,10 @@
+
+  
+    Document with Web Video
+  
+  
+    This document has some web video in it.
+    
+ + + diff --git a/browser/base/content/test/general/web_video1.ogv b/browser/base/content/test/general/web_video1.ogv new file mode 100644 index 0000000000..093158432a Binary files /dev/null and b/browser/base/content/test/general/web_video1.ogv differ diff --git a/browser/base/content/test/general/web_video1.ogv^headers^ b/browser/base/content/test/general/web_video1.ogv^headers^ new file mode 100644 index 0000000000..4511e92552 --- /dev/null +++ b/browser/base/content/test/general/web_video1.ogv^headers^ @@ -0,0 +1,3 @@ +Content-Disposition: filename="web-video1-expectedName.ogv" +Content-Type: video/ogg + -- cgit v1.2.3