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 --- dom/ipc/BrowserBridgeChild.cpp | 249 + dom/ipc/BrowserBridgeChild.h | 119 + dom/ipc/BrowserBridgeHost.cpp | 91 + dom/ipc/BrowserBridgeHost.h | 68 + dom/ipc/BrowserBridgeParent.cpp | 310 + dom/ipc/BrowserBridgeParent.h | 124 + dom/ipc/BrowserChild.cpp | 3848 +++++++++ dom/ipc/BrowserChild.h | 883 +++ dom/ipc/BrowserHost.cpp | 289 + dom/ipc/BrowserHost.h | 109 + dom/ipc/BrowserParent.cpp | 4120 ++++++++++ dom/ipc/BrowserParent.h | 1015 +++ dom/ipc/CSPMessageUtils.cpp | 50 + dom/ipc/CSPMessageUtils.h | 25 + dom/ipc/ClonedErrorHolder.cpp | 371 + dom/ipc/ClonedErrorHolder.h | 104 + dom/ipc/CoalescedInputData.cpp | 47 + dom/ipc/CoalescedInputData.h | 74 + dom/ipc/CoalescedMouseData.cpp | 83 + dom/ipc/CoalescedMouseData.h | 45 + dom/ipc/CoalescedTouchData.cpp | 132 + dom/ipc/CoalescedTouchData.h | 46 + dom/ipc/CoalescedWheelData.cpp | 46 + dom/ipc/CoalescedWheelData.h | 28 + dom/ipc/ColorPickerParent.cpp | 77 + dom/ipc/ColorPickerParent.h | 58 + dom/ipc/ContentChild.cpp | 5036 ++++++++++++ dom/ipc/ContentChild.h | 894 +++ dom/ipc/ContentParent.cpp | 8227 ++++++++++++++++++++ dom/ipc/ContentParent.h | 1741 +++++ dom/ipc/ContentProcess.cpp | 185 + dom/ipc/ContentProcess.h | 46 + dom/ipc/ContentProcessManager.cpp | 142 + dom/ipc/ContentProcessManager.h | 90 + dom/ipc/DOMTypes.ipdlh | 386 + dom/ipc/DocShellMessageUtils.cpp | 46 + dom/ipc/DocShellMessageUtils.h | 48 + dom/ipc/EffectsInfo.h | 73 + dom/ipc/FilePickerMessageUtils.h | 34 + dom/ipc/FilePickerParent.cpp | 293 + dom/ipc/FilePickerParent.h | 99 + dom/ipc/IPCTransferable.ipdlh | 88 + dom/ipc/IdType.h | 64 + dom/ipc/InProcessChild.h | 69 + dom/ipc/InProcessImpl.cpp | 324 + dom/ipc/InProcessParent.h | 74 + dom/ipc/JSOracleChild.cpp | 57 + dom/ipc/JSOracleChild.h | 87 + dom/ipc/JSOracleParent.cpp | 81 + dom/ipc/JSOracleParent.h | 48 + dom/ipc/JSValidatorChild.cpp | 247 + dom/ipc/JSValidatorChild.h | 57 + dom/ipc/JSValidatorParent.cpp | 99 + dom/ipc/JSValidatorParent.h | 40 + dom/ipc/JSValidatorUtils.cpp | 25 + dom/ipc/JSValidatorUtils.h | 26 + dom/ipc/LoginDetectionService.cpp | 158 + dom/ipc/LoginDetectionService.h | 60 + dom/ipc/MMPrinter.cpp | 92 + dom/ipc/MMPrinter.h | 32 + dom/ipc/ManifestMessagesChild.sys.mjs | 112 + dom/ipc/MaybeDiscarded.h | 133 + dom/ipc/MemMapSnapshot.cpp | 44 + dom/ipc/MemMapSnapshot.h | 54 + dom/ipc/MemoryReportRequest.cpp | 170 + dom/ipc/MemoryReportRequest.h | 73 + dom/ipc/MemoryReportTypes.ipdlh | 22 + dom/ipc/NativeThreadId.h | 16 + dom/ipc/PBrowser.ipdl | 1024 +++ dom/ipc/PBrowserBridge.ipdl | 135 + dom/ipc/PColorPicker.ipdl | 28 + dom/ipc/PContent.ipdl | 1986 +++++ dom/ipc/PContentPermission.ipdlh | 19 + dom/ipc/PContentPermissionRequest.ipdl | 29 + dom/ipc/PCycleCollectWithLogs.ipdl | 23 + dom/ipc/PFilePicker.ipdl | 46 + dom/ipc/PInProcess.ipdl | 32 + dom/ipc/PJSOracle.ipdl | 20 + dom/ipc/PJSValidator.ipdl | 32 + dom/ipc/PLoginReputation.ipdl | 29 + dom/ipc/PProcessHangMonitor.ipdl | 48 + dom/ipc/PTabContext.ipdlh | 53 + dom/ipc/PURLClassifier.ipdl | 24 + dom/ipc/PURLClassifierInfo.ipdlh | 15 + dom/ipc/PURLClassifierLocal.ipdl | 38 + dom/ipc/PVsync.ipdl | 40 + dom/ipc/PWindowGlobal.ipdl | 215 + dom/ipc/PageLoadEventUtils.h | 50 + dom/ipc/PermissionMessageUtils.cpp | 51 + dom/ipc/PermissionMessageUtils.h | 38 + dom/ipc/PreallocatedProcessManager.cpp | 440 ++ dom/ipc/PreallocatedProcessManager.h | 73 + dom/ipc/PrefsTypes.ipdlh | 32 + dom/ipc/ProcessActor.cpp | 37 + dom/ipc/ProcessActor.h | 36 + dom/ipc/ProcessHangMonitor.cpp | 1333 ++++ dom/ipc/ProcessHangMonitor.h | 100 + dom/ipc/ProcessHangMonitorIPC.h | 28 + dom/ipc/ProcessIsolation.cpp | 956 +++ dom/ipc/ProcessIsolation.h | 94 + dom/ipc/ProcessPriorityManager.cpp | 1027 +++ dom/ipc/ProcessPriorityManager.h | 95 + dom/ipc/PropertyBagUtils.cpp | 261 + dom/ipc/PropertyBagUtils.h | 38 + dom/ipc/RefMessageBodyService.cpp | 161 + dom/ipc/RefMessageBodyService.h | 137 + dom/ipc/ReferrerInfoUtils.cpp | 54 + dom/ipc/ReferrerInfoUtils.h | 24 + dom/ipc/RemoteBrowser.cpp | 31 + dom/ipc/RemoteBrowser.h | 72 + dom/ipc/RemoteType.h | 35 + dom/ipc/RemoteWebProgressRequest.cpp | 270 + dom/ipc/RemoteWebProgressRequest.h | 36 + dom/ipc/ServiceWorkerConfiguration.ipdlh | 18 + dom/ipc/SharedMap.cpp | 466 ++ dom/ipc/SharedMap.h | 361 + dom/ipc/SharedMapChangeEvent.h | 46 + dom/ipc/SharedMessageBody.cpp | 298 + dom/ipc/SharedMessageBody.h | 112 + dom/ipc/SharedStringMap.cpp | 139 + dom/ipc/SharedStringMap.h | 220 + dom/ipc/StringTable.h | 116 + dom/ipc/StructuredCloneData.cpp | 328 + dom/ipc/StructuredCloneData.h | 268 + dom/ipc/TabContext.cpp | 137 + dom/ipc/TabContext.h | 190 + dom/ipc/TabMessageTypes.h | 23 + dom/ipc/TabMessageUtils.h | 115 + dom/ipc/URLClassifierChild.h | 90 + dom/ipc/URLClassifierParent.cpp | 186 + dom/ipc/URLClassifierParent.h | 87 + dom/ipc/UserActivationIPCUtils.h | 28 + dom/ipc/VsyncChild.h | 24 + dom/ipc/VsyncMainChild.cpp | 73 + dom/ipc/VsyncMainChild.h | 51 + dom/ipc/VsyncParent.cpp | 103 + dom/ipc/VsyncParent.h | 54 + dom/ipc/VsyncWorkerChild.cpp | 67 + dom/ipc/VsyncWorkerChild.h | 52 + dom/ipc/WindowGlobalActor.cpp | 187 + dom/ipc/WindowGlobalActor.h | 55 + dom/ipc/WindowGlobalChild.cpp | 839 ++ dom/ipc/WindowGlobalChild.h | 222 + dom/ipc/WindowGlobalParent.cpp | 1604 ++++ dom/ipc/WindowGlobalParent.h | 425 + dom/ipc/WindowGlobalTypes.ipdlh | 40 + dom/ipc/components.conf | 17 + dom/ipc/jar.mn | 7 + dom/ipc/jsactor/JSActor.cpp | 503 ++ dom/ipc/jsactor/JSActor.h | 173 + dom/ipc/jsactor/JSActorManager.cpp | 250 + dom/ipc/jsactor/JSActorManager.h | 100 + dom/ipc/jsactor/JSActorProtocolUtils.h | 117 + dom/ipc/jsactor/JSActorService.cpp | 322 + dom/ipc/jsactor/JSActorService.h | 112 + dom/ipc/jsactor/JSProcessActorChild.cpp | 86 + dom/ipc/jsactor/JSProcessActorChild.h | 54 + dom/ipc/jsactor/JSProcessActorParent.cpp | 90 + dom/ipc/jsactor/JSProcessActorParent.h | 63 + dom/ipc/jsactor/JSProcessActorProtocol.cpp | 147 + dom/ipc/jsactor/JSProcessActorProtocol.h | 79 + dom/ipc/jsactor/JSWindowActorChild.cpp | 160 + dom/ipc/jsactor/JSWindowActorChild.h | 82 + dom/ipc/jsactor/JSWindowActorParent.cpp | 102 + dom/ipc/jsactor/JSWindowActorParent.h | 66 + dom/ipc/jsactor/JSWindowActorProtocol.cpp | 380 + dom/ipc/jsactor/JSWindowActorProtocol.h | 103 + dom/ipc/jsactor/moz.build | 42 + dom/ipc/jsactor/nsQueryActor.h | 90 + dom/ipc/moz.build | 274 + dom/ipc/nsIDOMProcessChild.idl | 59 + dom/ipc/nsIDOMProcessParent.idl | 58 + dom/ipc/nsIHangReport.idl | 51 + dom/ipc/nsILoginDetectionService.idl | 22 + dom/ipc/tests/JSProcessActor/browser.ini | 11 + dom/ipc/tests/JSProcessActor/browser_getActor.js | 47 + .../JSProcessActor/browser_getActor_filter.js | 80 + .../browser_observer_notification.js | 41 + .../JSProcessActor/browser_registerProcessActor.js | 16 + .../JSProcessActor/browser_sendAsyncMessage.js | 51 + dom/ipc/tests/JSProcessActor/browser_sendQuery.js | 99 + .../JSProcessActor/browser_uri_combination.js | 81 + dom/ipc/tests/JSProcessActor/head.js | 79 + dom/ipc/tests/JSWindowActor/audio.ogg | Bin 0 -> 14293 bytes dom/ipc/tests/JSWindowActor/browser.ini | 22 + .../tests/JSWindowActor/browser_contentWindow.js | 35 + .../tests/JSWindowActor/browser_crash_report.js | 114 + .../JSWindowActor/browser_destroy_callbacks.js | 193 + .../tests/JSWindowActor/browser_event_listener.js | 171 + dom/ipc/tests/JSWindowActor/browser_getActor.js | 36 + .../tests/JSWindowActor/browser_getActor_filter.js | 259 + .../JSWindowActor/browser_observer_notification.js | 118 + .../tests/JSWindowActor/browser_process_childid.js | 27 + .../JSWindowActor/browser_registerWindowActor.js | 16 + .../JSWindowActor/browser_sendAsyncMessage.js | 51 + dom/ipc/tests/JSWindowActor/browser_sendQuery.js | 131 + .../tests/JSWindowActor/browser_uri_combination.js | 81 + .../tests/JSWindowActor/file_dummyChromePage.html | 1 + .../tests/JSWindowActor/file_mediaPlayback.html | 2 + dom/ipc/tests/JSWindowActor/head.js | 82 + dom/ipc/tests/blob_verify.sjs | 26 + dom/ipc/tests/browser.ini | 75 + dom/ipc/tests/browser_CrashService_crash.js | 72 + dom/ipc/tests/browser_ProcessPriorityManager.js | 894 +++ dom/ipc/tests/browser_bug1646088.js | 71 + dom/ipc/tests/browser_bug1686194.js | 47 + dom/ipc/tests/browser_cancel_content_js.js | 72 + .../tests/browser_child_clipboard_restricted.js | 89 + .../browser_content_shutdown_with_endless_js.js | 86 + dom/ipc/tests/browser_crash_oopiframe.js | 235 + dom/ipc/tests/browser_domainPolicy.js | 187 + dom/ipc/tests/browser_gc_schedule.js | 366 + dom/ipc/tests/browser_hide_tooltip.js | 38 + ...browser_layers_unloaded_while_interruptingJS.js | 31 + .../tests/browser_memory_distribution_telemetry.js | 91 + dom/ipc/tests/browser_pbrowser_creation_failure.js | 56 + dom/ipc/tests/browser_subframesPreferUsed.js | 82 + dom/ipc/tests/browser_very_fission.js | 38 + dom/ipc/tests/browser_wpi_base.js | 305 + dom/ipc/tests/browser_wpi_isolate_everything.js | 27 + dom/ipc/tests/browser_wpi_isolate_high_value.js | 27 + dom/ipc/tests/browser_wpi_isolate_nothing.js | 27 + dom/ipc/tests/chrome.ini | 7 + .../tests/file_broadcast_currenturi_onload.html | 66 + dom/ipc/tests/file_cancel_content_js.html | 18 + dom/ipc/tests/file_cross_frame.html | 12 + dom/ipc/tests/file_disableScript.html | 11 + dom/ipc/tests/file_domainPolicy_base.html | 8 + dom/ipc/tests/file_dummy.html | 7 + dom/ipc/tests/file_endless_js.html | 17 + dom/ipc/tests/mochitest.ini | 23 + dom/ipc/tests/process_error.xhtml | 58 + dom/ipc/tests/test_Preallocated.html | 51 + dom/ipc/tests/test_bcg_processes.html | 45 + .../tests/test_blob_sliced_from_child_process.js | 140 + .../tests/test_blob_sliced_from_parent_process.js | 167 + dom/ipc/tests/test_browsingcontext_currenturi.html | 131 + dom/ipc/tests/test_bug1086684.js | 99 + dom/ipc/tests/test_child_docshell.js | 90 + dom/ipc/tests/test_process_error.xhtml | 22 + dom/ipc/tests/test_sharedMap.js | 377 + dom/ipc/tests/test_temporaryfile_stream.html | 84 + dom/ipc/tests/test_window_open_discarded_bc.html | 40 + dom/ipc/tests/xpcshell.ini | 10 + 244 files changed, 58350 insertions(+) create mode 100644 dom/ipc/BrowserBridgeChild.cpp create mode 100644 dom/ipc/BrowserBridgeChild.h create mode 100644 dom/ipc/BrowserBridgeHost.cpp create mode 100644 dom/ipc/BrowserBridgeHost.h create mode 100644 dom/ipc/BrowserBridgeParent.cpp create mode 100644 dom/ipc/BrowserBridgeParent.h create mode 100644 dom/ipc/BrowserChild.cpp create mode 100644 dom/ipc/BrowserChild.h create mode 100644 dom/ipc/BrowserHost.cpp create mode 100644 dom/ipc/BrowserHost.h create mode 100644 dom/ipc/BrowserParent.cpp create mode 100644 dom/ipc/BrowserParent.h create mode 100644 dom/ipc/CSPMessageUtils.cpp create mode 100644 dom/ipc/CSPMessageUtils.h create mode 100644 dom/ipc/ClonedErrorHolder.cpp create mode 100644 dom/ipc/ClonedErrorHolder.h create mode 100644 dom/ipc/CoalescedInputData.cpp create mode 100644 dom/ipc/CoalescedInputData.h create mode 100644 dom/ipc/CoalescedMouseData.cpp create mode 100644 dom/ipc/CoalescedMouseData.h create mode 100644 dom/ipc/CoalescedTouchData.cpp create mode 100644 dom/ipc/CoalescedTouchData.h create mode 100644 dom/ipc/CoalescedWheelData.cpp create mode 100644 dom/ipc/CoalescedWheelData.h create mode 100644 dom/ipc/ColorPickerParent.cpp create mode 100644 dom/ipc/ColorPickerParent.h create mode 100644 dom/ipc/ContentChild.cpp create mode 100644 dom/ipc/ContentChild.h create mode 100644 dom/ipc/ContentParent.cpp create mode 100644 dom/ipc/ContentParent.h create mode 100644 dom/ipc/ContentProcess.cpp create mode 100644 dom/ipc/ContentProcess.h create mode 100644 dom/ipc/ContentProcessManager.cpp create mode 100644 dom/ipc/ContentProcessManager.h create mode 100644 dom/ipc/DOMTypes.ipdlh create mode 100644 dom/ipc/DocShellMessageUtils.cpp create mode 100644 dom/ipc/DocShellMessageUtils.h create mode 100644 dom/ipc/EffectsInfo.h create mode 100644 dom/ipc/FilePickerMessageUtils.h create mode 100644 dom/ipc/FilePickerParent.cpp create mode 100644 dom/ipc/FilePickerParent.h create mode 100644 dom/ipc/IPCTransferable.ipdlh create mode 100644 dom/ipc/IdType.h create mode 100644 dom/ipc/InProcessChild.h create mode 100644 dom/ipc/InProcessImpl.cpp create mode 100644 dom/ipc/InProcessParent.h create mode 100644 dom/ipc/JSOracleChild.cpp create mode 100644 dom/ipc/JSOracleChild.h create mode 100644 dom/ipc/JSOracleParent.cpp create mode 100644 dom/ipc/JSOracleParent.h create mode 100644 dom/ipc/JSValidatorChild.cpp create mode 100644 dom/ipc/JSValidatorChild.h create mode 100644 dom/ipc/JSValidatorParent.cpp create mode 100644 dom/ipc/JSValidatorParent.h create mode 100644 dom/ipc/JSValidatorUtils.cpp create mode 100644 dom/ipc/JSValidatorUtils.h create mode 100644 dom/ipc/LoginDetectionService.cpp create mode 100644 dom/ipc/LoginDetectionService.h create mode 100644 dom/ipc/MMPrinter.cpp create mode 100644 dom/ipc/MMPrinter.h create mode 100644 dom/ipc/ManifestMessagesChild.sys.mjs create mode 100644 dom/ipc/MaybeDiscarded.h create mode 100644 dom/ipc/MemMapSnapshot.cpp create mode 100644 dom/ipc/MemMapSnapshot.h create mode 100644 dom/ipc/MemoryReportRequest.cpp create mode 100644 dom/ipc/MemoryReportRequest.h create mode 100644 dom/ipc/MemoryReportTypes.ipdlh create mode 100644 dom/ipc/NativeThreadId.h create mode 100644 dom/ipc/PBrowser.ipdl create mode 100644 dom/ipc/PBrowserBridge.ipdl create mode 100644 dom/ipc/PColorPicker.ipdl create mode 100644 dom/ipc/PContent.ipdl create mode 100644 dom/ipc/PContentPermission.ipdlh create mode 100644 dom/ipc/PContentPermissionRequest.ipdl create mode 100644 dom/ipc/PCycleCollectWithLogs.ipdl create mode 100644 dom/ipc/PFilePicker.ipdl create mode 100644 dom/ipc/PInProcess.ipdl create mode 100644 dom/ipc/PJSOracle.ipdl create mode 100644 dom/ipc/PJSValidator.ipdl create mode 100644 dom/ipc/PLoginReputation.ipdl create mode 100644 dom/ipc/PProcessHangMonitor.ipdl create mode 100644 dom/ipc/PTabContext.ipdlh create mode 100644 dom/ipc/PURLClassifier.ipdl create mode 100644 dom/ipc/PURLClassifierInfo.ipdlh create mode 100644 dom/ipc/PURLClassifierLocal.ipdl create mode 100644 dom/ipc/PVsync.ipdl create mode 100644 dom/ipc/PWindowGlobal.ipdl create mode 100644 dom/ipc/PageLoadEventUtils.h create mode 100644 dom/ipc/PermissionMessageUtils.cpp create mode 100644 dom/ipc/PermissionMessageUtils.h create mode 100644 dom/ipc/PreallocatedProcessManager.cpp create mode 100644 dom/ipc/PreallocatedProcessManager.h create mode 100644 dom/ipc/PrefsTypes.ipdlh create mode 100644 dom/ipc/ProcessActor.cpp create mode 100644 dom/ipc/ProcessActor.h create mode 100644 dom/ipc/ProcessHangMonitor.cpp create mode 100644 dom/ipc/ProcessHangMonitor.h create mode 100644 dom/ipc/ProcessHangMonitorIPC.h create mode 100644 dom/ipc/ProcessIsolation.cpp create mode 100644 dom/ipc/ProcessIsolation.h create mode 100644 dom/ipc/ProcessPriorityManager.cpp create mode 100644 dom/ipc/ProcessPriorityManager.h create mode 100644 dom/ipc/PropertyBagUtils.cpp create mode 100644 dom/ipc/PropertyBagUtils.h create mode 100644 dom/ipc/RefMessageBodyService.cpp create mode 100644 dom/ipc/RefMessageBodyService.h create mode 100644 dom/ipc/ReferrerInfoUtils.cpp create mode 100644 dom/ipc/ReferrerInfoUtils.h create mode 100644 dom/ipc/RemoteBrowser.cpp create mode 100644 dom/ipc/RemoteBrowser.h create mode 100644 dom/ipc/RemoteType.h create mode 100644 dom/ipc/RemoteWebProgressRequest.cpp create mode 100644 dom/ipc/RemoteWebProgressRequest.h create mode 100644 dom/ipc/ServiceWorkerConfiguration.ipdlh create mode 100644 dom/ipc/SharedMap.cpp create mode 100644 dom/ipc/SharedMap.h create mode 100644 dom/ipc/SharedMapChangeEvent.h create mode 100644 dom/ipc/SharedMessageBody.cpp create mode 100644 dom/ipc/SharedMessageBody.h create mode 100644 dom/ipc/SharedStringMap.cpp create mode 100644 dom/ipc/SharedStringMap.h create mode 100644 dom/ipc/StringTable.h create mode 100644 dom/ipc/StructuredCloneData.cpp create mode 100644 dom/ipc/StructuredCloneData.h create mode 100644 dom/ipc/TabContext.cpp create mode 100644 dom/ipc/TabContext.h create mode 100644 dom/ipc/TabMessageTypes.h create mode 100644 dom/ipc/TabMessageUtils.h create mode 100644 dom/ipc/URLClassifierChild.h create mode 100644 dom/ipc/URLClassifierParent.cpp create mode 100644 dom/ipc/URLClassifierParent.h create mode 100644 dom/ipc/UserActivationIPCUtils.h create mode 100644 dom/ipc/VsyncChild.h create mode 100644 dom/ipc/VsyncMainChild.cpp create mode 100644 dom/ipc/VsyncMainChild.h create mode 100644 dom/ipc/VsyncParent.cpp create mode 100644 dom/ipc/VsyncParent.h create mode 100644 dom/ipc/VsyncWorkerChild.cpp create mode 100644 dom/ipc/VsyncWorkerChild.h create mode 100644 dom/ipc/WindowGlobalActor.cpp create mode 100644 dom/ipc/WindowGlobalActor.h create mode 100644 dom/ipc/WindowGlobalChild.cpp create mode 100644 dom/ipc/WindowGlobalChild.h create mode 100644 dom/ipc/WindowGlobalParent.cpp create mode 100644 dom/ipc/WindowGlobalParent.h create mode 100644 dom/ipc/WindowGlobalTypes.ipdlh create mode 100644 dom/ipc/components.conf create mode 100644 dom/ipc/jar.mn create mode 100644 dom/ipc/jsactor/JSActor.cpp create mode 100644 dom/ipc/jsactor/JSActor.h create mode 100644 dom/ipc/jsactor/JSActorManager.cpp create mode 100644 dom/ipc/jsactor/JSActorManager.h create mode 100644 dom/ipc/jsactor/JSActorProtocolUtils.h create mode 100644 dom/ipc/jsactor/JSActorService.cpp create mode 100644 dom/ipc/jsactor/JSActorService.h create mode 100644 dom/ipc/jsactor/JSProcessActorChild.cpp create mode 100644 dom/ipc/jsactor/JSProcessActorChild.h create mode 100644 dom/ipc/jsactor/JSProcessActorParent.cpp create mode 100644 dom/ipc/jsactor/JSProcessActorParent.h create mode 100644 dom/ipc/jsactor/JSProcessActorProtocol.cpp create mode 100644 dom/ipc/jsactor/JSProcessActorProtocol.h create mode 100644 dom/ipc/jsactor/JSWindowActorChild.cpp create mode 100644 dom/ipc/jsactor/JSWindowActorChild.h create mode 100644 dom/ipc/jsactor/JSWindowActorParent.cpp create mode 100644 dom/ipc/jsactor/JSWindowActorParent.h create mode 100644 dom/ipc/jsactor/JSWindowActorProtocol.cpp create mode 100644 dom/ipc/jsactor/JSWindowActorProtocol.h create mode 100644 dom/ipc/jsactor/moz.build create mode 100644 dom/ipc/jsactor/nsQueryActor.h create mode 100644 dom/ipc/moz.build create mode 100644 dom/ipc/nsIDOMProcessChild.idl create mode 100644 dom/ipc/nsIDOMProcessParent.idl create mode 100644 dom/ipc/nsIHangReport.idl create mode 100644 dom/ipc/nsILoginDetectionService.idl create mode 100644 dom/ipc/tests/JSProcessActor/browser.ini create mode 100644 dom/ipc/tests/JSProcessActor/browser_getActor.js create mode 100644 dom/ipc/tests/JSProcessActor/browser_getActor_filter.js create mode 100644 dom/ipc/tests/JSProcessActor/browser_observer_notification.js create mode 100644 dom/ipc/tests/JSProcessActor/browser_registerProcessActor.js create mode 100644 dom/ipc/tests/JSProcessActor/browser_sendAsyncMessage.js create mode 100644 dom/ipc/tests/JSProcessActor/browser_sendQuery.js create mode 100644 dom/ipc/tests/JSProcessActor/browser_uri_combination.js create mode 100644 dom/ipc/tests/JSProcessActor/head.js create mode 100644 dom/ipc/tests/JSWindowActor/audio.ogg create mode 100644 dom/ipc/tests/JSWindowActor/browser.ini create mode 100644 dom/ipc/tests/JSWindowActor/browser_contentWindow.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_crash_report.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_event_listener.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_getActor.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_getActor_filter.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_observer_notification.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_process_childid.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_sendAsyncMessage.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_sendQuery.js create mode 100644 dom/ipc/tests/JSWindowActor/browser_uri_combination.js create mode 100644 dom/ipc/tests/JSWindowActor/file_dummyChromePage.html create mode 100644 dom/ipc/tests/JSWindowActor/file_mediaPlayback.html create mode 100644 dom/ipc/tests/JSWindowActor/head.js create mode 100644 dom/ipc/tests/blob_verify.sjs create mode 100644 dom/ipc/tests/browser.ini create mode 100644 dom/ipc/tests/browser_CrashService_crash.js create mode 100644 dom/ipc/tests/browser_ProcessPriorityManager.js create mode 100644 dom/ipc/tests/browser_bug1646088.js create mode 100644 dom/ipc/tests/browser_bug1686194.js create mode 100644 dom/ipc/tests/browser_cancel_content_js.js create mode 100644 dom/ipc/tests/browser_child_clipboard_restricted.js create mode 100644 dom/ipc/tests/browser_content_shutdown_with_endless_js.js create mode 100644 dom/ipc/tests/browser_crash_oopiframe.js create mode 100644 dom/ipc/tests/browser_domainPolicy.js create mode 100644 dom/ipc/tests/browser_gc_schedule.js create mode 100644 dom/ipc/tests/browser_hide_tooltip.js create mode 100644 dom/ipc/tests/browser_layers_unloaded_while_interruptingJS.js create mode 100644 dom/ipc/tests/browser_memory_distribution_telemetry.js create mode 100644 dom/ipc/tests/browser_pbrowser_creation_failure.js create mode 100644 dom/ipc/tests/browser_subframesPreferUsed.js create mode 100644 dom/ipc/tests/browser_very_fission.js create mode 100644 dom/ipc/tests/browser_wpi_base.js create mode 100644 dom/ipc/tests/browser_wpi_isolate_everything.js create mode 100644 dom/ipc/tests/browser_wpi_isolate_high_value.js create mode 100644 dom/ipc/tests/browser_wpi_isolate_nothing.js create mode 100644 dom/ipc/tests/chrome.ini create mode 100644 dom/ipc/tests/file_broadcast_currenturi_onload.html create mode 100644 dom/ipc/tests/file_cancel_content_js.html create mode 100644 dom/ipc/tests/file_cross_frame.html create mode 100644 dom/ipc/tests/file_disableScript.html create mode 100644 dom/ipc/tests/file_domainPolicy_base.html create mode 100644 dom/ipc/tests/file_dummy.html create mode 100644 dom/ipc/tests/file_endless_js.html create mode 100644 dom/ipc/tests/mochitest.ini create mode 100644 dom/ipc/tests/process_error.xhtml create mode 100644 dom/ipc/tests/test_Preallocated.html create mode 100644 dom/ipc/tests/test_bcg_processes.html create mode 100644 dom/ipc/tests/test_blob_sliced_from_child_process.js create mode 100644 dom/ipc/tests/test_blob_sliced_from_parent_process.js create mode 100644 dom/ipc/tests/test_browsingcontext_currenturi.html create mode 100644 dom/ipc/tests/test_bug1086684.js create mode 100644 dom/ipc/tests/test_child_docshell.js create mode 100644 dom/ipc/tests/test_process_error.xhtml create mode 100644 dom/ipc/tests/test_sharedMap.js create mode 100644 dom/ipc/tests/test_temporaryfile_stream.html create mode 100644 dom/ipc/tests/test_window_open_discarded_bc.html create mode 100644 dom/ipc/tests/xpcshell.ini (limited to 'dom/ipc') diff --git a/dom/ipc/BrowserBridgeChild.cpp b/dom/ipc/BrowserBridgeChild.cpp new file mode 100644 index 0000000000..3a2f7d8f84 --- /dev/null +++ b/dom/ipc/BrowserBridgeChild.cpp @@ -0,0 +1,249 @@ +/* -*- 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/. */ + +#ifdef ACCESSIBILITY +# include "mozilla/a11y/DocAccessible.h" +# include "mozilla/a11y/DocManager.h" +# include "mozilla/a11y/OuterDocAccessible.h" +#endif +#include "mozilla/dom/BrowserBridgeChild.h" +#include "mozilla/dom/BrowserBridgeHost.h" +#include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/MozFrameLoaderOwnerBinding.h" +#include "mozilla/PresShell.h" +#include "nsFocusManager.h" +#include "nsFrameLoader.h" +#include "nsFrameLoaderOwner.h" +#include "nsObjectLoadingContent.h" +#include "nsQueryObject.h" +#include "nsSubDocumentFrame.h" +#include "nsView.h" + +using namespace mozilla::ipc; + +mozilla::LazyLogModule gBrowserChildFocusLog("BrowserChildFocus"); + +#define LOGBROWSERCHILDFOCUS(args) \ + MOZ_LOG(gBrowserChildFocusLog, mozilla::LogLevel::Debug, args) + +namespace mozilla::dom { + +BrowserBridgeChild::BrowserBridgeChild(BrowsingContext* aBrowsingContext, + TabId aId, const LayersId& aLayersId) + : mId{aId}, mLayersId{aLayersId}, mBrowsingContext(aBrowsingContext) {} + +BrowserBridgeChild::~BrowserBridgeChild() {} + +already_AddRefed BrowserBridgeChild::FinishInit( + nsFrameLoader* aFrameLoader) { + MOZ_DIAGNOSTIC_ASSERT(!mFrameLoader); + mFrameLoader = aFrameLoader; + + RefPtr owner = mFrameLoader->GetOwnerContent(); + Document* doc = owner->OwnerDoc(); + doc->OOPChildLoadStarted(this); + +#if defined(ACCESSIBILITY) + if (a11y::DocAccessible* docAcc = a11y::GetExistingDocAccessible(doc)) { + if (a11y::LocalAccessible* ownerAcc = docAcc->GetAccessible(owner)) { + if (a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc()) { + outerAcc->SendEmbedderAccessible(this); + } + } + } +#endif // defined(ACCESSIBILITY) + + return MakeAndAddRef(this); +} + +nsILoadContext* BrowserBridgeChild::GetLoadContext() { + return mBrowsingContext; +} + +void BrowserBridgeChild::NavigateByKey(bool aForward, + bool aForDocumentNavigation) { + Unused << SendNavigateByKey(aForward, aForDocumentNavigation); +} + +void BrowserBridgeChild::Activate(uint64_t aActionId) { + LOGBROWSERCHILDFOCUS( + ("BrowserBridgeChild::Activate actionid: %" PRIu64, aActionId)); + Unused << SendActivate(aActionId); +} + +void BrowserBridgeChild::Deactivate(bool aWindowLowering, uint64_t aActionId) { + Unused << SendDeactivate(aWindowLowering, aActionId); +} + +/*static*/ +BrowserBridgeChild* BrowserBridgeChild::GetFrom(nsFrameLoader* aFrameLoader) { + if (!aFrameLoader) { + return nullptr; + } + return aFrameLoader->GetBrowserBridgeChild(); +} + +/*static*/ +BrowserBridgeChild* BrowserBridgeChild::GetFrom(nsIContent* aContent) { + RefPtr loaderOwner = do_QueryObject(aContent); + if (!loaderOwner) { + return nullptr; + } + RefPtr frameLoader = loaderOwner->GetFrameLoader(); + return GetFrom(frameLoader); +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvRequestFocus( + const bool& aCanRaise, const CallerType aCallerType) { + // Adapted from BrowserParent + RefPtr owner = mFrameLoader->GetOwnerContent(); + if (!owner) { + return IPC_OK(); + } + nsContentUtils::RequestFrameFocus(*owner, aCanRaise, aCallerType); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvMoveFocus( + const bool& aForward, const bool& aForDocumentNavigation) { + // Adapted from BrowserParent + RefPtr fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return IPC_OK(); + } + + RefPtr owner = mFrameLoader->GetOwnerContent(); + if (!owner) { + return IPC_OK(); + } + + RefPtr dummy; + + uint32_t type = + aForward + ? (aForDocumentNavigation + ? static_cast(nsIFocusManager::MOVEFOCUS_FORWARDDOC) + : static_cast(nsIFocusManager::MOVEFOCUS_FORWARD)) + : (aForDocumentNavigation + ? static_cast(nsIFocusManager::MOVEFOCUS_BACKWARDDOC) + : static_cast(nsIFocusManager::MOVEFOCUS_BACKWARD)); + fm->MoveFocus(nullptr, owner, type, nsIFocusManager::FLAG_BYKEY, + getter_AddRefs(dummy)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvMaybeFireEmbedderLoadEvents( + EmbedderElementEventType aFireEventAtEmbeddingElement) { + RefPtr owner = mFrameLoader->GetOwnerContent(); + if (!owner) { + return IPC_OK(); + } + + if (aFireEventAtEmbeddingElement == EmbedderElementEventType::LoadEvent) { + nsEventStatus status = nsEventStatus_eIgnore; + WidgetEvent event(/* aIsTrusted = */ true, eLoad); + event.mFlags.mBubbles = false; + event.mFlags.mCancelable = false; + EventDispatcher::Dispatch(owner, nullptr, &event, nullptr, &status); + } else if (aFireEventAtEmbeddingElement == + EmbedderElementEventType::ErrorEvent) { + mFrameLoader->FireErrorEvent(); + } + + UnblockOwnerDocsLoadEvent(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvScrollRectIntoView( + const nsRect& aRect, const ScrollAxis& aVertical, + const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags, + const int32_t& aAppUnitsPerDevPixel) { + RefPtr owner = mFrameLoader->GetOwnerContent(); + if (!owner) { + return IPC_OK(); + } + + nsIFrame* frame = owner->GetPrimaryFrame(); + if (!frame) { + return IPC_OK(); + } + + nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(frame); + if (!subdocumentFrame) { + return IPC_OK(); + } + + nsPoint extraOffset = subdocumentFrame->GetExtraOffset(); + + int32_t parentAPD = frame->PresContext()->AppUnitsPerDevPixel(); + nsRect rect = + aRect.ScaleToOtherAppUnitsRoundOut(aAppUnitsPerDevPixel, parentAPD); + rect += extraOffset; + RefPtr presShell = frame->PresShell(); + presShell->ScrollFrameIntoView(frame, Some(rect), aVertical, aHorizontal, + aScrollFlags); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvSubFrameCrashed() { + if (RefPtr frameLoaderOwner = + do_QueryObject(mFrameLoader->GetOwnerContent())) { + frameLoaderOwner->SubframeCrashed(); + } + return IPC_OK(); +} + +void BrowserBridgeChild::ActorDestroy(ActorDestroyReason aWhy) { + if (mFrameLoader) { + mFrameLoader->DestroyComplete(); + } + + if (!mBrowsingContext) { + // This BBC was never valid, skip teardown. + return; + } + + // Ensure we unblock our document's 'load' event (in case the OOP-iframe has + // been removed before it finished loading, or its subprocess crashed): + UnblockOwnerDocsLoadEvent(); +} + +void BrowserBridgeChild::UnblockOwnerDocsLoadEvent() { + if (!mHadInitialLoad) { + mHadInitialLoad = true; + + if (Document* doc = mBrowsingContext->GetParent()->GetExtantDocument()) { + doc->OOPChildLoadDone(this); + } + } +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvIntrinsicSizeOrRatioChanged( + const Maybe& aIntrinsicSize, + const Maybe& aIntrinsicRatio) { + if (RefPtr owner = mFrameLoader->GetOwnerContent()) { + if (nsCOMPtr olc = do_QueryInterface(owner)) { + static_cast(olc.get()) + ->SubdocumentIntrinsicSizeOrRatioChanged(aIntrinsicSize, + aIntrinsicRatio); + } + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserBridgeChild::RecvImageLoadComplete( + const nsresult& aResult) { + if (RefPtr owner = mFrameLoader->GetOwnerContent()) { + if (nsCOMPtr olc = do_QueryInterface(owner)) { + static_cast(olc.get()) + ->SubdocumentImageLoadComplete(aResult); + } + } + return IPC_OK(); +} + +} // namespace mozilla::dom diff --git a/dom/ipc/BrowserBridgeChild.h b/dom/ipc/BrowserBridgeChild.h new file mode 100644 index 0000000000..60e29d2483 --- /dev/null +++ b/dom/ipc/BrowserBridgeChild.h @@ -0,0 +1,119 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_BrowserBridgeChild_h +#define mozilla_dom_BrowserBridgeChild_h + +#include "mozilla/dom/PBrowserBridgeChild.h" +#include "mozilla/dom/BrowserChild.h" +#include "mozilla/dom/ipc/IdType.h" + +namespace mozilla::dom { +class BrowsingContext; +class ContentChild; +class BrowserBridgeHost; + +/** + * BrowserBridgeChild implements the child actor part of the PBrowserBridge + * protocol. See PBrowserBridge for more information. + */ +class BrowserBridgeChild : public PBrowserBridgeChild { + public: + typedef mozilla::layers::LayersId LayersId; + + NS_INLINE_DECL_REFCOUNTING(BrowserBridgeChild, final); + + BrowserChild* Manager() { + MOZ_ASSERT(CanSend()); + return static_cast(PBrowserBridgeChild::Manager()); + } + + TabId GetTabId() { return mId; } + + LayersId GetLayersId() { return mLayersId; } + + nsFrameLoader* GetFrameLoader() const { return mFrameLoader; } + + BrowsingContext* GetBrowsingContext() { return mBrowsingContext; } + + nsILoadContext* GetLoadContext(); + + void NavigateByKey(bool aForward, bool aForDocumentNavigation); + + void Activate(uint64_t aActionId); + + void Deactivate(bool aWindowLowering, uint64_t aActionId); + + already_AddRefed FinishInit(nsFrameLoader* aFrameLoader); + +#if defined(ACCESSIBILITY) + void SetEmbedderAccessible(PDocAccessibleChild* aDoc, uint64_t aID) { + MOZ_ASSERT((aDoc && aID) || (!aDoc && !aID)); + mEmbedderAccessibleID = aID; + Unused << SendSetEmbedderAccessible(aDoc, aID); + } + + uint64_t GetEmbedderAccessibleID() { return mEmbedderAccessibleID; } +#endif // defined(ACCESSIBILITY) + + static BrowserBridgeChild* GetFrom(nsFrameLoader* aFrameLoader); + + static BrowserBridgeChild* GetFrom(nsIContent* aContent); + + BrowserBridgeChild(BrowsingContext* aBrowsingContext, TabId aId, + const LayersId& aLayersId); + + protected: + friend class ContentChild; + friend class PBrowserBridgeChild; + + mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise, + const CallerType aCallerType); + + mozilla::ipc::IPCResult RecvMoveFocus(const bool& aForward, + const bool& aForDocumentNavigation); + + // TODO: Use MOZ_CAN_RUN_SCRIPT when it gains IPDL support (bug 1539864) + MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult + RecvMaybeFireEmbedderLoadEvents( + EmbedderElementEventType aFireEventAtEmbeddingElement); + + mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged( + const Maybe& aIntrinsicSize, + const Maybe& aIntrinsicRatio); + + mozilla::ipc::IPCResult RecvImageLoadComplete(const nsresult& aResult); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvScrollRectIntoView( + const nsRect& aRect, const ScrollAxis& aVertical, + const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags, + const int32_t& aAppUnitsPerDevPixel); + + mozilla::ipc::IPCResult RecvSubFrameCrashed(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + ~BrowserBridgeChild(); + + void UnblockOwnerDocsLoadEvent(); + + TabId mId; + LayersId mLayersId; + bool mHadInitialLoad = false; + RefPtr mFrameLoader; + RefPtr mBrowsingContext; +#if defined(ACCESSIBILITY) + // We need to keep track of the embedder accessible id we last sent to the + // parent process. + uint64_t mEmbedderAccessibleID = 0; +#endif // defined(ACCESSIBILITY) +}; + +} // namespace mozilla::dom + +#endif // !defined(mozilla_dom_BrowserBridgeParent_h) diff --git a/dom/ipc/BrowserBridgeHost.cpp b/dom/ipc/BrowserBridgeHost.cpp new file mode 100644 index 0000000000..2cb8decd58 --- /dev/null +++ b/dom/ipc/BrowserBridgeHost.cpp @@ -0,0 +1,91 @@ +/* -*- 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/. */ + +#include "mozilla/dom/BrowserBridgeHost.h" + +#include "mozilla/Unused.h" +#include "mozilla/dom/Element.h" +#include "nsFrameLoader.h" + +namespace mozilla::dom { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserBridgeHost) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION(BrowserBridgeHost) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserBridgeHost) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserBridgeHost) + +BrowserBridgeHost::BrowserBridgeHost(BrowserBridgeChild* aChild) + : mBridge(aChild) {} + +TabId BrowserBridgeHost::GetTabId() const { return mBridge->GetTabId(); } + +mozilla::layers::LayersId BrowserBridgeHost::GetLayersId() const { + return mBridge->GetLayersId(); +} + +BrowsingContext* BrowserBridgeHost::GetBrowsingContext() const { + return mBridge->GetBrowsingContext(); +} + +nsILoadContext* BrowserBridgeHost::GetLoadContext() const { + return mBridge->GetLoadContext(); +} + +bool BrowserBridgeHost::CanRecv() const { + return mBridge && mBridge->CanRecv(); +} + +void BrowserBridgeHost::LoadURL(nsDocShellLoadState* aLoadState) { + MOZ_ASSERT(aLoadState); + Unused << mBridge->SendLoadURL(WrapNotNull(aLoadState)); +} + +void BrowserBridgeHost::ResumeLoad(uint64_t aPendingSwitchId) { + Unused << mBridge->SendResumeLoad(aPendingSwitchId); +} + +void BrowserBridgeHost::DestroyStart() { + // We don't clear the bridge until BrowserBridgeChild::ActorDestroy is called, + // which will end up calling DestroyComplete(). + if (mBridge) { + Unused << mBridge->SendBeginDestroy(); + } +} + +void BrowserBridgeHost::DestroyComplete() { mBridge = nullptr; } + +bool BrowserBridgeHost::Show(const OwnerShowInfo& aShowInfo) { + Unused << mBridge->SendShow(aShowInfo); + return true; +} + +void BrowserBridgeHost::UpdateDimensions(const nsIntRect& aRect, + const ScreenIntSize& aSize) { + Unused << mBridge->SendUpdateDimensions(aRect, aSize); +} + +void BrowserBridgeHost::UpdateEffects(EffectsInfo aEffects) { + if (!mBridge || mEffectsInfo == aEffects) { + return; + } + mEffectsInfo = aEffects; + Unused << mBridge->SendUpdateEffects(mEffectsInfo); +} + +already_AddRefed BrowserBridgeHost::GetWidget() const { + RefPtr owner = mBridge->GetFrameLoader()->GetOwnerContent(); + nsCOMPtr widget = nsContentUtils::WidgetForContent(owner); + if (!widget) { + widget = nsContentUtils::WidgetForDocument(owner->OwnerDoc()); + } + return widget.forget(); +} + +} // namespace mozilla::dom diff --git a/dom/ipc/BrowserBridgeHost.h b/dom/ipc/BrowserBridgeHost.h new file mode 100644 index 0000000000..09f2f4dfdd --- /dev/null +++ b/dom/ipc/BrowserBridgeHost.h @@ -0,0 +1,68 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_BrowserBridgeHost_h +#define mozilla_dom_BrowserBridgeHost_h + +#include "mozilla/dom/RemoteBrowser.h" +#include "mozilla/dom/BrowserBridgeChild.h" + +namespace mozilla::dom { + +/** + * BrowserBridgeHost manages a remote browser from a content process. + * + * It is used via the RemoteBrowser interface in nsFrameLoader and proxies + * work to the chrome process via PBrowserBridge. + * + * See `dom/docs/Fission-IPC-Diagram.svg` for an overview of the DOM IPC + * actors. + */ +class BrowserBridgeHost : public RemoteBrowser { + public: + typedef mozilla::layers::LayersId LayersId; + + explicit BrowserBridgeHost(BrowserBridgeChild* aChild); + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(BrowserBridgeHost) + + // Get the IPDL actor for the BrowserBridgeChild. + BrowserBridgeChild* GetActor() { return mBridge; } + + BrowserHost* AsBrowserHost() override { return nullptr; } + BrowserBridgeHost* AsBrowserBridgeHost() override { return this; } + + TabId GetTabId() const override; + LayersId GetLayersId() const override; + BrowsingContext* GetBrowsingContext() const override; + nsILoadContext* GetLoadContext() const override; + bool CanRecv() const override; + + void LoadURL(nsDocShellLoadState* aLoadState) override; + void ResumeLoad(uint64_t aPendingSwitchId) override; + void DestroyStart() override; + void DestroyComplete() override; + + bool Show(const OwnerShowInfo&) override; + void UpdateDimensions(const nsIntRect& aRect, + const ScreenIntSize& aSize) override; + + void UpdateEffects(EffectsInfo aInfo) override; + + private: + virtual ~BrowserBridgeHost() = default; + + already_AddRefed GetWidget() const; + + // The IPDL actor for proxying browser operations + RefPtr mBridge; + EffectsInfo mEffectsInfo; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_BrowserBridgeHost_h diff --git a/dom/ipc/BrowserBridgeParent.cpp b/dom/ipc/BrowserBridgeParent.cpp new file mode 100644 index 0000000000..0d91b506db --- /dev/null +++ b/dom/ipc/BrowserBridgeParent.cpp @@ -0,0 +1,310 @@ +/* -*- 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/. */ + +#ifdef ACCESSIBILITY +# include "mozilla/a11y/DocAccessibleParent.h" +# include "nsAccessibilityService.h" +#endif + +#include "mozilla/Monitor.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/dom/BrowserBridgeParent.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/dom/BrowsingContextGroup.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/InputAPZContext.h" + +using namespace mozilla::ipc; +using namespace mozilla::layout; +using namespace mozilla::hal; + +namespace mozilla::dom { + +BrowserBridgeParent::BrowserBridgeParent() = default; + +BrowserBridgeParent::~BrowserBridgeParent() { Destroy(); } + +nsresult BrowserBridgeParent::InitWithProcess( + BrowserParent* aParentBrowser, ContentParent* aContentParent, + const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags, TabId aTabId) { + MOZ_ASSERT(!CanSend(), + "This should be called before the object is connected to IPC"); + MOZ_DIAGNOSTIC_ASSERT(!aContentParent->IsLaunching()); + MOZ_DIAGNOSTIC_ASSERT(!aContentParent->IsDead()); + + RefPtr browsingContext = + CanonicalBrowsingContext::Get(aWindowInit.context().mBrowsingContextId); + if (!browsingContext || browsingContext->IsDiscarded()) { + return NS_ERROR_UNEXPECTED; + } + + MOZ_DIAGNOSTIC_ASSERT( + !browsingContext->GetBrowserParent(), + "BrowsingContext must have had previous BrowserParent cleared"); + + MOZ_DIAGNOSTIC_ASSERT( + aParentBrowser->Manager() != aContentParent, + "Cannot create OOP iframe in the same process as its parent document"); + + // Unfortunately, due to the current racy destruction of BrowsingContext + // instances when Fission is enabled, while `browsingContext` may not be + // discarded, an ancestor might be. + // + // A discarded ancestor will cause us issues when creating our `BrowserParent` + // in the new content process, so abort the attempt if we have one. + // + // FIXME: We should never have a non-discarded BrowsingContext with discarded + // ancestors. (bug 1634759) + if (NS_WARN_IF(!browsingContext->AncestorsAreCurrent())) { + return NS_ERROR_UNEXPECTED; + } + + // Ensure that our content process is subscribed to our newly created + // BrowsingContextGroup. + browsingContext->Group()->EnsureHostProcess(aContentParent); + browsingContext->SetOwnerProcessId(aContentParent->ChildID()); + + // Construct the BrowserParent object for our subframe. + auto browserParent = MakeRefPtr( + aContentParent, aTabId, *aParentBrowser, browsingContext, aChromeFlags); + browserParent->SetBrowserBridgeParent(this); + + // Open a remote endpoint for our PBrowser actor. + ManagedEndpoint childEp = + aContentParent->OpenPBrowserEndpoint(browserParent); + if (NS_WARN_IF(!childEp.IsValid())) { + MOZ_ASSERT(false, "Browser Open Endpoint Failed"); + return NS_ERROR_FAILURE; + } + + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + if (!cpm) { + return NS_ERROR_UNEXPECTED; + } + cpm->RegisterRemoteFrame(browserParent); + + RefPtr windowParent = + WindowGlobalParent::CreateDisconnected(aWindowInit); + if (!windowParent) { + return NS_ERROR_UNEXPECTED; + } + + ManagedEndpoint windowChildEp = + browserParent->OpenPWindowGlobalEndpoint(windowParent); + if (NS_WARN_IF(!windowChildEp.IsValid())) { + MOZ_ASSERT(false, "WindowGlobal Open Endpoint Failed"); + return NS_ERROR_FAILURE; + } + + MOZ_DIAGNOSTIC_ASSERT(!browsingContext->IsDiscarded(), + "bc cannot have become discarded"); + + // Tell the content process to set up its PBrowserChild. + bool ok = aContentParent->SendConstructBrowser( + std::move(childEp), std::move(windowChildEp), aTabId, + browserParent->AsIPCTabContext(), aWindowInit, aChromeFlags, + aContentParent->ChildID(), aContentParent->IsForBrowser(), + /* aIsTopLevel */ false); + if (NS_WARN_IF(!ok)) { + MOZ_ASSERT(false, "Browser Constructor Failed"); + return NS_ERROR_FAILURE; + } + + // Set our BrowserParent object to the newly created browser. + mBrowserParent = std::move(browserParent); + mBrowserParent->SetOwnerElement(aParentBrowser->GetOwnerElement()); + mBrowserParent->InitRendering(); + + GetBrowsingContext()->SetCurrentBrowserParent(mBrowserParent); + + windowParent->Init(); + return NS_OK; +} + +CanonicalBrowsingContext* BrowserBridgeParent::GetBrowsingContext() { + return mBrowserParent->GetBrowsingContext(); +} + +BrowserParent* BrowserBridgeParent::Manager() { + MOZ_ASSERT(CanSend()); + return static_cast(PBrowserBridgeParent::Manager()); +} + +void BrowserBridgeParent::Destroy() { + if (mBrowserParent) { +#ifdef ACCESSIBILITY + if (mEmbedderAccessibleDoc && !mEmbedderAccessibleDoc->IsShutdown()) { + mEmbedderAccessibleDoc->RemovePendingOOPChildDoc(this); + } +#endif + mBrowserParent->Destroy(); + mBrowserParent->SetBrowserBridgeParent(nullptr); + mBrowserParent = nullptr; + } + if (CanSend()) { + Unused << Send__delete__(this); + } +} + +IPCResult BrowserBridgeParent::RecvShow(const OwnerShowInfo& aOwnerInfo) { + mBrowserParent->AttachWindowRenderer(); + Unused << mBrowserParent->SendShow(mBrowserParent->GetShowInfo(), aOwnerInfo); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvScrollbarPreferenceChanged( + ScrollbarPreference aPref) { + Unused << mBrowserParent->SendScrollbarPreferenceChanged(aPref); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvLoadURL(nsDocShellLoadState* aLoadState) { + Unused << mBrowserParent->SendLoadURL(WrapNotNull(aLoadState), + mBrowserParent->GetShowInfo()); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvResumeLoad(uint64_t aPendingSwitchID) { + mBrowserParent->ResumeLoad(aPendingSwitchID); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvUpdateDimensions( + const nsIntRect& aRect, const ScreenIntSize& aSize) { + mBrowserParent->UpdateDimensions(aRect, aSize); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvUpdateEffects(const EffectsInfo& aEffects) { + Unused << mBrowserParent->SendUpdateEffects(aEffects); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvUpdateRemotePrintSettings( + const embedding::PrintData& aPrintData) { + Unused << mBrowserParent->SendUpdateRemotePrintSettings(aPrintData); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvRenderLayers( + const bool& aEnabled, const layers::LayersObserverEpoch& aEpoch) { + Unused << mBrowserParent->SendRenderLayers(aEnabled, aEpoch); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvNavigateByKey( + const bool& aForward, const bool& aForDocumentNavigation) { + Unused << mBrowserParent->SendNavigateByKey(aForward, aForDocumentNavigation); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvBeginDestroy() { + Destroy(); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvDispatchSynthesizedMouseEvent( + const WidgetMouseEvent& aEvent) { + if (aEvent.mMessage != eMouseMove || + aEvent.mReason != WidgetMouseEvent::eSynthesized) { + return IPC_FAIL(this, "Unexpected event type"); + } + + WidgetMouseEvent event = aEvent; + // Convert mRefPoint from the dispatching child process coordinate space + // to the parent coordinate space. The SendRealMouseEvent call will convert + // it into the dispatchee child process coordinate space + event.mRefPoint = Manager()->TransformChildToParent(event.mRefPoint); + // We need to set up an InputAPZContext on the stack because + // BrowserParent::SendRealMouseEvent requires one. But the only thing in + // that context that is actually used in this scenario is the layers id, + // and we already have that on the mouse event. + layers::InputAPZContext context( + layers::ScrollableLayerGuid(event.mLayersId, 0, + layers::ScrollableLayerGuid::NULL_SCROLL_ID), + 0, nsEventStatus_eIgnore); + mBrowserParent->SendRealMouseEvent(event); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvWillChangeProcess() { + Unused << mBrowserParent->SendWillChangeProcess(); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvActivate(uint64_t aActionId) { + mBrowserParent->Activate(aActionId); + return IPC_OK(); +} + +IPCResult BrowserBridgeParent::RecvDeactivate(const bool& aWindowLowering, + uint64_t aActionId) { + mBrowserParent->Deactivate(aWindowLowering, aActionId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserBridgeParent::RecvUpdateRemoteStyle( + const StyleImageRendering& aImageRendering) { + Unused << mBrowserParent->SendUpdateRemoteStyle(aImageRendering); + return IPC_OK(); +} + +#ifdef ACCESSIBILITY +a11y::DocAccessibleParent* BrowserBridgeParent::GetDocAccessibleParent() { + auto* embeddedBrowser = GetBrowserParent(); + if (!embeddedBrowser) { + return nullptr; + } + a11y::DocAccessibleParent* docAcc = + embeddedBrowser->GetTopLevelDocAccessible(); + return docAcc && !docAcc->IsShutdown() ? docAcc : nullptr; +} + +IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible( + PDocAccessibleParent* aDoc, uint64_t aID) { +# if defined(ANDROID) + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); +# endif + MOZ_ASSERT(aDoc || mEmbedderAccessibleDoc, + "Embedder doc shouldn't be cleared if it wasn't set"); + MOZ_ASSERT(!mEmbedderAccessibleDoc || !aDoc || mEmbedderAccessibleDoc == aDoc, + "Embedder doc shouldn't change from one doc to another"); + if (!aDoc && mEmbedderAccessibleDoc && + !mEmbedderAccessibleDoc->IsShutdown()) { + // We're clearing the embedder doc, so remove the pending child doc addition + // (if any). + mEmbedderAccessibleDoc->RemovePendingOOPChildDoc(this); + } + mEmbedderAccessibleDoc = static_cast(aDoc); + mEmbedderAccessibleID = aID; + if (!aDoc) { + MOZ_ASSERT(!aID); + return IPC_OK(); + } + MOZ_ASSERT(aID); + if (GetDocAccessibleParent()) { + // The embedded DocAccessibleParent has already been created. This can + // happen if, for example, an iframe is hidden and then shown or + // an iframe is reflowed by layout. + mEmbedderAccessibleDoc->AddChildDoc(this); + } + return IPC_OK(); +} + +a11y::DocAccessibleParent* BrowserBridgeParent::GetEmbedderAccessibleDoc() { + return mEmbedderAccessibleDoc && !mEmbedderAccessibleDoc->IsShutdown() + ? mEmbedderAccessibleDoc.get() + : nullptr; +} +#endif + +void BrowserBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { Destroy(); } + +} // namespace mozilla::dom diff --git a/dom/ipc/BrowserBridgeParent.h b/dom/ipc/BrowserBridgeParent.h new file mode 100644 index 0000000000..7f49cea4d5 --- /dev/null +++ b/dom/ipc/BrowserBridgeParent.h @@ -0,0 +1,124 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_BrowserBridgeParent_h +#define mozilla_dom_BrowserBridgeParent_h + +#include "mozilla/dom/PBrowserBridgeParent.h" +#include "mozilla/dom/ipc/IdType.h" +#include "mozilla/dom/WindowGlobalTypes.h" + +namespace mozilla { + +namespace a11y { +class DocAccessibleParent; +} + +namespace embedding { +class PrintData; +} + +namespace dom { + +class BrowserParent; + +/** + * BrowserBridgeParent implements the parent actor part of the PBrowserBridge + * protocol. See PBrowserBridge for more information. + */ +class BrowserBridgeParent : public PBrowserBridgeParent { + public: + NS_INLINE_DECL_REFCOUNTING(BrowserBridgeParent, final); + + BrowserBridgeParent(); + + nsresult InitWithProcess(BrowserParent* aParentBrowser, + ContentParent* aContentParent, + const WindowGlobalInit& aWindowInit, + uint32_t aChromeFlags, TabId aTabId); + + BrowserParent* GetBrowserParent() { return mBrowserParent; } + + CanonicalBrowsingContext* GetBrowsingContext(); + + // Get our manager actor. + BrowserParent* Manager(); + +#if defined(ACCESSIBILITY) + /** + * Get the DocAccessibleParent which contains this iframe. + */ + a11y::DocAccessibleParent* GetEmbedderAccessibleDoc(); + + /** + * Get the unique id of the OuterDocAccessible associated with this iframe. + * This is the id of the RemoteAccessible inside the document returned by + * GetEmbedderAccessibleDoc. + */ + uint64_t GetEmbedderAccessibleId() { return mEmbedderAccessibleID; } + + /** + * Get the DocAccessibleParent for the embedded document. + */ + a11y::DocAccessibleParent* GetDocAccessibleParent(); +#endif // defined(ACCESSIBILITY) + + // Tear down this BrowserBridgeParent. + void Destroy(); + + protected: + friend class PBrowserBridgeParent; + + mozilla::ipc::IPCResult RecvShow(const OwnerShowInfo&); + mozilla::ipc::IPCResult RecvScrollbarPreferenceChanged(ScrollbarPreference); + mozilla::ipc::IPCResult RecvLoadURL(nsDocShellLoadState* aLoadState); + mozilla::ipc::IPCResult RecvResumeLoad(uint64_t aPendingSwitchID); + mozilla::ipc::IPCResult RecvUpdateDimensions(const nsIntRect& aRect, + const ScreenIntSize& aSize); + mozilla::ipc::IPCResult RecvUpdateEffects(const EffectsInfo& aEffects); + mozilla::ipc::IPCResult RecvUpdateRemotePrintSettings( + const embedding::PrintData&); + mozilla::ipc::IPCResult RecvRenderLayers(const bool& aEnabled, + const LayersObserverEpoch& aEpoch); + + mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward, + const bool& aForDocumentNavigation); + mozilla::ipc::IPCResult RecvBeginDestroy(); + + mozilla::ipc::IPCResult RecvDispatchSynthesizedMouseEvent( + const WidgetMouseEvent& aEvent); + + mozilla::ipc::IPCResult RecvWillChangeProcess(); + + mozilla::ipc::IPCResult RecvActivate(uint64_t aActionId); + + mozilla::ipc::IPCResult RecvDeactivate(const bool& aWindowLowering, + uint64_t aActionId); + + mozilla::ipc::IPCResult RecvUpdateRemoteStyle( + const StyleImageRendering& aImageRendering); + +#ifdef ACCESSIBILITY + mozilla::ipc::IPCResult RecvSetEmbedderAccessible(PDocAccessibleParent* aDoc, + uint64_t aID); +#endif + + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + ~BrowserBridgeParent(); + + RefPtr mBrowserParent; +#ifdef ACCESSIBILITY + RefPtr mEmbedderAccessibleDoc; + uint64_t mEmbedderAccessibleID = 0; +#endif // ACCESSIBILITY +}; + +} // namespace dom +} // namespace mozilla + +#endif // !defined(mozilla_dom_BrowserBridgeParent_h) diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp new file mode 100644 index 0000000000..95fc4de14d --- /dev/null +++ b/dom/ipc/BrowserChild.cpp @@ -0,0 +1,3848 @@ +/* -*- 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/. */ + +#include "base/basictypes.h" + +#include "BrowserChild.h" + +#ifdef ACCESSIBILITY +# include "mozilla/a11y/DocAccessibleChild.h" +#endif +#include +#include + +#include "BrowserParent.h" +#include "ContentChild.h" +#include "DocumentInlines.h" +#include "EventStateManager.h" +#include "MMPrinter.h" +#include "PermissionMessageUtils.h" +#include "PuppetWidget.h" +#include "StructuredCloneData.h" +#include "UnitTransforms.h" +#include "Units.h" +#include "VRManagerChild.h" +#include "ipc/nsGUIEventIPC.h" +#include "js/JSON.h" +#include "mozilla/Assertions.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/EventForwards.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/NativeKeyBindingsType.h" +#include "mozilla/NullPrincipal.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_fission.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TextEvents.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/ToString.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/AutoPrintEventDispatcher.h" +#include "mozilla/dom/BrowserBridgeChild.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/JSWindowActorChild.h" +#include "mozilla/dom/ImageDocument.h" +#include "mozilla/dom/LoadURIOptionsBinding.h" +#include "mozilla/dom/MediaDocument.h" +#include "mozilla/dom/MessageManagerBinding.h" +#include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/dom/Nullable.h" +#include "mozilla/dom/PBrowser.h" +#include "mozilla/dom/PaymentRequestChild.h" +#include "mozilla/dom/PointerEventHandler.h" +#include "mozilla/dom/SessionStoreUtils.h" +#include "mozilla/dom/SessionStoreChild.h" +#include "mozilla/dom/WindowGlobalChild.h" +#include "mozilla/dom/WindowProxyHolder.h" +#include "mozilla/gfx/CrossProcessPaint.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundChild.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/TouchActionHelper.h" +#include "mozilla/layers/APZCTreeManagerChild.h" +#include "mozilla/layers/APZChild.h" +#include "mozilla/layers/APZEventState.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/ContentProcessController.h" +#include "mozilla/layers/DoubleTapToZoom.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layers/WebRenderLayerManager.h" +#include "nsBrowserStatusFilter.h" +#include "nsColorPickerProxy.h" +#include "nsCommandParams.h" +#include "nsContentPermissionHelper.h" +#include "nsContentUtils.h" +#include "nsDeviceContext.h" +#include "nsDocShell.h" +#include "nsDocShellLoadState.h" +#include "nsEmbedCID.h" +#include "nsExceptionHandler.h" +#include "nsFilePickerProxy.h" +#include "nsFocusManager.h" +#include "nsGlobalWindow.h" +#include "nsIBaseWindow.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIClassifiedChannel.h" +#include "nsIDocShell.h" +#include "nsIFrame.h" +#include "nsILoadContext.h" +#include "nsISHEntry.h" +#include "nsISHistory.h" +#include "nsIScreenManager.h" +#include "nsIScriptError.h" +#include "nsISecureBrowserUI.h" +#include "nsIURI.h" +#include "nsIURIMutator.h" +#include "nsIWeakReferenceUtils.h" +#include "nsIWebBrowser.h" +#include "nsIWebProgress.h" +#include "nsLayoutUtils.h" +#include "nsNetUtil.h" +#include "nsIOpenWindowInfo.h" +#include "nsPIDOMWindow.h" +#include "nsPIWindowRoot.h" +#include "nsPointerHashKeys.h" +#include "nsPrintfCString.h" +#include "nsQueryActor.h" +#include "nsQueryObject.h" +#include "nsRefreshDriver.h" +#include "nsSandboxFlags.h" +#include "nsString.h" +#include "nsTHashtable.h" +#include "nsThreadManager.h" +#include "nsThreadUtils.h" +#include "nsViewManager.h" +#include "nsViewportInfo.h" +#include "nsWebBrowser.h" +#include "nsWindowWatcher.h" +#include "nsIXULRuntime.h" + +#ifdef MOZ_WAYLAND +# include "nsAppRunner.h" +#endif + +#ifdef NS_PRINTING +# include "mozilla/layout/RemotePrintJobChild.h" +# include "nsIPrintSettings.h" +# include "nsIPrintSettingsService.h" +# include "nsIWebBrowserPrint.h" +#endif + +static mozilla::LazyLogModule sApzChildLog("apz.child"); + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::dom::ipc; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::layout; +using namespace mozilla::widget; +using mozilla::layers::GeckoContentController; + +static const char BEFORE_FIRST_PAINT[] = "before-first-paint"; + +static uint32_t sConsecutiveTouchMoveCount = 0; + +using BrowserChildMap = nsTHashMap; +static BrowserChildMap* sBrowserChildren; +StaticMutex sBrowserChildrenMutex; + +already_AddRefed BrowserChild::GetTopLevelDocument() const { + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + nsCOMPtr doc = docShell ? docShell->GetExtantDocument() : nullptr; + return doc.forget(); +} + +PresShell* BrowserChild::GetTopLevelPresShell() const { + if (RefPtr doc = GetTopLevelDocument()) { + return doc->GetPresShell(); + } + return nullptr; +} + +bool BrowserChild::UpdateFrame(const RepaintRequest& aRequest) { + MOZ_ASSERT(aRequest.GetScrollId() != ScrollableLayerGuid::NULL_SCROLL_ID); + + if (aRequest.IsRootContent()) { + if (PresShell* presShell = GetTopLevelPresShell()) { + // Guard against stale updates (updates meant for a pres shell which + // has since been torn down and destroyed). + if (aRequest.GetPresShellId() == presShell->GetPresShellId()) { + APZCCallbackHelper::UpdateRootFrame(aRequest); + return true; + } + } + } else { + // aRequest.mIsRoot is false, so we are trying to update a subframe. + // This requires special handling. + APZCCallbackHelper::UpdateSubFrame(aRequest); + return true; + } + return true; +} + +class BrowserChild::DelayedDeleteRunnable final : public Runnable, + public nsIRunnablePriority { + RefPtr mBrowserChild; + + // In order to try that this runnable runs after everything that could + // possibly touch this tab, we send it through the event queue twice. + bool mReadyToDelete = false; + + public: + explicit DelayedDeleteRunnable(BrowserChild* aBrowserChild) + : Runnable("BrowserChild::DelayedDeleteRunnable"), + mBrowserChild(aBrowserChild) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aBrowserChild); + } + + NS_DECL_ISUPPORTS_INHERITED + + private: + ~DelayedDeleteRunnable() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mBrowserChild); + } + + NS_IMETHOD GetPriority(uint32_t* aPriority) override { + *aPriority = nsIRunnablePriority::PRIORITY_NORMAL; + return NS_OK; + } + + NS_IMETHOD + Run() override { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mBrowserChild); + + if (!mReadyToDelete) { + // This time run this runnable at input priority. + mReadyToDelete = true; + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this)); + return NS_OK; + } + + // Check in case ActorDestroy was called after RecvDestroy message. + if (mBrowserChild->IPCOpen()) { + Unused << PBrowserChild::Send__delete__(mBrowserChild); + } + + mBrowserChild = nullptr; + return NS_OK; + } +}; + +NS_IMPL_ISUPPORTS_INHERITED(BrowserChild::DelayedDeleteRunnable, Runnable, + nsIRunnablePriority) + +namespace { +std::map>& NestedBrowserChildMap() { + MOZ_ASSERT(NS_IsMainThread()); + static std::map> sNestedBrowserChildMap; + return sNestedBrowserChildMap; +} +} // namespace + +already_AddRefed BrowserChild::FindBrowserChild( + const TabId& aTabId) { + auto iter = NestedBrowserChildMap().find(aTabId); + if (iter == NestedBrowserChildMap().end()) { + return nullptr; + } + RefPtr browserChild = iter->second; + return browserChild.forget(); +} + +/*static*/ +already_AddRefed BrowserChild::Create( + ContentChild* aManager, const TabId& aTabId, const TabContext& aContext, + BrowsingContext* aBrowsingContext, uint32_t aChromeFlags, + bool aIsTopLevel) { + RefPtr iframe = new BrowserChild( + aManager, aTabId, aContext, aBrowsingContext, aChromeFlags, aIsTopLevel); + return iframe.forget(); +} + +BrowserChild::BrowserChild(ContentChild* aManager, const TabId& aTabId, + const TabContext& aContext, + BrowsingContext* aBrowsingContext, + uint32_t aChromeFlags, bool aIsTopLevel) + : TabContext(aContext), + mBrowserChildMessageManager(nullptr), + mManager(aManager), + mBrowsingContext(aBrowsingContext), + mChromeFlags(aChromeFlags), + mMaxTouchPoints(0), + mLayersId{0}, + mEffectsInfo{EffectsInfo::FullyHidden()}, + mDynamicToolbarMaxHeight(0), + mUniqueId(aTabId), + mDidFakeShow(false), + mTriedBrowserInit(false), + mIgnoreKeyPressEvent(false), + mHasValidInnerSize(false), + mDestroyed(false), + mIsTopLevel(aIsTopLevel), + mIsTransparent(false), + mIPCOpen(false), + mDidSetRealShowInfo(false), + mDidLoadURLInit(false), + mSkipKeyPress(false), + mDidSetEffectsInfo(false), + mShouldSendWebProgressEventsToParent(false), + mRenderLayers(true), + mIsPreservingLayers(false), + mLayersObserverEpoch{1}, +#if defined(XP_WIN) && defined(ACCESSIBILITY) + mNativeWindowHandle(0), +#endif +#if defined(ACCESSIBILITY) + mTopLevelDocAccessibleChild(nullptr), +#endif + mCancelContentJSEpoch(0) { + mozilla::HoldJSObjects(this); + + // preloaded BrowserChild should not be added to child map + if (mUniqueId) { + MOZ_ASSERT(NestedBrowserChildMap().find(mUniqueId) == + NestedBrowserChildMap().end()); + NestedBrowserChildMap()[mUniqueId] = this; + } + mCoalesceMouseMoveEvents = StaticPrefs::dom_events_coalesce_mousemove(); + if (mCoalesceMouseMoveEvents) { + mCoalescedMouseEventFlusher = new CoalescedMouseMoveFlusher(this); + } + + if (StaticPrefs::dom_events_coalesce_touchmove()) { + mCoalescedTouchMoveEventFlusher = new CoalescedTouchMoveFlusher(this); + } +} + +const CompositorOptions& BrowserChild::GetCompositorOptions() const { + // If you're calling this before mCompositorOptions is set, well.. don't. + MOZ_ASSERT(mCompositorOptions); + return mCompositorOptions.ref(); +} + +bool BrowserChild::AsyncPanZoomEnabled() const { + // This might get called by the TouchEvent::PrefEnabled code before we have + // mCompositorOptions populated (bug 1370089). In that case we just assume + // APZ is enabled because we're in a content process (because BrowserChild) + // and APZ is probably going to be enabled here since e10s is enabled. + return mCompositorOptions ? mCompositorOptions->UseAPZ() : true; +} + +NS_IMETHODIMP +BrowserChild::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) { + if (AsyncPanZoomEnabled()) { + nsCOMPtr subject(do_QueryInterface(aSubject)); + nsCOMPtr doc(GetTopLevelDocument()); + + if (subject == doc) { + RefPtr presShell = doc->GetPresShell(); + if (presShell) { + presShell->SetIsFirstPaint(true); + } + + APZCCallbackHelper::InitializeRootDisplayport(presShell); + } + } + } + + return NS_OK; +} + +void BrowserChild::ContentReceivedInputBlock(uint64_t aInputBlockId, + bool aPreventDefault) const { + if (mApzcTreeManager) { + mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); + } +} + +void BrowserChild::SetTargetAPZC( + uint64_t aInputBlockId, + const nsTArray& aTargets) const { + if (mApzcTreeManager) { + mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets); + } +} + +bool BrowserChild::DoUpdateZoomConstraints( + const uint32_t& aPresShellId, const ViewID& aViewId, + const Maybe& aConstraints) { + if (!mApzcTreeManager || mDestroyed) { + return false; + } + + ScrollableLayerGuid guid = + ScrollableLayerGuid(mLayersId, aPresShellId, aViewId); + + mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints); + return true; +} + +nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent, + WindowGlobalChild* aInitialWindowChild) { + MOZ_ASSERT_IF(aInitialWindowChild, + aInitialWindowChild->BrowsingContext() == mBrowsingContext); + + nsCOMPtr widget = nsIWidget::CreatePuppetWidget(this); + mPuppetWidget = static_cast(widget.get()); + if (!mPuppetWidget) { + NS_ERROR("couldn't create fake widget"); + return NS_ERROR_FAILURE; + } + mPuppetWidget->InfallibleCreate(nullptr, + nullptr, // no parents + LayoutDeviceIntRect(0, 0, 0, 0), + nullptr); // HandleWidgetEvent + + mWebBrowser = nsWebBrowser::Create(this, mPuppetWidget, mBrowsingContext, + aInitialWindowChild); + nsIWebBrowser* webBrowser = mWebBrowser; + + mWebNav = do_QueryInterface(webBrowser); + NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?"); + + // IPC uses a WebBrowser object for which DNS prefetching is turned off + // by default. But here we really want it, so enable it explicitly + mWebBrowser->SetAllowDNSPrefetch(true); + + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + MOZ_ASSERT(docShell); + + mStatusFilter = new nsBrowserStatusFilter(); + + nsresult rv = + mStatusFilter->AddProgressListener(this, nsIWebProgress::NOTIFY_ALL); + NS_ENSURE_SUCCESS(rv, rv); + + { + nsCOMPtr webProgress = do_QueryInterface(docShell); + rv = webProgress->AddProgressListener(mStatusFilter, + nsIWebProgress::NOTIFY_ALL); + NS_ENSURE_SUCCESS(rv, rv); + } + +#ifdef DEBUG + nsCOMPtr loadContext = do_GetInterface(WebNavigation()); + MOZ_ASSERT(loadContext); + MOZ_ASSERT(loadContext->UseRemoteTabs() == + !!(mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW)); + MOZ_ASSERT(loadContext->UseRemoteSubframes() == + !!(mChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW)); +#endif // defined(DEBUG) + + // Few lines before, baseWindow->Create() will end up creating a new + // window root in nsGlobalWindow::SetDocShell. + // Then this chrome event handler, will be inherited to inner windows. + // We want to also set it to the docshell so that inner windows + // and any code that has access to the docshell + // can all listen to the same chrome event handler. + // XXX: ideally, we would set a chrome event handler earlier, + // and all windows, even the root one, will use the docshell one. + nsCOMPtr window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + nsCOMPtr chromeHandler = window->GetChromeEventHandler(); + docShell->SetChromeEventHandler(chromeHandler); + + // Window scrollbar flags only affect top level remote frames, not fission + // frames. + if (mIsTopLevel) { + nsContentUtils::SetScrollbarsVisibility( + docShell, !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS)); + } + + nsWeakPtr weakPtrThis = do_GetWeakReference( + static_cast(this)); // for capture by the lambda + ContentReceivedInputBlockCallback callback( + [weakPtrThis](uint64_t aInputBlockId, bool aPreventDefault) { + if (nsCOMPtr browserChild = + do_QueryReferent(weakPtrThis)) { + static_cast(browserChild.get()) + ->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); + } + }); + mAPZEventState = new APZEventState(mPuppetWidget, std::move(callback)); + + mIPCOpen = true; + + if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) { + mSessionStoreChild = SessionStoreChild::GetOrCreate(mBrowsingContext); + } + + // We've all set up, make sure our visibility state is consistent. This is + // important for OOP iframes, which start off as hidden. + UpdateVisibility(); + + return NS_OK; +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(BrowserChild) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowserChild) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChildMessageManager) + tmp->nsMessageManagerScriptExecutor::Unlink(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusFilter) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreChild) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentTransformPromise) + NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowserChild) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChildMessageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusFilter) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreChild) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentTransformPromise) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowserChild) + tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure); +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserChild) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + NS_INTERFACE_MAP_ENTRY(nsIBrowserChild) + NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsITooltipListener) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener2) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIBrowserChild) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserChild) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserChild) + +NS_IMETHODIMP +BrowserChild::GetChromeFlags(uint32_t* aChromeFlags) { + *aChromeFlags = mChromeFlags; + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::SetChromeFlags(uint32_t aChromeFlags) { + NS_WARNING("trying to SetChromeFlags from content process?"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +BrowserChild::RemoteDropLinks( + const nsTArray>& aLinks) { + nsTArray linksArray; + nsresult rv = NS_OK; + for (nsIDroppedLinkItem* link : aLinks) { + nsString tmp; + rv = link->GetUrl(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + + rv = link->GetName(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + + rv = link->GetType(tmp); + if (NS_FAILED(rv)) { + return rv; + } + linksArray.AppendElement(tmp); + } + bool sent = SendDropLinks(linksArray); + + return sent ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +BrowserChild::ShowAsModal() { + NS_WARNING("BrowserChild::ShowAsModal not supported in BrowserChild"); + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +BrowserChild::IsWindowModal(bool* aRetVal) { + *aRetVal = false; + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::SetLinkStatus(const nsAString& aStatusText) { + // We can only send the status after the ipc machinery is set up + if (IPCOpen()) { + SendSetLinkStatus(aStatusText); + } + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::SetDimensions(DimensionRequest&& aRequest) { + // The parent is in charge of the dimension changes. If JS code wants to + // change the dimensions (moveTo, screenX, etc.) we send a message to the + // parent about the new requested dimension, the parent does the resize/move + // then send a message to the child to update itself. For APIs like screenX + // this function is called with only the changed values. In a series of calls + // like window.screenX = 10; window.screenY = 10; for the second call, since + // screenX is not yet updated we might accidentally reset back screenX to it's + // old value. To avoid this, if a parameter did not change, we want the parent + // to handle the unchanged values. + + double scale = mPuppetWidget ? mPuppetWidget->GetDefaultScale().scale : 1.0; + SendSetDimensions(aRequest, scale); + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, + int32_t* aY, int32_t* aCx, int32_t* aCy) { + ScreenIntRect rect = GetOuterRect(); + if (aDimensionKind == DimensionKind::Inner) { + if (aX || aY) { + return NS_ERROR_NOT_IMPLEMENTED; + } + rect.SizeTo(GetInnerSize()); + } + if (aX) { + *aX = rect.x; + } + if (aY) { + *aY = rect.y; + } + if (aCx) { + *aCx = rect.width; + } + if (aCy) { + *aCy = rect.height; + } + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::Blur() { return NS_ERROR_NOT_IMPLEMENTED; } + +NS_IMETHODIMP +BrowserChild::FocusNextElement(bool aForDocumentNavigation) { + SendMoveFocus(true, aForDocumentNavigation); + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::FocusPrevElement(bool aForDocumentNavigation) { + SendMoveFocus(false, aForDocumentNavigation); + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::GetInterface(const nsIID& aIID, void** aSink) { + // XXXbz should we restrict the set of interfaces we hand out here? + // See bug 537429 + return QueryInterface(aIID, aSink); +} + +NS_IMETHODIMP +BrowserChild::ProvideWindow(nsIOpenWindowInfo* aOpenWindowInfo, + uint32_t aChromeFlags, bool aCalledFromJS, + nsIURI* aURI, const nsAString& aName, + const nsACString& aFeatures, bool aForceNoOpener, + bool aForceNoReferrer, bool aIsPopupRequested, + nsDocShellLoadState* aLoadState, bool* aWindowIsNew, + BrowsingContext** aReturn) { + *aReturn = nullptr; + + RefPtr parent = aOpenWindowInfo->GetParent(); + + int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation( + parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, + aOpenWindowInfo->GetIsForPrinting()); + + // If it turns out we're opening in the current browser, just hand over the + // current browser's docshell. + if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) { + nsCOMPtr browser = do_GetInterface(WebNavigation()); + *aWindowIsNew = false; + + nsCOMPtr win; + MOZ_TRY(browser->GetContentDOMWindow(getter_AddRefs(win))); + + RefPtr bc( + nsPIDOMWindowOuter::From(win)->GetBrowsingContext()); + bc.forget(aReturn); + return NS_OK; + } + + // Note that ProvideWindowCommon may return NS_ERROR_ABORT if the + // open window call was canceled. It's important that we pass this error + // code back to our caller. + ContentChild* cc = ContentChild::GetSingleton(); + return cc->ProvideWindowCommon( + WrapNotNull(this), aOpenWindowInfo, aChromeFlags, aCalledFromJS, aURI, + aName, aFeatures, aForceNoOpener, aForceNoReferrer, aIsPopupRequested, + aLoadState, aWindowIsNew, aReturn); +} + +void BrowserChild::DestroyWindow() { + mBrowsingContext = nullptr; + + if (mStatusFilter) { + if (nsCOMPtr webProgress = + do_QueryInterface(WebNavigation())) { + webProgress->RemoveProgressListener(mStatusFilter); + } + + mStatusFilter->RemoveProgressListener(this); + mStatusFilter = nullptr; + } + + if (mCoalescedMouseEventFlusher) { + mCoalescedMouseEventFlusher->RemoveObserver(); + mCoalescedMouseEventFlusher = nullptr; + } + + if (mCoalescedTouchMoveEventFlusher) { + mCoalescedTouchMoveEventFlusher->RemoveObserver(); + mCoalescedTouchMoveEventFlusher = nullptr; + } + + if (mSessionStoreChild) { + mSessionStoreChild->Stop(); + mSessionStoreChild = nullptr; + } + + // In case we don't have chance to process all entries, clean all data in + // the queue. + while (mToBeDispatchedMouseData.GetSize() > 0) { + UniquePtr data( + static_cast(mToBeDispatchedMouseData.PopFront())); + data.reset(); + } + + nsCOMPtr baseWindow = do_QueryInterface(WebNavigation()); + if (baseWindow) baseWindow->Destroy(); + + if (mPuppetWidget) { + mPuppetWidget->Destroy(); + } + + mLayersConnected = Nothing(); + + if (mLayersId.IsValid()) { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + + MOZ_ASSERT(sBrowserChildren); + sBrowserChildren->Remove(uint64_t(mLayersId)); + if (!sBrowserChildren->Count()) { + delete sBrowserChildren; + sBrowserChildren = nullptr; + } + mLayersId = layers::LayersId{0}; + } +} + +void BrowserChild::ActorDestroy(ActorDestroyReason why) { + mIPCOpen = false; + + DestroyWindow(); + + if (mBrowserChildMessageManager) { + // We should have a message manager if the global is alive, but it + // seems sometimes we don't. Assert in aurora/nightly, but don't + // crash in release builds. + MOZ_DIAGNOSTIC_ASSERT(mBrowserChildMessageManager->GetMessageManager()); + if (mBrowserChildMessageManager->GetMessageManager()) { + // The messageManager relays messages via the BrowserChild which + // no longer exists. + mBrowserChildMessageManager->DisconnectMessageManager(); + } + } + + if (GetTabId() != 0) { + NestedBrowserChildMap().erase(GetTabId()); + } +} + +BrowserChild::~BrowserChild() { + mAnonymousGlobalScopes.Clear(); + + DestroyWindow(); + + nsCOMPtr webBrowser = do_QueryInterface(WebNavigation()); + if (webBrowser) { + webBrowser->SetContainerWindow(nullptr); + } + + mozilla::DropJSObjects(this); +} + +mozilla::ipc::IPCResult BrowserChild::RecvWillChangeProcess() { + if (mWebBrowser) { + mWebBrowser->SetWillChangeProcess(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvLoadURL( + nsDocShellLoadState* aLoadState, const ParentShowInfo& aInfo) { + if (!mDidLoadURLInit) { + mDidLoadURLInit = true; + if (!InitBrowserChildMessageManager()) { + return IPC_FAIL_NO_REASON(this); + } + + ApplyParentShowInfo(aInfo); + } + nsAutoCString spec; + aLoadState->URI()->GetSpec(spec); + + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + NS_WARNING("WebNavigation does not have a docshell"); + return IPC_OK(); + } + docShell->LoadURI(aLoadState, true); + + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, spec); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvCreateAboutBlankContentViewer( + nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal) { + if (aPrincipal->GetIsExpandedPrincipal() || + aPartitionedPrincipal->GetIsExpandedPrincipal()) { + return IPC_FAIL(this, "Cannot create document with an expanded principal"); + } + if (aPrincipal->IsSystemPrincipal() || + aPartitionedPrincipal->IsSystemPrincipal()) { + MOZ_ASSERT_UNREACHABLE( + "Cannot use CreateAboutBlankContentViewer to create system principal " + "document in content"); + return IPC_OK(); + } + + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + MOZ_ASSERT_UNREACHABLE("WebNavigation does not have a docshell"); + return IPC_OK(); + } + + nsCOMPtr currentURI; + MOZ_ALWAYS_SUCCEEDS( + WebNavigation()->GetCurrentURI(getter_AddRefs(currentURI))); + if (!currentURI || !NS_IsAboutBlank(currentURI)) { + NS_WARNING("Can't create a ContentViewer unless on about:blank"); + return IPC_OK(); + } + + docShell->CreateAboutBlankContentViewer(aPrincipal, aPartitionedPrincipal, + nullptr); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvResumeLoad( + const uint64_t& aPendingSwitchID, const ParentShowInfo& aInfo) { + if (!mDidLoadURLInit) { + mDidLoadURLInit = true; + if (!InitBrowserChildMessageManager()) { + return IPC_FAIL_NO_REASON(this); + } + + ApplyParentShowInfo(aInfo); + } + + nsresult rv = WebNavigation()->ResumeRedirectedLoad(aPendingSwitchID, -1); + if (NS_FAILED(rv)) { + NS_WARNING("WebNavigation()->ResumeRedirectedLoad failed"); + } + + return IPC_OK(); +} + +nsresult BrowserChild::CloneDocumentTreeIntoSelf( + const MaybeDiscarded& aSourceBC, + const embedding::PrintData& aPrintData) { +#ifdef NS_PRINTING + if (NS_WARN_IF(aSourceBC.IsNullOrDiscarded())) { + return NS_ERROR_FAILURE; + } + nsCOMPtr sourceDocument = aSourceBC.get()->GetDocument(); + if (NS_WARN_IF(!sourceDocument)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr cv; + ourDocShell->GetContentViewer(getter_AddRefs(cv)); + if (NS_WARN_IF(!cv)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr printSettings; + nsresult rv = + printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + + RefPtr clone; + { + AutoPrintEventDispatcher dispatcher(*sourceDocument); + nsAutoScriptBlocker scriptBlocker; + bool hasInProcessCallbacks = false; + clone = sourceDocument->CreateStaticClone(ourDocShell, cv, printSettings, + &hasInProcessCallbacks); + if (NS_WARN_IF(!clone)) { + return NS_ERROR_FAILURE; + } + } + + rv = UpdateRemotePrintSettings(aPrintData); + if (NS_FAILED(rv)) { + return rv; + } + +#endif + return NS_OK; +} + +mozilla::ipc::IPCResult BrowserChild::RecvCloneDocumentTreeIntoSelf( + const MaybeDiscarded& aSourceBC, + const embedding::PrintData& aPrintData, + CloneDocumentTreeIntoSelfResolver&& aResolve) { + nsresult rv = NS_OK; + +#ifdef NS_PRINTING + rv = CloneDocumentTreeIntoSelf(aSourceBC, aPrintData); +#endif + + aResolve(NS_SUCCEEDED(rv)); + return IPC_OK(); +} + +nsresult BrowserChild::UpdateRemotePrintSettings( + const embedding::PrintData& aPrintData) { +#ifdef NS_PRINTING + nsCOMPtr ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return NS_ERROR_FAILURE; + } + + RefPtr doc = ourDocShell->GetExtantDocument(); + if (NS_WARN_IF(!doc) || NS_WARN_IF(!doc->IsStaticDocument())) { + return NS_ERROR_FAILURE; + } + + RefPtr bc = ourDocShell->GetBrowsingContext(); + if (NS_WARN_IF(!bc)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr printSettings; + nsresult rv = + printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + + bc->PreOrderWalk([&](BrowsingContext* aBc) { + if (nsCOMPtr inProcess = aBc->GetDocShell()) { + nsCOMPtr cv; + inProcess->GetContentViewer(getter_AddRefs(cv)); + if (NS_WARN_IF(!cv)) { + return BrowsingContext::WalkFlag::Skip; + } + // The CanRunScript analysis is not smart enough to see across + // the std::function PreOrderWalk uses, so we cheat a bit here, but it is + // fine because PreOrderWalk does deal with arbitrary script changing the + // BC tree, and our code above is simple enough and keeps strong refs to + // everything. + ([&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { + RefPtr printJob = + static_cast( + aPrintData.remotePrintJob().AsChild()); + cv->SetPrintSettingsForSubdocument(printSettings, printJob); + }()); + } else if (RefPtr remoteChild = + BrowserBridgeChild::GetFrom(aBc->GetEmbedderElement())) { + Unused << remoteChild->SendUpdateRemotePrintSettings(aPrintData); + return BrowsingContext::WalkFlag::Skip; + } + return BrowsingContext::WalkFlag::Next; + }); +#endif + + return NS_OK; +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateRemotePrintSettings( + const embedding::PrintData& aPrintData) { +#ifdef NS_PRINTING + UpdateRemotePrintSettings(aPrintData); +#endif + + return IPC_OK(); +} + +void BrowserChild::DoFakeShow(const ParentShowInfo& aParentShowInfo) { + OwnerShowInfo ownerInfo{ScreenIntSize(), ScrollbarPreference::Auto, + nsSizeMode_Normal}; + RecvShow(aParentShowInfo, ownerInfo); + mDidFakeShow = true; +} + +void BrowserChild::ApplyParentShowInfo(const ParentShowInfo& aInfo) { + // Even if we already set real show info, the dpi / rounding & scale may still + // be invalid (if BrowserParent wasn't able to get widget it would just send + // 0). So better to always set up-to-date values here. + if (aInfo.dpi() > 0) { + mPuppetWidget->UpdateBackingScaleCache(aInfo.dpi(), aInfo.widgetRounding(), + aInfo.defaultScale()); + } + + if (mDidSetRealShowInfo) { + return; + } + + if (!aInfo.fakeShowInfo()) { + // Once we've got one ShowInfo from parent, no need to update the values + // anymore. + mDidSetRealShowInfo = true; + } + + mIsTransparent = aInfo.isTransparent(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvShow( + const ParentShowInfo& aParentInfo, const OwnerShowInfo& aOwnerInfo) { + bool res = true; + + mPuppetWidget->SetSizeMode(aOwnerInfo.sizeMode()); + if (!mDidFakeShow) { + nsCOMPtr baseWindow = do_QueryInterface(WebNavigation()); + if (!baseWindow) { + NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow"); + return IPC_FAIL_NO_REASON(this); + } + + baseWindow->SetVisibility(true); + res = InitBrowserChildMessageManager(); + } + + ApplyParentShowInfo(aParentInfo); + + if (!mIsTopLevel) { + RecvScrollbarPreferenceChanged(aOwnerInfo.scrollbarPreference()); + } + + if (!res) { + return IPC_FAIL_NO_REASON(this); + } + + UpdateVisibility(); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvInitRendering( + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const layers::LayersId& aLayersId, + const CompositorOptions& aCompositorOptions, const bool& aLayersConnected) { + mLayersConnected = Some(aLayersConnected); + mLayersConnectRequested = Some(aLayersConnected); + InitRenderingState(aTextureFactoryIdentifier, aLayersId, aCompositorOptions); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvScrollbarPreferenceChanged( + ScrollbarPreference aPreference) { + MOZ_ASSERT(!mIsTopLevel, + "Scrollbar visibility should be derived from chrome flags for " + "top-level windows"); + if (nsCOMPtr docShell = do_GetInterface(WebNavigation())) { + nsDocShell::Cast(docShell)->SetScrollbarPreference(aPreference); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvCompositorOptionsChanged( + const CompositorOptions& aNewOptions) { + MOZ_ASSERT(mCompositorOptions); + + // The only compositor option we currently support changing is APZ + // enablement. Even that is only partially supported for now: + // * Going from APZ to non-APZ is fine - we just flip the stored flag. + // Note that we keep the actors (mApzcTreeManager, and the APZChild + // created in InitAPZState()) around (read on for why). + // * Going from non-APZ to APZ is only supported if we were using + // APZ initially (at InitRendering() time) and we are transitioning + // back. In this case, we just reuse the actors which we kept around. + // Fully supporting a non-APZ to APZ transition (i.e. even in cases + // where we initialized as non-APZ) would require setting up the actors + // here. (In that case, we would also have the options of destroying + // the actors in the APZ --> non-APZ case, and always re-creating them + // during a non-APZ --> APZ transition). + mCompositorOptions->SetUseAPZ(aNewOptions.UseAPZ()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions( + const DimensionInfo& aDimensionInfo) { + if (mLayersConnected.isNothing()) { + return IPC_OK(); + } + + mUnscaledOuterRect = aDimensionInfo.rect(); + mClientOffset = aDimensionInfo.clientOffset(); + mChromeOffset = aDimensionInfo.chromeOffset(); + MOZ_ASSERT_IF(!IsTopLevel(), mChromeOffset == LayoutDeviceIntPoint()); + + SetUnscaledInnerSize(aDimensionInfo.size()); + if (!mHasValidInnerSize && aDimensionInfo.size().width != 0 && + aDimensionInfo.size().height != 0) { + mHasValidInnerSize = true; + } + + ScreenIntSize screenSize = GetInnerSize(); + ScreenIntRect screenRect = GetOuterRect(); + + // Make sure to set the size on the document viewer first. The + // MobileViewportManager needs the content viewer size to be updated before + // the reflow, otherwise it gets a stale size when it computes a new CSS + // viewport. + nsCOMPtr baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + nsIBaseWindow::eRepaint); + + mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x, + screenRect.y + mClientOffset.y + mChromeOffset.y, + screenSize.width, screenSize.height, true); + + RecvSafeAreaInsetsChanged(mPuppetWidget->GetSafeAreaInsets()); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSizeModeChanged( + const nsSizeMode& aSizeMode) { + mPuppetWidget->SetSizeMode(aSizeMode); + if (!mPuppetWidget->IsVisible()) { + return IPC_OK(); + } + nsCOMPtr document(GetTopLevelDocument()); + if (!document) { + return IPC_OK(); + } + nsPresContext* presContext = document->GetPresContext(); + if (presContext) { + presContext->SizeModeChanged(aSizeMode); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvChildToParentMatrix( + const Maybe& aMatrix, + const ScreenRect& aTopLevelViewportVisibleRectInBrowserCoords) { + mChildToParentConversionMatrix = + LayoutDeviceToLayoutDeviceMatrix4x4::FromUnknownMatrix(aMatrix); + mTopLevelViewportVisibleRectInBrowserCoords = + aTopLevelViewportVisibleRectInBrowserCoords; + + if (mContentTransformPromise) { + mContentTransformPromise->MaybeResolveWithUndefined(); + mContentTransformPromise = nullptr; + } + + // Trigger an intersection observation update since ancestor viewports + // changed. + if (RefPtr toplevelDoc = GetTopLevelDocument()) { + if (nsPresContext* pc = toplevelDoc->GetPresContext()) { + pc->RefreshDriver()->EnsureIntersectionObservationsUpdateHappens(); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateRemoteStyle( + const StyleImageRendering& aImageRendering) { + BrowsingContext* context = GetBrowsingContext(); + if (!context) { + return IPC_OK(); + } + + Document* document = context->GetDocument(); + if (!document) { + return IPC_OK(); + } + + if (document->IsImageDocument()) { + document->AsImageDocument()->UpdateRemoteStyle(aImageRendering); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarMaxHeightChanged( + const ScreenIntCoord& aHeight) { +#if defined(MOZ_WIDGET_ANDROID) + mDynamicToolbarMaxHeight = aHeight; + + RefPtr document = GetTopLevelDocument(); + if (!document) { + return IPC_OK(); + } + + if (RefPtr presContext = document->GetPresContext()) { + presContext->SetDynamicToolbarMaxHeight(aHeight); + } +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarOffsetChanged( + const ScreenIntCoord& aOffset) { +#if defined(MOZ_WIDGET_ANDROID) + RefPtr document = GetTopLevelDocument(); + if (!document) { + return IPC_OK(); + } + + if (nsPresContext* presContext = document->GetPresContext()) { + presContext->UpdateDynamicToolbarOffset(aOffset); + } +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSuppressDisplayport( + const bool& aEnabled) { + if (RefPtr presShell = GetTopLevelPresShell()) { + presShell->SuppressDisplayport(aEnabled); + } + return IPC_OK(); +} + +void BrowserChild::HandleDoubleTap(const CSSPoint& aPoint, + const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid) { + MOZ_LOG( + sApzChildLog, LogLevel::Debug, + ("Handling double tap at %s with %p %p\n", ToString(aPoint).c_str(), + mBrowserChildMessageManager ? mBrowserChildMessageManager->GetWrapper() + : nullptr, + mBrowserChildMessageManager.get())); + + if (!mBrowserChildMessageManager) { + return; + } + + // Note: there is nothing to do with the modifiers here, as we are not + // synthesizing any sort of mouse event. + RefPtr document = GetTopLevelDocument(); + ZoomTarget zoomTarget = CalculateRectToZoomTo(document, aPoint); + // The double-tap can be dispatched by any scroll frame (so |aGuid| could be + // the guid of any scroll frame), but the zoom-to-rect operation must be + // performed by the root content scroll frame, so query its identifiers + // for the SendZoomToRect() call rather than using the ones from |aGuid|. + uint32_t presShellId; + ViewID viewId; + if (APZCCallbackHelper::GetOrCreateScrollIdentifiers( + document->GetDocumentElement(), &presShellId, &viewId) && + mApzcTreeManager) { + ScrollableLayerGuid guid(mLayersId, presShellId, viewId); + + mApzcTreeManager->ZoomToRect(guid, zoomTarget, + ZoomToRectBehavior::DEFAULT_BEHAVIOR); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvHandleTap( + const GeckoContentController::TapType& aType, + const LayoutDevicePoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) { + // IPDL doesn't hold a strong reference to protocols as they're not required + // to be refcounted. This function can run script, which may trigger a nested + // event loop, which may release this, so we hold a strong reference here. + RefPtr kungFuDeathGrip(this); + RefPtr presShell = GetTopLevelPresShell(); + if (!presShell) { + return IPC_OK(); + } + if (!presShell->GetPresContext()) { + return IPC_OK(); + } + CSSToLayoutDeviceScale scale( + presShell->GetPresContext()->CSSToDevPixelScale()); + CSSPoint point = aPoint / scale; + + // Stash the guid in InputAPZContext so that when the visual-to-layout + // transform is applied to the event's coordinates, we use the right transform + // based on the scroll frame being targeted. + // The other values don't really matter. + InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel); + + switch (aType) { + case GeckoContentController::TapType::eSingleTap: + if (mBrowserChildMessageManager) { + mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1, + aInputBlockId); + } + break; + case GeckoContentController::TapType::eDoubleTap: + HandleDoubleTap(point, aModifiers, aGuid); + break; + case GeckoContentController::TapType::eSecondTap: + if (mBrowserChildMessageManager) { + mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2, + aInputBlockId); + } + break; + case GeckoContentController::TapType::eLongTap: + if (mBrowserChildMessageManager) { + RefPtr eventState(mAPZEventState); + eventState->ProcessLongTap(presShell, point, scale, aModifiers, + aInputBlockId); + } + break; + case GeckoContentController::TapType::eLongTapUp: + if (mBrowserChildMessageManager) { + RefPtr eventState(mAPZEventState); + eventState->ProcessLongTapUp(presShell, point, scale, aModifiers); + } + break; + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityHandleTap( + const GeckoContentController::TapType& aType, + const LayoutDevicePoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) { + // IPDL doesn't hold a strong reference to protocols as they're not required + // to be refcounted. This function can run script, which may trigger a nested + // event loop, which may release this, so we hold a strong reference here. + RefPtr kungFuDeathGrip(this); + return RecvHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId); +} + +bool BrowserChild::NotifyAPZStateChange( + const ViewID& aViewId, + const layers::GeckoContentController::APZStateChange& aChange, + const int& aArg, Maybe aInputBlockId) { + mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg, aInputBlockId); + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (aChange == + layers::GeckoContentController::APZStateChange::eTransformEnd) { + // This is used by tests to determine when the APZ is done doing whatever + // it's doing. XXX generify this as needed when writing additional tests. + observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr); + observerService->NotifyObservers(nullptr, "PanZoom:StateChange", + u"NOTHING"); + } else if (aChange == + layers::GeckoContentController::APZStateChange::eTransformBegin) { + observerService->NotifyObservers(nullptr, "PanZoom:StateChange", + u"PANNING"); + } + return true; +} + +void BrowserChild::StartScrollbarDrag( + const layers::AsyncDragMetrics& aDragMetrics) { + ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId, + aDragMetrics.mViewId); + + if (mApzcTreeManager) { + mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics); + } +} + +void BrowserChild::ZoomToRect(const uint32_t& aPresShellId, + const ScrollableLayerGuid::ViewID& aViewId, + const CSSRect& aRect, const uint32_t& aFlags) { + ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId); + + if (mApzcTreeManager) { + mApzcTreeManager->ZoomToRect(guid, ZoomTarget{aRect}, aFlags); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvActivate(uint64_t aActionId) { + MOZ_ASSERT(mWebBrowser); + mWebBrowser->FocusActivate(aActionId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvDeactivate(uint64_t aActionId) { + MOZ_ASSERT(mWebBrowser); + mWebBrowser->FocusDeactivate(aActionId); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvStopIMEStateManagement() { + IMEStateManager::StopIMEStateManagement(); + return IPC_OK(); +} + +void BrowserChild::ProcessPendingCoalescedTouchData() { + MOZ_ASSERT(StaticPrefs::dom_events_coalesce_touchmove()); + + if (mCoalescedTouchData.IsEmpty()) { + return; + } + + if (mCoalescedTouchMoveEventFlusher) { + mCoalescedTouchMoveEventFlusher->RemoveObserver(); + } + + UniquePtr touchMoveEvent = + mCoalescedTouchData.TakeCoalescedEvent(); + Unused << RecvRealTouchEvent(*touchMoveEvent, + mCoalescedTouchData.GetScrollableLayerGuid(), + mCoalescedTouchData.GetInputBlockId(), + mCoalescedTouchData.GetApzResponse()); +} + +void BrowserChild::ProcessPendingCoalescedMouseDataAndDispatchEvents() { + if (!mCoalesceMouseMoveEvents || !mCoalescedMouseEventFlusher) { + // We don't enable mouse coalescing or we are destroying BrowserChild. + return; + } + + // We may reentry the event loop and push more data to + // mToBeDispatchedMouseData while dispatching an event. + + // We may have some pending coalesced data while dispatch an event and reentry + // the event loop. In that case we don't have chance to consume the remainding + // pending data until we get new mouse events. Get some helps from + // mCoalescedMouseEventFlusher to trigger it. + mCoalescedMouseEventFlusher->StartObserver(); + + while (mToBeDispatchedMouseData.GetSize() > 0) { + UniquePtr data( + static_cast(mToBeDispatchedMouseData.PopFront())); + + UniquePtr event = data->TakeCoalescedEvent(); + if (event) { + // Dispatch the pending events. Using HandleRealMouseButtonEvent + // to bypass the coalesce handling in RecvRealMouseMoveEvent. Can't use + // RecvRealMouseButtonEvent because we may also put some mouse events + // other than mousemove. + HandleRealMouseButtonEvent(*event, data->GetScrollableLayerGuid(), + data->GetInputBlockId()); + } + } + // mCoalescedMouseEventFlusher may be destroyed when reentrying the event + // loop. + if (mCoalescedMouseEventFlusher) { + mCoalescedMouseEventFlusher->RemoveObserver(); + } +} + +LayoutDeviceToLayoutDeviceMatrix4x4 +BrowserChild::GetChildToParentConversionMatrix() const { + if (mChildToParentConversionMatrix) { + return *mChildToParentConversionMatrix; + } + LayoutDevicePoint offset(GetChromeOffset()); + return LayoutDeviceToLayoutDeviceMatrix4x4::Translation(offset); +} + +Maybe BrowserChild::GetTopLevelViewportVisibleRectInBrowserCoords() + const { + if (!mChildToParentConversionMatrix) { + return Nothing(); + } + return Some(mTopLevelViewportVisibleRectInBrowserCoords); +} + +void BrowserChild::FlushAllCoalescedMouseData() { + MOZ_ASSERT(mCoalesceMouseMoveEvents); + + // Move all entries from mCoalescedMouseData to mToBeDispatchedMouseData. + for (const auto& data : mCoalescedMouseData.Values()) { + if (!data || data->IsEmpty()) { + continue; + } + UniquePtr dispatchData = + MakeUnique(); + + dispatchData->RetrieveDataFrom(*data); + mToBeDispatchedMouseData.Push(dispatchData.release()); + } + mCoalescedMouseData.Clear(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher) { + CoalescedMouseData* data = + mCoalescedMouseData.GetOrInsertNew(aEvent.pointerId); + MOZ_ASSERT(data); + if (data->CanCoalesce(aEvent, aGuid, aInputBlockId)) { + data->Coalesce(aEvent, aGuid, aInputBlockId); + mCoalescedMouseEventFlusher->StartObserver(); + return IPC_OK(); + } + // Can't coalesce current mousemove event. Put the coalesced mousemove data + // with the same pointer id to mToBeDispatchedMouseData, coalesce the + // current one, and process all pending data in mToBeDispatchedMouseData. + UniquePtr dispatchData = + MakeUnique(); + + dispatchData->RetrieveDataFrom(*data); + mToBeDispatchedMouseData.Push(dispatchData.release()); + + // Put new data to replace the old one in the hash table. + CoalescedMouseData* newData = + mCoalescedMouseData + .InsertOrUpdate(aEvent.pointerId, MakeUnique()) + .get(); + newData->Coalesce(aEvent, aGuid, aInputBlockId); + + // Dispatch all pending mouse events. + ProcessPendingCoalescedMouseDataAndDispatchEvents(); + mCoalescedMouseEventFlusher->StartObserver(); + } else if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEventForTests( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseMoveEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult +BrowserChild::RecvNormalPriorityRealMouseMoveEventForTests( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSynthMouseMoveEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySynthMouseMoveEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvSynthMouseMoveEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealMouseButtonEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher && + aEvent.mMessage != eMouseMove) { + // When receiving a mouse event other than mousemove, we have to dispatch + // all coalesced events before it. However, we can't dispatch all pending + // coalesced events directly because we may reentry the event loop while + // dispatching. To make sure we won't dispatch disorder events, we move all + // coalesced mousemove events and current event to a deque to dispatch them. + // When reentrying the event loop and dispatching more events, we put new + // events in the end of the nsQueue and dispatch events from the beginning. + FlushAllCoalescedMouseData(); + + UniquePtr dispatchData = + MakeUnique(); + + dispatchData->Coalesce(aEvent, aGuid, aInputBlockId); + mToBeDispatchedMouseData.Push(dispatchData.release()); + + ProcessPendingCoalescedMouseDataAndDispatchEvents(); + return IPC_OK(); + } + HandleRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); + return IPC_OK(); +} + +void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + WidgetMouseEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + // We need one InputAPZContext here to propagate |aGuid| to places in + // SendSetTargetAPZCNotification() which apply the visual-to-layout transform, + // and another below to propagate the |postLayerization| flag (whose value + // we don't know until SendSetTargetAPZCNotification() returns) into + // the event dispatch code. + InputAPZContext context1(aGuid, aInputBlockId, nsEventStatus_eSentinel); + + // Mouse events like eMouseEnterIntoWidget, that are created in the parent + // process EventStateManager code, have an input block id which they get from + // the InputAPZContext in the parent process stack. However, they did not + // actually go through the APZ code and so their mHandledByAPZ flag is false. + // Since thos events didn't go through APZ, we don't need to send + // notifications for them. + RefPtr postLayerization; + if (aInputBlockId && localEvent.mFlags.mHandledByAPZ) { + nsCOMPtr document(GetTopLevelDocument()); + postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, localEvent, aGuid.mLayersId, aInputBlockId); + } + + InputAPZContext context2(aGuid, aInputBlockId, nsEventStatus_eSentinel, + postLayerization != nullptr); + + DispatchWidgetEventViaAPZ(localEvent); + + if (aInputBlockId && localEvent.mFlags.mHandledByAPZ) { + mAPZEventState->ProcessMouseEvent(localEvent, aInputBlockId); + } + + // Do this after the DispatchWidgetEventViaAPZ call above, so that if the + // mouse event triggered a post-refresh AsyncDragMetrics message to be sent + // to APZ (from scrollbar dragging in nsSliderFrame), then that will reach + // APZ before the SetTargetAPZC message. This ensures the drag input block + // gets the drag metrics before handling the input events. + if (postLayerization) { + postLayerization->Register(); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseButtonEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealMouseEnterExitWidgetEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult +BrowserChild::RecvNormalPriorityRealMouseEnterExitWidgetEvent( + const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId); +} + +nsEventStatus BrowserChild::DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent) { + aEvent.ResetWaitingReplyFromRemoteProcessState(); + return APZCCallbackHelper::DispatchWidgetEvent(aEvent); +} + +void BrowserChild::DispatchCoalescedWheelEvent() { + UniquePtr wheelEvent = + mCoalescedWheelData.TakeCoalescedEvent(); + MOZ_ASSERT(wheelEvent); + DispatchWheelEvent(*wheelEvent, mCoalescedWheelData.GetScrollableLayerGuid(), + mCoalescedWheelData.GetInputBlockId()); +} + +void BrowserChild::DispatchWheelEvent(const WidgetWheelEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + WidgetWheelEvent localEvent(aEvent); + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + nsCOMPtr document(GetTopLevelDocument()); + RefPtr postLayerization = + APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, aEvent, aGuid.mLayersId, aInputBlockId); + if (postLayerization) { + postLayerization->Register(); + } + } + + localEvent.mWidget = mPuppetWidget; + + // Stash the guid in InputAPZContext so that when the visual-to-layout + // transform is applied to the event's coordinates, we use the right transform + // based on the scroll frame being targeted. + // The other values don't really matter. + InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel); + + DispatchWidgetEventViaAPZ(localEvent); + + if (localEvent.mCanTriggerSwipe) { + SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe()); + } + + if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) { + mAPZEventState->ProcessWheelEvent(localEvent, aInputBlockId); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvMouseWheelEvent( + const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + bool isNextWheelEvent = false; + // We only coalesce the current event when + // 1. It's eWheel (we don't coalesce eOperationStart and eWheelOperationEnd) + // 2. It has same attributes as the coalesced wheel event which is not yet + // fired. + if (aEvent.mMessage == eWheel) { + GetIPCChannel()->PeekMessages( + [&isNextWheelEvent](const IPC::Message& aMsg) -> bool { + if (aMsg.type() == mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID) { + isNextWheelEvent = true; + } + return false; // Stop peeking. + }); + + if (!mCoalescedWheelData.IsEmpty() && + !mCoalescedWheelData.CanCoalesce(aEvent, aGuid, aInputBlockId)) { + DispatchCoalescedWheelEvent(); + MOZ_ASSERT(mCoalescedWheelData.IsEmpty()); + } + mCoalescedWheelData.Coalesce(aEvent, aGuid, aInputBlockId); + + MOZ_ASSERT(!mCoalescedWheelData.IsEmpty()); + // If the next event isn't a wheel event, make sure we dispatch. + if (!isNextWheelEvent) { + DispatchCoalescedWheelEvent(); + } + } else { + DispatchWheelEvent(aEvent, aGuid, aInputBlockId); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityMouseWheelEvent( + const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + return RecvMouseWheelEvent(aEvent, aGuid, aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealTouchEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + MOZ_LOG(sApzChildLog, LogLevel::Debug, + ("Receiving touch event of type %d\n", aEvent.mMessage)); + + if (StaticPrefs::dom_events_coalesce_touchmove()) { + if (aEvent.mMessage == eTouchEnd || aEvent.mMessage == eTouchStart) { + ProcessPendingCoalescedTouchData(); + } + + if (aEvent.mMessage != eTouchMove) { + sConsecutiveTouchMoveCount = 0; + } + } + + WidgetTouchEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + // Stash the guid in InputAPZContext so that when the visual-to-layout + // transform is applied to the event's coordinates, we use the right transform + // based on the scroll frame being targeted. + // The other values don't really matter. + InputAPZContext context(aGuid, aInputBlockId, aApzResponse); + + nsTArray allowedTouchBehaviors; + if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) { + nsCOMPtr document = GetTopLevelDocument(); + allowedTouchBehaviors = TouchActionHelper::GetAllowedTouchBehavior( + mPuppetWidget, document, localEvent); + if (!allowedTouchBehaviors.IsEmpty() && mApzcTreeManager) { + mApzcTreeManager->SetAllowedTouchBehavior(aInputBlockId, + allowedTouchBehaviors); + } + RefPtr postLayerization = + APZCCallbackHelper::SendSetTargetAPZCNotification( + mPuppetWidget, document, localEvent, aGuid.mLayersId, + aInputBlockId); + if (postLayerization) { + postLayerization->Register(); + } + } + + // Dispatch event to content (potentially a long-running operation) + nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent); + + if (!AsyncPanZoomEnabled()) { + // We shouldn't have any e10s platforms that have touch events enabled + // without APZ. + MOZ_ASSERT(false); + return IPC_OK(); + } + + mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId, + aApzResponse, status, + std::move(allowedTouchBehaviors)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealTouchMoveEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + if (StaticPrefs::dom_events_coalesce_touchmove()) { + ++sConsecutiveTouchMoveCount; + if (mCoalescedTouchMoveEventFlusher) { + MOZ_ASSERT(aEvent.mMessage == eTouchMove); + if (mCoalescedTouchData.IsEmpty() || + mCoalescedTouchData.CanCoalesce(aEvent, aGuid, aInputBlockId, + aApzResponse)) { + mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId, + aApzResponse); + } else { + UniquePtr touchMoveEvent = + mCoalescedTouchData.TakeCoalescedEvent(); + + mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId, + aApzResponse); + + if (!RecvRealTouchEvent(*touchMoveEvent, + mCoalescedTouchData.GetScrollableLayerGuid(), + mCoalescedTouchData.GetInputBlockId(), + mCoalescedTouchData.GetApzResponse())) { + return IPC_FAIL_NO_REASON(this); + } + } + + if (sConsecutiveTouchMoveCount > 1) { + mCoalescedTouchMoveEventFlusher->StartObserver(); + } else { + // Flush the pending coalesced touch in order to avoid the first + // touchmove be overridden by the second one. + ProcessPendingCoalescedTouchData(); + } + return IPC_OK(); + } + } + + if (!RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchMoveEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + return RecvRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, aApzResponse); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealDragEvent( + const WidgetDragEvent& aEvent, const uint32_t& aDragAction, + const uint32_t& aDropEffect, nsIPrincipal* aPrincipal, + nsIContentSecurityPolicy* aCsp) { + WidgetDragEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + + nsCOMPtr dragSession = nsContentUtils::GetDragSession(); + if (dragSession) { + dragSession->SetDragAction(aDragAction); + dragSession->SetTriggeringPrincipal(aPrincipal); + dragSession->SetCsp(aCsp); + RefPtr initialDataTransfer = dragSession->GetDataTransfer(); + if (initialDataTransfer) { + initialDataTransfer->SetDropEffectInt(aDropEffect); + } + } + + if (aEvent.mMessage == eDrop) { + bool canDrop = true; + if (!dragSession || NS_FAILED(dragSession->GetCanDrop(&canDrop)) || + !canDrop) { + localEvent.mMessage = eDragExit; + } + } else if (aEvent.mMessage == eDragOver) { + nsCOMPtr dragService = + do_GetService("@mozilla.org/widget/dragservice;1"); + if (dragService) { + // This will dispatch 'drag' event at the source if the + // drag transaction started in this process. + dragService->FireDragEventAtSource(eDrag, aEvent.mModifiers); + } + } + + DispatchWidgetEventViaAPZ(localEvent); + return IPC_OK(); +} + +void BrowserChild::RequestEditCommands(NativeKeyBindingsType aType, + const WidgetKeyboardEvent& aEvent, + nsTArray& aCommands) { + MOZ_ASSERT(aCommands.IsEmpty()); + + if (NS_WARN_IF(aEvent.IsEditCommandsInitialized(aType))) { + aCommands = aEvent.EditCommandsConstRef(aType).Clone(); + return; + } + + switch (aType) { + case NativeKeyBindingsType::SingleLineEditor: + case NativeKeyBindingsType::MultiLineEditor: + case NativeKeyBindingsType::RichTextEditor: + break; + default: + MOZ_ASSERT_UNREACHABLE("Invalid native key bindings type"); + } + + // Don't send aEvent to the parent process directly because it'll be marked + // as posted to remote process. + WidgetKeyboardEvent localEvent(aEvent); + SendRequestNativeKeyBindings(static_cast(aType), localEvent, + &aCommands); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNativeSynthesisResponse( + const uint64_t& aObserverId, const nsCString& aResponse) { + mozilla::widget::AutoObserverNotifier::NotifySavedObserver(aObserverId, + aResponse.get()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory() { + if (mSessionStoreChild) { + mSessionStoreChild->UpdateSHistoryChanges(); + } + return IPC_OK(); +} + +// In case handling repeated keys takes much time, we skip firing new ones. +bool BrowserChild::SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent) { + if (mRepeatedKeyEventTime.IsNull() || !aEvent.CanSkipInRemoteProcess() || + (aEvent.mMessage != eKeyDown && aEvent.mMessage != eKeyPress)) { + mRepeatedKeyEventTime = TimeStamp(); + mSkipKeyPress = false; + return false; + } + + if ((aEvent.mMessage == eKeyDown && + (mRepeatedKeyEventTime > aEvent.mTimeStamp)) || + (mSkipKeyPress && (aEvent.mMessage == eKeyPress))) { + // If we skip a keydown event, also the following keypress events should be + // skipped. + mSkipKeyPress |= aEvent.mMessage == eKeyDown; + return true; + } + + if (aEvent.mMessage == eKeyDown) { + // If keydown wasn't skipped, nor should the possible following keypress. + mRepeatedKeyEventTime = TimeStamp(); + mSkipKeyPress = false; + } + return false; +} + +void BrowserChild::UpdateRepeatedKeyEventEndTime( + const WidgetKeyboardEvent& aEvent) { + if (aEvent.mIsRepeat && + (aEvent.mMessage == eKeyDown || aEvent.mMessage == eKeyPress)) { + mRepeatedKeyEventTime = TimeStamp::Now(); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvRealKeyEvent( + const WidgetKeyboardEvent& aEvent, const nsID& aUUID) { + MOZ_ASSERT_IF(aEvent.mMessage == eKeyPress, + aEvent.AreAllEditCommandsInitialized()); + + // If content code called preventDefault() on a keydown event, then we don't + // want to process any following keypress events. + const bool isPrecedingKeyDownEventConsumed = + aEvent.mMessage == eKeyPress && mIgnoreKeyPressEvent; + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + localEvent.mUniqueId = aEvent.mUniqueId; + + if (!SkipRepeatedKeyEvent(aEvent) && !isPrecedingKeyDownEventConsumed) { + nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent); + + // Update the end time of the possible repeated event so that we can skip + // some incoming events in case event handling took long time. + UpdateRepeatedKeyEventEndTime(localEvent); + + if (aEvent.mMessage == eKeyDown) { + mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault; + } + + if (localEvent.mFlags.mIsSuppressedOrDelayed) { + localEvent.PreventDefault(); + } + + // If the event's default isn't prevented but the status is no default, + // That means that the event was consumed by EventStateManager or something + // which is not a usual event handler. In such case, prevent its default + // as a default handler. For example, when an eKeyPress event matches + // with a content accesskey, and it's executed, preventDefault() of the + // event won't be called but the status is set to "no default". Then, + // the event shouldn't be handled by nsMenuBarListener in the main process. + if (!localEvent.DefaultPrevented() && + status == nsEventStatus_eConsumeNoDefault) { + localEvent.PreventDefault(); + } + + MOZ_DIAGNOSTIC_ASSERT(!localEvent.PropagationStopped()); + } + // The keyboard event which we ignore should not be handled in the main + // process for shortcut key handling. For notifying if we skipped it, we can + // use "stop propagation" flag here because it must be cleared by + // `EventTargetChainItem` if we've dispatched it. + else { + localEvent.StopPropagation(); + } + + // If we don't need to send a rely for the given keyboard event, we do nothing + // anymore here. + if (!aEvent.WantReplyFromContentProcess()) { + return IPC_OK(); + } + + // This is an ugly hack, mNoRemoteProcessDispatch is set to true when the + // event's PreventDefault() or StopScrollProcessForwarding() is called. + // And then, it'll be checked by ParamTraits::Write() + // whether the event is being sent to remote process unexpectedly. + // However, unfortunately, it cannot check the destination. Therefore, + // we need to clear the flag explicitly here because ParamTraits should + // keep checking the flag for avoiding regression. + localEvent.mFlags.mNoRemoteProcessDispatch = false; + SendReplyKeyEvent(localEvent, aUUID); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealKeyEvent( + const WidgetKeyboardEvent& aEvent, const nsID& aUUID) { + return RecvRealKeyEvent(aEvent, aUUID); +} + +mozilla::ipc::IPCResult BrowserChild::RecvCompositionEvent( + const WidgetCompositionEvent& aEvent) { + WidgetCompositionEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + DispatchWidgetEventViaAPZ(localEvent); + Unused << SendOnEventNeedingAckHandled(aEvent.mMessage); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityCompositionEvent( + const WidgetCompositionEvent& aEvent) { + return RecvCompositionEvent(aEvent); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSelectionEvent( + const WidgetSelectionEvent& aEvent) { + WidgetSelectionEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + DispatchWidgetEventViaAPZ(localEvent); + Unused << SendOnEventNeedingAckHandled(aEvent.mMessage); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySelectionEvent( + const WidgetSelectionEvent& aEvent) { + return RecvSelectionEvent(aEvent); +} + +mozilla::ipc::IPCResult BrowserChild::RecvInsertText( + const nsAString& aStringToInsert) { + // Use normal event path to reach focused document. + WidgetContentCommandEvent localEvent(true, eContentCommandInsertText, + mPuppetWidget); + localEvent.mString = Some(nsString(aStringToInsert)); + DispatchWidgetEventViaAPZ(localEvent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityInsertText( + const nsAString& aStringToInsert) { + return RecvInsertText(aStringToInsert); +} + +mozilla::ipc::IPCResult BrowserChild::RecvPasteTransferable( + const IPCTransferableData& aTransferableData, const bool& aIsPrivateData, + nsIPrincipal* aRequestingPrincipal, + const nsContentPolicyType& aContentPolicyType) { + nsresult rv; + nsCOMPtr trans = + do_CreateInstance("@mozilla.org/widget/transferable;1", &rv); + NS_ENSURE_SUCCESS(rv, IPC_OK()); + trans->Init(nullptr); + + rv = nsContentUtils::IPCTransferableDataToTransferable( + aTransferableData, aIsPrivateData, aRequestingPrincipal, + aContentPolicyType, true /* aAddDataFlavor */, trans, + false /* aFilterUnknownFlavors */); + NS_ENSURE_SUCCESS(rv, IPC_OK()); + + nsCOMPtr ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return IPC_OK(); + } + + RefPtr params = new nsCommandParams(); + rv = params->SetISupports("transferable", trans); + NS_ENSURE_SUCCESS(rv, IPC_OK()); + + ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params); + return IPC_OK(); +} + +#ifdef ACCESSIBILITY +a11y::PDocAccessibleChild* BrowserChild::AllocPDocAccessibleChild( + PDocAccessibleChild*, const uint64_t&, + const MaybeDiscardedBrowsingContext&) { + MOZ_ASSERT(false, "should never call this!"); + return nullptr; +} + +bool BrowserChild::DeallocPDocAccessibleChild( + a11y::PDocAccessibleChild* aChild) { + delete static_cast(aChild); + return true; +} +#endif + +RefPtr BrowserChild::GetVsyncChild() { + // Initializing mVsyncChild here turns on per-BrowserChild Vsync for a + // given platform. Note: this only makes sense if nsWindow returns a + // window-specific VsyncSource. +#if defined(MOZ_WAYLAND) + if (IsWaylandEnabled() && !mVsyncChild) { + mVsyncChild = MakeRefPtr(); + if (!SendPVsyncConstructor(mVsyncChild)) { + mVsyncChild = nullptr; + } + } +#endif + return mVsyncChild; +} + +mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript( + const nsAString& aURL, const bool& aRunInGlobalScope) { + if (!InitBrowserChildMessageManager()) + // This can happen if we're half-destroyed. It's not a fatal + // error. + return IPC_OK(); + + JS::Rooted mm(RootingCx(), + mBrowserChildMessageManager->GetOrCreateWrapper()); + if (!mm) { + // This can happen if we're half-destroyed. It's not a fatal error. + return IPC_OK(); + } + + LoadScriptInternal(mm, aURL, !aRunInGlobalScope); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvAsyncMessage( + const nsAString& aMessage, const ClonedMessageData& aData) { + AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserChild::RecvAsyncMessage", + OTHER, aMessage); + MMPrinter::Print("BrowserChild::RecvAsyncMessage", aMessage, aData); + + if (!mBrowserChildMessageManager) { + return IPC_OK(); + } + + RefPtr mm = + mBrowserChildMessageManager->GetMessageManager(); + + // We should have a message manager if the global is alive, but it + // seems sometimes we don't. Assert in aurora/nightly, but don't + // crash in release builds. + MOZ_DIAGNOSTIC_ASSERT(mm); + if (!mm) { + return IPC_OK(); + } + + JS::Rooted kungFuDeathGrip( + dom::RootingCx(), mBrowserChildMessageManager->GetWrapper()); + StructuredCloneData data; + UnpackClonedMessageData(aData, data); + mm->ReceiveMessage(static_cast(mBrowserChildMessageManager), + nullptr, aMessage, false, &data, nullptr, IgnoreErrors()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSwappedWithOtherRemoteLoader( + const IPCTabContext& aContext) { + nsCOMPtr ourDocShell = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourDocShell)) { + return IPC_OK(); + } + + nsCOMPtr ourWindow = ourDocShell->GetWindow(); + if (NS_WARN_IF(!ourWindow)) { + return IPC_OK(); + } + + RefPtr docShell = static_cast(ourDocShell.get()); + + nsCOMPtr ourEventTarget = nsGlobalWindowOuter::Cast(ourWindow); + + docShell->SetInFrameSwap(true); + + nsContentUtils::FirePageShowEventForFrameLoaderSwap( + ourDocShell, ourEventTarget, false, true); + nsContentUtils::FirePageHideEventForFrameLoaderSwap(ourDocShell, + ourEventTarget, true); + + // Owner content type may have changed, so store the possibly updated context + // and notify others. + MaybeInvalidTabContext maybeContext(aContext); + if (!maybeContext.IsValid()) { + NS_ERROR(nsPrintfCString("Received an invalid TabContext from " + "the parent process. (%s)", + maybeContext.GetInvalidReason()) + .get()); + MOZ_CRASH("Invalid TabContext received from the parent process."); + } + + if (!UpdateTabContextAfterSwap(maybeContext.GetTabContext())) { + MOZ_CRASH("Update to TabContext after swap was denied."); + } + + // Ignore previous value of mTriedBrowserInit since owner content has changed. + mTriedBrowserInit = true; + + nsContentUtils::FirePageShowEventForFrameLoaderSwap( + ourDocShell, ourEventTarget, true, true); + + docShell->SetInFrameSwap(false); + + // This is needed to get visibility state right in cases when we swapped a + // visible tab (foreground in visible window) with a non-visible tab. + if (RefPtr doc = docShell->GetDocument()) { + doc->UpdateVisibilityState(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvHandleAccessKey( + const WidgetKeyboardEvent& aEvent, nsTArray&& aCharCodes) { + nsCOMPtr document(GetTopLevelDocument()); + RefPtr pc = document->GetPresContext(); + if (pc) { + if (!pc->EventStateManager()->HandleAccessKey( + &(const_cast(aEvent)), pc, aCharCodes)) { + // If no accesskey was found, inform the parent so that accesskeys on + // menus can be handled. + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = mPuppetWidget; + SendAccessKeyNotHandled(localEvent); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvPrintPreview( + const PrintData& aPrintData, const MaybeDiscardedBrowsingContext& aSourceBC, + PrintPreviewResolver&& aCallback) { +#ifdef NS_PRINTING + // If we didn't succeed in passing off ownership of aCallback, then something + // went wrong. + auto sendCallbackError = MakeScopeExit([&] { + if (aCallback) { + // signal error + aCallback(PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {})); + } + }); + + if (NS_WARN_IF(aSourceBC.IsDiscarded())) { + return IPC_OK(); + } + + RefPtr sourceWindow; + if (!aSourceBC.IsNull()) { + sourceWindow = nsGlobalWindowOuter::Cast(aSourceBC.get()->GetDOMWindow()); + if (NS_WARN_IF(!sourceWindow)) { + return IPC_OK(); + } + } else { + nsCOMPtr ourWindow = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!ourWindow)) { + return IPC_OK(); + } + sourceWindow = nsGlobalWindowOuter::Cast(ourWindow); + } + + RefPtr printSettings; + nsCOMPtr printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return IPC_OK(); + } + printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(!printSettings)) { + return IPC_OK(); + } + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + + nsCOMPtr docShellToCloneInto; + if (!aSourceBC.IsNull()) { + docShellToCloneInto = do_GetInterface(WebNavigation()); + if (NS_WARN_IF(!docShellToCloneInto)) { + return IPC_OK(); + } + } + + sourceWindow->Print(printSettings, + /* aRemotePrintJob = */ nullptr, + /* aListener = */ nullptr, docShellToCloneInto, + nsGlobalWindowOuter::IsPreview::Yes, + nsGlobalWindowOuter::IsForWindowDotPrint::No, + std::move(aCallback), IgnoreErrors()); +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvExitPrintPreview() { +#ifdef NS_PRINTING + nsCOMPtr webBrowserPrint = + do_GetInterface(ToSupports(WebNavigation())); + if (NS_WARN_IF(!webBrowserPrint)) { + return IPC_OK(); + } + webBrowserPrint->ExitPrintPreview(); +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvPrint( + const MaybeDiscardedBrowsingContext& aBc, const PrintData& aPrintData) { +#ifdef NS_PRINTING + if (NS_WARN_IF(aBc.IsNullOrDiscarded())) { + return IPC_OK(); + } + RefPtr outerWindow = + nsGlobalWindowOuter::Cast(aBc.get()->GetDOMWindow()); + if (NS_WARN_IF(!outerWindow)) { + return IPC_OK(); + } + + nsCOMPtr printSettingsSvc = + do_GetService("@mozilla.org/gfx/printsettings-service;1"); + if (NS_WARN_IF(!printSettingsSvc)) { + return IPC_OK(); + } + + nsCOMPtr printSettings; + nsresult rv = + printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return IPC_OK(); + } + + printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings); + { + IgnoredErrorResult rv; + RefPtr printJob = static_cast( + aPrintData.remotePrintJob().AsChild()); + outerWindow->Print(printSettings, printJob, + /* aListener = */ nullptr, + /* aWindowToCloneInto = */ nullptr, + nsGlobalWindowOuter::IsPreview::No, + nsGlobalWindowOuter::IsForWindowDotPrint::No, + /* aPrintPreviewCallback = */ nullptr, rv); + if (NS_WARN_IF(rv.Failed())) { + return IPC_OK(); + } + } +#endif + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvUpdateNativeWindowHandle( + const uintptr_t& aNewHandle) { +#if defined(XP_WIN) && defined(ACCESSIBILITY) + mNativeWindowHandle = aNewHandle; + return IPC_OK(); +#else + return IPC_FAIL_NO_REASON(this); +#endif +} + +mozilla::ipc::IPCResult BrowserChild::RecvDestroy() { + MOZ_ASSERT(mDestroyed == false); + mDestroyed = true; + + nsTArray childArray = + nsContentPermissionUtils::GetContentPermissionRequestChildById( + GetTabId()); + + // Need to close undeleted ContentPermissionRequestChilds before tab is + // closed. + for (auto& permissionRequestChild : childArray) { + auto child = static_cast(permissionRequestChild); + child->Destroy(); + } + + if (mBrowserChildMessageManager) { + // Message handlers are called from the event loop, so it better be safe to + // run script. + MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); + mBrowserChildMessageManager->DispatchTrustedEvent(u"unload"_ns); + } + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + + observerService->RemoveObserver(this, BEFORE_FIRST_PAINT); + + // XXX what other code in ~BrowserChild() should we be running here? + DestroyWindow(); + + // Bounce through the event loop once to allow any delayed teardown runnables + // that were just generated to have a chance to run. + nsCOMPtr deleteRunnable = new DelayedDeleteRunnable(this); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable)); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers( + const bool& aEnabled, const layers::LayersObserverEpoch& aEpoch) { + // Since requests to change the rendering state come in from both the hang + // monitor channel and the PContent channel, we have an ordering problem. This + // code ensures that we respect the order in which the requests were made and + // ignore stale requests. + if (mLayersObserverEpoch >= aEpoch) { + return IPC_OK(); + } + mLayersObserverEpoch = aEpoch; + + auto clearPaintWhileInterruptingJS = MakeScopeExit([&] { + // We might force a paint, or we might already have painted and this is a + // no-op. In either case, once we exit this scope, we need to alert the + // ProcessHangMonitor that we've finished responding to what might have + // been a request to force paint. This is so that the BackgroundHangMonitor + // for force painting can be made to wait again. + if (aEnabled) { + ProcessHangMonitor::ClearPaintWhileInterruptingJS(mLayersObserverEpoch); + } + }); + + if (aEnabled) { + ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS(); + } + + if (mCompositorOptions) { + MOZ_ASSERT(mPuppetWidget); + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + if (lm) { + // We send the current layer observer epoch to the compositor so that + // BrowserParent knows whether a layer update notification corresponds to + // the latest RecvRenderLayers request that was made. + lm->SetLayersObserverEpoch(mLayersObserverEpoch); + } + } + + mRenderLayers = aEnabled; + + if (aEnabled && IsVisible()) { + // This request is a no-op. + // In this case, we still want a MozLayerTreeReady notification to fire + // in the parent (so that it knows that the child has updated its epoch). + // PaintWhileInterruptingJSNoOp does that. + if (IPCOpen()) { + Unused << SendPaintWhileInterruptingJSNoOp(mLayersObserverEpoch); + } + return IPC_OK(); + } + + // FIXME(emilio): Probably / maybe this shouldn't be needed? See the comment + // in MakeVisible(), having the two separate states is not great. + UpdateVisibility(); + + if (!aEnabled) { + return IPC_OK(); + } + + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return IPC_OK(); + } + + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content + // viewer can cause JS to run, which we want to avoid. + // nsIDocShell::GetPresShell returns null if no content viewer exists yet. + RefPtr presShell = docShell->GetPresShell(); + if (!presShell) { + return IPC_OK(); + } + + if (nsIFrame* root = presShell->GetRootFrame()) { + root->SchedulePaint(); + } + + Telemetry::AutoTimer timer; + // If we need to repaint, let's do that right away. No sense waiting until + // we get back to the event loop again. We suppress the display port so + // that we only paint what's visible. This ensures that the tab we're + // switching to paints as quickly as possible. + presShell->SuppressDisplayport(true); + if (nsContentUtils::IsSafeToRunScript()) { + WebWidget()->PaintNowIfNeeded(); + } else { + RefPtr vm = presShell->GetViewManager(); + if (nsView* view = vm->GetRootView()) { + presShell->PaintAndRequestComposite(view, PaintFlags::None); + } + } + presShell->SuppressDisplayport(false); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvNavigateByKey( + const bool& aForward, const bool& aForDocumentNavigation) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return IPC_OK(); + } + + RefPtr result; + nsCOMPtr window = do_GetInterface(WebNavigation()); + + // Move to the first or last document. + { + uint32_t type = + aForward + ? (aForDocumentNavigation + ? static_cast(nsIFocusManager::MOVEFOCUS_FIRSTDOC) + : static_cast(nsIFocusManager::MOVEFOCUS_ROOT)) + : (aForDocumentNavigation + ? static_cast(nsIFocusManager::MOVEFOCUS_LASTDOC) + : static_cast(nsIFocusManager::MOVEFOCUS_LAST)); + uint32_t flags = nsIFocusManager::FLAG_BYKEY; + if (aForward || aForDocumentNavigation) { + flags |= nsIFocusManager::FLAG_NOSCROLL; + } + fm->MoveFocus(window, nullptr, type, flags, getter_AddRefs(result)); + } + + // No valid root element was found, so move to the first focusable element. + if (!result && aForward && !aForDocumentNavigation) { + fm->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_FIRST, + nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result)); + } + + SendRequestFocus(false, CallerType::System); + return IPC_OK(); +} + +bool BrowserChild::InitBrowserChildMessageManager() { + mShouldSendWebProgressEventsToParent = true; + + if (!mBrowserChildMessageManager) { + nsCOMPtr window = do_GetInterface(WebNavigation()); + NS_ENSURE_TRUE(window, false); + nsCOMPtr chromeHandler = window->GetChromeEventHandler(); + NS_ENSURE_TRUE(chromeHandler, false); + + RefPtr scope = mBrowserChildMessageManager = + new BrowserChildMessageManager(this); + + MOZ_ALWAYS_TRUE(nsMessageManagerScriptExecutor::Init()); + + nsCOMPtr root = do_QueryInterface(chromeHandler); + if (NS_WARN_IF(!root)) { + mBrowserChildMessageManager = nullptr; + return false; + } + root->SetParentTarget(scope); + } + + if (!mTriedBrowserInit) { + mTriedBrowserInit = true; + } + + return true; +} + +void BrowserChild::InitRenderingState( + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const layers::LayersId& aLayersId, + const CompositorOptions& aCompositorOptions) { + mPuppetWidget->InitIMEState(); + + MOZ_ASSERT(aLayersId.IsValid()); + mTextureFactoryIdentifier = aTextureFactoryIdentifier; + + // Pushing layers transactions directly to a separate + // compositor context. + PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get(); + if (!compositorChild) { + mLayersConnected = Some(false); + NS_WARNING("failed to get CompositorBridgeChild instance"); + return; + } + + mCompositorOptions = Some(aCompositorOptions); + + if (aLayersId.IsValid()) { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + + if (!sBrowserChildren) { + sBrowserChildren = new BrowserChildMap; + } + MOZ_ASSERT(!sBrowserChildren->Contains(uint64_t(aLayersId))); + sBrowserChildren->InsertOrUpdate(uint64_t(aLayersId), this); + mLayersId = aLayersId; + } + + // Depending on timing, we might paint too early and fall back to basic + // layers. CreateRemoteLayerManager will destroy us if we manage to get a + // remote layer manager though, so that's fine. + MOZ_ASSERT(!mPuppetWidget->HasWindowRenderer() || + mPuppetWidget->GetWindowRenderer()->GetBackendType() == + layers::LayersBackend::LAYERS_NONE); + bool success = false; + if (mLayersConnected == Some(true)) { + success = CreateRemoteLayerManager(compositorChild); + } + + if (success) { + MOZ_ASSERT(mLayersConnected == Some(true)); + // Succeeded to create "remote" layer manager + ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier); + InitAPZState(); + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + if (lm) { + lm->SetLayersObserverEpoch(mLayersObserverEpoch); + } + } else { + NS_WARNING("Fallback to FallbackRenderer"); + mLayersConnected = Some(false); + } + + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + + if (observerService) { + observerService->AddObserver(this, BEFORE_FIRST_PAINT, false); + } +} + +bool BrowserChild::CreateRemoteLayerManager( + mozilla::layers::PCompositorBridgeChild* aCompositorChild) { + MOZ_ASSERT(aCompositorChild); + + return mPuppetWidget->CreateRemoteLayerManager( + [&](WebRenderLayerManager* aLayerManager) -> bool { + nsCString error; + return aLayerManager->Initialize(aCompositorChild, + wr::AsPipelineId(mLayersId), + &mTextureFactoryIdentifier, error); + }); +} + +void BrowserChild::InitAPZState() { + if (!mCompositorOptions->UseAPZ()) { + return; + } + auto cbc = CompositorBridgeChild::Get(); + + // Initialize the ApzcTreeManager. This takes multiple casts because of ugly + // multiple inheritance. + PAPZCTreeManagerChild* baseProtocol = + cbc->SendPAPZCTreeManagerConstructor(mLayersId); + if (!baseProtocol) { + MOZ_ASSERT(false, + "Allocating a TreeManager should not fail with APZ enabled"); + return; + } + APZCTreeManagerChild* derivedProtocol = + static_cast(baseProtocol); + + mApzcTreeManager = RefPtr(derivedProtocol); + + // Initialize the GeckoContentController for this tab. We don't hold a + // reference because we don't need it. The ContentProcessController will hold + // a reference to the tab, and will be destroyed by the compositor or ipdl + // during destruction. + RefPtr contentController = + new ContentProcessController(this); + APZChild* apzChild = new APZChild(contentController); + cbc->SendPAPZConstructor(apzChild, mLayersId); +} + +IPCResult BrowserChild::RecvUpdateEffects(const EffectsInfo& aEffects) { + mDidSetEffectsInfo = true; + + bool needInvalidate = false; + if (mEffectsInfo.IsVisible() && aEffects.IsVisible() && + mEffectsInfo != aEffects) { + // if we are staying visible and either the visrect or scale changed we need + // to invalidate + needInvalidate = true; + } + + mEffectsInfo = aEffects; + UpdateVisibility(); + + if (needInvalidate) { + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (docShell) { + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content + // viewer can cause JS to run, which we want to avoid. + // nsIDocShell::GetPresShell returns null if no content viewer exists yet. + RefPtr presShell = docShell->GetPresShell(); + if (presShell) { + if (nsIFrame* root = presShell->GetRootFrame()) { + root->InvalidateFrame(); + } + } + } + } + + return IPC_OK(); +} + +bool BrowserChild::IsVisible() { + return mPuppetWidget && mPuppetWidget->IsVisible(); +} + +void BrowserChild::UpdateVisibility() { + bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.IsVisible(); + bool isVisible = IsVisible(); + + if (shouldBeVisible != isVisible) { + if (shouldBeVisible) { + MakeVisible(); + } else { + MakeHidden(); + } + } +} + +void BrowserChild::MakeVisible() { + if (IsVisible()) { + return; + } + + if (mPuppetWidget) { + mPuppetWidget->Show(true); + } + + PresShellActivenessMaybeChanged(); +} + +void BrowserChild::MakeHidden() { + if (!IsVisible()) { + return; + } + + // Due to the nested event loop in ContentChild::ProvideWindowCommon, + // it's possible to be told to become hidden before we're finished + // setting up a layer manager. We should skip clearing cached layers + // in that case, since doing so might accidentally put is into + // BasicLayers mode. + if (mPuppetWidget) { + if (mPuppetWidget->HasWindowRenderer()) { + ClearCachedResources(); + } + mPuppetWidget->Show(false); + } + + PresShellActivenessMaybeChanged(); +} + +IPCResult BrowserChild::RecvPreserveLayers(bool aPreserve) { + mIsPreservingLayers = aPreserve; + + PresShellActivenessMaybeChanged(); + + return IPC_OK(); +} + +void BrowserChild::PresShellActivenessMaybeChanged() { + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content + // viewer can cause JS to run, which we want to avoid. + // nsIDocShell::GetPresShell returns null if no content viewer exists yet. + // + // When this method is called we don't want to go through the browsing context + // because we don't want to change the visibility state of the document, which + // has side effects like firing events to content, unblocking media playback, + // unthrottling timeouts... PresShell activeness has a lot less side effects. + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return; + } + RefPtr presShell = docShell->GetPresShell(); + if (!presShell) { + return; + } + presShell->ActivenessMaybeChanged(); +} + +NS_IMETHODIMP +BrowserChild::GetMessageManager(ContentFrameMessageManager** aResult) { + RefPtr mm(mBrowserChildMessageManager); + mm.forget(aResult); + return *aResult ? NS_OK : NS_ERROR_FAILURE; +} + +void BrowserChild::SendRequestFocus(bool aCanFocus, CallerType aCallerType) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (!fm) { + return; + } + + nsCOMPtr window = do_GetInterface(WebNavigation()); + if (!window) { + return; + } + + BrowsingContext* focusedBC = fm->GetFocusedBrowsingContext(); + if (focusedBC == window->GetBrowsingContext()) { + // BrowsingContext has the focus already, do not request again. + return; + } + + PBrowserChild::SendRequestFocus(aCanFocus, aCallerType); +} + +NS_IMETHODIMP +BrowserChild::GetTabId(uint64_t* aId) { + *aId = GetTabId(); + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::GetChromeOuterWindowID(uint64_t* aId) { + *aId = ChromeOuterWindowID(); + return NS_OK; +} + +bool BrowserChild::DoSendBlockingMessage( + const nsAString& aMessage, StructuredCloneData& aData, + nsTArray* aRetVal) { + ClonedMessageData data; + if (!BuildClonedMessageData(aData, data)) { + return false; + } + return SendSyncMessage(PromiseFlatString(aMessage), data, aRetVal); +} + +nsresult BrowserChild::DoSendAsyncMessage(const nsAString& aMessage, + StructuredCloneData& aData) { + ClonedMessageData data; + if (!BuildClonedMessageData(aData, data)) { + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + if (!SendAsyncMessage(PromiseFlatString(aMessage), data)) { + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +/* static */ +nsTArray> BrowserChild::GetAll() { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + + if (!sBrowserChildren) { + return {}; + } + + return ToTArray>>(sBrowserChildren->Values()); +} + +BrowserChild* BrowserChild::GetFrom(PresShell* aPresShell) { + Document* doc = aPresShell->GetDocument(); + if (!doc) { + return nullptr; + } + nsCOMPtr docShell(doc->GetDocShell()); + return GetFrom(docShell); +} + +BrowserChild* BrowserChild::GetFrom(layers::LayersId aLayersId) { + StaticMutexAutoLock lock(sBrowserChildrenMutex); + if (!sBrowserChildren) { + return nullptr; + } + return sBrowserChildren->Get(uint64_t(aLayersId)); +} + +void BrowserChild::DidComposite(mozilla::layers::TransactionId aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd) { + MOZ_ASSERT(mPuppetWidget); + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + MOZ_ASSERT(lm); + + if (lm) { + lm->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd); + } +} + +void BrowserChild::DidRequestComposite(const TimeStamp& aCompositeReqStart, + const TimeStamp& aCompositeReqEnd) { + nsCOMPtr docShellComPtr = do_GetInterface(WebNavigation()); + if (!docShellComPtr) { + return; + } + + nsDocShell* docShell = static_cast(docShellComPtr.get()); + + if (TimelineConsumers::HasConsumer(docShell)) { + // Since we're assuming that it's impossible for content JS to directly + // trigger a synchronous paint, we can avoid capturing a stack trace here, + // which means we won't run into JS engine reentrancy issues like bug + // 1310014. + TimelineConsumers::AddMarkerForDocShell( + docShell, "CompositeForwardTransaction", aCompositeReqStart, + MarkerTracingType::START, MarkerStackRequest::NO_STACK); + TimelineConsumers::AddMarkerForDocShell( + docShell, "CompositeForwardTransaction", aCompositeReqEnd, + MarkerTracingType::END, MarkerStackRequest::NO_STACK); + } +} + +void BrowserChild::ClearCachedResources() { + MOZ_ASSERT(mPuppetWidget); + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + if (lm) { + lm->ClearCachedResources(); + } + + if (nsCOMPtr document = GetTopLevelDocument()) { + nsPresContext* presContext = document->GetPresContext(); + if (presContext) { + presContext->NotifyPaintStatusReset(); + } + } +} + +void BrowserChild::SchedulePaint() { + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + if (!docShell) { + return; + } + + // We don't use BrowserChildBase::GetPresShell() here because that would + // create a content viewer if one doesn't exist yet. Creating a content viewer + // can cause JS to run, which we want to avoid. nsIDocShell::GetPresShell + // returns null if no content viewer exists yet. + if (RefPtr presShell = docShell->GetPresShell()) { + if (nsIFrame* root = presShell->GetRootFrame()) { + root->SchedulePaint(); + } + } +} + +void BrowserChild::ReinitRendering() { + MOZ_ASSERT(mLayersId.IsValid()); + + // In some cases, like when we create a windowless browser, + // RemoteLayerTreeOwner/BrowserChild is not connected to a compositor. + if (mLayersConnectRequested.isNothing() || + mLayersConnectRequested == Some(false)) { + return; + } + + // Before we establish a new PLayerTransaction, we must connect our layer tree + // id, CompositorBridge, and the widget compositor all together again. + // Normally this happens in BrowserParent before BrowserChild is given + // rendering information. + // + // In this case, we will send a sync message to our BrowserParent, which in + // turn will send a sync message to the Compositor of the widget owning this + // tab. This guarantees the correct association is in place before our + // PLayerTransaction constructor message arrives on the cross-process + // compositor bridge. + CompositorOptions options; + SendEnsureLayersConnected(&options); + mCompositorOptions = Some(options); + + bool success = false; + RefPtr cb = CompositorBridgeChild::Get(); + + if (cb) { + success = CreateRemoteLayerManager(cb); + } + + if (!success) { + NS_WARNING("failed to recreate layer manager"); + return; + } + + mLayersConnected = Some(true); + ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier); + + InitAPZState(); + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + if (lm) { + lm->SetLayersObserverEpoch(mLayersObserverEpoch); + } + + if (nsCOMPtr doc = GetTopLevelDocument()) { + doc->NotifyLayerManagerRecreated(); + } + + if (mRenderLayers) { + SchedulePaint(); + } +} + +void BrowserChild::ReinitRenderingForDeviceReset() { + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + if (lm) { + lm->DoDestroy(/* aIsSync */ true); + } + + // Proceed with destroying and recreating the layer manager. + ReinitRendering(); +} + +NS_IMETHODIMP +BrowserChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, + const nsAString& aTipText, + const nsAString& aTipDir) { + nsString str(aTipText); + nsString dir(aTipDir); + SendShowTooltip(aXCoords, aYCoords, str, dir); + return NS_OK; +} + +NS_IMETHODIMP +BrowserChild::OnHideTooltip() { + SendHideTooltip(); + return NS_OK; +} + +void BrowserChild::NotifyJankedAnimations( + const nsTArray& aJankedAnimations) { + MOZ_ASSERT(mPuppetWidget); + RefPtr lm = + mPuppetWidget->GetWindowRenderer()->AsWebRender(); + if (lm) { + lm->UpdatePartialPrerenderedAnimations(aJankedAnimations); + } +} + +mozilla::ipc::IPCResult BrowserChild::RecvUIResolutionChanged( + const float& aDpi, const int32_t& aRounding, const double& aScale) { + ScreenIntSize oldScreenSize = GetInnerSize(); + if (aDpi > 0) { + mPuppetWidget->UpdateBackingScaleCache(aDpi, aRounding, aScale); + } + + ScreenIntSize screenSize = GetInnerSize(); + if (mHasValidInnerSize && oldScreenSize != screenSize) { + ScreenIntRect screenRect = GetOuterRect(); + + // See RecvUpdateDimensions for the order of these operations. + nsCOMPtr baseWin = do_QueryInterface(WebNavigation()); + baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height, + nsIBaseWindow::eRepaint); + + mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x, + screenRect.y + mClientOffset.y + mChromeOffset.y, + screenSize.width, screenSize.height, true); + } + + nsCOMPtr document(GetTopLevelDocument()); + RefPtr presContext = + document ? document->GetPresContext() : nullptr; + if (presContext) { + presContext->UIResolutionChangedSync(); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvSafeAreaInsetsChanged( + const mozilla::ScreenIntMargin& aSafeAreaInsets) { + mPuppetWidget->UpdateSafeAreaInsets(aSafeAreaInsets); + + nsCOMPtr screenMgr = + do_GetService("@mozilla.org/gfx/screenmanager;1"); + ScreenIntMargin currentSafeAreaInsets; + if (screenMgr) { + // aSafeAreaInsets is for current screen. But we have to calculate + // safe insets for content window. + int32_t x, y, cx, cy; + GetDimensions(DimensionKind::Outer, &x, &y, &cx, &cy); + nsCOMPtr screen; + screenMgr->ScreenForRect(x, y, cx, cy, getter_AddRefs(screen)); + + if (screen) { + LayoutDeviceIntRect windowRect(x + mClientOffset.x + mChromeOffset.x, + y + mClientOffset.y + mChromeOffset.y, cx, + cy); + currentSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets( + screen, aSafeAreaInsets, windowRect); + } + } + + if (nsCOMPtr document = GetTopLevelDocument()) { + nsPresContext* presContext = document->GetPresContext(); + if (presContext) { + presContext->SetSafeAreaInsets(currentSafeAreaInsets); + } + } + + // https://github.com/w3c/csswg-drafts/issues/4670 + // Actually we don't set this value on sub document. This behaviour is + // same as Blink that safe area insets isn't set on sub document. + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvAllowScriptsToClose() { + nsCOMPtr window = do_GetInterface(WebNavigation()); + if (window) { + nsGlobalWindowOuter::Cast(window)->AllowScriptsToClose(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserChild::RecvReleaseAllPointerCapture() { + PointerEventHandler::ReleaseAllPointerCapture(); + return IPC_OK(); +} + +PPaymentRequestChild* BrowserChild::AllocPPaymentRequestChild() { + MOZ_CRASH( + "We should never be manually allocating PPaymentRequestChild actors"); + return nullptr; +} + +bool BrowserChild::DeallocPPaymentRequestChild(PPaymentRequestChild* actor) { + delete actor; + return true; +} + +ScreenIntSize BrowserChild::GetInnerSize() { + LayoutDeviceIntSize innerSize = + RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale()); + return ViewAs( + innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +}; + +Maybe BrowserChild::GetVisibleRect() const { + if (mIsTopLevel) { + // We are conservative about visible rects for top-level browsers to avoid + // artifacts when resizing + return Nothing(); + } + + return mDidSetEffectsInfo ? Some(mEffectsInfo.mVisibleRect) : Nothing(); +} + +Maybe +BrowserChild::GetTopLevelViewportVisibleRectInSelfCoords() const { + if (mIsTopLevel) { + return Nothing(); + } + + if (!mChildToParentConversionMatrix) { + // We have no way to tell this remote document visible rect right now. + return Nothing(); + } + + Maybe inverse = + mChildToParentConversionMatrix->MaybeInverse(); + if (!inverse) { + return Nothing(); + } + + // Convert the remote document visible rect to the coordinate system of the + // iframe document. + Maybe rect = UntransformBy( + *inverse, + ViewAs( + mTopLevelViewportVisibleRectInBrowserCoords, + PixelCastJustification::ContentProcessIsLayerInUiProcess), + LayoutDeviceRect::MaxIntRect()); + if (!rect) { + return Nothing(); + } + + return rect; +} + +ScreenIntRect BrowserChild::GetOuterRect() { + LayoutDeviceIntRect outerRect = + RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale()); + return ViewAs( + outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); +} + +void BrowserChild::PaintWhileInterruptingJS( + const layers::LayersObserverEpoch& aEpoch) { + if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasWindowRenderer()) { + // Don't bother doing anything now. Better to wait until we receive the + // message on the PContent channel. + return; + } + + MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript()); + nsAutoScriptBlocker scriptBlocker; + RecvRenderLayers(true /* aEnabled */, aEpoch); +} + +void BrowserChild::UnloadLayersWhileInterruptingJS( + const layers::LayersObserverEpoch& aEpoch) { + if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasWindowRenderer()) { + // Don't bother doing anything now. Better to wait until we receive the + // message on the PContent channel. + return; + } + + MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript()); + nsAutoScriptBlocker scriptBlocker; + RecvRenderLayers(false /* aEnabled */, aEpoch); +} + +nsresult BrowserChild::CanCancelContentJS( + nsIRemoteTab::NavigationType aNavigationType, int32_t aNavigationIndex, + nsIURI* aNavigationURI, int32_t aEpoch, bool* aCanCancel) { + nsresult rv; + *aCanCancel = false; + + if (aEpoch <= mCancelContentJSEpoch) { + // The next page loaded before we got here, so we shouldn't try to cancel + // the content JS. + return NS_OK; + } + + // If we have session history in the parent we've already performed + // the checks following, so we can return early. + if (mozilla::SessionHistoryInParent()) { + *aCanCancel = true; + return NS_OK; + } + + nsCOMPtr docShell = do_GetInterface(WebNavigation()); + nsCOMPtr history; + if (docShell) { + history = nsDocShell::Cast(docShell)->GetSessionHistory()->LegacySHistory(); + } + + if (!history) { + return NS_ERROR_FAILURE; + } + + int32_t current; + rv = history->GetIndex(¤t); + NS_ENSURE_SUCCESS(rv, rv); + + if (current == -1) { + // This tab has no history! Just return. + return NS_OK; + } + + nsCOMPtr entry; + rv = history->GetEntryAtIndex(current, getter_AddRefs(entry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr currentURI = entry->GetURI(); + if (!currentURI->SchemeIs("http") && !currentURI->SchemeIs("https") && + !currentURI->SchemeIs("file")) { + // Only cancel content JS for http(s) and file URIs. Other URIs are probably + // internal and we should just let them run to completion. + return NS_OK; + } + + if (aNavigationType == nsIRemoteTab::NAVIGATE_BACK) { + aNavigationIndex = current - 1; + } else if (aNavigationType == nsIRemoteTab::NAVIGATE_FORWARD) { + aNavigationIndex = current + 1; + } else if (aNavigationType == nsIRemoteTab::NAVIGATE_URL) { + if (!aNavigationURI) { + return NS_ERROR_FAILURE; + } + + if (aNavigationURI->SchemeIs("javascript")) { + // "javascript:" URIs don't (necessarily) trigger navigation to a + // different page, so don't allow the current page's JS to terminate. + return NS_OK; + } + + // If navigating directly to a URL (e.g. via hitting Enter in the location + // bar), then we can cancel anytime the next URL is different from the + // current, *excluding* the ref ("#"). + bool equals; + rv = currentURI->EqualsExceptRef(aNavigationURI, &equals); + NS_ENSURE_SUCCESS(rv, rv); + *aCanCancel = !equals; + return NS_OK; + } + // Note: aNavigationType may also be NAVIGATE_INDEX, in which case we don't + // need to do anything special. + + int32_t delta = aNavigationIndex > current ? 1 : -1; + for (int32_t i = current + delta; i != aNavigationIndex + delta; i += delta) { + nsCOMPtr nextEntry; + // If `i` happens to be negative, this call will fail (which is what we + // would want to happen). + rv = history->GetEntryAtIndex(i, getter_AddRefs(nextEntry)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr laterEntry = delta == 1 ? nextEntry : entry; + nsCOMPtr thisURI = entry->GetURI(); + nsCOMPtr nextURI = nextEntry->GetURI(); + + // If we changed origin and the load wasn't in a subframe, we know it was + // a full document load, so we can cancel the content JS safely. + if (!laterEntry->GetIsSubFrame()) { + nsAutoCString thisHost; + rv = thisURI->GetPrePath(thisHost); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoCString nextHost; + rv = nextURI->GetPrePath(nextHost); + NS_ENSURE_SUCCESS(rv, rv); + + if (!thisHost.Equals(nextHost)) { + *aCanCancel = true; + return NS_OK; + } + } + + entry = nextEntry; + } + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aStateFlags, + nsresult aStatus) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + // We shouldn't need to notify the parent of redirect state changes, since + // with DocumentChannel that only happens when we switch to the real channel, + // and that's an implementation detail that we can hide. + if (aStateFlags & nsIWebProgressListener::STATE_IS_REDIRECTED_DOCUMENT) { + return NS_OK; + } + + // Our OnStateChange call must have provided the nsIDocShell which the source + // comes from. We'll use this to locate the corresponding BrowsingContext in + // the parent process. + nsCOMPtr docShell = do_QueryInterface(aWebProgress); + if (!docShell) { + MOZ_ASSERT_UNREACHABLE("aWebProgress is null or not a nsIDocShell?"); + return NS_ERROR_UNEXPECTED; + } + + WebProgressData webProgressData; + Maybe stateChangeData; + RequestData requestData; + + MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData, + requestData)); + + RefPtr browsingContext = docShell->GetBrowsingContext(); + if (browsingContext->IsTopContent()) { + stateChangeData.emplace(); + + stateChangeData->isNavigating() = docShell->GetIsNavigating(); + stateChangeData->mayEnableCharacterEncodingMenu() = + docShell->GetMayEnableCharacterEncodingMenu(); + + RefPtr document = browsingContext->GetExtantDocument(); + if (document && aStateFlags & nsIWebProgressListener::STATE_STOP) { + document->GetContentType(stateChangeData->contentType()); + document->GetCharacterSet(stateChangeData->charset()); + stateChangeData->documentURI() = document->GetDocumentURIObject(); + } else { + stateChangeData->contentType().SetIsVoid(true); + stateChangeData->charset().SetIsVoid(true); + } + } + + Unused << SendOnStateChange(webProgressData, requestData, aStateFlags, + aStatus, stateChangeData); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + // FIXME: We currently ignore ProgressChange events from out-of-process + // subframes both here and in BrowserParent. We may want to change this + // behaviour in the future. + if (!GetBrowsingContext()->IsTopContent()) { + return NS_OK; + } + + // As we're being filtered by nsBrowserStatusFilter, we will be passed either + // nullptr or 0 for all arguments other than aCurTotalProgress and + // aMaxTotalProgress. Don't bother sending them. + MOZ_ASSERT(!aWebProgress); + MOZ_ASSERT(!aRequest); + MOZ_ASSERT(aCurSelfProgress == 0); + MOZ_ASSERT(aMaxSelfProgress == 0); + + Unused << SendOnProgressChange(aCurTotalProgress, aMaxTotalProgress); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + nsCOMPtr docShell = do_QueryInterface(aWebProgress); + if (!docShell) { + MOZ_ASSERT_UNREACHABLE("aWebProgress is null or not a nsIDocShell?"); + return NS_ERROR_UNEXPECTED; + } + + RefPtr browsingContext = docShell->GetBrowsingContext(); + RefPtr document = browsingContext->GetExtantDocument(); + if (!document) { + return NS_OK; + } + + WebProgressData webProgressData; + RequestData requestData; + + MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData, + requestData)); + + Maybe locationChangeData; + + bool canGoBack = false; + bool canGoForward = false; + if (!mozilla::SessionHistoryInParent()) { + MOZ_TRY(WebNavigation()->GetCanGoBack(&canGoBack)); + MOZ_TRY(WebNavigation()->GetCanGoForward(&canGoForward)); + } + + if (browsingContext->IsTopContent()) { + MOZ_ASSERT( + browsingContext == GetBrowsingContext(), + "Toplevel content BrowsingContext which isn't GetBrowsingContext()?"); + + locationChangeData.emplace(); + + document->GetContentType(locationChangeData->contentType()); + locationChangeData->isNavigating() = docShell->GetIsNavigating(); + locationChangeData->documentURI() = document->GetDocumentURIObject(); + document->GetTitle(locationChangeData->title()); + document->GetCharacterSet(locationChangeData->charset()); + + locationChangeData->mayEnableCharacterEncodingMenu() = + docShell->GetMayEnableCharacterEncodingMenu(); + + locationChangeData->contentPrincipal() = document->NodePrincipal(); + locationChangeData->contentPartitionedPrincipal() = + document->PartitionedPrincipal(); + locationChangeData->csp() = document->GetCsp(); + locationChangeData->referrerInfo() = document->ReferrerInfo(); + locationChangeData->isSyntheticDocument() = document->IsSyntheticDocument(); + + if (nsCOMPtr loadGroup = document->GetDocumentLoadGroup()) { + uint64_t requestContextID = 0; + MOZ_TRY(loadGroup->GetRequestContextID(&requestContextID)); + locationChangeData->requestContextID() = Some(requestContextID); + } + +#ifdef MOZ_CRASHREPORTER + if (CrashReporter::GetEnabled()) { + nsCOMPtr annotationURI; + + nsresult rv = + NS_MutateURI(aLocation).SetUserPass(""_ns).Finalize(annotationURI); + + if (NS_FAILED(rv)) { + // Ignore failures on about: URIs. + annotationURI = aLocation; + } + + CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, + annotationURI->GetSpecOrDefault()); + } +#endif + } + + Unused << SendOnLocationChange(webProgressData, requestData, aLocation, + aFlags, canGoBack, canGoForward, + locationChangeData); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) { + if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) { + return NS_OK; + } + + // FIXME: We currently ignore StatusChange from out-of-process subframes both + // here and in BrowserParent. We may want to change this behaviour in the + // future. + if (!GetBrowsingContext()->IsTopContent()) { + return NS_OK; + } + + // As we're being filtered by nsBrowserStatusFilter, we will be passed either + // nullptr or NS_OK for all arguments other than aMessage. Don't bother + // sending them. + MOZ_ASSERT(!aWebProgress); + MOZ_ASSERT(!aRequest); + MOZ_ASSERT(aStatus == NS_OK); + + Unused << SendOnStatusChange(nsDependentString(aMessage)); + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnSecurityChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aState) { + // Security changes are now handled entirely in the parent process + // so we don't need to worry about forwarding them (and we shouldn't + // be receiving any to forward). + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::OnContentBlockingEvent(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aEvent) { + // The OnContentBlockingEvent only happenes in the parent process. It should + // not be seen in the content process. + MOZ_DIAGNOSTIC_ASSERT( + false, "OnContentBlockingEvent should not be seen in content process."); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP BrowserChild::OnProgressChange64(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int64_t aCurSelfProgress, + int64_t aMaxSelfProgress, + int64_t aCurTotalProgress, + int64_t aMaxTotalProgress) { + // All the events we receive are filtered through an nsBrowserStatusFilter, + // which accepts ProgressChange64 events, but truncates the progress values to + // uint32_t and calls OnProgressChange. + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP BrowserChild::OnRefreshAttempted(nsIWebProgress* aWebProgress, + nsIURI* aRefreshURI, + uint32_t aMillis, bool aSameURI, + bool* aOut) { + NS_ENSURE_ARG_POINTER(aOut); + *aOut = true; + + return NS_OK; +} + +NS_IMETHODIMP BrowserChild::NotifyNavigationFinished() { + Unused << SendNavigationFinished(); + return NS_OK; +} + +nsresult BrowserChild::PrepareRequestData(nsIRequest* aRequest, + RequestData& aRequestData) { + nsCOMPtr channel = do_QueryInterface(aRequest); + if (!channel) { + aRequestData.requestURI() = nullptr; + return NS_OK; + } + + nsresult rv = channel->GetURI(getter_AddRefs(aRequestData.requestURI())); + NS_ENSURE_SUCCESS(rv, rv); + + rv = channel->GetOriginalURI( + getter_AddRefs(aRequestData.originalRequestURI())); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr classifiedChannel = do_QueryInterface(channel); + if (classifiedChannel) { + rv = classifiedChannel->GetMatchedList(aRequestData.matchedList()); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +nsresult BrowserChild::PrepareProgressListenerData( + nsIWebProgress* aWebProgress, nsIRequest* aRequest, + WebProgressData& aWebProgressData, RequestData& aRequestData) { + nsCOMPtr docShell = do_QueryInterface(aWebProgress); + if (!docShell) { + MOZ_ASSERT_UNREACHABLE("aWebProgress is null or not a nsIDocShell?"); + return NS_ERROR_UNEXPECTED; + } + + aWebProgressData.browsingContext() = docShell->GetBrowsingContext(); + nsresult rv = aWebProgress->GetLoadType(&aWebProgressData.loadType()); + NS_ENSURE_SUCCESS(rv, rv); + + return PrepareRequestData(aRequest, aRequestData); +} + +void BrowserChild::UpdateSessionStore() { + if (mSessionStoreChild) { + mSessionStoreChild->UpdateSessionStore(); + } +} + +#ifdef XP_WIN +RefPtr +BrowserChild::DoesWindowSupportProtectedMedia() { + MOZ_ASSERT( + NS_IsMainThread(), + "Protected media support check should be done on main thread only."); + if (mWindowSupportsProtectedMedia) { + // If we've already checked and have a cached result, resolve with that. + return IsWindowSupportingProtectedMediaPromise::CreateAndResolve( + mWindowSupportsProtectedMedia.value(), __func__); + } + RefPtr self = this; + // We chain off the promise rather than passing it directly so we can cache + // the result and use that for future calls. + return SendIsWindowSupportingProtectedMedia(ChromeOuterWindowID()) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self](bool isSupported) { + // If a result was cached while this check was inflight, ensure the + // results match. + MOZ_ASSERT_IF( + self->mWindowSupportsProtectedMedia, + self->mWindowSupportsProtectedMedia.value() == isSupported); + // Cache the response as it will not change during the lifetime + // of this object. + self->mWindowSupportsProtectedMedia = Some(isSupported); + return IsWindowSupportingProtectedMediaPromise::CreateAndResolve( + self->mWindowSupportsProtectedMedia.value(), __func__); + }, + [](ResponseRejectReason reason) { + return IsWindowSupportingProtectedMediaPromise::CreateAndReject( + reason, __func__); + }); +} +#endif + +void BrowserChild::NotifyContentBlockingEvent( + uint32_t aEvent, nsIChannel* aChannel, bool aBlocked, + const nsACString& aTrackingOrigin, + const nsTArray& aTrackingFullHashes, + const Maybe< + mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>& + aReason) { + if (!IPCOpen()) { + return; + } + + RequestData requestData; + if (NS_SUCCEEDED(PrepareRequestData(aChannel, requestData))) { + Unused << SendNotifyContentBlockingEvent( + aEvent, requestData, aBlocked, PromiseFlatCString(aTrackingOrigin), + aTrackingFullHashes, aReason); + } +} + +NS_IMETHODIMP +BrowserChild::ContentTransformsReceived(JSContext* aCx, + dom::Promise** aPromise) { + auto* globalObject = xpc::CurrentNativeGlobal(aCx); + ErrorResult rv; + if (mChildToParentConversionMatrix) { + // Already received content transforms + RefPtr promise = + Promise::CreateResolvedWithUndefined(globalObject, rv); + promise.forget(aPromise); + return rv.StealNSResult(); + } + + if (!mContentTransformPromise) { + mContentTransformPromise = Promise::Create(globalObject, rv); + } + + MOZ_ASSERT(globalObject == mContentTransformPromise->GetGlobalObject()); + NS_IF_ADDREF(*aPromise = mContentTransformPromise); + return rv.StealNSResult(); +} + +BrowserChildMessageManager::BrowserChildMessageManager( + BrowserChild* aBrowserChild) + : ContentFrameMessageManager(new nsFrameMessageManager(aBrowserChild)), + mBrowserChild(aBrowserChild) {} + +BrowserChildMessageManager::~BrowserChildMessageManager() = default; + +NS_IMPL_CYCLE_COLLECTION_CLASS(BrowserChildMessageManager) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BrowserChildMessageManager, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild); + NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BrowserChildMessageManager, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserChildMessageManager) + NS_INTERFACE_MAP_ENTRY(nsIMessageSender) + NS_INTERFACE_MAP_ENTRY_CONCRETE(ContentFrameMessageManager) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(BrowserChildMessageManager, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(BrowserChildMessageManager, DOMEventTargetHelper) + +JSObject* BrowserChildMessageManager::WrapObject( + JSContext* aCx, JS::Handle aGivenProto) { + return ContentFrameMessageManager_Binding::Wrap(aCx, this, aGivenProto); +} + +void BrowserChildMessageManager::MarkForCC() { + if (mBrowserChild) { + mBrowserChild->MarkScopesForCC(); + } + EventListenerManager* elm = GetExistingListenerManager(); + if (elm) { + elm->MarkForCC(); + } + MessageManagerGlobal::MarkForCC(); +} + +Nullable BrowserChildMessageManager::GetContent( + ErrorResult& aError) { + nsCOMPtr docShell = GetDocShell(aError); + if (!docShell) { + return nullptr; + } + return WindowProxyHolder(docShell->GetBrowsingContext()); +} + +already_AddRefed BrowserChildMessageManager::GetDocShell( + ErrorResult& aError) { + if (!mBrowserChild) { + aError.Throw(NS_ERROR_NULL_POINTER); + return nullptr; + } + nsCOMPtr window = + do_GetInterface(mBrowserChild->WebNavigation()); + return window.forget(); +} + +already_AddRefed +BrowserChildMessageManager::GetTabEventTarget() { + nsCOMPtr target = EventTargetFor(TaskCategory::Other); + return target.forget(); +} + +nsresult BrowserChildMessageManager::Dispatch( + TaskCategory aCategory, already_AddRefed&& aRunnable) { + return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable)); +} + +nsISerialEventTarget* BrowserChildMessageManager::EventTargetFor( + TaskCategory aCategory) const { + return DispatcherTrait::EventTargetFor(aCategory); +} + +AbstractThread* BrowserChildMessageManager::AbstractMainThreadFor( + TaskCategory aCategory) { + return DispatcherTrait::AbstractMainThreadFor(aCategory); +} diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h new file mode 100644 index 0000000000..d55591d10a --- /dev/null +++ b/dom/ipc/BrowserChild.h @@ -0,0 +1,883 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_BrowserChild_h +#define mozilla_dom_BrowserChild_h + +#include "mozilla/dom/ContentFrameMessageManager.h" +#include "mozilla/dom/PBrowserChild.h" +#include "nsIWebNavigation.h" +#include "nsCOMPtr.h" +#include "nsIWebBrowserChrome.h" +#include "nsIWebBrowserChromeFocus.h" +#include "nsIDOMEventListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIWindowProvider.h" +#include "nsIDocShell.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsWeakReference.h" +#include "nsIBrowserChild.h" +#include "nsITooltipListener.h" +#include "nsIWebProgressListener.h" +#include "nsIWebProgressListener2.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/TabContext.h" +#include "mozilla/dom/CoalescedMouseData.h" +#include "mozilla/dom/CoalescedTouchData.h" +#include "mozilla/dom/CoalescedWheelData.h" +#include "mozilla/dom/MessageManagerCallback.h" +#include "mozilla/dom/VsyncMainChild.h" +#include "mozilla/DOMEventTargetHelper.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventForwards.h" +#include "mozilla/layers/CompositorTypes.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/GeckoContentControllerTypes.h" +#include "mozilla/dom/ipc/IdType.h" +#include "AudioChannelService.h" +#include "PuppetWidget.h" +#include "nsDeque.h" +#include "nsIRemoteTab.h" +#include "nsTHashSet.h" + +class nsBrowserStatusFilter; +class nsIDOMWindow; +class nsIHttpChannel; +class nsIRequest; +class nsISerialEventTarget; +class nsIWebProgress; +class nsWebBrowser; +class nsDocShellLoadState; + +template +class nsTHashtable; +template +class nsPtrHashKey; + +namespace mozilla { +enum class NativeKeyBindingsType : uint8_t; + +class AbstractThread; +class PresShell; + +namespace layers { +class APZChild; +class APZEventState; +class AsyncDragMetrics; +class IAPZCTreeManager; +class ImageCompositeNotification; +class PCompositorBridgeChild; +} // namespace layers + +namespace widget { +struct AutoCacheNativeKeyCommands; +} // namespace widget + +namespace dom { + +class BrowserChild; +class BrowsingContext; +class TabGroup; +class ClonedMessageData; +class CoalescedMouseData; +class CoalescedWheelData; +class SessionStoreChild; +class RequestData; +class WebProgressData; + +class BrowserChildMessageManager : public ContentFrameMessageManager, + public nsIMessageSender, + public DispatcherTrait, + public nsSupportsWeakReference { + public: + explicit BrowserChildMessageManager(BrowserChild* aBrowserChild); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BrowserChildMessageManager, + DOMEventTargetHelper) + + void MarkForCC(); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + virtual Nullable GetContent(ErrorResult& aError) override; + virtual already_AddRefed GetDocShell( + ErrorResult& aError) override; + virtual already_AddRefed GetTabEventTarget() override; + + NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager) + + void GetEventTargetParent(EventChainPreVisitor& aVisitor) override { + aVisitor.mForceContentDispatch = true; + } + + // Dispatch a runnable related to the global. + virtual nsresult Dispatch(mozilla::TaskCategory aCategory, + already_AddRefed&& aRunnable) override; + + virtual nsISerialEventTarget* EventTargetFor( + mozilla::TaskCategory aCategory) const override; + + virtual AbstractThread* AbstractMainThreadFor( + mozilla::TaskCategory aCategory) override; + + RefPtr mBrowserChild; + + protected: + ~BrowserChildMessageManager(); +}; + +/** + * BrowserChild implements the child actor part of the PBrowser protocol. See + * PBrowser for more information. + */ +class BrowserChild final : public nsMessageManagerScriptExecutor, + public ipc::MessageManagerCallback, + public PBrowserChild, + public nsIWebBrowserChrome, + public nsIWebBrowserChromeFocus, + public nsIInterfaceRequestor, + public nsIWindowProvider, + public nsSupportsWeakReference, + public nsIBrowserChild, + public nsIObserver, + public nsIWebProgressListener2, + public TabContext, + public nsITooltipListener, + public mozilla::ipc::IShmemAllocator { + using PuppetWidget = mozilla::widget::PuppetWidget; + using ClonedMessageData = mozilla::dom::ClonedMessageData; + using CoalescedMouseData = mozilla::dom::CoalescedMouseData; + using CoalescedWheelData = mozilla::dom::CoalescedWheelData; + using APZEventState = mozilla::layers::APZEventState; + using TouchBehaviorFlags = mozilla::layers::TouchBehaviorFlags; + + friend class PBrowserChild; + + public: + /** + * Find BrowserChild of aTabId in the same content process of the + * caller. + */ + static already_AddRefed FindBrowserChild(const TabId& aTabId); + + // Return a list of all active BrowserChildren. + static nsTArray> GetAll(); + + public: + /** + * Create a new BrowserChild object. + */ + BrowserChild(ContentChild* aManager, const TabId& aTabId, + const TabContext& aContext, + dom::BrowsingContext* aBrowsingContext, uint32_t aChromeFlags, + bool aIsTopLevel); + + MOZ_CAN_RUN_SCRIPT nsresult Init(mozIDOMWindowProxy* aParent, + WindowGlobalChild* aInitialWindowChild); + + /** Return a BrowserChild with the given attributes. */ + static already_AddRefed Create( + ContentChild* aManager, const TabId& aTabId, const TabContext& aContext, + BrowsingContext* aBrowsingContext, uint32_t aChromeFlags, + bool aIsTopLevel); + + // Let managees query if it is safe to send messages. + bool IsDestroyed() const { return mDestroyed; } + + const TabId GetTabId() const { + MOZ_ASSERT(mUniqueId != 0); + return mUniqueId; + } + + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSIWEBBROWSERCHROME + NS_DECL_NSIWEBBROWSERCHROMEFOCUS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIWINDOWPROVIDER + NS_DECL_NSIBROWSERCHILD + NS_DECL_NSIOBSERVER + NS_DECL_NSIWEBPROGRESSLISTENER + NS_DECL_NSIWEBPROGRESSLISTENER2 + NS_DECL_NSITOOLTIPLISTENER + + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(BrowserChild, + nsIBrowserChild) + + FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild) + + JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) { + return mBrowserChildMessageManager->WrapObject(aCx, aGivenProto); + } + + // Get the Document for the top-level window in this tab. + already_AddRefed GetTopLevelDocument() const; + + // Get the pres-shell of the document for the top-level window in this tab. + PresShell* GetTopLevelPresShell() const; + + BrowserChildMessageManager* GetMessageManager() { + return mBrowserChildMessageManager; + } + + bool IsTopLevel() const { return mIsTopLevel; } + + bool ShouldSendWebProgressEventsToParent() const { + return mShouldSendWebProgressEventsToParent; + } + + /** + * MessageManagerCallback methods that we override. + */ + virtual bool DoSendBlockingMessage( + const nsAString& aMessage, StructuredCloneData& aData, + nsTArray* aRetVal) override; + + virtual nsresult DoSendAsyncMessage(const nsAString& aMessage, + StructuredCloneData& aData) override; + + bool DoUpdateZoomConstraints(const uint32_t& aPresShellId, + const ViewID& aViewId, + const Maybe& aConstraints); + + mozilla::ipc::IPCResult RecvLoadURL(nsDocShellLoadState* aLoadState, + const ParentShowInfo& aInfo); + + mozilla::ipc::IPCResult RecvCreateAboutBlankContentViewer( + nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal); + + mozilla::ipc::IPCResult RecvResumeLoad(const uint64_t& aPendingSwitchID, + const ParentShowInfo&); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult + RecvCloneDocumentTreeIntoSelf( + const MaybeDiscarded& aSourceBC, + const embedding::PrintData& aPrintData, + CloneDocumentTreeIntoSelfResolver&& aResolve); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvUpdateRemotePrintSettings( + const embedding::PrintData& aPrintData); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvShow(const ParentShowInfo&, const OwnerShowInfo&); + + mozilla::ipc::IPCResult RecvInitRendering( + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const layers::LayersId& aLayersId, + const mozilla::layers::CompositorOptions& aCompositorOptions, + const bool& aLayersConnected); + + mozilla::ipc::IPCResult RecvCompositorOptionsChanged( + const mozilla::layers::CompositorOptions& aNewOptions); + + mozilla::ipc::IPCResult RecvUpdateDimensions( + const mozilla::dom::DimensionInfo& aDimensionInfo); + mozilla::ipc::IPCResult RecvSizeModeChanged(const nsSizeMode& aSizeMode); + + mozilla::ipc::IPCResult RecvChildToParentMatrix( + const mozilla::Maybe& aMatrix, + const mozilla::ScreenRect& aTopLevelViewportVisibleRectInBrowserCoords); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvDynamicToolbarMaxHeightChanged( + const mozilla::ScreenIntCoord& aHeight); + + mozilla::ipc::IPCResult RecvDynamicToolbarOffsetChanged( + const mozilla::ScreenIntCoord& aOffset); + + mozilla::ipc::IPCResult RecvActivate(uint64_t aActionId); + + mozilla::ipc::IPCResult RecvDeactivate(uint64_t aActionId); + + mozilla::ipc::IPCResult RecvRealMouseMoveEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + mozilla::ipc::IPCResult RecvNormalPriorityRealMouseMoveEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + mozilla::ipc::IPCResult RecvRealMouseMoveEventForTests( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + mozilla::ipc::IPCResult RecvNormalPriorityRealMouseMoveEventForTests( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + mozilla::ipc::IPCResult RecvSynthMouseMoveEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + mozilla::ipc::IPCResult RecvNormalPrioritySynthMouseMoveEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + mozilla::ipc::IPCResult RecvRealMouseButtonEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + mozilla::ipc::IPCResult RecvNormalPriorityRealMouseButtonEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + mozilla::ipc::IPCResult RecvRealMouseEnterExitWidgetEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + mozilla::ipc::IPCResult RecvNormalPriorityRealMouseEnterExitWidgetEvent( + const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvRealDragEvent(const WidgetDragEvent& aEvent, + const uint32_t& aDragAction, + const uint32_t& aDropEffect, + nsIPrincipal* aPrincipal, + nsIContentSecurityPolicy* aCsp); + + mozilla::ipc::IPCResult RecvRealKeyEvent( + const mozilla::WidgetKeyboardEvent& aEvent, const nsID& aUUID); + + mozilla::ipc::IPCResult RecvNormalPriorityRealKeyEvent( + const mozilla::WidgetKeyboardEvent& aEvent, const nsID& aUUID); + + mozilla::ipc::IPCResult RecvMouseWheelEvent( + const mozilla::WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + mozilla::ipc::IPCResult RecvNormalPriorityMouseWheelEvent( + const mozilla::WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + mozilla::ipc::IPCResult RecvRealTouchEvent(const WidgetTouchEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, + const nsEventStatus& aApzResponse); + + mozilla::ipc::IPCResult RecvNormalPriorityRealTouchEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse); + + mozilla::ipc::IPCResult RecvRealTouchMoveEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse); + + mozilla::ipc::IPCResult RecvNormalPriorityRealTouchMoveEvent( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse); + + mozilla::ipc::IPCResult RecvRealTouchMoveEvent2( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + return RecvRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, aApzResponse); + } + + mozilla::ipc::IPCResult RecvNormalPriorityRealTouchMoveEvent2( + const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) { + return RecvNormalPriorityRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, + aApzResponse); + } + + mozilla::ipc::IPCResult RecvUpdateSHistory(); + + mozilla::ipc::IPCResult RecvNativeSynthesisResponse( + const uint64_t& aObserverId, const nsCString& aResponse); + + mozilla::ipc::IPCResult RecvCompositionEvent( + const mozilla::WidgetCompositionEvent& aEvent); + + mozilla::ipc::IPCResult RecvNormalPriorityCompositionEvent( + const mozilla::WidgetCompositionEvent& aEvent); + + mozilla::ipc::IPCResult RecvSelectionEvent( + const mozilla::WidgetSelectionEvent& aEvent); + + mozilla::ipc::IPCResult RecvNormalPrioritySelectionEvent( + const mozilla::WidgetSelectionEvent& aEvent); + + mozilla::ipc::IPCResult RecvInsertText(const nsAString& aStringToInsert); + + mozilla::ipc::IPCResult RecvUpdateRemoteStyle( + const StyleImageRendering& aImageRendering); + + mozilla::ipc::IPCResult RecvNormalPriorityInsertText( + const nsAString& aStringToInsert); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvPasteTransferable( + const IPCTransferableData& aTransferableData, const bool& aIsPrivateData, + nsIPrincipal* aRequestingPrincipal, + const nsContentPolicyType& aContentPolicyType); + + mozilla::ipc::IPCResult RecvLoadRemoteScript(const nsAString& aURL, + const bool& aRunInGlobalScope); + + mozilla::ipc::IPCResult RecvAsyncMessage(const nsAString& aMessage, + const ClonedMessageData& aData); + mozilla::ipc::IPCResult RecvSwappedWithOtherRemoteLoader( + const IPCTabContext& aContext); + + mozilla::ipc::IPCResult RecvSafeAreaInsetsChanged( + const mozilla::ScreenIntMargin& aSafeAreaInsets); + +#ifdef ACCESSIBILITY + PDocAccessibleChild* AllocPDocAccessibleChild( + PDocAccessibleChild*, const uint64_t&, + const MaybeDiscardedBrowsingContext&); + bool DeallocPDocAccessibleChild(PDocAccessibleChild*); +#endif + + RefPtr GetVsyncChild(); + + nsIWebNavigation* WebNavigation() const { return mWebNav; } + + PuppetWidget* WebWidget() { return mPuppetWidget; } + + bool IsTransparent() const { return mIsTransparent; } + + const EffectsInfo& GetEffectsInfo() const { return mEffectsInfo; } + + void SetBackgroundColor(const nscolor& aColor); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual mozilla::ipc::IPCResult RecvUpdateEffects( + const EffectsInfo& aEffects); + + void RequestEditCommands(NativeKeyBindingsType aType, + const WidgetKeyboardEvent& aEvent, + nsTArray& aCommands); + + bool IsVisible(); + bool IsPreservingLayers() const { return mIsPreservingLayers; } + + /** + * Signal to this BrowserChild that it should be made visible: + * activated widget, retained layer tree, etc. (Respectively, + * made not visible.) + */ + MOZ_CAN_RUN_SCRIPT void UpdateVisibility(); + MOZ_CAN_RUN_SCRIPT void MakeVisible(); + void MakeHidden(); + void PresShellActivenessMaybeChanged(); + + ContentChild* Manager() const { return mManager; } + + static inline BrowserChild* GetFrom(nsIDocShell* aDocShell) { + if (!aDocShell) { + return nullptr; + } + + nsCOMPtr tc = aDocShell->GetBrowserChild(); + return static_cast(tc.get()); + } + + static inline BrowserChild* GetFrom(mozIDOMWindow* aWindow) { + nsCOMPtr webNav = do_GetInterface(aWindow); + nsCOMPtr docShell = do_QueryInterface(webNav); + return GetFrom(docShell); + } + + static inline BrowserChild* GetFrom(mozIDOMWindowProxy* aWindow) { + nsCOMPtr webNav = do_GetInterface(aWindow); + nsCOMPtr docShell = do_QueryInterface(webNav); + return GetFrom(docShell); + } + + static BrowserChild* GetFrom(PresShell* aPresShell); + static BrowserChild* GetFrom(layers::LayersId aLayersId); + + layers::LayersId GetLayersId() { return mLayersId; } + Maybe IsLayersConnected() { return mLayersConnected; } + + void DidComposite(mozilla::layers::TransactionId aTransactionId, + const TimeStamp& aCompositeStart, + const TimeStamp& aCompositeEnd); + + void DidRequestComposite(const TimeStamp& aCompositeReqStart, + const TimeStamp& aCompositeReqEnd); + + void ClearCachedResources(); + void SchedulePaint(); + void ReinitRendering(); + void ReinitRenderingForDeviceReset(); + + void NotifyJankedAnimations(const nsTArray& aJankedAnimations); + + static inline BrowserChild* GetFrom(nsIDOMWindow* aWindow) { + nsCOMPtr webNav = do_GetInterface(aWindow); + nsCOMPtr docShell = do_QueryInterface(webNav); + return GetFrom(docShell); + } + + mozilla::ipc::IPCResult RecvUIResolutionChanged(const float& aDpi, + const int32_t& aRounding, + const double& aScale); + + mozilla::ipc::IPCResult RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray&& aCharCodes); + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvPrintPreview(const PrintData& aPrintData, + const MaybeDiscardedBrowsingContext&, + PrintPreviewResolver&& aCallback); + + mozilla::ipc::IPCResult RecvExitPrintPreview(); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvPrint( + const MaybeDiscardedBrowsingContext&, const PrintData&); + + mozilla::ipc::IPCResult RecvUpdateNativeWindowHandle( + const uintptr_t& aNewHandle); + + mozilla::ipc::IPCResult RecvWillChangeProcess(); + + PPaymentRequestChild* AllocPPaymentRequestChild(); + + bool DeallocPPaymentRequestChild(PPaymentRequestChild* aActor); + + LayoutDeviceIntPoint GetClientOffset() const { return mClientOffset; } + LayoutDeviceIntPoint GetChromeOffset() const { return mChromeOffset; }; + ScreenIntCoord GetDynamicToolbarMaxHeight() const { + return mDynamicToolbarMaxHeight; + }; + + bool IPCOpen() const { return mIPCOpen; } + + const mozilla::layers::CompositorOptions& GetCompositorOptions() const; + bool AsyncPanZoomEnabled() const; + + ScreenIntSize GetInnerSize(); + CSSSize GetUnscaledInnerSize() { return mUnscaledInnerSize; } + + Maybe GetVisibleRect() const; + + // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow(). + void DoFakeShow(const ParentShowInfo&); + + void ContentReceivedInputBlock(uint64_t aInputBlockId, + bool aPreventDefault) const; + void SetTargetAPZC( + uint64_t aInputBlockId, + const nsTArray& aTargets) const; + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvHandleTap( + const layers::GeckoContentController_TapType& aType, + const LayoutDevicePoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvNormalPriorityHandleTap( + const layers::GeckoContentController_TapType& aType, + const LayoutDevicePoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId); + + bool UpdateFrame(const layers::RepaintRequest& aRequest); + bool NotifyAPZStateChange( + const ViewID& aViewId, + const layers::GeckoContentController_APZStateChange& aChange, + const int& aArg, Maybe aInputBlockId); + void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics); + void ZoomToRect(const uint32_t& aPresShellId, + const ScrollableLayerGuid::ViewID& aViewId, + const CSSRect& aRect, const uint32_t& aFlags); + + // Request that the docshell be marked as active. + void PaintWhileInterruptingJS(const layers::LayersObserverEpoch& aEpoch); + + void UnloadLayersWhileInterruptingJS( + const layers::LayersObserverEpoch& aEpoch); + + nsresult CanCancelContentJS(nsIRemoteTab::NavigationType aNavigationType, + int32_t aNavigationIndex, nsIURI* aNavigationURI, + int32_t aEpoch, bool* aCanCancel); + + layers::LayersObserverEpoch LayersObserverEpoch() const { + return mLayersObserverEpoch; + } + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; } +#endif + + BrowsingContext* GetBrowsingContext() const { return mBrowsingContext; } + +#if defined(ACCESSIBILITY) + void SetTopLevelDocAccessibleChild(PDocAccessibleChild* aTopLevelChild) { + mTopLevelDocAccessibleChild = aTopLevelChild; + } + + PDocAccessibleChild* GetTopLevelDocAccessibleChild() { + return mTopLevelDocAccessibleChild; + } +#endif + + // The transform from the coordinate space of this BrowserChild to the + // coordinate space of the native window its BrowserParent is in. + mozilla::LayoutDeviceToLayoutDeviceMatrix4x4 + GetChildToParentConversionMatrix() const; + + // Returns the portion of the visible rect of this remote document in the + // top browser window coordinate system. This is the result of being clipped + // by all ancestor viewports. + Maybe GetTopLevelViewportVisibleRectInBrowserCoords() const; + + // Similar to above GetTopLevelViewportVisibleRectInBrowserCoords(), but in + // this out-of-process document's coordinate system. + Maybe GetTopLevelViewportVisibleRectInSelfCoords() const; + + // Prepare to dispatch all coalesced mousemove events. We'll move all data + // in mCoalescedMouseData to a nsDeque; then we start processing them. We + // can't fetch the coalesced event one by one and dispatch it because we may + // reentry the event loop and access to the same hashtable. It's called when + // dispatching some mouse events other than mousemove. + void FlushAllCoalescedMouseData(); + void ProcessPendingCoalescedMouseDataAndDispatchEvents(); + + void ProcessPendingCoalescedTouchData(); + + void HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + void SetCancelContentJSEpoch(int32_t aEpoch) { + mCancelContentJSEpoch = aEpoch; + } + + void UpdateSessionStore(); + + mozilla::dom::SessionStoreChild* GetSessionStoreChild() { + return mSessionStoreChild; + } + +#ifdef XP_WIN + // Check if the window this BrowserChild is associated with supports + // protected media (EME) or not. + // Returns a promise the will resolve true if the window supports protected + // media or false if it does not. The promise will be rejected with an + // ResponseRejectReason if the IPC needed to do the check fails. Callers + // should treat the reject case as if the window does not support protected + // media to ensure robust handling. + RefPtr + DoesWindowSupportProtectedMedia(); +#endif + + // Notify the content blocking event in the parent process. This sends an IPC + // message to the BrowserParent in the parent. The BrowserParent will find the + // top-level WindowGlobalParent and notify the event from it. + void NotifyContentBlockingEvent( + uint32_t aEvent, nsIChannel* aChannel, bool aBlocked, + const nsACString& aTrackingOrigin, + const nsTArray& aTrackingFullHashes, + const Maybe< + ContentBlockingNotifier::StorageAccessPermissionGrantedReason>& + aReason); + + protected: + virtual ~BrowserChild(); + + mozilla::ipc::IPCResult RecvDestroy(); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvRenderLayers( + const bool& aEnabled, const layers::LayersObserverEpoch& aEpoch); + + mozilla::ipc::IPCResult RecvPreserveLayers(bool); + + mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward, + const bool& aForDocumentNavigation); + + mozilla::ipc::IPCResult RecvSuppressDisplayport(const bool& aEnabled); + + mozilla::ipc::IPCResult RecvScrollbarPreferenceChanged(ScrollbarPreference); + + mozilla::ipc::IPCResult RecvStopIMEStateManagement(); + + mozilla::ipc::IPCResult RecvAllowScriptsToClose(); + + mozilla::ipc::IPCResult RecvReleaseAllPointerCapture(); + + private: + void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers, + const ScrollableLayerGuid& aGuid); + + void ActorDestroy(ActorDestroyReason why) override; + + bool InitBrowserChildMessageManager(); + + void InitRenderingState( + const TextureFactoryIdentifier& aTextureFactoryIdentifier, + const layers::LayersId& aLayersId, + const mozilla::layers::CompositorOptions& aCompositorOptions); + void InitAPZState(); + + void DestroyWindow(); + + void ApplyParentShowInfo(const ParentShowInfo&); + + bool HasValidInnerSize(); + + ScreenIntRect GetOuterRect(); + + void SetUnscaledInnerSize(const CSSSize& aSize) { + mUnscaledInnerSize = aSize; + } + + bool SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent); + + void UpdateRepeatedKeyEventEndTime(const WidgetKeyboardEvent& aEvent); + + void DispatchCoalescedWheelEvent(); + + /** + * Dispatch aEvent on aEvent.mWidget. + */ + nsEventStatus DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent); + + void DispatchWheelEvent(const WidgetWheelEvent& aEvent, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId); + + void InternalSetDocShellIsActive(bool aIsActive); + + bool CreateRemoteLayerManager( + mozilla::layers::PCompositorBridgeChild* aCompositorChild); + + nsresult PrepareRequestData(nsIRequest* aRequest, RequestData& aRequestData); + nsresult PrepareProgressListenerData(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + WebProgressData& aWebProgressData, + RequestData& aRequestData); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + nsresult UpdateRemotePrintSettings(const embedding::PrintData& aPrintData); + + MOZ_CAN_RUN_SCRIPT_BOUNDARY + nsresult CloneDocumentTreeIntoSelf( + const MaybeDiscarded& aSourceBC, + const embedding::PrintData& aPrintData); + + class DelayedDeleteRunnable; + + RefPtr mBrowserChildMessageManager; + TextureFactoryIdentifier mTextureFactoryIdentifier; + RefPtr mWebBrowser; + nsCOMPtr mWebNav; + RefPtr mPuppetWidget; + nsCOMPtr mLastURI; + RefPtr mManager; + RefPtr mBrowsingContext; + RefPtr mStatusFilter; + uint32_t mChromeFlags; + uint32_t mMaxTouchPoints; + layers::LayersId mLayersId; + CSSRect mUnscaledOuterRect; + Maybe mLayersConnected; + Maybe mLayersConnectRequested; + EffectsInfo mEffectsInfo; + + RefPtr mVsyncChild; + + RefPtr mAPZEventState; + + // Position of client area relative to the outer window + LayoutDeviceIntPoint mClientOffset; + // Position of tab, relative to parent widget (typically the window) + // NOTE: This value is valuable only for the top level browser. + LayoutDeviceIntPoint mChromeOffset; + ScreenIntCoord mDynamicToolbarMaxHeight; + TabId mUniqueId; + + bool mDidFakeShow : 1; + bool mTriedBrowserInit : 1; + bool mIgnoreKeyPressEvent : 1; + bool mHasValidInnerSize : 1; + bool mDestroyed : 1; + + // Whether or not this browser is the child part of the top level PBrowser + // actor in a remote browser. + bool mIsTopLevel : 1; + + bool mIsTransparent : 1; + bool mIPCOpen : 1; + + bool mDidSetRealShowInfo : 1; + bool mDidLoadURLInit : 1; + + bool mSkipKeyPress : 1; + bool mDidSetEffectsInfo : 1; + + bool mCoalesceMouseMoveEvents : 1; + + bool mShouldSendWebProgressEventsToParent : 1; + + // Whether we are rendering to the compositor or not. + bool mRenderLayers : 1; + + // Whether we're artificially preserving layers. + bool mIsPreservingLayers : 1; + + // Holds the compositor options for the compositor rendering this tab, + // once we find out which compositor that is. + Maybe mCompositorOptions; + + friend class ContentChild; + + CSSSize mUnscaledInnerSize; + + // Store the end time of the handling of the last repeated keydown/keypress + // event so that in case event handling takes time, some repeated events can + // be skipped to not flood child process. + mozilla::TimeStamp mRepeatedKeyEventTime; + + // Similar to mRepeatedKeyEventTime, store the end time (from parent process) + // of handling the last repeated wheel event so that in case event handling + // takes time, some repeated events can be skipped to not flood child process. + mozilla::TimeStamp mLastWheelProcessedTimeFromParent; + mozilla::TimeDuration mLastWheelProcessingDuration; + + // Hash table to track coalesced mousemove events for different pointers. + nsClassHashtable mCoalescedMouseData; + + nsDeque mToBeDispatchedMouseData; + + CoalescedWheelData mCoalescedWheelData; + CoalescedTouchData mCoalescedTouchData; + + RefPtr mCoalescedMouseEventFlusher; + RefPtr mCoalescedTouchMoveEventFlusher; + + RefPtr mApzcTreeManager; + RefPtr mSessionStoreChild; + + // The most recently seen layer observer epoch in RecvSetDocShellIsActive. + layers::LayersObserverEpoch mLayersObserverEpoch; + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + // The handle associated with the native window that contains this tab + uintptr_t mNativeWindowHandle; +#endif // defined(XP_WIN) + +#if defined(ACCESSIBILITY) + PDocAccessibleChild* mTopLevelDocAccessibleChild; +#endif + int32_t mCancelContentJSEpoch; + + Maybe mChildToParentConversionMatrix; + // When mChildToParentConversionMatrix is Nothing() this value is invalid. + ScreenRect mTopLevelViewportVisibleRectInBrowserCoords; + +#ifdef XP_WIN + // Should only be accessed on main thread. + Maybe mWindowSupportsProtectedMedia; +#endif + + // If set, resolve when we receive ChildToParentMatrix. + RefPtr mContentTransformPromise; + + DISALLOW_EVIL_CONSTRUCTORS(BrowserChild); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_BrowserChild_h diff --git a/dom/ipc/BrowserHost.cpp b/dom/ipc/BrowserHost.cpp new file mode 100644 index 0000000000..d0ce7c6ba4 --- /dev/null +++ b/dom/ipc/BrowserHost.cpp @@ -0,0 +1,289 @@ +/* -*- 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/. */ + +#include "mozilla/dom/BrowserHost.h" + +#include "mozilla/Unused.h" +#include "mozilla/dom/CancelContentJSOptionsBinding.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/ProcessPriorityManager.h" + +#include "nsIObserverService.h" + +namespace mozilla::dom { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserHost) + NS_INTERFACE_MAP_ENTRY(nsIRemoteTab) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, RemoteBrowser) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_WEAK(BrowserHost, mRoot) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserHost) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserHost) + +BrowserHost::BrowserHost(BrowserParent* aParent) + : mId(aParent->GetTabId()), + mRoot(aParent), + mEffectsInfo{EffectsInfo::FullyHidden()} { + mRoot->SetBrowserHost(this); +} + +BrowserHost* BrowserHost::GetFrom(nsIRemoteTab* aRemoteTab) { + return static_cast(aRemoteTab); +} + +TabId BrowserHost::GetTabId() const { return mId; } + +mozilla::layers::LayersId BrowserHost::GetLayersId() const { + return mRoot->GetLayersId(); +} + +BrowsingContext* BrowserHost::GetBrowsingContext() const { + return mRoot->GetBrowsingContext(); +} + +nsILoadContext* BrowserHost::GetLoadContext() const { + RefPtr loadContext = mRoot->GetLoadContext(); + return loadContext; +} + +bool BrowserHost::CanRecv() const { return mRoot && mRoot->CanRecv(); } + +a11y::DocAccessibleParent* BrowserHost::GetTopLevelDocAccessible() const { + return mRoot ? mRoot->GetTopLevelDocAccessible() : nullptr; +} + +void BrowserHost::LoadURL(nsDocShellLoadState* aLoadState) { + MOZ_ASSERT(aLoadState); + mRoot->LoadURL(aLoadState); +} + +void BrowserHost::ResumeLoad(uint64_t aPendingSwitchId) { + mRoot->ResumeLoad(aPendingSwitchId); +} + +void BrowserHost::DestroyStart() { mRoot->Destroy(); } + +void BrowserHost::DestroyComplete() { + if (!mRoot) { + return; + } + mRoot->SetOwnerElement(nullptr); + mRoot->Destroy(); + mRoot->SetBrowserHost(nullptr); + mRoot = nullptr; + + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIRemoteTab*, this), + "ipc:browser-destroyed", nullptr); + } +} + +bool BrowserHost::Show(const OwnerShowInfo& aShowInfo) { + return mRoot->Show(aShowInfo); +} + +void BrowserHost::UpdateDimensions(const nsIntRect& aRect, + const ScreenIntSize& aSize) { + mRoot->UpdateDimensions(aRect, aSize); +} + +void BrowserHost::UpdateEffects(EffectsInfo aEffects) { + if (!mRoot || mEffectsInfo == aEffects) { + return; + } + mEffectsInfo = aEffects; + Unused << mRoot->SendUpdateEffects(mEffectsInfo); +} + +/* attribute boolean renderLayers; */ +NS_IMETHODIMP +BrowserHost::GetRenderLayers(bool* aRenderLayers) { + if (!mRoot) { + *aRenderLayers = false; + return NS_OK; + } + *aRenderLayers = mRoot->GetRenderLayers(); + return NS_OK; +} + +NS_IMETHODIMP +BrowserHost::SetRenderLayers(bool aRenderLayers) { + if (!mRoot) { + return NS_OK; + } + + mRoot->SetRenderLayers(aRenderLayers); + return NS_OK; +} + +/* readonly attribute boolean hasLayers; */ +NS_IMETHODIMP +BrowserHost::GetHasLayers(bool* aHasLayers) { + *aHasLayers = mRoot && mRoot->GetHasLayers(); + return NS_OK; +} + +/* attribute boolean priorityHint; */ +NS_IMETHODIMP +BrowserHost::SetPriorityHint(bool aPriorityHint) { + if (!mRoot) { + return NS_OK; + } + mRoot->SetPriorityHint(aPriorityHint); + return NS_OK; +} + +NS_IMETHODIMP +BrowserHost::GetPriorityHint(bool* aPriorityHint) { + *aPriorityHint = mRoot && mRoot->GetPriorityHint(); + return NS_OK; +} + +/* void resolutionChanged (); */ +NS_IMETHODIMP +BrowserHost::NotifyResolutionChanged() { + if (!mRoot) { + return NS_OK; + } + VisitAll([](BrowserParent* aBrowserParent) { + aBrowserParent->NotifyResolutionChanged(); + }); + return NS_OK; +} + +/* void deprioritize (); */ +NS_IMETHODIMP +BrowserHost::Deprioritize() { + if (!mRoot) { + return NS_OK; + } + auto* bc = GetBrowsingContext()->Canonical(); + ProcessPriorityManager::BrowserPriorityChanged(bc, + /* aPriority = */ false); + return NS_OK; +} + +/* void preserveLayers (in boolean aPreserveLayers); */ +NS_IMETHODIMP +BrowserHost::PreserveLayers(bool aPreserveLayers) { + if (!mRoot) { + return NS_OK; + } + VisitAll([&](BrowserParent* aBrowserParent) { + aBrowserParent->PreserveLayers(aPreserveLayers); + }); + return NS_OK; +} + +/* readonly attribute uint64_t tabId; */ +NS_IMETHODIMP +BrowserHost::GetTabId(uint64_t* aTabId) { + *aTabId = mId; + return NS_OK; +} + +/* readonly attribute uint64_t contentProcessId; */ +NS_IMETHODIMP +BrowserHost::GetContentProcessId(uint64_t* aContentProcessId) { + if (!mRoot) { + *aContentProcessId = 0; + return NS_OK; + } + *aContentProcessId = GetContentParent()->ChildID(); + return NS_OK; +} + +/* readonly attribute int32_t osPid; */ +NS_IMETHODIMP +BrowserHost::GetOsPid(int32_t* aOsPid) { + if (!mRoot) { + *aOsPid = 0; + return NS_OK; + } + *aOsPid = GetContentParent()->Pid(); + return NS_OK; +} + +/* readonly attribute BrowsingContext browsingContext; */ +NS_IMETHODIMP +BrowserHost::GetBrowsingContext(BrowsingContext** aBc) { + if (!mRoot) { + *aBc = nullptr; + return NS_OK; + } + RefPtr bc = mRoot->GetBrowsingContext(); + bc.forget(aBc); + return NS_OK; +} + +/* readonly attribute boolean hasPresented; */ +NS_IMETHODIMP +BrowserHost::GetHasPresented(bool* aHasPresented) { + if (!mRoot) { + *aHasPresented = false; + return NS_OK; + } + *aHasPresented = mRoot->GetHasPresented(); + return NS_OK; +} + +/* void transmitPermissionsForPrincipal (in nsIPrincipal aPrincipal); */ +NS_IMETHODIMP +BrowserHost::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal) { + if (!mRoot) { + return NS_OK; + } + return GetContentParent()->TransmitPermissionsForPrincipal(aPrincipal); +} + +/* void createAboutBlankContentViewer(in nsIPrincipal aPrincipal, in + * nsIPrincipal aPartitionedPrincipal); */ +NS_IMETHODIMP +BrowserHost::CreateAboutBlankContentViewer( + nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal) { + if (!mRoot) { + return NS_OK; + } + + // Ensure the content process has permisisons for the new document we're about + // to create in it. + nsresult rv = GetContentParent()->TransmitPermissionsForPrincipal(aPrincipal); + if (NS_FAILED(rv)) { + return rv; + } + + Unused << mRoot->SendCreateAboutBlankContentViewer(aPrincipal, + aPartitionedPrincipal); + return NS_OK; +} + +NS_IMETHODIMP +BrowserHost::MaybeCancelContentJSExecutionFromScript( + nsIRemoteTab::NavigationType aNavigationType, + JS::Handle aCancelContentJSOptions, JSContext* aCx) { + // If we're in the process of creating a new window (via window.open), then + // the load that called this function isn't a "normal" load and should be + // ignored for the purposes of cancelling content JS. + if (!mRoot || mRoot->CreatingWindow()) { + return NS_OK; + } + dom::CancelContentJSOptions cancelContentJSOptions; + if (!cancelContentJSOptions.Init(aCx, aCancelContentJSOptions)) { + return NS_ERROR_INVALID_ARG; + } + if (StaticPrefs::dom_ipc_cancel_content_js_when_navigating()) { + GetContentParent()->CancelContentJSExecutionIfRunning( + mRoot, aNavigationType, cancelContentJSOptions); + } + return NS_OK; +} + +} // namespace mozilla::dom diff --git a/dom/ipc/BrowserHost.h b/dom/ipc/BrowserHost.h new file mode 100644 index 0000000000..5b651a1df0 --- /dev/null +++ b/dom/ipc/BrowserHost.h @@ -0,0 +1,109 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_BrowserHost_h +#define mozilla_dom_BrowserHost_h + +#include "nsIRemoteTab.h" +#include "mozilla/dom/RemoteBrowser.h" +#include "mozilla/dom/BrowserParent.h" + +class nsPIDOMWindowOuter; + +namespace mozilla { + +namespace a11y { +class DocAccessibleParent; +} // namespace a11y + +namespace dom { + +class Element; + +/** + * BrowserHost manages a remote browser from the chrome process. + * + * It is used via the RemoteBrowser interface in nsFrameLoader and supports + * operations over the tree of BrowserParent/BrowserBridgeParent's. + * + * See `dom/docs/Fission-IPC-Diagram.svg` for an overview of the DOM IPC + * actors. + */ +class BrowserHost : public RemoteBrowser, + public nsIRemoteTab, + public nsSupportsWeakReference { + public: + typedef mozilla::layers::LayersId LayersId; + + explicit BrowserHost(BrowserParent* aParent); + + static BrowserHost* GetFrom(nsIRemoteTab* aRemoteTab); + + // NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + // nsIRemoteTab + NS_DECL_NSIREMOTETAB + + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(BrowserHost, RemoteBrowser) + + // Get the IPDL actor for the root BrowserParent. This method should be + // avoided and consumers migrated to use this class. + BrowserParent* GetActor() { return mRoot; } + ContentParent* GetContentParent() const { + return mRoot ? mRoot->Manager() : nullptr; + } + + BrowserHost* AsBrowserHost() override { return this; } + BrowserBridgeHost* AsBrowserBridgeHost() override { return nullptr; } + + TabId GetTabId() const override; + LayersId GetLayersId() const override; + BrowsingContext* GetBrowsingContext() const override; + nsILoadContext* GetLoadContext() const override; + bool CanRecv() const override; + + Element* GetOwnerElement() const { return mRoot->GetOwnerElement(); } + already_AddRefed GetParentWindowOuter() const { + return mRoot->GetParentWindowOuter(); + } + a11y::DocAccessibleParent* GetTopLevelDocAccessible() const; + + // Visit each BrowserParent in the tree formed by PBrowser and + // PBrowserBridge that is anchored by `mRoot`. + template + void VisitAll(Callback aCallback) { + if (!mRoot) { + return; + } + mRoot->VisitAll(aCallback); + } + + void LoadURL(nsDocShellLoadState* aLoadState) override; + void ResumeLoad(uint64_t aPendingSwitchId) override; + void DestroyStart() override; + void DestroyComplete() override; + + bool Show(const OwnerShowInfo&) override; + void UpdateDimensions(const nsIntRect& aRect, + const ScreenIntSize& aSize) override; + + void UpdateEffects(EffectsInfo aInfo) override; + + private: + virtual ~BrowserHost() = default; + + // The TabID for the root BrowserParent, we cache this so that we can access + // it after the remote browser has been destroyed + TabId mId; + // The root BrowserParent of this remote browser + RefPtr mRoot; + EffectsInfo mEffectsInfo; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_BrowserHost_h diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp new file mode 100644 index 0000000000..2fc97d4c64 --- /dev/null +++ b/dom/ipc/BrowserParent.cpp @@ -0,0 +1,4120 @@ +/* -*- 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/. */ + +#include "base/basictypes.h" + +#include "BrowserParent.h" +#include "mozilla/AlreadyAddRefed.h" +#include "mozilla/EventForwards.h" + +#ifdef ACCESSIBILITY +# include "mozilla/a11y/DocAccessibleParent.h" +# include "mozilla/a11y/Platform.h" +# include "mozilla/a11y/RemoteAccessibleBase.h" +# include "nsAccessibilityService.h" +#endif +#include "mozilla/Components.h" +#include "mozilla/dom/BrowserHost.h" +#include "mozilla/dom/BrowserSessionStore.h" +#include "mozilla/dom/BrowsingContextGroup.h" +#include "mozilla/dom/CancelContentJSOptionsBinding.h" +#include "mozilla/dom/ChromeMessageSender.h" +#include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/ContentProcessManager.h" +#include "mozilla/dom/DataTransfer.h" +#include "mozilla/dom/DataTransferItemList.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/dom/PaymentRequestParent.h" +#include "mozilla/dom/PointerEventHandler.h" +#include "mozilla/dom/BrowserBridgeParent.h" +#include "mozilla/dom/RemoteDragStartData.h" +#include "mozilla/dom/RemoteWebProgressRequest.h" +#include "mozilla/dom/SessionHistoryEntry.h" +#include "mozilla/dom/SessionStoreParent.h" +#include "mozilla/dom/UserActivation.h" +#include "mozilla/EventStateManager.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/layers/AsyncDragMetrics.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layout/RemoteLayerTreeOwner.h" +#include "mozilla/LookAndFeel.h" +#include "mozilla/Maybe.h" +#include "mozilla/MiscEvents.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/NativeKeyBindingsType.h" +#include "mozilla/net/NeckoChild.h" +#include "mozilla/net/CookieJarSettings.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/ProcessHangMonitor.h" +#include "mozilla/RefPtr.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/TextEventDispatcher.h" +#include "mozilla/TextEvents.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsDebug.h" +#include "nsFocusManager.h" +#include "nsFrameLoader.h" +#include "nsFrameLoaderOwner.h" +#include "nsFrameManager.h" +#include "nsIBaseWindow.h" +#include "nsIBrowser.h" +#include "nsIBrowserController.h" +#include "nsIContent.h" +#include "nsICookieJarSettings.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeOwner.h" +#include "nsImportModule.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsILoadInfo.h" +#include "nsIPromptFactory.h" +#include "nsIURI.h" +#include "nsIWebBrowserChrome.h" +#include "nsIWebProtocolHandlerRegistrar.h" +#include "nsIWindowWatcher.h" +#include "nsIXPConnect.h" +#include "nsIXULBrowserWindow.h" +#include "nsIAppWindow.h" +#include "nsLayoutUtils.h" +#include "nsQueryActor.h" +#include "nsSHistory.h" +#include "nsViewManager.h" +#include "nsVariant.h" +#include "nsIWidget.h" +#include "nsNetUtil.h" +#ifndef XP_WIN +# include "nsJARProtocolHandler.h" +#endif +#include "nsPIDOMWindow.h" +#include "nsPrintfCString.h" +#include "nsQueryObject.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "PermissionMessageUtils.h" +#include "StructuredCloneData.h" +#include "ColorPickerParent.h" +#include "FilePickerParent.h" +#include "BrowserChild.h" +#include "nsNetCID.h" +#include "nsIAuthInformation.h" +#include "nsIAuthPromptCallback.h" +#include "nsAuthInformationHolder.h" +#include "nsICancelable.h" +#include "gfxUtils.h" +#include "nsILoginManagerAuthPrompter.h" +#include "nsPIWindowRoot.h" +#include "nsReadableUtils.h" +#include "nsIAuthPrompt2.h" +#include "gfxDrawable.h" +#include "ImageOps.h" +#include "UnitTransforms.h" +#include +#include "mozilla/NullPrincipal.h" +#include "mozilla/WebBrowserPersistDocumentParent.h" +#include "ProcessPriorityManager.h" +#include "nsString.h" +#include "IHistory.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" +#include "mozilla/ProfilerLabels.h" +#include "MMPrinter.h" +#include "mozilla/dom/CrashReport.h" +#include "nsISecureBrowserUI.h" +#include "nsIXULRuntime.h" +#include "VsyncSource.h" +#include "nsSubDocumentFrame.h" + +#ifdef XP_WIN +# include "FxRWindowManager.h" +#endif + +#if defined(XP_WIN) && defined(ACCESSIBILITY) +# include "mozilla/a11y/AccessibleWrap.h" +# include "mozilla/a11y/Compatibility.h" +# include "mozilla/a11y/nsWinUtils.h" +#endif + +#ifdef MOZ_ANDROID_HISTORY +# include "GeckoViewHistory.h" +#endif + +#if defined(MOZ_WIDGET_ANDROID) +# include "mozilla/widget/nsWindow.h" +#endif // defined(MOZ_WIDGET_ANDROID) + +using namespace mozilla::dom; +using namespace mozilla::ipc; +using namespace mozilla::layers; +using namespace mozilla::layout; +using namespace mozilla::services; +using namespace mozilla::widget; +using namespace mozilla::gfx; + +using mozilla::LazyLogModule; + +extern mozilla::LazyLogModule gSHIPBFCacheLog; + +LazyLogModule gBrowserFocusLog("BrowserFocus"); + +#define LOGBROWSERFOCUS(args) \ + MOZ_LOG(gBrowserFocusLog, mozilla::LogLevel::Debug, args) + +/* static */ +BrowserParent* BrowserParent::sFocus = nullptr; +/* static */ +BrowserParent* BrowserParent::sTopLevelWebFocus = nullptr; +/* static */ +BrowserParent* BrowserParent::sLastMouseRemoteTarget = nullptr; + +// The flags passed by the webProgress notifications are 16 bits shifted +// from the ones registered by webProgressListeners. +#define NOTIFY_FLAG_SHIFT 16 + +namespace mozilla { + +/** + * Store data of a keypress event which is requesting to handled it in a remote + * process or some remote processes. + */ +class RequestingAccessKeyEventData { + public: + RequestingAccessKeyEventData() = delete; + + static void OnBrowserParentCreated() { + MOZ_ASSERT(sBrowserParentCount <= INT32_MAX); + sBrowserParentCount++; + } + static void OnBrowserParentDestroyed() { + MOZ_ASSERT(sBrowserParentCount > 0); + sBrowserParentCount--; + // To avoid memory leak, we need to reset sData when the last BrowserParent + // is destroyed. + if (!sBrowserParentCount) { + Clear(); + } + } + + static void Set(const WidgetKeyboardEvent& aKeyPressEvent) { + MOZ_ASSERT(aKeyPressEvent.mMessage == eKeyPress); + MOZ_ASSERT(sBrowserParentCount > 0); + sData = + Some(Data{aKeyPressEvent.mAlternativeCharCodes, aKeyPressEvent.mKeyCode, + aKeyPressEvent.mCharCode, aKeyPressEvent.mKeyNameIndex, + aKeyPressEvent.mCodeNameIndex, aKeyPressEvent.mKeyValue, + aKeyPressEvent.mModifiers}); + } + + static void Clear() { sData.reset(); } + + [[nodiscard]] static bool Equals(const WidgetKeyboardEvent& aKeyPressEvent) { + MOZ_ASSERT(sBrowserParentCount > 0); + return sData.isSome() && sData->Equals(aKeyPressEvent); + } + + [[nodiscard]] static bool IsSet() { + MOZ_ASSERT(sBrowserParentCount > 0); + return sData.isSome(); + } + + private: + struct Data { + [[nodiscard]] bool Equals(const WidgetKeyboardEvent& aKeyPressEvent) { + return mKeyCode == aKeyPressEvent.mKeyCode && + mCharCode == aKeyPressEvent.mCharCode && + mKeyNameIndex == aKeyPressEvent.mKeyNameIndex && + mCodeNameIndex == aKeyPressEvent.mCodeNameIndex && + mKeyValue == aKeyPressEvent.mKeyValue && + mModifiers == aKeyPressEvent.mModifiers && + mAlternativeCharCodes == aKeyPressEvent.mAlternativeCharCodes; + } + + CopyableTArray mAlternativeCharCodes; + uint32_t mKeyCode; + uint32_t mCharCode; + KeyNameIndex mKeyNameIndex; + CodeNameIndex mCodeNameIndex; + nsString mKeyValue; + Modifiers mModifiers; + }; + static Maybe sData; + static int32_t sBrowserParentCount; +}; +int32_t RequestingAccessKeyEventData::sBrowserParentCount = 0; +Maybe RequestingAccessKeyEventData::sData; + +namespace dom { + +BrowserParent::LayerToBrowserParentTable* + BrowserParent::sLayerToBrowserParentTable = nullptr; + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserParent) + NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener) +NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTION_WEAK(BrowserParent, mFrameLoader, mBrowsingContext) +NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserParent) +NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserParent) + +BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId, + const TabContext& aContext, + CanonicalBrowsingContext* aBrowsingContext, + uint32_t aChromeFlags) + : TabContext(aContext), + mTabId(aTabId), + mManager(aManager), + mBrowsingContext(aBrowsingContext), + mFrameElement(nullptr), + mBrowserDOMWindow(nullptr), + mFrameLoader(nullptr), + mChromeFlags(aChromeFlags), + mBrowserBridgeParent(nullptr), + mBrowserHost(nullptr), + mContentCache(*this), + mRemoteLayerTreeOwner{}, + mLayerTreeEpoch{1}, + mChildToParentConversionMatrix{}, + mRect(0, 0, 0, 0), + mDimensions(0, 0), + mDPI(0), + mRounding(0), + mDefaultScale(0), + mUpdatedDimensions(false), + mSizeMode(nsSizeMode_Normal), + mClientOffset{}, + mChromeOffset{}, + mCreatingWindow(false), + mDelayedFrameScripts{}, + mVsyncParent(nullptr), + mMarkedDestroying(false), + mIsDestroyed(false), + mRemoteTargetSetsCursor(false), + mIsPreservingLayers(false), + mRenderLayers(true), + mPriorityHint(false), + mHasLayers(false), + mHasPresented(false), + mIsReadyToHandleInputEvents(false), + mIsMouseEnterIntoWidgetEventSuppressed(false), + mLockedNativePointer(false), + mShowingTooltip(false) { + MOZ_ASSERT(aManager); + + RequestingAccessKeyEventData::OnBrowserParentCreated(); + + // When the input event queue is disabled, we don't need to handle the case + // that some input events are dispatched before PBrowserConstructor. + mIsReadyToHandleInputEvents = !ContentParent::IsInputEventQueueSupported(); + + // Make sure to compute our process priority if needed before the block of + // code below. This makes sure the block below prioritizes our process if + // needed. + if (aBrowsingContext->IsTop()) { + RecomputeProcessPriority(); + } + + // If we're in a BC tree that is active with respect to the priority manager, + // ensure that this new BrowserParent is marked as active. This ensures that + // the process will be prioritized in a cross-site iframe navigation in an + // active tab, and also that the process is correctly prioritized if we got + // created for a browsing context which was already active. + if (aBrowsingContext->Top()->IsPriorityActive()) { + ProcessPriorityManager::BrowserPriorityChanged(this, true); + } +} + +BrowserParent::~BrowserParent() { + RequestingAccessKeyEventData::OnBrowserParentDestroyed(); +} + +/* static */ +BrowserParent* BrowserParent::GetFocused() { return sFocus; } + +/* static */ +BrowserParent* BrowserParent::GetLastMouseRemoteTarget() { + return sLastMouseRemoteTarget; +} + +/*static*/ +BrowserParent* BrowserParent::GetFrom(nsFrameLoader* aFrameLoader) { + if (!aFrameLoader) { + return nullptr; + } + return aFrameLoader->GetBrowserParent(); +} + +/*static*/ +BrowserParent* BrowserParent::GetFrom(PBrowserParent* aBrowserParent) { + return static_cast(aBrowserParent); +} + +/*static*/ +BrowserParent* BrowserParent::GetFrom(nsIContent* aContent) { + RefPtr loaderOwner = do_QueryObject(aContent); + if (!loaderOwner) { + return nullptr; + } + RefPtr frameLoader = loaderOwner->GetFrameLoader(); + return GetFrom(frameLoader); +} + +/* static */ +BrowserParent* BrowserParent::GetBrowserParentFromLayersId( + layers::LayersId aLayersId) { + if (!sLayerToBrowserParentTable) { + return nullptr; + } + return sLayerToBrowserParentTable->Get(uint64_t(aLayersId)); +} + +/*static*/ +TabId BrowserParent::GetTabIdFrom(nsIDocShell* docShell) { + nsCOMPtr browserChild(BrowserChild::GetFrom(docShell)); + if (browserChild) { + return static_cast(browserChild.get())->GetTabId(); + } + return TabId(0); +} + +void BrowserParent::AddBrowserParentToTable(layers::LayersId aLayersId, + BrowserParent* aBrowserParent) { + if (!sLayerToBrowserParentTable) { + sLayerToBrowserParentTable = new LayerToBrowserParentTable(); + } + sLayerToBrowserParentTable->InsertOrUpdate(uint64_t(aLayersId), + aBrowserParent); +} + +void BrowserParent::RemoveBrowserParentFromTable(layers::LayersId aLayersId) { + if (!sLayerToBrowserParentTable) { + return; + } + sLayerToBrowserParentTable->Remove(uint64_t(aLayersId)); + if (sLayerToBrowserParentTable->Count() == 0) { + delete sLayerToBrowserParentTable; + sLayerToBrowserParentTable = nullptr; + } +} + +already_AddRefed BrowserParent::GetLoadContext() { + return do_AddRef(mBrowsingContext); +} + +/** + * Will return nullptr if there is no outer window available for the + * document hosting the owner element of this BrowserParent. Also will return + * nullptr if that outer window is in the process of closing. + */ +already_AddRefed BrowserParent::GetParentWindowOuter() { + nsCOMPtr frame = GetOwnerElement(); + if (!frame) { + return nullptr; + } + + nsCOMPtr parent = frame->OwnerDoc()->GetWindow(); + if (!parent || parent->Closed()) { + return nullptr; + } + + return parent.forget(); +} + +already_AddRefed BrowserParent::GetTopLevelWidget() { + if (RefPtr element = mFrameElement) { + if (PresShell* presShell = element->OwnerDoc()->GetPresShell()) { + return do_AddRef(presShell->GetViewManager()->GetRootWidget()); + } + } + return nullptr; +} + +already_AddRefed BrowserParent::GetTextInputHandlingWidget() const { + if (!mFrameElement) { + return nullptr; + } + PresShell* presShell = mFrameElement->OwnerDoc()->GetPresShell(); + if (!presShell) { + return nullptr; + } + nsPresContext* presContext = presShell->GetPresContext(); + if (!presContext) { + return nullptr; + } + nsCOMPtr widget = presContext->GetTextInputHandlingWidget(); + return widget.forget(); +} + +already_AddRefed BrowserParent::GetWidget() const { + if (!mFrameElement) { + return nullptr; + } + nsCOMPtr widget = nsContentUtils::WidgetForContent(mFrameElement); + if (!widget) { + widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc()); + } + return widget.forget(); +} + +already_AddRefed BrowserParent::GetDocWidget() const { + if (!mFrameElement) { + return nullptr; + } + return do_AddRef( + nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc())); +} + +nsIXULBrowserWindow* BrowserParent::GetXULBrowserWindow() { + if (!mFrameElement) { + return nullptr; + } + + nsCOMPtr docShell = mFrameElement->OwnerDoc()->GetDocShell(); + if (!docShell) { + return nullptr; + } + + nsCOMPtr treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + if (!treeOwner) { + return nullptr; + } + + nsCOMPtr window = do_GetInterface(treeOwner); + if (!window) { + return nullptr; + } + + nsCOMPtr xulBrowserWindow; + window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow)); + return xulBrowserWindow; +} + +uint32_t BrowserParent::GetMaxTouchPoints(Element* aElement) { + if (!aElement) { + return 0; + } + + if (StaticPrefs::dom_maxtouchpoints_testing_value() >= 0) { + return StaticPrefs::dom_maxtouchpoints_testing_value(); + } + + nsIWidget* widget = nsContentUtils::WidgetForDocument(aElement->OwnerDoc()); + return widget ? widget->GetMaxTouchPoints() : 0; +} + +a11y::DocAccessibleParent* BrowserParent::GetTopLevelDocAccessible() const { +#ifdef ACCESSIBILITY + // XXX Consider managing non top level PDocAccessibles with their parent + // document accessible. + const ManagedContainer& docs = + ManagedPDocAccessibleParent(); + for (auto* key : docs) { + auto* doc = static_cast(key); + // We want the document for this BrowserParent even if it's for an + // embedded out-of-process iframe. Therefore, we use + // IsTopLevelInContentProcess. In contrast, using IsToplevel would only + // include documents that aren't embedded; e.g. tab documents. + if (doc->IsTopLevelInContentProcess() && !doc->IsShutdown()) { + return doc; + } + } +#endif + return nullptr; +} + +LayersId BrowserParent::GetLayersId() const { + if (!mRemoteLayerTreeOwner.IsInitialized()) { + return LayersId{}; + } + return mRemoteLayerTreeOwner.GetLayersId(); +} + +BrowserBridgeParent* BrowserParent::GetBrowserBridgeParent() const { + return mBrowserBridgeParent; +} + +BrowserHost* BrowserParent::GetBrowserHost() const { return mBrowserHost; } + +ParentShowInfo BrowserParent::GetShowInfo() { + TryCacheDPIAndScale(); + if (mFrameElement) { + nsAutoString name; + mFrameElement->GetAttr(nsGkAtoms::name, name); + bool isTransparent = + nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) && + mFrameElement->HasAttr(nsGkAtoms::transparent); + return ParentShowInfo(name, false, isTransparent, mDPI, mRounding, + mDefaultScale.scale); + } + + return ParentShowInfo(u""_ns, false, false, mDPI, mRounding, + mDefaultScale.scale); +} + +already_AddRefed BrowserParent::GetContentPrincipal() const { + nsCOMPtr browser = + mFrameElement ? mFrameElement->AsBrowser() : nullptr; + NS_ENSURE_TRUE(browser, nullptr); + + RefPtr principal; + + nsresult rv; + rv = browser->GetContentPrincipal(getter_AddRefs(principal)); + NS_ENSURE_SUCCESS(rv, nullptr); + + return principal.forget(); +} + +void BrowserParent::SetOwnerElement(Element* aElement) { + // If we held previous content then unregister for its events. + RemoveWindowListeners(); + + // If we change top-level documents then we need to change our + // registration with them. + RefPtr curTopLevelWin, newTopLevelWin; + if (mFrameElement) { + curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc()); + } + if (aElement) { + newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc()); + } + bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin; + if (mBrowserHost && curTopLevelWin && !isSameTopLevelWin) { + curTopLevelWin->RemoveBrowser(mBrowserHost); + } + + // Update to the new content, and register to listen for events from it. + mFrameElement = aElement; + + if (mBrowserHost && newTopLevelWin && !isSameTopLevelWin) { + newTopLevelWin->AddBrowser(mBrowserHost); + } + +#if defined(XP_WIN) && defined(ACCESSIBILITY) + if (!mIsDestroyed) { + uintptr_t newWindowHandle = 0; + if (nsCOMPtr widget = GetWidget()) { + newWindowHandle = + reinterpret_cast(widget->GetNativeData(NS_NATIVE_WINDOW)); + } + Unused << SendUpdateNativeWindowHandle(newWindowHandle); + a11y::DocAccessibleParent* doc = GetTopLevelDocAccessible(); + if (doc) { + HWND hWnd = reinterpret_cast(doc->GetEmulatedWindowHandle()); + if (hWnd) { + HWND parentHwnd = reinterpret_cast(newWindowHandle); + if (parentHwnd != ::GetParent(hWnd)) { + ::SetParent(hWnd, parentHwnd); + } + } + } + } +#endif + + AddWindowListeners(); + + // The DPI depends on our frame element's widget, so invalidate now in case + // we've tried to cache it already. + mDPI = -1; + TryCacheDPIAndScale(); + + if (mRemoteLayerTreeOwner.IsInitialized()) { + mRemoteLayerTreeOwner.OwnerContentChanged(); + } + + // Set our BrowsingContext's embedder if we're not embedded within a + // BrowserBridgeParent. + if (!GetBrowserBridgeParent() && mBrowsingContext && mFrameElement) { + mBrowsingContext->SetEmbedderElement(mFrameElement); + } + + UpdateVsyncParentVsyncDispatcher(); + + VisitChildren([aElement](BrowserBridgeParent* aBrowser) { + if (auto* browserParent = aBrowser->GetBrowserParent()) { + browserParent->SetOwnerElement(aElement); + } + }); +} + +void BrowserParent::CacheFrameLoader(nsFrameLoader* aFrameLoader) { + mFrameLoader = aFrameLoader; +} + +void BrowserParent::AddWindowListeners() { + if (mFrameElement) { + if (nsCOMPtr window = + mFrameElement->OwnerDoc()->GetWindow()) { + nsCOMPtr eventTarget = window->GetTopWindowRoot(); + if (eventTarget) { + eventTarget->AddEventListener(u"MozUpdateWindowPos"_ns, this, false, + false); + eventTarget->AddEventListener(u"fullscreenchange"_ns, this, false, + false); + } + } + } +} + +void BrowserParent::RemoveWindowListeners() { + if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) { + nsCOMPtr window = + mFrameElement->OwnerDoc()->GetWindow(); + nsCOMPtr eventTarget = window->GetTopWindowRoot(); + if (eventTarget) { + eventTarget->RemoveEventListener(u"MozUpdateWindowPos"_ns, this, false); + eventTarget->RemoveEventListener(u"fullscreenchange"_ns, this, false); + } + } +} + +void BrowserParent::Deactivated() { + if (mShowingTooltip) { + // Reuse the normal tooltip hiding method. + mozilla::Unused << RecvHideTooltip(); + } + UnlockNativePointer(); + UnsetTopLevelWebFocus(this); + UnsetLastMouseRemoteTarget(this); + PointerLockManager::ReleaseLockedRemoteTarget(this); + PointerEventHandler::ReleasePointerCaptureRemoteTarget(this); + PresShell::ReleaseCapturingRemoteTarget(this); + ProcessPriorityManager::BrowserPriorityChanged(this, /* aPriority = */ false); +} + +void BrowserParent::DestroyInternal() { + Deactivated(); + + RemoveWindowListeners(); + +#ifdef ACCESSIBILITY + if (a11y::DocAccessibleParent* tabDoc = GetTopLevelDocAccessible()) { +# if defined(ANDROID) + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); +# endif + tabDoc->Destroy(); + } +#endif + + // If this fails, it's most likely due to a content-process crash, + // and auto-cleanup will kick in. Otherwise, the child side will + // destroy itself and send back __delete__(). + Unused << SendDestroy(); +} + +void BrowserParent::Destroy() { + // Aggressively release the window to avoid leaking the world in shutdown + // corner cases. + mBrowserDOMWindow = nullptr; + + if (mIsDestroyed) { + return; + } + + // If we are shutting down everything or we know to be the last + // BrowserParent, signal the impending shutdown early to the content process + // to avoid to run the SendDestroy before we know we are ExpectingShutdown. + Manager()->NotifyTabWillDestroy(); + + DestroyInternal(); + + mIsDestroyed = true; + + Manager()->NotifyTabDestroying(); + + // This `AddKeepAlive` will be cleared if `mMarkedDestroying` is set in + // `ActorDestroy`. Out of caution, we don't add the `KeepAlive` if our IPC + // actor has somehow already been destroyed, as that would mean `ActorDestroy` + // won't be called. + if (CanRecv()) { + mBrowsingContext->Group()->AddKeepAlive(); + } + + mMarkedDestroying = true; +} + +mozilla::ipc::IPCResult BrowserParent::RecvDidUnsuppressPainting() { + if (!mFrameElement) { + return IPC_OK(); + } + nsSubDocumentFrame* subdocFrame = + do_QueryFrame(mFrameElement->GetPrimaryFrame()); + if (subdocFrame && subdocFrame->HasRetainedPaintData()) { + subdocFrame->ClearRetainedPaintData(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvEnsureLayersConnected( + CompositorOptions* aCompositorOptions) { + if (mRemoteLayerTreeOwner.IsInitialized()) { + mRemoteLayerTreeOwner.EnsureLayersConnected(aCompositorOptions); + } + return IPC_OK(); +} + +void BrowserParent::ActorDestroy(ActorDestroyReason why) { + Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying); + + ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); + if (cpm) { + cpm->UnregisterRemoteFrame(mTabId); + } + + if (mRemoteLayerTreeOwner.IsInitialized()) { + auto layersId = mRemoteLayerTreeOwner.GetLayersId(); + if (mFrameElement) { + nsSubDocumentFrame* f = do_QueryFrame(mFrameElement->GetPrimaryFrame()); + if (f && f->HasRetainedPaintData() && + f->GetRemotePaintData().mLayersId == layersId) { + f->ClearRetainedPaintData(); + } + } + + // It's important to unmap layers after the remote browser has been + // destroyed, otherwise it may still send messages to the compositor which + // will reject them, causing assertions. + RemoveBrowserParentFromTable(layersId); + mRemoteLayerTreeOwner.Destroy(); + } + + // Even though BrowserParent::Destroy calls this, we need to do it here too in + // case of a crash. + Deactivated(); + + if (why == AbnormalShutdown) { + // dom_reporting_header must also be enabled for the report to be sent. + if (StaticPrefs::dom_reporting_crash_enabled()) { + nsCOMPtr principal = GetContentPrincipal(); + + if (principal) { + nsAutoCString crash_reason; + CrashReporter::GetAnnotation(OtherPid(), + CrashReporter::Annotation::MozCrashReason, + crash_reason); + // FIXME(arenevier): Find a less fragile way to identify that a crash + // was caused by OOM + bool is_oom = false; + if (crash_reason == "OOM" || crash_reason == "OOM!" || + StringBeginsWith(crash_reason, "[unhandlable oom]"_ns) || + StringBeginsWith(crash_reason, "Unhandlable OOM"_ns)) { + is_oom = true; + } + + CrashReport::Deliver(principal, is_oom); + } + } + } + + // If we were shutting down normally, we held a reference to our + // BrowsingContextGroup in `BrowserParent::Destroy`. Clear that reference + // here. + if (mMarkedDestroying) { + mBrowsingContext->Group()->RemoveKeepAlive(); + } + + // Tell our embedder that the tab is now going away unless we're an + // out-of-process iframe. + RefPtr frameLoader = GetFrameLoader(true); + if (frameLoader) { + ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr); + + if (mBrowsingContext->IsTop()) { + // If this is a top-level BrowsingContext, tell the frameloader it's time + // to go away. Otherwise, this is a subframe crash, and we can keep the + // frameloader around. + frameLoader->DestroyComplete(); + } + + // If this was a crash, tell our nsFrameLoader to fire crash events. + if (why == AbnormalShutdown) { + frameLoader->MaybeNotifyCrashed(mBrowsingContext, Manager()->ChildID(), + GetIPCChannel()); + } else if (why == ManagedEndpointDropped) { + // If we instead failed due to a constructor error, don't include process + // information, as the process did not crash. + frameLoader->MaybeNotifyCrashed(mBrowsingContext, ContentParentId{}, + nullptr); + } + } + + mFrameLoader = nullptr; + + // If we were destroyed due to our ManagedEndpoints being dropped, make a + // point of showing the subframe crashed UI. We don't fire the full + // `MaybeNotifyCrashed` codepath, as the entire process hasn't crashed on us, + // and it may confuse the frontend. + mBrowsingContext->BrowserParentDestroyed( + this, why == AbnormalShutdown || why == ManagedEndpointDropped); +} + +mozilla::ipc::IPCResult BrowserParent::RecvMoveFocus( + const bool& aForward, const bool& aForDocumentNavigation) { + LOGBROWSERFOCUS(("RecvMoveFocus %p, aForward: %d, aForDocumentNavigation: %d", + this, aForward, aForDocumentNavigation)); + BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent(); + if (bridgeParent) { + mozilla::Unused << bridgeParent->SendMoveFocus(aForward, + aForDocumentNavigation); + return IPC_OK(); + } + + RefPtr fm = nsFocusManager::GetFocusManager(); + if (fm) { + RefPtr dummy; + + uint32_t type = + aForward + ? (aForDocumentNavigation + ? static_cast( + nsIFocusManager::MOVEFOCUS_FORWARDDOC) + : static_cast(nsIFocusManager::MOVEFOCUS_FORWARD)) + : (aForDocumentNavigation + ? static_cast( + nsIFocusManager::MOVEFOCUS_BACKWARDDOC) + : static_cast( + nsIFocusManager::MOVEFOCUS_BACKWARD)); + fm->MoveFocus(nullptr, mFrameElement, type, nsIFocusManager::FLAG_BYKEY, + getter_AddRefs(dummy)); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvDropLinks( + nsTArray&& aLinks) { + nsCOMPtr browser = + mFrameElement ? mFrameElement->AsBrowser() : nullptr; + if (browser) { + // Verify that links have not been modified by the child. If links have + // not been modified then it's safe to load those links using the + // SystemPrincipal. If they have been modified by web content, then + // we use a NullPrincipal which still allows to load web links. + bool loadUsingSystemPrincipal = true; + if (aLinks.Length() != mVerifyDropLinks.Length()) { + loadUsingSystemPrincipal = false; + } + for (uint32_t i = 0; i < aLinks.Length(); i++) { + if (loadUsingSystemPrincipal) { + if (!aLinks[i].Equals(mVerifyDropLinks[i])) { + loadUsingSystemPrincipal = false; + } + } + } + mVerifyDropLinks.Clear(); + nsCOMPtr triggeringPrincipal; + if (loadUsingSystemPrincipal) { + triggeringPrincipal = nsContentUtils::GetSystemPrincipal(); + } else { + triggeringPrincipal = NullPrincipal::CreateWithoutOriginAttributes(); + } + browser->DropLinks(aLinks, triggeringPrincipal); + } + return IPC_OK(); +} + +bool BrowserParent::SendLoadRemoteScript(const nsAString& aURL, + const bool& aRunInGlobalScope) { + if (mCreatingWindow) { + mDelayedFrameScripts.AppendElement( + FrameScriptInfo(nsString(aURL), aRunInGlobalScope)); + return true; + } + + MOZ_ASSERT(mDelayedFrameScripts.IsEmpty()); + return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope); +} + +void BrowserParent::LoadURL(nsDocShellLoadState* aLoadState) { + MOZ_ASSERT(aLoadState); + MOZ_ASSERT(aLoadState->URI()); + if (mIsDestroyed) { + return; + } + + if (mCreatingWindow) { + // Don't send the message if the child wants to load its own URL. + return; + } + + Unused << SendLoadURL(WrapNotNull(aLoadState), GetShowInfo()); +} + +void BrowserParent::ResumeLoad(uint64_t aPendingSwitchID) { + MOZ_ASSERT(aPendingSwitchID != 0); + + if (NS_WARN_IF(mIsDestroyed)) { + return; + } + + Unused << SendResumeLoad(aPendingSwitchID, GetShowInfo()); +} + +void BrowserParent::InitRendering() { + if (mRemoteLayerTreeOwner.IsInitialized()) { + return; + } + mRemoteLayerTreeOwner.Initialize(this); + + layers::LayersId layersId = mRemoteLayerTreeOwner.GetLayersId(); + AddBrowserParentToTable(layersId, this); + + RefPtr frameLoader = GetFrameLoader(); + if (frameLoader) { + nsIFrame* frame = frameLoader->GetPrimaryFrameOfOwningContent(); + if (frame) { + frame->InvalidateFrame(); + } + } + + TextureFactoryIdentifier textureFactoryIdentifier; + mRemoteLayerTreeOwner.GetTextureFactoryIdentifier(&textureFactoryIdentifier); + Unused << SendInitRendering(textureFactoryIdentifier, layersId, + mRemoteLayerTreeOwner.GetCompositorOptions(), + mRemoteLayerTreeOwner.IsLayersConnected()); + + RefPtr widget = GetTopLevelWidget(); + if (widget) { + ScreenIntMargin safeAreaInsets = widget->GetSafeAreaInsets(); + Unused << SendSafeAreaInsetsChanged(safeAreaInsets); + } + +#if defined(MOZ_WIDGET_ANDROID) + MOZ_ASSERT(widget); + + if (GetBrowsingContext()->IsTopContent()) { + Unused << SendDynamicToolbarMaxHeightChanged( + widget->GetDynamicToolbarMaxHeight()); + } +#endif +} + +bool BrowserParent::AttachWindowRenderer() { + return mRemoteLayerTreeOwner.AttachWindowRenderer(); +} + +void BrowserParent::MaybeShowFrame() { + RefPtr frameLoader = GetFrameLoader(); + if (!frameLoader) { + return; + } + frameLoader->MaybeShowFrame(); +} + +bool BrowserParent::Show(const OwnerShowInfo& aOwnerInfo) { + mDimensions = aOwnerInfo.size(); + if (mIsDestroyed) { + return false; + } + + MOZ_ASSERT(mRemoteLayerTreeOwner.IsInitialized()); + if (!mRemoteLayerTreeOwner.AttachWindowRenderer()) { + return false; + } + + mSizeMode = aOwnerInfo.sizeMode(); + Unused << SendShow(GetShowInfo(), aOwnerInfo); + return true; +} + +mozilla::ipc::IPCResult BrowserParent::RecvSetDimensions( + mozilla::DimensionRequest aRequest, const double& aScale) { + NS_ENSURE_TRUE(mFrameElement, IPC_OK()); + nsCOMPtr docShell = mFrameElement->OwnerDoc()->GetDocShell(); + NS_ENSURE_TRUE(docShell, IPC_OK()); + nsCOMPtr treeOwner; + docShell->GetTreeOwner(getter_AddRefs(treeOwner)); + nsCOMPtr treeOwnerAsWin = do_QueryInterface(treeOwner); + NS_ENSURE_TRUE(treeOwnerAsWin, IPC_OK()); + + // `BrowserChild` only sends the values to actually be changed, see more + // details in `BrowserChild::SetDimensions()`. + // Note that `BrowserChild::SetDimensions()` may be called before receiving + // our `SendUIResolutionChanged()` call. Therefore, if given each coordinate + // shouldn't be ignored, we need to recompute it if DPI has been changed. + // And also note that don't use `mDefaultScale.scale` here since it may be + // different from the result of `GetWidgetCSSToDeviceScale()`. + // NOTE(emilio): We use GetWidgetCSSToDeviceScale() because the old scale is a + // widget scale, and we only use the current scale to scale up/down the + // relevant values. + + CSSToLayoutDeviceScale oldScale((float)aScale); + CSSToLayoutDeviceScale currentScale( + (float)treeOwnerAsWin->GetWidgetCSSToDeviceScale()); + + if (oldScale != currentScale) { + auto rescaleFunc = [&oldScale, ¤tScale](LayoutDeviceIntCoord& aVal) { + aVal = (LayoutDeviceCoord(aVal) / oldScale * currentScale).Rounded(); + }; + aRequest.mX.apply(rescaleFunc); + aRequest.mY.apply(rescaleFunc); + aRequest.mWidth.apply(rescaleFunc); + aRequest.mHeight.apply(rescaleFunc); + } + + // treeOwner is the chrome tree owner, but we wan't the content tree owner. + nsCOMPtr webBrowserChrome = do_GetInterface(treeOwner); + NS_ENSURE_TRUE(webBrowserChrome, IPC_OK()); + webBrowserChrome->SetDimensions(std::move(aRequest)); + return IPC_OK(); +} + +nsresult BrowserParent::UpdatePosition() { + RefPtr frameLoader = GetFrameLoader(); + if (!frameLoader) { + return NS_OK; + } + nsIntRect windowDims; + NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims), + NS_ERROR_FAILURE); + // Avoid updating sizes here. + windowDims.SizeTo(mRect.Size()); + UpdateDimensions(windowDims, mDimensions); + return NS_OK; +} + +void BrowserParent::NotifyPositionUpdatedForContentsInPopup() { + if (CanonicalBrowsingContext* bc = GetBrowsingContext()) { + bc->PreOrderWalk([](BrowsingContext* aContext) { + if (WindowGlobalParent* windowGlobalParent = + aContext->Canonical()->GetCurrentWindowGlobal()) { + if (RefPtr browserParent = + windowGlobalParent->GetBrowserParent()) { + browserParent->UpdatePosition(); + } + } + }); + } +} + +void BrowserParent::UpdateDimensions(const nsIntRect& rect, + const ScreenIntSize& size) { + if (mIsDestroyed) { + return; + } + nsCOMPtr widget = GetWidget(); + if (!widget) { + NS_WARNING("No widget found in BrowserParent::UpdateDimensions"); + return; + } + + LayoutDeviceIntPoint clientOffset = GetClientOffset(); + LayoutDeviceIntPoint chromeOffset = !GetBrowserBridgeParent() + ? -GetChildProcessOffset() + : LayoutDeviceIntPoint(); + + if (!mUpdatedDimensions || mDimensions != size || !mRect.IsEqualEdges(rect) || + clientOffset != mClientOffset || chromeOffset != mChromeOffset) { + mUpdatedDimensions = true; + mRect = rect; + mDimensions = size; + mClientOffset = clientOffset; + mChromeOffset = chromeOffset; + + Unused << SendUpdateDimensions(GetDimensionInfo()); + UpdateNativePointerLockCenter(widget); + } +} + +DimensionInfo BrowserParent::GetDimensionInfo() { + LayoutDeviceIntRect devicePixelRect = ViewAs( + mRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims); + LayoutDeviceIntSize devicePixelSize = ViewAs( + mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims); + + CSSRect unscaledRect = devicePixelRect / mDefaultScale; + CSSSize unscaledSize = devicePixelSize / mDefaultScale; + DimensionInfo di(unscaledRect, unscaledSize, mClientOffset, mChromeOffset); + return di; +} + +void BrowserParent::UpdateNativePointerLockCenter(nsIWidget* aWidget) { + if (!mLockedNativePointer) { + return; + } + LayoutDeviceIntRect dims( + {0, 0}, + ViewAs( + mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims)); + aWidget->SetNativePointerLockCenter((dims + mChromeOffset).Center()); +} + +void BrowserParent::SizeModeChanged(const nsSizeMode& aSizeMode) { + if (!mIsDestroyed && aSizeMode != mSizeMode) { + mSizeMode = aSizeMode; + Unused << SendSizeModeChanged(aSizeMode); + } +} + +#if defined(MOZ_WIDGET_ANDROID) +void BrowserParent::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) { + if (!mIsDestroyed) { + Unused << SendDynamicToolbarMaxHeightChanged(aHeight); + } +} + +void BrowserParent::DynamicToolbarOffsetChanged(ScreenIntCoord aOffset) { + if (!mIsDestroyed) { + Unused << SendDynamicToolbarOffsetChanged(aOffset); + } +} +#endif + +void BrowserParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent, + nsTArray& aCharCodes) { + if (!mIsDestroyed) { + // Note that we don't need to mark aEvent is posted to a remote process + // because the event may be dispatched to it as normal keyboard event. + // Therefore, we should use local copy to send it. + WidgetKeyboardEvent localEvent(aEvent); + RequestingAccessKeyEventData::Set(localEvent); + Unused << SendHandleAccessKey(localEvent, aCharCodes); + } +} + +void BrowserParent::Activate(uint64_t aActionId) { + LOGBROWSERFOCUS(("Activate %p actionid: %" PRIu64, this, aActionId)); + if (!mIsDestroyed) { + SetTopLevelWebFocus(this); // Intentionally inside "if" + Unused << SendActivate(aActionId); + } +} + +void BrowserParent::Deactivate(bool aWindowLowering, uint64_t aActionId) { + LOGBROWSERFOCUS(("Deactivate %p actionid: %" PRIu64, this, aActionId)); + if (!aWindowLowering) { + UnsetTopLevelWebFocus(this); // Intentionally outside the next "if" + } + if (!mIsDestroyed) { + Unused << SendDeactivate(aActionId); + } +} + +#ifdef ACCESSIBILITY +a11y::PDocAccessibleParent* BrowserParent::AllocPDocAccessibleParent( + PDocAccessibleParent* aParent, const uint64_t&, + const MaybeDiscardedBrowsingContext&) { + // Reference freed in DeallocPDocAccessibleParent. + return a11y::DocAccessibleParent::New().take(); +} + +bool BrowserParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent) { + // Free reference from AllocPDocAccessibleParent. + static_cast(aParent)->Release(); + return true; +} + +mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor( + PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, + const uint64_t& aParentID, + const MaybeDiscardedBrowsingContext& aBrowsingContext) { +# if defined(ANDROID) + MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor()); +# endif + auto doc = static_cast(aDoc); + + // If this tab is already shutting down just mark the new actor as shutdown + // and ignore it. When the tab actor is destroyed it will be too. + if (mIsDestroyed) { + doc->MarkAsShutdown(); + return IPC_OK(); + } + + if (aParentDoc) { + // Iframe document rendered in the same process as its embedder. + // A document should never directly be the parent of another document. + // There should always be an outer doc accessible child of the outer + // document containing the child. + MOZ_ASSERT(aParentID); + if (!aParentID) { + return IPC_FAIL_NO_REASON(this); + } + + auto parentDoc = static_cast(aParentDoc); + if (parentDoc->IsShutdown()) { + // This can happen if parentDoc is an OOP iframe, but its embedder has + // been destroyed. (DocAccessibleParent::Destroy destroys any child + // documents.) The OOP iframe (and anything it embeds) will die soon + // anyway, so mark this document as shutdown and ignore it. + doc->MarkAsShutdown(); + return IPC_OK(); + } + + if (aBrowsingContext) { + doc->SetBrowsingContext(aBrowsingContext.get_canonical()); + } + + mozilla::ipc::IPCResult added = parentDoc->AddChildDoc(doc, aParentID); + if (!added) { +# ifdef DEBUG + return added; +# else + return IPC_OK(); +# endif + } + +# ifdef XP_WIN + if (a11y::nsWinUtils::IsWindowEmulationStarted()) { + doc->SetEmulatedWindowHandle(parentDoc->GetEmulatedWindowHandle()); + } +# endif + + return IPC_OK(); + } + + if (aBrowsingContext) { + doc->SetBrowsingContext(aBrowsingContext.get_canonical()); + } + + if (auto* bridge = GetBrowserBridgeParent()) { + // Iframe document rendered in a different process to its embedder. + // In this case, we don't get aParentDoc and aParentID. + MOZ_ASSERT(!aParentDoc && !aParentID); + doc->SetTopLevelInContentProcess(); + a11y::ProxyCreated(doc); + // It's possible the embedder accessible hasn't been set yet; e.g. + // a hidden iframe. In that case, embedderDoc will be null and this will + // be handled when the embedder is set. + if (a11y::DocAccessibleParent* embedderDoc = + bridge->GetEmbedderAccessibleDoc()) { + mozilla::ipc::IPCResult added = embedderDoc->AddChildDoc(bridge); + if (!added) { +# ifdef DEBUG + return added; +# else + return IPC_OK(); +# endif + } + } + return IPC_OK(); + } else { + // null aParentDoc means this document is at the top level in the child + // process. That means it makes no sense to get an id for an accessible + // that is its parent. + MOZ_ASSERT(!aParentID); + if (aParentID) { + return IPC_FAIL_NO_REASON(this); + } + + if (auto* prevTopLevel = GetTopLevelDocAccessible()) { + // Sometimes, we can get a new top level DocAccessibleParent before the + // old one gets destroyed. The old one will die pretty shortly anyway, + // so just destroy it now. If we don't do this, GetTopLevelDocAccessible() + // might return the wrong document for a short while. + prevTopLevel->Destroy(); + } + doc->SetTopLevel(); + a11y::DocManager::RemoteDocAdded(doc); +# ifdef XP_WIN + doc->MaybeInitWindowEmulation(); +# endif + } + return IPC_OK(); +} +#endif + +already_AddRefed BrowserParent::AllocPFilePickerParent( + const nsString& aTitle, const nsIFilePicker::Mode& aMode) { + return MakeAndAddRef(aTitle, aMode); +} + +already_AddRefed +BrowserParent::AllocPSessionStoreParent() { + RefPtr sessionStore = + BrowserSessionStore::GetOrCreate(mBrowsingContext->Top()); + if (!sessionStore) { + return nullptr; + } + + return do_AddRef(new SessionStoreParent(mBrowsingContext, sessionStore)); +} + +IPCResult BrowserParent::RecvNewWindowGlobal( + ManagedEndpoint&& aEndpoint, + const WindowGlobalInit& aInit) { + RefPtr browsingContext = + CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId); + if (!browsingContext) { + return IPC_FAIL(this, "Cannot create for missing BrowsingContext"); + } + if (!aInit.principal()) { + return IPC_FAIL(this, "Cannot create without valid principal"); + } + + // Ensure we never load a document with a content principal in + // the wrong type of webIsolated process + EnumSet validationOptions = {}; + nsCOMPtr docURI = aInit.documentURI(); + if (docURI->SchemeIs("about") || docURI->SchemeIs("blob") || + docURI->SchemeIs("chrome")) { + // XXXckerschb TODO - Do not use SystemPrincipal for: + // Bug 1700639: about:plugins + // Bug 1699385: Remove allowSystem for blobs + // Bug 1698087: chrome://devtools/content/shared/webextension-fallback.html + // chrome reftests, e.g. + // * chrome://reftest/content/writing-mode/ua-style-sheet-button-1a-ref.html + // * chrome://reftest/content/xul-document-load/test003.xhtml + // * chrome://reftest/content/forms/input/text/centering-1.xhtml + validationOptions = {ContentParent::ValidatePrincipalOptions::AllowSystem}; + } + + if (!mManager->ValidatePrincipal(aInit.principal(), validationOptions)) { + ContentParent::LogAndAssertFailedPrincipalValidationInfo(aInit.principal(), + __func__); + } + + // Construct our new WindowGlobalParent, bind, and initialize it. + RefPtr wgp = + WindowGlobalParent::CreateDisconnected(aInit); + BindPWindowGlobalEndpoint(std::move(aEndpoint), wgp); + wgp->Init(); + return IPC_OK(); +} + +PVsyncParent* BrowserParent::AllocPVsyncParent() { + MOZ_ASSERT(!mVsyncParent); + mVsyncParent = new VsyncParent(); + UpdateVsyncParentVsyncDispatcher(); + return mVsyncParent.get(); +} + +bool BrowserParent::DeallocPVsyncParent(PVsyncParent* aActor) { + MOZ_ASSERT(aActor); + mVsyncParent = nullptr; + return true; +} + +void BrowserParent::UpdateVsyncParentVsyncDispatcher() { + if (!mVsyncParent) { + return; + } + + if (nsCOMPtr widget = GetWidget()) { + RefPtr vsyncDispatcher = widget->GetVsyncDispatcher(); + if (!vsyncDispatcher) { + vsyncDispatcher = gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher(); + } + mVsyncParent->UpdateVsyncDispatcher(vsyncDispatcher); + } +} + +void BrowserParent::MouseEnterIntoWidget() { + if (nsCOMPtr widget = GetWidget()) { + // When we mouseenter the remote target, the remote target's cursor should + // become the current cursor. When we mouseexit, we stop. + mRemoteTargetSetsCursor = true; + widget->SetCursor(mCursor); + } + + // Mark that we have missed a mouse enter event, so that + // the next mouse event will create a replacement mouse + // enter event and send it to the child. + mIsMouseEnterIntoWidgetEventSuppressed = true; +} + +void BrowserParent::SendRealMouseEvent(WidgetMouseEvent& aEvent) { + if (mIsDestroyed) { + return; + } + + // XXXedgar, if the synthesized mouse events could deliver to the correct + // process directly (see + // https://bugzilla.mozilla.org/show_bug.cgi?id=1549355), we probably don't + // need to check mReason then. + if (aEvent.mReason == WidgetMouseEvent::eReal) { + if (aEvent.mMessage == eMouseExitFromWidget) { + // Since we are leaving this remote target, so don't need to update + // sLastMouseRemoteTarget, and if we are sLastMouseRemoteTarget, reset it + // to null. + BrowserParent::UnsetLastMouseRemoteTarget(this); + } else { + // Last remote target should not be changed without eMouseExitFromWidget. + MOZ_ASSERT_IF(sLastMouseRemoteTarget, sLastMouseRemoteTarget == this); + sLastMouseRemoteTarget = this; + } + } + + aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); + + nsCOMPtr widget = GetWidget(); + if (widget) { + // When we mouseenter the remote target, the remote target's cursor should + // become the current cursor. When we mouseexit, we stop. + if (eMouseEnterIntoWidget == aEvent.mMessage) { + mRemoteTargetSetsCursor = true; + widget->SetCursor(mCursor); + } else if (eMouseExitFromWidget == aEvent.mMessage) { + mRemoteTargetSetsCursor = false; + } + } + if (!mIsReadyToHandleInputEvents) { + if (eMouseEnterIntoWidget == aEvent.mMessage) { + mIsMouseEnterIntoWidgetEventSuppressed = true; + } else if (eMouseExitFromWidget == aEvent.mMessage) { + mIsMouseEnterIntoWidgetEventSuppressed = false; + } + return; + } + + ScrollableLayerGuid guid; + uint64_t blockId; + ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); + + bool isInputPriorityEventEnabled = Manager()->IsInputPriorityEventEnabled(); + + if (mIsMouseEnterIntoWidgetEventSuppressed) { + // In the case that the BrowserParent suppressed the eMouseEnterWidget event + // due to its corresponding BrowserChild wasn't ready to handle it, we have + // to resend it when the BrowserChild is ready. + mIsMouseEnterIntoWidgetEventSuppressed = false; + WidgetMouseEvent localEvent(aEvent); + localEvent.mMessage = eMouseEnterIntoWidget; + DebugOnly ret = + isInputPriorityEventEnabled + ? SendRealMouseEnterExitWidgetEvent(localEvent, guid, blockId) + : SendNormalPriorityRealMouseEnterExitWidgetEvent(localEvent, guid, + blockId); + NS_WARNING_ASSERTION(ret, "SendRealMouseEnterExitWidgetEvent() failed"); + MOZ_ASSERT(!ret || localEvent.HasBeenPostedToRemoteProcess()); + } + + if (eMouseMove == aEvent.mMessage) { + if (aEvent.mReason == WidgetMouseEvent::eSynthesized) { + DebugOnly ret = + isInputPriorityEventEnabled + ? SendSynthMouseMoveEvent(aEvent, guid, blockId) + : SendNormalPrioritySynthMouseMoveEvent(aEvent, guid, blockId); + NS_WARNING_ASSERTION(ret, "SendSynthMouseMoveEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); + return; + } + + if (!aEvent.mFlags.mIsSynthesizedForTests) { + DebugOnly ret = + isInputPriorityEventEnabled + ? SendRealMouseMoveEvent(aEvent, guid, blockId) + : SendNormalPriorityRealMouseMoveEvent(aEvent, guid, blockId); + NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); + return; + } + + DebugOnly ret = + isInputPriorityEventEnabled + ? SendRealMouseMoveEventForTests(aEvent, guid, blockId) + : SendNormalPriorityRealMouseMoveEventForTests(aEvent, guid, + blockId); + NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEventForTests() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); + return; + } + + if (eMouseEnterIntoWidget == aEvent.mMessage || + eMouseExitFromWidget == aEvent.mMessage) { + DebugOnly ret = + isInputPriorityEventEnabled + ? SendRealMouseEnterExitWidgetEvent(aEvent, guid, blockId) + : SendNormalPriorityRealMouseEnterExitWidgetEvent(aEvent, guid, + blockId); + NS_WARNING_ASSERTION(ret, "SendRealMouseEnterExitWidgetEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); + return; + } + + DebugOnly ret = + isInputPriorityEventEnabled + ? SendRealMouseButtonEvent(aEvent, guid, blockId) + : SendNormalPriorityRealMouseButtonEvent(aEvent, guid, blockId); + NS_WARNING_ASSERTION(ret, "SendRealMouseButtonEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); +} + +LayoutDeviceToCSSScale BrowserParent::GetLayoutDeviceToCSSScale() { + Document* doc = (mFrameElement ? mFrameElement->OwnerDoc() : nullptr); + nsPresContext* ctx = (doc ? doc->GetPresContext() : nullptr); + return LayoutDeviceToCSSScale( + ctx ? (float)ctx->AppUnitsPerDevPixel() / AppUnitsPerCSSPixel() : 0.0f); +} + +bool BrowserParent::QueryDropLinksForVerification() { + // Before sending the dragEvent, we query the links being dragged and + // store them on the parent, to make sure the child can not modify links. + nsCOMPtr dragSession = nsContentUtils::GetDragSession(); + if (!dragSession) { + NS_WARNING("No dragSession to query links for verification"); + return false; + } + + RefPtr initialDataTransfer = dragSession->GetDataTransfer(); + if (!initialDataTransfer) { + NS_WARNING("No initialDataTransfer to query links for verification"); + return false; + } + + nsCOMPtr dropHandler = + do_GetService("@mozilla.org/content/dropped-link-handler;1"); + if (!dropHandler) { + NS_WARNING("No dropHandler to query links for verification"); + return false; + } + + // No more than one drop event can happen simultaneously; reset the link + // verification array and store all links that are being dragged. + mVerifyDropLinks.Clear(); + + nsTArray> droppedLinkItems; + dropHandler->QueryLinks(initialDataTransfer, droppedLinkItems); + + // Since the entire event is cancelled if one of the links is invalid, + // we can store all links on the parent side without any prior + // validation checks. + nsresult rv = NS_OK; + for (nsIDroppedLinkItem* item : droppedLinkItems) { + nsString tmp; + rv = item->GetUrl(tmp); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to query url for verification"); + break; + } + mVerifyDropLinks.AppendElement(tmp); + + rv = item->GetName(tmp); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to query name for verification"); + break; + } + mVerifyDropLinks.AppendElement(tmp); + + rv = item->GetType(tmp); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to query type for verification"); + break; + } + mVerifyDropLinks.AppendElement(tmp); + } + if (NS_FAILED(rv)) { + mVerifyDropLinks.Clear(); + return false; + } + return true; +} + +void BrowserParent::SendRealDragEvent(WidgetDragEvent& aEvent, + uint32_t aDragAction, + uint32_t aDropEffect, + nsIPrincipal* aPrincipal, + nsIContentSecurityPolicy* aCsp) { + if (mIsDestroyed || !mIsReadyToHandleInputEvents) { + return; + } + MOZ_ASSERT(!Manager()->IsInputPriorityEventEnabled()); + aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); + if (aEvent.mMessage == eDrop) { + if (!QueryDropLinksForVerification()) { + return; + } + } + DebugOnly ret = PBrowserParent::SendRealDragEvent( + aEvent, aDragAction, aDropEffect, aPrincipal, aCsp); + NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealDragEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); +} + +void BrowserParent::SendMouseWheelEvent(WidgetWheelEvent& aEvent) { + if (mIsDestroyed || !mIsReadyToHandleInputEvents) { + return; + } + + ScrollableLayerGuid guid; + uint64_t blockId; + ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); + aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); + DebugOnly ret = + Manager()->IsInputPriorityEventEnabled() + ? PBrowserParent::SendMouseWheelEvent(aEvent, guid, blockId) + : PBrowserParent::SendNormalPriorityMouseWheelEvent(aEvent, guid, + blockId); + + NS_WARNING_ASSERTION(ret, "PBrowserParent::SendMouseWheelEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); +} + +mozilla::ipc::IPCResult BrowserParent::RecvDispatchWheelEvent( + const mozilla::WidgetWheelEvent& aEvent) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return IPC_OK(); + } + + WidgetWheelEvent localEvent(aEvent); + localEvent.mWidget = widget; + localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint); + + widget->DispatchInputEvent(&localEvent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvDispatchMouseEvent( + const mozilla::WidgetMouseEvent& aEvent) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return IPC_OK(); + } + + WidgetMouseEvent localEvent(aEvent); + localEvent.mWidget = widget; + localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint); + + widget->DispatchInputEvent(&localEvent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvDispatchKeyboardEvent( + const mozilla::WidgetKeyboardEvent& aEvent) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return IPC_OK(); + } + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = widget; + localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint); + + widget->DispatchInputEvent(&localEvent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvDispatchTouchEvent( + const mozilla::WidgetTouchEvent& aEvent) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return IPC_OK(); + } + + WidgetTouchEvent localEvent(aEvent); + localEvent.mWidget = widget; + + for (uint32_t i = 0; i < localEvent.mTouches.Length(); i++) { + localEvent.mTouches[i]->mRefPoint = + TransformChildToParent(localEvent.mTouches[i]->mRefPoint); + } + + widget->DispatchInputEvent(&localEvent); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvRequestNativeKeyBindings( + const uint32_t& aType, const WidgetKeyboardEvent& aEvent, + nsTArray* aCommands) { + MOZ_ASSERT(aCommands); + MOZ_ASSERT(aCommands->IsEmpty()); + + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + NativeKeyBindingsType keyBindingsType = + static_cast(aType); + switch (keyBindingsType) { + case NativeKeyBindingsType::SingleLineEditor: + case NativeKeyBindingsType::MultiLineEditor: + case NativeKeyBindingsType::RichTextEditor: + break; + default: + return IPC_FAIL(this, "Invalid aType value"); + } + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return IPC_OK(); + } + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.mWidget = widget; + + if (NS_FAILED(widget->AttachNativeKeyEvent(localEvent))) { + return IPC_OK(); + } + + Maybe writingMode; + if (RefPtr dispatcher = + widget->GetTextEventDispatcher()) { + writingMode = dispatcher->MaybeQueryWritingModeAtSelection(); + } + if (localEvent.InitEditCommandsFor(keyBindingsType, writingMode)) { + *aCommands = localEvent.EditCommandsConstRef(keyBindingsType).Clone(); + } + + return IPC_OK(); +} + +class SynthesizedEventObserver : public nsIObserver { + NS_DECL_ISUPPORTS + + public: + SynthesizedEventObserver(BrowserParent* aBrowserParent, + const uint64_t& aObserverId) + : mBrowserParent(aBrowserParent), mObserverId(aObserverId) { + MOZ_ASSERT(mBrowserParent); + } + + NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) override { + if (!mBrowserParent || !mObserverId) { + // We already sent the notification, or we don't actually need to + // send any notification at all. + return NS_OK; + } + + if (mBrowserParent->IsDestroyed()) { + // If this happens it's probably a bug in the test that's triggering this. + NS_WARNING( + "BrowserParent was unexpectedly destroyed during event " + "synthesization!"); + } else if (!mBrowserParent->SendNativeSynthesisResponse( + mObserverId, nsCString(aTopic))) { + NS_WARNING("Unable to send native event synthesization response!"); + } + // Null out browserParent to indicate we already sent the response + mBrowserParent = nullptr; + return NS_OK; + } + + private: + virtual ~SynthesizedEventObserver() = default; + + RefPtr mBrowserParent; + uint64_t mObserverId; +}; + +NS_IMPL_ISUPPORTS(SynthesizedEventObserver, nsIObserver) + +class MOZ_STACK_CLASS AutoSynthesizedEventResponder { + public: + AutoSynthesizedEventResponder(BrowserParent* aBrowserParent, + const uint64_t& aObserverId, const char* aTopic) + : mObserver(new SynthesizedEventObserver(aBrowserParent, aObserverId)), + mTopic(aTopic) {} + + ~AutoSynthesizedEventResponder() { + // This may be a no-op if the observer already sent a response. + mObserver->Observe(nullptr, mTopic, nullptr); + } + + nsIObserver* GetObserver() { return mObserver; } + + private: + nsCOMPtr mObserver; + const char* mTopic; +}; + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeKeyEvent( + const int32_t& aNativeKeyboardLayout, const int32_t& aNativeKeyCode, + const uint32_t& aModifierFlags, const nsString& aCharacters, + const nsString& aUnmodifiedCharacters, const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + AutoSynthesizedEventResponder responder(this, aObserverId, "keyevent"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeKeyEvent( + aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters, + aUnmodifiedCharacters, responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseEvent( + const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage, + const int16_t& aButton, const uint32_t& aModifierFlags, + const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + const uint32_t last = + static_cast(nsIWidget::NativeMouseMessage::LeaveWindow); + NS_ENSURE_TRUE(aNativeMessage <= last, IPC_FAIL(this, "Bogus message")); + AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeMouseEvent( + aPoint, static_cast(aNativeMessage), + static_cast(aButton), + static_cast(aModifierFlags), + responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseMove( + const LayoutDeviceIntPoint& aPoint, const uint64_t& aObserverId) { + // This is used by pointer lock API. So, even if it's not in the automation + // mode, we need to accept the request. + AutoSynthesizedEventResponder responder(this, aObserverId, "mousemove"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeMouseMove(aPoint, responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseScrollEvent( + const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage, + const double& aDeltaX, const double& aDeltaY, const double& aDeltaZ, + const uint32_t& aModifierFlags, const uint32_t& aAdditionalFlags, + const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + AutoSynthesizedEventResponder responder(this, aObserverId, + "mousescrollevent"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeMouseScrollEvent( + aPoint, aNativeMessage, aDeltaX, aDeltaY, aDeltaZ, aModifierFlags, + aAdditionalFlags, responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchPoint( + const uint32_t& aPointerId, const TouchPointerState& aPointerState, + const LayoutDeviceIntPoint& aPoint, const double& aPointerPressure, + const uint32_t& aPointerOrientation, const uint64_t& aObserverId) { + // This is used by DevTools to emulate touch events from mouse events in the + // responsive design mode. Therefore, we should accept the IPC messages even + // if it's not in the automation mode but the browsing context is in RDM pane. + // And the IPC message could be just delayed after closing the responsive + // design mode. Therefore, we shouldn't return IPC_FAIL since doing it makes + // the tab crash. + if (!xpc::IsInAutomation()) { + NS_ENSURE_TRUE(mBrowsingContext, IPC_OK()); + NS_ENSURE_TRUE(mBrowsingContext->Top()->GetInRDMPane(), IPC_OK()); + } + + AutoSynthesizedEventResponder responder(this, aObserverId, "touchpoint"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPoint, + aPointerPressure, aPointerOrientation, + responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchPadPinch( + const TouchpadGesturePhase& aEventPhase, const float& aScale, + const LayoutDeviceIntPoint& aPoint, const int32_t& aModifierFlags) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchPadPinch(aEventPhase, aScale, aPoint, + aModifierFlags); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchTap( + const LayoutDeviceIntPoint& aPoint, const bool& aLongTap, + const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + AutoSynthesizedEventResponder responder(this, aObserverId, "touchtap"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchTap(aPoint, aLongTap, responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvClearNativeTouchSequence( + const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + AutoSynthesizedEventResponder responder(this, aObserverId, "cleartouch"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->ClearNativeTouchSequence(responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativePenInput( + const uint32_t& aPointerId, const TouchPointerState& aPointerState, + const LayoutDeviceIntPoint& aPoint, const double& aPressure, + const uint32_t& aRotation, const int32_t& aTiltX, const int32_t& aTiltY, + const int32_t& aButton, const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + AutoSynthesizedEventResponder responder(this, aObserverId, "peninput"); + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativePenInput(aPointerId, aPointerState, aPoint, + aPressure, aRotation, aTiltX, aTiltY, + aButton, responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchpadDoubleTap( + const LayoutDeviceIntPoint& aPoint, const uint32_t& aModifierFlags) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchpadPan( + const TouchpadGesturePhase& aEventPhase, const LayoutDeviceIntPoint& aPoint, + const double& aDeltaX, const double& aDeltaY, const int32_t& aModifierFlags, + const uint64_t& aObserverId) { + NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event")); + + AutoSynthesizedEventResponder responder(this, aObserverId, + "touchpadpanevent"); + + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->SynthesizeNativeTouchpadPan(aEventPhase, aPoint, aDeltaX, aDeltaY, + aModifierFlags, + responder.GetObserver()); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvLockNativePointer() { + if (nsCOMPtr widget = GetWidget()) { + mLockedNativePointer = true; // do before updating the center + UpdateNativePointerLockCenter(widget); + widget->LockNativePointer(); + } + return IPC_OK(); +} + +void BrowserParent::UnlockNativePointer() { + if (!mLockedNativePointer) { + return; + } + if (nsCOMPtr widget = GetWidget()) { + widget->UnlockNativePointer(); + mLockedNativePointer = false; + } +} + +mozilla::ipc::IPCResult BrowserParent::RecvUnlockNativePointer() { + UnlockNativePointer(); + return IPC_OK(); +} + +void BrowserParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent) { + if (mIsDestroyed || !mIsReadyToHandleInputEvents) { + return; + } + aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint); + + // NOTE: If you call `InitAllEditCommands()` for the other messages too, + // you also need to update + // TextEventDispatcher::DispatchKeyboardEventInternal(). + if (aEvent.mMessage == eKeyPress) { + // If current input context is editable, the edit commands are initialized + // by TextEventDispatcher::DispatchKeyboardEventInternal(). Otherwise, + // we need to do it here (they are not necessary for the parent process, + // therefore, we need to do it here for saving the runtime cost). + if (!aEvent.AreAllEditCommandsInitialized()) { + // XXX Is it good thing that the keypress event will be handled in an + // editor even though the user pressed the key combination before the + // focus change has not been completed in the parent process yet or + // focus change will happen? If no, we can stop doing this. + Maybe writingMode; + if (aEvent.mWidget) { + if (RefPtr dispatcher = + aEvent.mWidget->GetTextEventDispatcher()) { + writingMode = dispatcher->MaybeQueryWritingModeAtSelection(); + } + } + aEvent.InitAllEditCommands(writingMode); + } + } else { + aEvent.PreventNativeKeyBindings(); + } + SentKeyEventData sendKeyEventData{ + aEvent.mKeyCode, aEvent.mCharCode, aEvent.mPseudoCharCode, + aEvent.mKeyNameIndex, aEvent.mCodeNameIndex, aEvent.mModifiers, + nsID::GenerateUUID()}; + const bool ok = + Manager()->IsInputPriorityEventEnabled() + ? PBrowserParent::SendRealKeyEvent(aEvent, sendKeyEventData.mUUID) + : PBrowserParent::SendNormalPriorityRealKeyEvent( + aEvent, sendKeyEventData.mUUID); + + NS_WARNING_ASSERTION(ok, "PBrowserParent::SendRealKeyEvent() failed"); + MOZ_ASSERT(!ok || aEvent.HasBeenPostedToRemoteProcess()); + if (ok && aEvent.IsWaitingReplyFromRemoteProcess()) { + mWaitingReplyKeyboardEvents.AppendElement(sendKeyEventData); + } +} + +void BrowserParent::SendRealTouchEvent(WidgetTouchEvent& aEvent) { + if (mIsDestroyed || !mIsReadyToHandleInputEvents) { + return; + } + + // PresShell::HandleEventInternal adds touches on touch end/cancel. This + // confuses remote content and the panning and zooming logic into thinking + // that the added touches are part of the touchend/cancel, when actually + // they're not. + if (aEvent.mMessage == eTouchEnd || aEvent.mMessage == eTouchCancel) { + aEvent.mTouches.RemoveElementsBy( + [](const auto& touch) { return !touch->mChanged; }); + } + + APZData apzData; + ApzAwareEventRoutingToChild(&apzData.guid, &apzData.blockId, + &apzData.apzResponse); + + if (mIsDestroyed) { + return; + } + + for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) { + aEvent.mTouches[i]->mRefPoint = + TransformParentToChild(aEvent.mTouches[i]->mRefPoint); + } + + static uint32_t sConsecutiveTouchMoveCount = 0; + if (aEvent.mMessage == eTouchMove) { + ++sConsecutiveTouchMoveCount; + SendRealTouchMoveEvent(aEvent, apzData, sConsecutiveTouchMoveCount); + return; + } + + sConsecutiveTouchMoveCount = 0; + DebugOnly ret = + Manager()->IsInputPriorityEventEnabled() + ? PBrowserParent::SendRealTouchEvent( + aEvent, apzData.guid, apzData.blockId, apzData.apzResponse) + : PBrowserParent::SendNormalPriorityRealTouchEvent( + aEvent, apzData.guid, apzData.blockId, apzData.apzResponse); + + NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); +} + +void BrowserParent::SendRealTouchMoveEvent( + WidgetTouchEvent& aEvent, APZData& aAPZData, + uint32_t aConsecutiveTouchMoveCount) { + // Touchmove handling is complicated, since IPC compression should be used + // only when there are consecutive touch objects for the same touch on the + // same BrowserParent. IPC compression can be disabled by switching to + // different IPC message. + static bool sIPCMessageType1 = true; + static TabId sLastTargetBrowserParent(0); + static Maybe sPreviousAPZData; + // Artificially limit max touch points to 10. That should be in practise + // more than enough. + const uint32_t kMaxTouchMoveIdentifiers = 10; + static Maybe sLastTouchMoveIdentifiers[kMaxTouchMoveIdentifiers]; + + // Returns true if aIdentifiers contains all the touches in + // sLastTouchMoveIdentifiers. + auto LastTouchMoveIdentifiersContainedIn = + [&](const nsTArray& aIdentifiers) -> bool { + for (Maybe& entry : sLastTouchMoveIdentifiers) { + if (entry.isSome() && !aIdentifiers.Contains(entry.value())) { + return false; + } + } + return true; + }; + + // Cache touch identifiers in sLastTouchMoveIdentifiers array to be used + // when checking whether compression can be done for the next touchmove. + auto SetLastTouchMoveIdentifiers = + [&](const nsTArray& aIdentifiers) { + for (Maybe& entry : sLastTouchMoveIdentifiers) { + entry.reset(); + } + + MOZ_ASSERT(aIdentifiers.Length() <= kMaxTouchMoveIdentifiers); + for (uint32_t j = 0; j < aIdentifiers.Length(); ++j) { + sLastTouchMoveIdentifiers[j].emplace(aIdentifiers[j]); + } + }; + + AutoTArray changedTouches; + bool preventCompression = !StaticPrefs::dom_events_compress_touchmove() || + // Ensure the very first touchmove isn't overridden + // by the second one, so that web pages can get + // accurate coordinates for the first touchmove. + aConsecutiveTouchMoveCount < 3 || + sPreviousAPZData.isNothing() || + sPreviousAPZData.value() != aAPZData || + sLastTargetBrowserParent != GetTabId() || + aEvent.mTouches.Length() > kMaxTouchMoveIdentifiers; + + if (!preventCompression) { + for (RefPtr& touch : aEvent.mTouches) { + if (touch->mChanged) { + changedTouches.AppendElement(touch->mIdentifier); + } + } + + // Prevent compression if the new event has fewer or different touches + // than the old one. + preventCompression = !LastTouchMoveIdentifiersContainedIn(changedTouches); + } + + if (preventCompression) { + sIPCMessageType1 = !sIPCMessageType1; + } + + // Update the last touch move identifiers always, so that when the next + // event comes in, the new identifiers can be compared to the old ones. + // If the pref is disabled, this just does a quick small loop. + SetLastTouchMoveIdentifiers(changedTouches); + sPreviousAPZData.reset(); + sPreviousAPZData.emplace(aAPZData); + sLastTargetBrowserParent = GetTabId(); + + DebugOnly ret = true; + if (sIPCMessageType1) { + ret = + Manager()->IsInputPriorityEventEnabled() + ? PBrowserParent::SendRealTouchMoveEvent( + aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse) + : PBrowserParent::SendNormalPriorityRealTouchMoveEvent( + aEvent, aAPZData.guid, aAPZData.blockId, + aAPZData.apzResponse); + } else { + ret = + Manager()->IsInputPriorityEventEnabled() + ? PBrowserParent::SendRealTouchMoveEvent2( + aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse) + : PBrowserParent::SendNormalPriorityRealTouchMoveEvent2( + aEvent, aAPZData.guid, aAPZData.blockId, + aAPZData.apzResponse); + } + + NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchMoveEvent() failed"); + MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess()); +} + +bool BrowserParent::SendHandleTap(TapType aType, + const LayoutDevicePoint& aPoint, + Modifiers aModifiers, + const ScrollableLayerGuid& aGuid, + uint64_t aInputBlockId) { + if (mIsDestroyed || !mIsReadyToHandleInputEvents) { + return false; + } + if ((aType == TapType::eSingleTap || aType == TapType::eSecondTap)) { + if (RefPtr fm = nsFocusManager::GetFocusManager()) { + if (RefPtr frameLoader = GetFrameLoader()) { + if (RefPtr element = frameLoader->GetOwnerContent()) { + fm->SetFocus(element, nsIFocusManager::FLAG_BYMOUSE | + nsIFocusManager::FLAG_BYTOUCH | + nsIFocusManager::FLAG_NOSCROLL); + } + } + } + } + return Manager()->IsInputPriorityEventEnabled() + ? PBrowserParent::SendHandleTap(aType, + TransformParentToChild(aPoint), + aModifiers, aGuid, aInputBlockId) + : PBrowserParent::SendNormalPriorityHandleTap( + aType, TransformParentToChild(aPoint), aModifiers, aGuid, + aInputBlockId); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSyncMessage( + const nsString& aMessage, const ClonedMessageData& aData, + nsTArray* aRetVal) { + AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserParent::RecvSyncMessage", + OTHER, aMessage); + MMPrinter::Print("BrowserParent::RecvSyncMessage", aMessage, aData); + + StructuredCloneData data; + ipc::UnpackClonedMessageData(aData, data); + + if (!ReceiveMessage(aMessage, true, &data, aRetVal)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvAsyncMessage( + const nsString& aMessage, const ClonedMessageData& aData) { + AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserParent::RecvAsyncMessage", + OTHER, aMessage); + MMPrinter::Print("BrowserParent::RecvAsyncMessage", aMessage, aData); + + StructuredCloneData data; + ipc::UnpackClonedMessageData(aData, data); + + if (!ReceiveMessage(aMessage, false, &data, nullptr)) { + return IPC_FAIL_NO_REASON(this); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSetCursor( + const nsCursor& aCursor, const bool& aHasCustomCursor, + Maybe&& aCursorData, const uint32_t& aWidth, + const uint32_t& aHeight, const float& aResolutionX, + const float& aResolutionY, const uint32_t& aStride, + const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX, + const uint32_t& aHotspotY, const bool& aForce) { + nsCOMPtr widget = GetWidget(); + if (!widget) { + return IPC_OK(); + } + + if (aForce) { + widget->ClearCachedCursor(); + } + + nsCOMPtr cursorImage; + if (aHasCustomCursor) { + const bool cursorDataValid = [&] { + if (!aCursorData) { + return false; + } + auto expectedSize = CheckedInt(aHeight) * aStride; + if (!expectedSize.isValid() || + expectedSize.value() != aCursorData->Size()) { + return false; + } + auto minStride = + CheckedInt(aWidth) * gfx::BytesPerPixel(aFormat); + if (!minStride.isValid() || aStride < minStride.value()) { + return false; + } + return true; + }(); + if (!cursorDataValid) { + return IPC_FAIL(this, "Invalid custom cursor data"); + } + const gfx::IntSize size(aWidth, aHeight); + RefPtr customCursor = + gfx::CreateDataSourceSurfaceFromData(size, aFormat, aCursorData->Data(), + aStride); + + RefPtr drawable = new gfxSurfaceDrawable(customCursor, size); + cursorImage = image::ImageOps::CreateFromDrawable(drawable); + } + + mCursor = nsIWidget::Cursor{aCursor, + std::move(cursorImage), + aHotspotX, + aHotspotY, + {aResolutionX, aResolutionY}}; + if (!mRemoteTargetSetsCursor) { + return IPC_OK(); + } + + widget->SetCursor(mCursor); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvSetLinkStatus( + const nsString& aStatus) { + nsCOMPtr xulBrowserWindow = GetXULBrowserWindow(); + if (!xulBrowserWindow) { + return IPC_OK(); + } + + xulBrowserWindow->SetOverLink(aStatus); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvShowTooltip( + const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip, + const nsString& aDirection) { + nsCOMPtr xulBrowserWindow = GetXULBrowserWindow(); + if (!xulBrowserWindow) { + return IPC_OK(); + } + + // ShowTooltip will end up accessing XULElement properties in JS (specifically + // BoxObject). However, to get it to JS, we need to make sure we're a + // nsFrameLoaderOwner, which implies we're a XULFrameElement. We can then + // safely pass Element into JS. + RefPtr flo = do_QueryObject(mFrameElement); + if (!flo) return IPC_OK(); + + nsCOMPtr el = do_QueryInterface(flo); + if (!el) return IPC_OK(); + + if (NS_SUCCEEDED( + xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection, el))) { + mShowingTooltip = true; + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvHideTooltip() { + mShowingTooltip = false; + + nsCOMPtr xulBrowserWindow = GetXULBrowserWindow(); + if (!xulBrowserWindow) { + return IPC_OK(); + } + + xulBrowserWindow->HideTooltip(); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEFocus( + const ContentCache& aContentCache, const IMENotification& aIMENotification, + NotifyIMEFocusResolver&& aResolve) { + if (mIsDestroyed) { + return IPC_OK(); + } + + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget) { + aResolve(IMENotificationRequests()); + return IPC_OK(); + } + if (NS_WARN_IF(!aContentCache.IsValid())) { + return IPC_FAIL(this, "Invalid content cache data"); + } + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + IMEStateManager::NotifyIME(aIMENotification, widget, this); + + IMENotificationRequests requests; + if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) { + requests = widget->IMENotificationRequestsRef(); + } + aResolve(requests); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMETextChange( + const ContentCache& aContentCache, + const IMENotification& aIMENotification) { + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { + return IPC_OK(); + } + if (NS_WARN_IF(!aContentCache.IsValid())) { + return IPC_FAIL(this, "Invalid content cache data"); + } + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMECompositionUpdate( + const ContentCache& aContentCache, + const IMENotification& aIMENotification) { + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { + return IPC_OK(); + } + if (NS_WARN_IF(!aContentCache.IsValid())) { + return IPC_FAIL(this, "Invalid content cache data"); + } + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMESelection( + const ContentCache& aContentCache, + const IMENotification& aIMENotification) { + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { + return IPC_OK(); + } + if (NS_WARN_IF(!aContentCache.IsValid())) { + return IPC_FAIL(this, "Invalid content cache data"); + } + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvUpdateContentCache( + const ContentCache& aContentCache) { + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { + return IPC_OK(); + } + if (NS_WARN_IF(!aContentCache.IsValid())) { + return IPC_FAIL(this, "Invalid content cache data"); + } + mContentCache.AssignContent(aContentCache, widget); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEMouseButtonEvent( + const IMENotification& aIMENotification, bool* aConsumedByIME) { + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { + *aConsumedByIME = false; + return IPC_OK(); + } + nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, this); + *aConsumedByIME = rv == NS_SUCCESS_EVENT_CONSUMED; + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEPositionChange( + const ContentCache& aContentCache, + const IMENotification& aIMENotification) { + nsCOMPtr widget = GetTextInputHandlingWidget(); + if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) { + return IPC_OK(); + } + if (NS_WARN_IF(!aContentCache.IsValid())) { + return IPC_FAIL(this, "Invalid content cache data"); + } + mContentCache.AssignContent(aContentCache, widget, &aIMENotification); + mContentCache.MaybeNotifyIME(widget, aIMENotification); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvOnEventNeedingAckHandled( + const EventMessage& aMessage) { + // This is called when the child process receives WidgetCompositionEvent or + // WidgetSelectionEvent. + // FYI: Don't check if widget is nullptr here because it's more important to + // notify mContentCahce of this than handling something in it. + nsCOMPtr widget = GetTextInputHandlingWidget(); + + // While calling OnEventNeedingAckHandled(), BrowserParent *might* be + // destroyed since it may send notifications to IME. + RefPtr kungFuDeathGrip(this); + mContentCache.OnEventNeedingAckHandled(widget, aMessage); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvRequestFocus( + const bool& aCanRaise, const CallerType aCallerType) { + LOGBROWSERFOCUS(("RecvRequestFocus %p, aCanRaise: %d", this, aCanRaise)); + if (BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent()) { + mozilla::Unused << bridgeParent->SendRequestFocus(aCanRaise, aCallerType); + return IPC_OK(); + } + + if (!mFrameElement) { + return IPC_OK(); + } + + nsContentUtils::RequestFrameFocus(*mFrameElement, aCanRaise, aCallerType); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvWheelZoomChange(bool aIncrease) { + RefPtr bc = GetBrowsingContext(); + if (!bc) { + return IPC_OK(); + } + + bc->Canonical()->DispatchWheelZoomChange(aIncrease); + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvEnableDisableCommands( + const MaybeDiscarded& aContext, const nsString& aAction, + nsTArray&& aEnabledCommands, + nsTArray&& aDisabledCommands) { + if (aContext.IsNullOrDiscarded()) { + return IPC_OK(); + } + + nsCOMPtr browserController = do_QueryActor( + "Controllers", aContext.get_canonical()->GetCurrentWindowGlobal()); + if (browserController) { + browserController->EnableDisableCommands(aAction, aEnabledCommands, + aDisabledCommands); + } + + return IPC_OK(); +} + +LayoutDeviceIntPoint BrowserParent::TransformPoint( + const LayoutDeviceIntPoint& aPoint, + const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix) { + LayoutDevicePoint floatPoint(aPoint); + LayoutDevicePoint floatTransformed = TransformPoint(floatPoint, aMatrix); + // The next line loses precision if an out-of-process iframe + // has been scaled or rotated. + return RoundedToInt(floatTransformed); +} + +LayoutDevicePoint BrowserParent::TransformPoint( + const LayoutDevicePoint& aPoint, + const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix) { + return aMatrix.TransformPoint(aPoint); +} + +LayoutDeviceIntPoint BrowserParent::TransformParentToChild( + const LayoutDeviceIntPoint& aPoint) { + LayoutDeviceToLayoutDeviceMatrix4x4 matrix = + GetChildToParentConversionMatrix(); + if (!matrix.Invert()) { + return LayoutDeviceIntPoint(); + } + auto transformed = UntransformBy(matrix, aPoint); + if (!transformed) { + return LayoutDeviceIntPoint(); + } + return transformed.ref(); +} + +LayoutDevicePoint BrowserParent::TransformParentToChild( + const LayoutDevicePoint& aPoint) { + LayoutDeviceToLayoutDeviceMatrix4x4 matrix = + GetChildToParentConversionMatrix(); + if (!matrix.Invert()) { + return LayoutDevicePoint(); + } + auto transformed = UntransformBy(matrix, aPoint); + if (!transformed) { + return LayoutDeviceIntPoint(); + } + return transformed.ref(); +} + +LayoutDeviceIntPoint BrowserParent::TransformChildToParent( + const LayoutDeviceIntPoint& aPoint) { + return TransformPoint(aPoint, GetChildToParentConversionMatrix()); +} + +LayoutDevicePoint BrowserParent::TransformChildToParent( + const LayoutDevicePoint& aPoint) { + return TransformPoint(aPoint, GetChildToParentConversionMatrix()); +} + +LayoutDeviceIntRect BrowserParent::TransformChildToParent( + const LayoutDeviceIntRect& aRect) { + LayoutDeviceToLayoutDeviceMatrix4x4 matrix = + GetChildToParentConversionMatrix(); + LayoutDeviceRect floatRect(aRect); + // The outcome is not ideal if an out-of-process iframe has been rotated + LayoutDeviceRect floatTransformed = matrix.TransformBounds(floatRect); + // The next line loses precision if an out-of-process iframe + // has been scaled or rotated. + return RoundedToInt(floatTransformed); +} + +LayoutDeviceToLayoutDeviceMatrix4x4 +BrowserParent::GetChildToParentConversionMatrix() { + if (mChildToParentConversionMatrix) { + return *mChildToParentConversionMatrix; + } + LayoutDevicePoint offset(-GetChildProcessOffset()); + return LayoutDeviceToLayoutDeviceMatrix4x4::Translation(offset); +} + +void BrowserParent::SetChildToParentConversionMatrix( + const Maybe& aMatrix, + const ScreenRect& aRemoteDocumentRect) { + if (mChildToParentConversionMatrix == aMatrix && + mRemoteDocumentRect.isSome() && + mRemoteDocumentRect.value() == aRemoteDocumentRect) { + return; + } + + mChildToParentConversionMatrix = aMatrix; + mRemoteDocumentRect = Some(aRemoteDocumentRect); + if (mIsDestroyed) { + return; + } + mozilla::Unused << SendChildToParentMatrix(ToUnknownMatrix(aMatrix), + aRemoteDocumentRect); +} + +LayoutDeviceIntPoint BrowserParent::GetChildProcessOffset() { + // The "toplevel widget" in child processes is always at position + // 0,0. Map the event coordinates to match that. + + LayoutDeviceIntPoint offset(0, 0); + RefPtr frameLoader = GetFrameLoader(); + if (!frameLoader) { + return offset; + } + nsIFrame* targetFrame = frameLoader->GetPrimaryFrameOfOwningContent(); + if (!targetFrame) { + return offset; + } + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return offset; + } + + nsPresContext* presContext = targetFrame->PresContext(); + nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame(); + nsView* rootView = rootFrame ? rootFrame->GetView() : nullptr; + if (!rootView) { + return offset; + } + + // Note that we don't want to take into account transforms here: +#if 0 + nsPoint pt(0, 0); + nsLayoutUtils::TransformPoint(targetFrame, rootFrame, pt); +#endif + // In practice, when transforms are applied to this frameLoader, we currently + // get the wrong results whether we take transforms into account here or not. + // But applying transforms here gives us the wrong results in all + // circumstances when transforms are applied, unless they're purely + // translational. It also gives us the wrong results whenever CSS transitions + // are used to apply transforms, since the offeets aren't updated as the + // transition is animated. + // + // What we actually need to do is apply the transforms to the coordinates of + // any events we send to the child, and reverse them for any screen + // coordinates that we retrieve from the child. + + // TODO: Once we take into account transforms here, set viewportType + // correctly. For now we use Visual as this means we don't apply + // the layout-to-visual transform in TranslateViewToWidget(). + ViewportType viewportType = ViewportType::Visual; + + nsPoint pt = targetFrame->GetOffsetTo(rootFrame); + return -nsLayoutUtils::TranslateViewToWidget(presContext, rootView, pt, + viewportType, widget); +} + +LayoutDeviceIntPoint BrowserParent::GetClientOffset() { + nsCOMPtr widget = GetWidget(); + nsCOMPtr docWidget = GetDocWidget(); + + if (widget == docWidget) { + return widget->GetClientOffset(); + } + + return (docWidget->GetClientOffset() + + nsLayoutUtils::WidgetToWidgetOffset(widget, docWidget)); +} + +void BrowserParent::StopIMEStateManagement() { + if (mIsDestroyed) { + return; + } + Unused << SendStopIMEStateManagement(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent( + const WidgetKeyboardEvent& aEvent, const nsID& aUUID) { + NS_ENSURE_TRUE(mFrameElement, IPC_OK()); + + // First, verify aEvent is what we've sent to a remote process. + Maybe index = [&]() -> Maybe { + for (const size_t i : IntegerRange(mWaitingReplyKeyboardEvents.Length())) { + const SentKeyEventData& data = mWaitingReplyKeyboardEvents[i]; + if (data.mUUID.Equals(aUUID)) { + if (NS_WARN_IF(data.mKeyCode != aEvent.mKeyCode) || + NS_WARN_IF(data.mCharCode != aEvent.mCharCode) || + NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) || + NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) || + NS_WARN_IF(data.mCodeNameIndex != aEvent.mCodeNameIndex) || + NS_WARN_IF(data.mModifiers != aEvent.mModifiers)) { + // Got different event data from what we stored before dispatching an + // event with the ID. + return Nothing(); + } + return Some(i); + } + } + // No entry found. + return Nothing(); + }(); + if (MOZ_UNLIKELY(index.isNothing())) { + return IPC_FAIL(this, "Bogus reply keyboard event"); + } + // Don't discard the older keyboard events because the order may be changed if + // the remote process has a event listener which takes too long time and while + // the freezing, user may switch the tab, or if the remote process sends + // synchronous XMLHttpRequest. + mWaitingReplyKeyboardEvents.RemoveElementAt(*index); + + // If the event propagation was stopped by the child, it means that the event + // was ignored in the child. In the case, we should ignore it too because the + // focused web app didn't have a chance to prevent its default. + if (aEvent.PropagationStopped()) { + return IPC_OK(); + } + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.MarkAsHandledInRemoteProcess(); + + // Here we convert the WidgetEvent that we received to an Event + // to be able to dispatch it to the element as the target element. + RefPtr presContext = + mFrameElement->OwnerDoc()->GetPresContext(); + NS_ENSURE_TRUE(presContext, IPC_OK()); + + AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(), + &localEvent); + + nsEventStatus status = nsEventStatus_eIgnore; + + // Handle access key in this process before dispatching reply event because + // ESM handles it before dispatching the event to the DOM tree. + if (localEvent.mMessage == eKeyPress && + (localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eChrome) || + localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eContent))) { + RefPtr esm = presContext->EventStateManager(); + AutoTArray accessCharCodes; + localEvent.GetAccessKeyCandidates(accessCharCodes); + if (esm->HandleAccessKey(&localEvent, presContext, accessCharCodes)) { + status = nsEventStatus_eConsumeNoDefault; + } + } + + RefPtr frameElement = mFrameElement; + EventDispatcher::Dispatch(frameElement, presContext, &localEvent, nullptr, + &status); + + if (!localEvent.DefaultPrevented() && + !localEvent.mFlags.mIsSynthesizedForTests) { + nsCOMPtr widget = GetWidget(); + if (widget) { + widget->PostHandleKeyEvent(&localEvent); + localEvent.StopPropagation(); + } + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvAccessKeyNotHandled( + const WidgetKeyboardEvent& aEvent) { + NS_ENSURE_TRUE(mFrameElement, IPC_OK()); + + // This is called only when this process had focus and HandleAccessKey + // message was posted to all remote process and each remote process didn't + // execute any content access keys. + + if (MOZ_UNLIKELY(aEvent.mMessage != eKeyPress || !aEvent.IsTrusted())) { + return IPC_FAIL(this, "Called with unexpected event"); + } + + // If there is no requesting event, the event may have already been handled + // when it's returned from another remote process. + if (MOZ_UNLIKELY(!RequestingAccessKeyEventData::IsSet())) { + return IPC_OK(); + } + + // If the event does not match with the one which we requested a remote + // process to handle access key of (that means that we has already requested + // for another key press), we should ignore this call because user focuses + // to the last key press. + if (MOZ_UNLIKELY(!RequestingAccessKeyEventData::Equals(aEvent))) { + return IPC_OK(); + } + + RequestingAccessKeyEventData::Clear(); + + WidgetKeyboardEvent localEvent(aEvent); + localEvent.MarkAsHandledInRemoteProcess(); + localEvent.mMessage = eAccessKeyNotFound; + + // Here we convert the WidgetEvent that we received to an Event + // to be able to dispatch it to the element as the target element. + Document* doc = mFrameElement->OwnerDoc(); + PresShell* presShell = doc->GetPresShell(); + NS_ENSURE_TRUE(presShell, IPC_OK()); + + if (presShell->CanDispatchEvent()) { + RefPtr presContext = presShell->GetPresContext(); + NS_ENSURE_TRUE(presContext, IPC_OK()); + + RefPtr frameElement = mFrameElement; + EventDispatcher::Dispatch(frameElement, presContext, &localEvent); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvRegisterProtocolHandler( + const nsString& aScheme, nsIURI* aHandlerURI, const nsString& aTitle, + nsIURI* aDocURI) { + nsCOMPtr registrar = + do_GetService(NS_WEBPROTOCOLHANDLERREGISTRAR_CONTRACTID); + if (registrar) { + registrar->RegisterProtocolHandler(aScheme, aHandlerURI, aTitle, aDocURI, + mFrameElement); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvOnStateChange( + const WebProgressData& aWebProgressData, const RequestData& aRequestData, + const uint32_t aStateFlags, const nsresult aStatus, + const Maybe& aStateChangeData) { + RefPtr browsingContext = + BrowsingContextForWebProgress(aWebProgressData); + if (!browsingContext) { + return IPC_OK(); + } + + nsCOMPtr request; + if (aRequestData.requestURI()) { + request = MakeAndAddRef( + aRequestData.requestURI(), aRequestData.originalRequestURI(), + aRequestData.matchedList()); + } + + if (aStateChangeData.isSome()) { + if (!browsingContext->IsTopContent()) { + return IPC_FAIL( + this, + "Unexpected WebProgressStateChangeData for non toplevel webProgress"); + } + + if (nsCOMPtr browser = GetBrowser()) { + Unused << browser->SetIsNavigating(aStateChangeData->isNavigating()); + Unused << browser->SetMayEnableCharacterEncodingMenu( + aStateChangeData->mayEnableCharacterEncodingMenu()); + Unused << browser->UpdateForStateChange(aStateChangeData->charset(), + aStateChangeData->documentURI(), + aStateChangeData->contentType()); + } + } + + if (auto* listener = browsingContext->GetWebProgress()) { + listener->OnStateChange(listener, request, aStateFlags, aStatus); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvOnProgressChange( + const int32_t aCurTotalProgress, const int32_t aMaxTotalProgress) { + // We only collect progress change notifications for the toplevel + // BrowserParent. + // FIXME: In the future, consider merging in progress change information from + // oop subframes. + if (!GetBrowsingContext()->IsTopContent() || + !GetBrowsingContext()->GetWebProgress()) { + return IPC_OK(); + } + + GetBrowsingContext()->GetWebProgress()->OnProgressChange( + nullptr, nullptr, 0, 0, aCurTotalProgress, aMaxTotalProgress); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange( + const WebProgressData& aWebProgressData, const RequestData& aRequestData, + nsIURI* aLocation, const uint32_t aFlags, const bool aCanGoBack, + const bool aCanGoForward, + const Maybe& aLocationChangeData) { + RefPtr browsingContext = + BrowsingContextForWebProgress(aWebProgressData); + if (!browsingContext) { + return IPC_OK(); + } + + nsCOMPtr request; + if (aRequestData.requestURI()) { + request = MakeAndAddRef( + aRequestData.requestURI(), aRequestData.originalRequestURI(), + aRequestData.matchedList()); + } + + browsingContext->SetCurrentRemoteURI(aLocation); + + nsCOMPtr browser = GetBrowser(); + if (!mozilla::SessionHistoryInParent() && browser) { + Unused << browser->UpdateWebNavigationForLocationChange(aCanGoBack, + aCanGoForward); + } + + if (aLocationChangeData.isSome()) { + if (!browsingContext->IsTopContent()) { + return IPC_FAIL(this, + "Unexpected WebProgressLocationChangeData for non " + "toplevel webProgress"); + } + + if (browser) { + Unused << browser->SetIsNavigating(aLocationChangeData->isNavigating()); + Unused << browser->UpdateForLocationChange( + aLocation, aLocationChangeData->charset(), + aLocationChangeData->mayEnableCharacterEncodingMenu(), + aLocationChangeData->documentURI(), aLocationChangeData->title(), + aLocationChangeData->contentPrincipal(), + aLocationChangeData->contentPartitionedPrincipal(), + aLocationChangeData->csp(), aLocationChangeData->referrerInfo(), + aLocationChangeData->isSyntheticDocument(), + aLocationChangeData->requestContextID().isSome(), + aLocationChangeData->requestContextID().valueOr(0), + aLocationChangeData->contentType()); + } + } + + if (auto* listener = browsingContext->GetWebProgress()) { + listener->OnLocationChange(listener, request, aLocation, aFlags); + } + + // Since we've now changed Documents, notify the BrowsingContext that we've + // changed. Ideally we'd just let the BrowsingContext do this when it changes + // the current window global, but that happens before this and we have a lot + // of tests that depend on the specific ordering of messages. + if (browsingContext->IsTopContent() && + !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) { + browsingContext->UpdateSecurityState(); + } + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvOnStatusChange( + const nsString& aMessage) { + // We only collect status change notifications for the toplevel + // BrowserParent. + // FIXME: In the future, consider merging in status change information from + // oop subframes. + if (!GetBrowsingContext()->IsTopContent() || + !GetBrowsingContext()->GetWebProgress()) { + return IPC_OK(); + } + + GetBrowsingContext()->GetWebProgress()->OnStatusChange(nullptr, nullptr, + NS_OK, aMessage.get()); + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNavigationFinished() { + nsCOMPtr browser = + mFrameElement ? mFrameElement->AsBrowser() : nullptr; + + if (browser) { + browser->SetIsNavigating(false); + } + + return IPC_OK(); +} + +mozilla::ipc::IPCResult BrowserParent::RecvNotifyContentBlockingEvent( + const uint32_t& aEvent, const RequestData& aRequestData, + const bool aBlocked, const nsACString& aTrackingOrigin, + nsTArray&& aTrackingFullHashes, + const Maybe< + mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>& + aReason) { + RefPtr bc = GetBrowsingContext(); + + if (!bc || bc->IsDiscarded()) { + return IPC_OK(); + } + + // Get the top-level browsing context. + bc = bc->Top(); + RefPtr wgp = + bc->Canonical()->GetCurrentWindowGlobal(); + + // The WindowGlobalParent would be null while running the test + // browser_339445.js. This is unexpected and we will address this in a + // following bug. For now, we first workaround this issue. + if (!wgp) { + return IPC_OK(); + } + + nsCOMPtr request = MakeAndAddRef( + aRequestData.requestURI(), aRequestData.originalRequestURI(), + aRequestData.matchedList()); + + wgp->NotifyContentBlockingEvent(aEvent, request, aBlocked, aTrackingOrigin, + aTrackingFullHashes, aReason); + + return IPC_OK(); +} + +already_AddRefed BrowserParent::GetBrowser() { + nsCOMPtr browser; + RefPtr currentElement = mFrameElement; + + // In Responsive Design Mode, mFrameElement will be the ", + async browser => { + let bcid = await SpecialPowers.spawn(browser, [], () => { + return content.document.body.querySelector("iframe").browsingContext.id; + }); + + // We currently have no known way to trigger PBrowser creation failure, + // other than to use this custom pref for the purpose. + info(`enabling failPBrowserCreation for browsingContext: ${bcid}`); + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.tabs.remote.testOnly.failPBrowserCreation.enabled", true], + [ + "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext", + `${bcid}`, + ], + ], + }); + + let eventFiredPromise = BrowserTestUtils.waitForEvent( + browser, + "oop-browser-crashed" + ); + + info("triggering navigation which will fail pbrowser creation"); + await SpecialPowers.spawn(browser, [], () => { + content.document.body.querySelector("iframe").src = + "https://example.org/document-builder.sjs?html=frame"; + }); + + info("Waiting for oop-browser-crashed event."); + let event = await eventFiredPromise; + ok(!event.isTopFrame, "should be reporting subframe crash"); + ok( + event.childID == 0, + "childID should be zero, as no process actually crashed" + ); + is(event.browsingContextId, bcid, "bcid should match"); + + let { subject: windowGlobal } = await BrowserUtils.promiseObserved( + "window-global-created", + wgp => wgp.documentURI.spec.startsWith("about:framecrashed") + ); + is(windowGlobal.browsingContext.id, bcid, "bcid is correct"); + + await SpecialPowers.popPrefEnv(); + } + ); +}); diff --git a/dom/ipc/tests/browser_subframesPreferUsed.js b/dom/ipc/tests/browser_subframesPreferUsed.js new file mode 100644 index 0000000000..f2f9ed2593 --- /dev/null +++ b/dom/ipc/tests/browser_subframesPreferUsed.js @@ -0,0 +1,82 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +ok( + Services.appinfo.fissionAutostart, + "this test requires fission to function!" +); + +function documentURL(origin, html) { + let params = new URLSearchParams(); + params.append("html", html.trim()); + return `${origin}/document-builder.sjs?${params.toString()}`; +} + +async function singleTest(preferUsed) { + info(`running test with preferUsed=${preferUsed}`); + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.ipc.processCount.webIsolated", 4], + ["browser.tabs.remote.subframesPreferUsed", preferUsed], + ], + }); + + const TEST_URL = documentURL( + "https://example.com", + `` + ); + + await BrowserTestUtils.withNewTab(TEST_URL, async browser1 => { + is(browser1.browsingContext.children.length, 1); + let topProc1 = browser1.browsingContext.currentWindowGlobal.domProcess; + let frameProc1 = + browser1.browsingContext.children[0].currentWindowGlobal.domProcess; + isnot( + topProc1.childID, + frameProc1.childID, + "the frame should be in a separate process" + ); + + await BrowserTestUtils.withNewTab(TEST_URL, async browser2 => { + is(browser2.browsingContext.children.length, 1); + let topProc2 = browser2.browsingContext.currentWindowGlobal.domProcess; + let frameProc2 = + browser2.browsingContext.children[0].currentWindowGlobal.domProcess; + isnot( + topProc2.childID, + frameProc2.childID, + "the frame should be in a separate process" + ); + + // Compare processes used for the two tabs. + isnot( + topProc1.childID, + topProc2.childID, + "the toplevel windows should be loaded in separate processes" + ); + if (preferUsed) { + is( + frameProc1.childID, + frameProc2.childID, + "the iframes should load in the same process with subframesPreferUsed" + ); + } else { + isnot( + frameProc1.childID, + frameProc2.childID, + "the iframes should load in different processes without subframesPreferUsed" + ); + } + }); + }); +} + +add_task(async function test_preferUsed() { + await singleTest(true); +}); + +add_task(async function test_noPreferUsed() { + await singleTest(false); +}); diff --git a/dom/ipc/tests/browser_very_fission.js b/dom/ipc/tests/browser_very_fission.js new file mode 100644 index 0000000000..582fe00133 --- /dev/null +++ b/dom/ipc/tests/browser_very_fission.js @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// This test creates a large number of content processes as a +// regression test for bug 1635451. + +const TEST_PAGE = + "http://mochi.test:8888/browser/dom/ipc/tests/file_dummy.html"; + +const NUM_TABS = 256; + +add_task(async () => { + let promises = []; + for (let i = 0; i < NUM_TABS; ++i) { + promises.push( + BrowserTestUtils.openNewForegroundTab({ + gBrowser, + opening: TEST_PAGE, + waitForLoad: true, + forceNewProcess: true, + }) + ); + } + + let tabs = []; + for (const p of promises) { + tabs.push(await p); + } + + ok(true, "All of the tabs loaded"); + + for (const t of tabs) { + BrowserTestUtils.removeTab(t); + } +}); diff --git a/dom/ipc/tests/browser_wpi_base.js b/dom/ipc/tests/browser_wpi_base.js new file mode 100644 index 0000000000..7a01c9a161 --- /dev/null +++ b/dom/ipc/tests/browser_wpi_base.js @@ -0,0 +1,305 @@ +// This test is fission-only! Make that clear before continuing, to avoid +// confusing failures. +ok( + Services.appinfo.fissionAutostart, + "this test requires fission to function!" +); + +requestLongerTimeout(2); + +const WebContentIsolationStrategy = { + IsolateNothing: 0, + IsolateEverything: 1, + IsolateHighValue: 2, +}; + +const COM_ORIGIN = "https://example.com"; +const ORG_ORIGIN = "https://example.org"; +const MOZ_ORIGIN = "https://www.mozilla.org"; + +// Helper for building document-builder.sjs URLs which have specific headers & +// HTML content. +function documentURL(origin, headers, html) { + let params = new URLSearchParams(); + params.append("html", html.trim()); + for (const [key, value] of Object.entries(headers)) { + params.append("headers", `${key}:${value}`); + } + return `${origin}/document-builder.sjs?${params.toString()}`; +} + +async function testTreeRemoteTypes(name, testpage) { + // Use document-builder.sjs to build up the expected document tree. + function buildURL(path, page) { + let html = `

${path}

`; + for (let i = 0; i < page.children.length; ++i) { + const inner = buildURL(`${path}[${i}]`, page.children[i]); + html += ``; + } + return documentURL(page.origin, page.headers, html); + } + const url = buildURL(name, testpage); + + // Load the tab and confirm that properties of the loaded documents match + // expectation. + await BrowserTestUtils.withNewTab(url, async browser => { + let stack = [ + { + path: name, + bc: browser.browsingContext, + ...testpage, + }, + ]; + + while (stack.length) { + const { path, bc, remoteType, children, origin } = stack.pop(); + is( + Services.scriptSecurityManager.createContentPrincipal( + bc.currentWindowGlobal.documentURI, + {} + ).originNoSuffix, + origin, + `Frame ${path} has expected originNoSuffix` + ); + is( + bc.currentWindowGlobal.domProcess.remoteType, + remoteType, + `Frame ${path} has expected remote type` + ); + is( + bc.children.length, + children.length, + `Frame ${path} has the expected number of children` + ); + for (let i = 0; i < bc.children.length; ++i) { + stack.push({ + path: `${path}[${i}]`, + bc: bc.children[i], + ...children[i], + }); + } + } + }); +} + +function mkTestPage({ + comRemoteType, + orgRemoteType, + mozRemoteType, + topOrigin, + topHeaders = {}, + frameHeaders = {}, +}) { + const topRemoteType = { + [COM_ORIGIN]: comRemoteType, + [ORG_ORIGIN]: orgRemoteType, + [MOZ_ORIGIN]: mozRemoteType, + }[topOrigin]; + + const innerChildren = [ + { + origin: COM_ORIGIN, + headers: frameHeaders, + remoteType: comRemoteType, + children: [], + }, + { + origin: ORG_ORIGIN, + headers: frameHeaders, + remoteType: orgRemoteType, + children: [], + }, + { + origin: MOZ_ORIGIN, + headers: frameHeaders, + remoteType: mozRemoteType, + children: [], + }, + ]; + + return { + origin: topOrigin, + headers: topHeaders, + remoteType: topRemoteType, + children: [ + { + origin: COM_ORIGIN, + headers: frameHeaders, + remoteType: comRemoteType, + children: [...innerChildren], + }, + { + origin: ORG_ORIGIN, + headers: frameHeaders, + remoteType: orgRemoteType, + children: [...innerChildren], + }, + { + origin: MOZ_ORIGIN, + headers: frameHeaders, + remoteType: mozRemoteType, + children: [...innerChildren], + }, + ], + }; +} + +const heuristics = [ + { + name: "coop", + setup_com: async expected => { + // Set the COOP header, and load + await testTreeRemoteTypes( + "com_set_coop", + mkTestPage({ + topOrigin: COM_ORIGIN, + topHeaders: { "Cross-Origin-Opener-Policy": "same-origin" }, + comRemoteType: expected.com_high, + orgRemoteType: expected.org_normal, + mozRemoteType: expected.moz_normal, + }) + ); + }, + run_extra_test: async expected => { + // Load with both the COOP and COEP headers set. + await testTreeRemoteTypes( + "com_coop_coep", + mkTestPage({ + topOrigin: COM_ORIGIN, + topHeaders: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, + frameHeaders: { + "Cross-Origin-Embedder-Policy": "require-corp", + "Cross-Origin-Resource-Policy": "cross-origin", + }, + comRemoteType: expected.com_coop_coep, + orgRemoteType: expected.org_coop_coep, + mozRemoteType: expected.moz_coop_coep, + }) + ); + }, + }, + { + name: "hasSavedLogin", + setup_com: async expected => { + // add .com to the password manager + let LoginInfo = new Components.Constructor( + "@mozilla.org/login-manager/loginInfo;1", + Ci.nsILoginInfo, + "init" + ); + await Services.logins.addLoginAsync( + new LoginInfo(COM_ORIGIN, "", null, "username", "password", "", "") + ); + + // Init login detection service to trigger fetching logins + let loginDetection = Cc[ + "@mozilla.org/login-detection-service;1" + ].createInstance(Ci.nsILoginDetectionService); + loginDetection.init(); + + await TestUtils.waitForCondition(() => { + let x = loginDetection.isLoginsLoaded(); + return x; + }, "waiting for loading logins from the password manager"); + }, + }, + { + name: "isLoggedIn", + setup_com: async expected => { + let p = new Promise(resolve => { + Services.obs.addObserver(function obs() { + Services.obs.removeObserver( + obs, + "passwordmgr-form-submission-detected" + ); + resolve(); + }, "passwordmgr-form-submission-detected"); + }); + + const TEST_URL = documentURL( + COM_ORIGIN, + {}, + `
+ + + +
` + ); + + // submit the form to simulate the login behavior + await BrowserTestUtils.withNewTab(TEST_URL, async browser => { + await SpecialPowers.spawn(browser, [], async () => { + content.document.querySelector("form").submit(); + }); + }); + await p; + }, + }, +]; + +async function do_tests(expected) { + for (let heuristic of heuristics) { + info(`Starting ${heuristic.name} test`); + // Clear all site-specific data, as we don't want to have any high-value site + // permissions from any previous iterations. + await new Promise(resolve => + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve) + ); + + // Loads for basic URLs with no special headers set. + await testTreeRemoteTypes( + "basic_com", + mkTestPage({ + topOrigin: COM_ORIGIN, + comRemoteType: expected.com_normal, + orgRemoteType: expected.org_normal, + mozRemoteType: expected.moz_normal, + }) + ); + + await testTreeRemoteTypes( + "basic_org", + mkTestPage({ + topOrigin: ORG_ORIGIN, + comRemoteType: expected.com_normal, + orgRemoteType: expected.org_normal, + mozRemoteType: expected.moz_normal, + }) + ); + + info(`Setting up ${heuristic.name} test`); + await heuristic.setup_com(expected); + + // Load again after the heuristic is triggered + info(`Running ${heuristic.name} tests after setup`); + await testTreeRemoteTypes( + `com_after_${heuristic.name}`, + mkTestPage({ + topOrigin: COM_ORIGIN, + comRemoteType: expected.com_high, + orgRemoteType: expected.org_normal, + mozRemoteType: expected.moz_normal, + }) + ); + + // Load again with a .org toplevel + await testTreeRemoteTypes( + `org_after_${heuristic.name}`, + mkTestPage({ + topOrigin: ORG_ORIGIN, + comRemoteType: expected.com_high, + orgRemoteType: expected.org_normal, + mozRemoteType: expected.moz_normal, + }) + ); + + // Run heuristic dependent tests + if (heuristic.run_extra_test) { + info(`Running extra tests for ${heuristic.name}`); + await heuristic.run_extra_test(expected); + } + } +} diff --git a/dom/ipc/tests/browser_wpi_isolate_everything.js b/dom/ipc/tests/browser_wpi_isolate_everything.js new file mode 100644 index 0000000000..e902fec9d0 --- /dev/null +++ b/dom/ipc/tests/browser_wpi_isolate_everything.js @@ -0,0 +1,27 @@ +// Import this in order to use `do_tests()`. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/dom/ipc/tests/browser_wpi_base.js", + this +); + +add_task(async function test_isolate_everything() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.tabs.remote.separatedMozillaDomains", "mozilla.org"], + [ + "fission.webContentIsolationStrategy", + WebContentIsolationStrategy.IsolateEverything, + ], + ], + }); + + await do_tests({ + com_normal: "webIsolated=https://example.com", + org_normal: "webIsolated=https://example.org", + moz_normal: "privilegedmozilla", + com_high: "webIsolated=https://example.com", + com_coop_coep: "webCOOP+COEP=https://example.com", + org_coop_coep: "webCOOP+COEP=https://example.org", + moz_coop_coep: "privilegedmozilla", + }); +}); diff --git a/dom/ipc/tests/browser_wpi_isolate_high_value.js b/dom/ipc/tests/browser_wpi_isolate_high_value.js new file mode 100644 index 0000000000..bf6b99d5f5 --- /dev/null +++ b/dom/ipc/tests/browser_wpi_isolate_high_value.js @@ -0,0 +1,27 @@ +// Import this in order to use `do_tests()`. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/dom/ipc/tests/browser_wpi_base.js", + this +); + +add_task(async function test_isolate_high_value() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.tabs.remote.separatedMozillaDomains", "mozilla.org"], + [ + "fission.webContentIsolationStrategy", + WebContentIsolationStrategy.IsolateHighValue, + ], + ], + }); + + await do_tests({ + com_normal: "web", + org_normal: "web", + moz_normal: "privilegedmozilla", + com_high: "webIsolated=https://example.com", + com_coop_coep: "webCOOP+COEP=https://example.com", + org_coop_coep: "webCOOP+COEP=https://example.org", + moz_coop_coep: "privilegedmozilla", + }); +}); diff --git a/dom/ipc/tests/browser_wpi_isolate_nothing.js b/dom/ipc/tests/browser_wpi_isolate_nothing.js new file mode 100644 index 0000000000..afd5e51640 --- /dev/null +++ b/dom/ipc/tests/browser_wpi_isolate_nothing.js @@ -0,0 +1,27 @@ +// Import this in order to use `do_tests()`. +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/dom/ipc/tests/browser_wpi_base.js", + this +); + +add_task(async function test_isolate_nothing() { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.tabs.remote.separatedMozillaDomains", "mozilla.org"], + [ + "fission.webContentIsolationStrategy", + WebContentIsolationStrategy.IsolateNothing, + ], + ], + }); + + await do_tests({ + com_normal: "web", + org_normal: "web", + moz_normal: "privilegedmozilla", + com_high: "web", + com_coop_coep: "webCOOP+COEP=https://example.com", + org_coop_coep: "webCOOP+COEP=https://example.org", + moz_coop_coep: "privilegedmozilla", + }); +}); diff --git a/dom/ipc/tests/chrome.ini b/dom/ipc/tests/chrome.ini new file mode 100644 index 0000000000..1f0bc29954 --- /dev/null +++ b/dom/ipc/tests/chrome.ini @@ -0,0 +1,7 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + process_error.xhtml + +[test_process_error.xhtml] +skip-if = !crashreporter diff --git a/dom/ipc/tests/file_broadcast_currenturi_onload.html b/dom/ipc/tests/file_broadcast_currenturi_onload.html new file mode 100644 index 0000000000..b92c46c944 --- /dev/null +++ b/dom/ipc/tests/file_broadcast_currenturi_onload.html @@ -0,0 +1,66 @@ + + + + + diff --git a/dom/ipc/tests/file_cancel_content_js.html b/dom/ipc/tests/file_cancel_content_js.html new file mode 100644 index 0000000000..d2caf03c6a --- /dev/null +++ b/dom/ipc/tests/file_cancel_content_js.html @@ -0,0 +1,18 @@ + + + + Wait for it... + + + Try to go to another page. + + + diff --git a/dom/ipc/tests/file_cross_frame.html b/dom/ipc/tests/file_cross_frame.html new file mode 100644 index 0000000000..b52d920dd0 --- /dev/null +++ b/dom/ipc/tests/file_cross_frame.html @@ -0,0 +1,12 @@ + + + + + Different-origin iframe + + + +I am a web page + + + diff --git a/dom/ipc/tests/file_disableScript.html b/dom/ipc/tests/file_disableScript.html new file mode 100644 index 0000000000..f4888cd586 --- /dev/null +++ b/dom/ipc/tests/file_disableScript.html @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/dom/ipc/tests/file_domainPolicy_base.html b/dom/ipc/tests/file_domainPolicy_base.html new file mode 100644 index 0000000000..6e3ec7aec4 --- /dev/null +++ b/dom/ipc/tests/file_domainPolicy_base.html @@ -0,0 +1,8 @@ + + + + + + + + + + diff --git a/dom/ipc/tests/test_bug1086684.js b/dom/ipc/tests/test_bug1086684.js new file mode 100644 index 0000000000..8a34906686 --- /dev/null +++ b/dom/ipc/tests/test_bug1086684.js @@ -0,0 +1,99 @@ +"use strict"; + +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); + +XPCShellContentUtils.init(this); + +const childFramePath = "/file_bug1086684.html"; +const childFrameURL = "http://example.com" + childFramePath; + +const childFrameContents = ` + + + + + +
+ +
+ +`; + +const server = XPCShellContentUtils.createHttpServer({ + hosts: ["example.com"], +}); +server.registerPathHandler(childFramePath, (request, response) => { + response.write(childFrameContents); +}); + +function childFrameScript() { + /* eslint-env mozilla/frame-script */ + "use strict"; + + let { MockFilePicker } = ChromeUtils.importESModule( + "resource://testing-common/MockFilePicker.sys.mjs" + ); + + function parentReady(message) { + MockFilePicker.init(content); + MockFilePicker.setFiles([message.data.file]); + MockFilePicker.returnValue = MockFilePicker.returnOK; + + let input = content.document.getElementById("f"); + input.addEventListener("change", () => { + MockFilePicker.cleanup(); + let value = input.value; + message.target.sendAsyncMessage("testBug1086684:childDone", { value }); + }); + + input.focus(); + input.click(); + } + + addMessageListener("testBug1086684:parentReady", function (message) { + parentReady(message); + }); +} + +add_task(async function () { + Services.prefs.setBoolPref("dom.security.https_first", false); + let page = await XPCShellContentUtils.loadContentPage(childFrameURL, { + remote: true, + }); + + page.loadFrameScript(childFrameScript); + + await new Promise(resolve => { + let test; + function* testStructure(mm) { + let value; + + function testDone(msg) { + test.next(msg.data.value); + } + + mm.addMessageListener("testBug1086684:childDone", testDone); + + let blob = new Blob([]); + let file = new File([blob], "helloworld.txt", { type: "text/plain" }); + + mm.sendAsyncMessage("testBug1086684:parentReady", { file }); + value = yield; + + // Note that the "helloworld.txt" passed in above doesn't affect the + // 'value' getter. Because we're mocking a file using a blob, we ask the + // blob for its path, which is the empty string. + equal(value, "", "got the right answer and didn't crash"); + + resolve(); + } + + test = testStructure(page.browser.messageManager); + test.next(); + }); + + await page.close(); + Services.prefs.clearUserPref("dom.security.https_first"); +}); diff --git a/dom/ipc/tests/test_child_docshell.js b/dom/ipc/tests/test_child_docshell.js new file mode 100644 index 0000000000..ee79a509dc --- /dev/null +++ b/dom/ipc/tests/test_child_docshell.js @@ -0,0 +1,90 @@ +"use strict"; + +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); + +XPCShellContentUtils.init(this); + +add_task(async function test() { + let page = await XPCShellContentUtils.loadContentPage("about:blank", { + remote: true, + }); + + await new Promise(resolve => { + let mm = page.browser.messageManager; + mm.addMessageListener("chromeEventHandler", function (msg) { + var result = msg.json; + equal( + result.processType, + Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT, + "The frame script is running in a real distinct child process" + ); + ok( + result.hasCorrectInterface, + "docshell.chromeEventHandler has EventTarget interface" + ); + }); + + mm.addMessageListener("DOMWindowCreatedReceived", function (msg) { + ok(true, "the chrome event handler looks functional"); + var result = msg.json; + ok( + result.stableChromeEventHandler, + "docShell.chromeEventHandler is stable" + ); + ok(result.iframeHasNewDocShell, "iframe spawns a new docShell"); + ok( + result.iframeHasSameChromeEventHandler, + "but iframe has the same chrome event handler" + ); + resolve(); + }); + + // Inject a frame script in the child process: + page.loadFrameScript(async function () { + /* eslint-env mozilla/frame-script */ + var chromeEventHandler = docShell.chromeEventHandler; + sendAsyncMessage("chromeEventHandler", { + processType: Services.appinfo.processType, + hasCorrectInterface: + chromeEventHandler && EventTarget.isInstance(chromeEventHandler), + }); + + /* + Ensure that this chromeEventHandler actually works, + by creating a new window and listening for its DOMWindowCreated event + */ + chromeEventHandler.addEventListener( + "DOMWindowCreated", + function listener(evt) { + if (evt.target == content.document) { + return; + } + chromeEventHandler.removeEventListener("DOMWindowCreated", listener); + let new_win = evt.target.defaultView; + let new_docShell = new_win.docShell; + sendAsyncMessage("DOMWindowCreatedReceived", { + stableChromeEventHandler: + chromeEventHandler === docShell.chromeEventHandler, + iframeHasNewDocShell: new_docShell !== docShell, + iframeHasSameChromeEventHandler: + new_docShell.chromeEventHandler === chromeEventHandler, + }); + } + ); + + if (content.document.readyState != "complete") { + await new Promise(res => + addEventListener("load", res, { once: true, capture: true }) + ); + } + + let iframe = content.document.createElement("iframe"); + iframe.setAttribute("src", "data:text/html,foo"); + content.document.documentElement.appendChild(iframe); + }); + }); + + await page.close(); +}); diff --git a/dom/ipc/tests/test_process_error.xhtml b/dom/ipc/tests/test_process_error.xhtml new file mode 100644 index 0000000000..d122e7fedd --- /dev/null +++ b/dom/ipc/tests/test_process_error.xhtml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/dom/ipc/tests/test_sharedMap.js b/dom/ipc/tests/test_sharedMap.js new file mode 100644 index 0000000000..2a6c3a7142 --- /dev/null +++ b/dom/ipc/tests/test_sharedMap.js @@ -0,0 +1,377 @@ +"use strict"; + +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { XPCShellContentUtils } = ChromeUtils.importESModule( + "resource://testing-common/XPCShellContentUtils.sys.mjs" +); + +const PROCESS_COUNT_PREF = "dom.ipc.processCount"; + +const remote = AppConstants.platform !== "android"; + +XPCShellContentUtils.init(this); + +let contentPage; + +async function readBlob(key, sharedData = Services.cpmm.sharedData) { + const { ExtensionUtils } = ChromeUtils.importESModule( + "resource://gre/modules/ExtensionUtils.sys.mjs" + ); + + let reader = new FileReader(); + reader.readAsText(sharedData.get(key)); + await ExtensionUtils.promiseEvent(reader, "loadend"); + return reader.result; +} + +function getKey(key, sharedData = Services.cpmm.sharedData) { + return sharedData.get(key); +} + +function hasKey(key, sharedData = Services.cpmm.sharedData) { + return sharedData.has(key); +} + +function getContents(sharedMap = Services.cpmm.sharedData) { + return { + keys: Array.from(sharedMap.keys()), + values: Array.from(sharedMap.values()), + entries: Array.from(sharedMap.entries()), + getValues: Array.from(sharedMap.keys(), key => sharedMap.get(key)), + }; +} + +function checkMap(contents, expected) { + expected = Array.from(expected); + + equal(contents.keys.length, expected.length, "Got correct number of keys"); + equal( + contents.values.length, + expected.length, + "Got correct number of values" + ); + equal( + contents.entries.length, + expected.length, + "Got correct number of entries" + ); + + for (let [i, [key, val]] of contents.entries.entries()) { + equal(key, contents.keys[i], `keys()[${i}] matches entries()[${i}]`); + deepEqual( + val, + contents.values[i], + `values()[${i}] matches entries()[${i}]` + ); + } + + expected.sort(([a], [b]) => a.localeCompare(b)); + contents.entries.sort(([a], [b]) => a.localeCompare(b)); + + for (let [i, [key, val]] of contents.entries.entries()) { + equal( + key, + expected[i][0], + `expected[${i}].key matches entries()[${i}].key` + ); + deepEqual( + val, + expected[i][1], + `expected[${i}].value matches entries()[${i}].value` + ); + } +} + +function checkParentMap(expected) { + info("Checking parent map"); + checkMap(getContents(Services.ppmm.sharedData), expected); +} + +async function checkContentMaps(expected, parentOnly = false) { + info("Checking in-process content map"); + checkMap(getContents(Services.cpmm.sharedData), expected); + + if (!parentOnly) { + info("Checking out-of-process content map"); + let contents = await contentPage.spawn([], getContents); + checkMap(contents, expected); + } +} + +async function loadContentPage() { + let page = await XPCShellContentUtils.loadContentPage("data:text/html,", { + remote, + }); + registerCleanupFunction(() => page.close()); + return page; +} + +add_setup(async function () { + // Start with one content process so that we can increase the number + // later and test the behavior of a fresh content process. + Services.prefs.setIntPref(PROCESS_COUNT_PREF, 1); + + contentPage = await loadContentPage(); +}); + +add_task(async function test_sharedMap() { + let { sharedData } = Services.ppmm; + + info("Check that parent and child maps are both initially empty"); + + checkParentMap([]); + await checkContentMaps([]); + + let expected = [ + ["foo-a", { foo: "a" }], + ["foo-b", { foo: "b" }], + ["bar-c", null], + ["bar-d", 42], + ]; + + function setKey(key, val) { + sharedData.set(key, val); + expected = expected.filter(([k]) => k != key); + expected.push([key, val]); + } + function deleteKey(key) { + sharedData.delete(key); + expected = expected.filter(([k]) => k != key); + } + + for (let [key, val] of expected) { + sharedData.set(key, val); + } + + info( + "Add some entries, test that they are initially only available in the parent" + ); + + checkParentMap(expected); + await checkContentMaps([]); + + info("Flush. Check that changes are visible in both parent and children"); + + sharedData.flush(); + + checkParentMap(expected); + await checkContentMaps(expected); + + info( + "Add another entry. Check that it is initially only available in the parent" + ); + + let oldExpected = Array.from(expected); + + setKey("baz-a", { meh: "meh" }); + + // When we do several checks in a row, we can't check the values in + // the content process, since the async checks may allow the idle + // flush task to run, and update it before we're ready. + + checkParentMap(expected); + checkContentMaps(oldExpected, true); + + info( + "Add another entry. Check that both new entries are only available in the parent" + ); + + setKey("baz-a", { meh: 12 }); + + checkParentMap(expected); + checkContentMaps(oldExpected, true); + + info( + "Delete an entry. Check that all changes are only visible in the parent" + ); + + deleteKey("foo-b"); + + checkParentMap(expected); + checkContentMaps(oldExpected, true); + + info( + "Flush. Check that all entries are available in both parent and children" + ); + + sharedData.flush(); + + checkParentMap(expected); + await checkContentMaps(expected); + + info("Test that entries are automatically flushed on idle:"); + + info( + "Add a new entry. Check that it is initially only available in the parent" + ); + + // Test the idle flush task. + oldExpected = Array.from(expected); + + setKey("thing", "stuff"); + + checkParentMap(expected); + checkContentMaps(oldExpected, true); + + info( + "Wait for an idle timeout. Check that changes are now visible in all children" + ); + + await new Promise(resolve => ChromeUtils.idleDispatch(resolve)); + + checkParentMap(expected); + await checkContentMaps(expected); + + // Test that has() rebuilds map after a flush. + sharedData.set("grick", true); + sharedData.flush(); + equal( + await contentPage.spawn(["grick"], hasKey), + true, + "has() should see key after flush" + ); + + sharedData.set("grack", true); + sharedData.flush(); + equal( + await contentPage.spawn(["gruck"], hasKey), + false, + "has() should return false for nonexistent key" + ); +}); + +add_task(async function test_blobs() { + let { sharedData } = Services.ppmm; + + let text = [ + "The quick brown fox jumps over the lazy dog", + "Lorem ipsum dolor sit amet, consectetur adipiscing elit", + "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + ]; + let blobs = text.map(str => new Blob([str])); + + let data = { foo: { bar: "baz" } }; + + sharedData.set("blob0", blobs[0]); + sharedData.set("blob1", blobs[1]); + sharedData.set("data", data); + + equal( + await readBlob("blob0", sharedData), + text[0], + "Expected text for blob0 in parent ppmm" + ); + + sharedData.flush(); + + equal( + await readBlob("blob0", sharedData), + text[0], + "Expected text for blob0 in parent ppmm" + ); + equal( + await readBlob("blob1", sharedData), + text[1], + "Expected text for blob1 in parent ppmm" + ); + + equal( + await readBlob("blob0"), + text[0], + "Expected text for blob0 in parent cpmm" + ); + equal( + await readBlob("blob1"), + text[1], + "Expected text for blob1 in parent cpmm" + ); + + equal( + await contentPage.spawn(["blob0"], readBlob), + text[0], + "Expected text for blob0 in child 1 cpmm" + ); + equal( + await contentPage.spawn(["blob1"], readBlob), + text[1], + "Expected text for blob1 in child 1 cpmm" + ); + + // Start a second child process + Services.prefs.setIntPref(PROCESS_COUNT_PREF, 2); + + let page2 = await loadContentPage(); + + equal( + await page2.spawn(["blob0"], readBlob), + text[0], + "Expected text for blob0 in child 2 cpmm" + ); + equal( + await page2.spawn(["blob1"], readBlob), + text[1], + "Expected text for blob1 in child 2 cpmm" + ); + + sharedData.set("blob0", blobs[2]); + + equal( + await readBlob("blob0", sharedData), + text[2], + "Expected text for blob0 in parent ppmm" + ); + + sharedData.flush(); + + equal( + await readBlob("blob0", sharedData), + text[2], + "Expected text for blob0 in parent ppmm" + ); + equal( + await readBlob("blob1", sharedData), + text[1], + "Expected text for blob1 in parent ppmm" + ); + + equal( + await readBlob("blob0"), + text[2], + "Expected text for blob0 in parent cpmm" + ); + equal( + await readBlob("blob1"), + text[1], + "Expected text for blob1 in parent cpmm" + ); + + equal( + await contentPage.spawn(["blob0"], readBlob), + text[2], + "Expected text for blob0 in child 1 cpmm" + ); + equal( + await contentPage.spawn(["blob1"], readBlob), + text[1], + "Expected text for blob1 in child 1 cpmm" + ); + + equal( + await page2.spawn(["blob0"], readBlob), + text[2], + "Expected text for blob0 in child 2 cpmm" + ); + equal( + await page2.spawn(["blob1"], readBlob), + text[1], + "Expected text for blob1 in child 2 cpmm" + ); + + deepEqual( + await page2.spawn(["data"], getKey), + data, + "Expected data for data key in child 2 cpmm" + ); +}); diff --git a/dom/ipc/tests/test_temporaryfile_stream.html b/dom/ipc/tests/test_temporaryfile_stream.html new file mode 100644 index 0000000000..9fa76a2155 --- /dev/null +++ b/dom/ipc/tests/test_temporaryfile_stream.html @@ -0,0 +1,84 @@ + + + + Send an nsTemporaryFileInputStream cross-process + + + + + +
+
+
+ +
+ + diff --git a/dom/ipc/tests/test_window_open_discarded_bc.html b/dom/ipc/tests/test_window_open_discarded_bc.html new file mode 100644 index 0000000000..4cd81463e0 --- /dev/null +++ b/dom/ipc/tests/test_window_open_discarded_bc.html @@ -0,0 +1,40 @@ + + + + Discard a new BrowsingContext during window.open nested event loop + + + + + + + diff --git a/dom/ipc/tests/xpcshell.ini b/dom/ipc/tests/xpcshell.ini new file mode 100644 index 0000000000..a93537c9b6 --- /dev/null +++ b/dom/ipc/tests/xpcshell.ini @@ -0,0 +1,10 @@ +[test_sharedMap.js] +skip-if = os == "android" && processor == "x86_64" +[test_blob_sliced_from_child_process.js] +skip-if = os == "android" && processor == "x86_64" +[test_blob_sliced_from_parent_process.js] +skip-if = os == "android" && processor == "x86_64" +[test_bug1086684.js] +skip-if = os == "android" && processor == "x86_64" +[test_child_docshell.js] +skip-if = os == "android" && processor == "x86_64" -- cgit v1.2.3