diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 91969d27f35b..336e7b9a0585 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -19626,6 +19626,11 @@ bool Document::HasStorageAccessPermissionGrantedByAllowList() { nsILoadInfo::StoragePermissionAllowListed; } +bool Document::InAndroidPipMode() const { + auto* bc = BrowserChild::GetFrom(GetDocShell()); + return bc && bc->InAndroidPipMode(); +} + nsIPrincipal* Document::EffectiveStoragePrincipal() const { if (!StaticPrefs:: privacy_partition_always_partition_third_party_non_cookie_storage()) { diff --git a/dom/base/Document.h b/dom/base/Document.h index 4d696b6906ce..b41d8be0740c 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -2192,6 +2192,10 @@ class Document : public nsINode, * Returns true if this document was created from a nsXULPrototypeDocument. */ bool LoadedFromPrototype() const { return mPrototypeDocument; } + + /* Returns true if we're currently in Android's PiP mode. */ + bool InAndroidPipMode() const; + /** * Returns the prototype the document was created from, or null if it was not * created from a prototype. diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 24b950350a1f..66dfe24b5b4d 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -285,6 +285,7 @@ BrowserChild::BrowserChild(ContentChild* aManager, const TabId& aTabId, mTriedBrowserInit(false), mHasValidInnerSize(false), mDestroyed(false), + mInAndroidPipMode(false), mIsTopLevel(aIsTopLevel), mIsTransparent(false), mIPCOpen(false), @@ -1257,6 +1258,21 @@ mozilla::ipc::IPCResult BrowserChild::RecvKeyboardHeightChanged( return IPC_OK(); } +mozilla::ipc::IPCResult BrowserChild::RecvAndroidPipModeChanged(bool aPipMode) { + if (mInAndroidPipMode == aPipMode) { + return IPC_OK(); + } + mInAndroidPipMode = aPipMode; + if (RefPtr document = GetTopLevelDocument()) { + nsContentUtils::DispatchEventOnlyToChrome( + document, document, + aPipMode ? u"MozAndroidPipModeEntered"_ns + : u"MozAndroidPipModeExited"_ns, + CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr); + } + return IPC_OK(); +} + mozilla::ipc::IPCResult BrowserChild::RecvSuppressDisplayport( const bool& aEnabled) { if (RefPtr presShell = GetTopLevelPresShell()) { diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h index 27767514c1d8..c41e13086802 100644 --- a/dom/ipc/BrowserChild.h +++ b/dom/ipc/BrowserChild.h @@ -85,12 +85,8 @@ class SessionStoreChild; class RequestData; class WebProgressData; -#define DOM_BROWSERCHILD_IID \ - { \ - 0x58a5775d, 0xba05, 0x45bf, { \ - 0xbd, 0xb8, 0xd7, 0x61, 0xf9, 0x01, 0x01, 0x31 \ - } \ - } +#define DOM_BROWSERCHILD_IID \ + {0x58a5775d, 0xba05, 0x45bf, {0xbd, 0xb8, 0xd7, 0x61, 0xf9, 0x01, 0x01, 0x31}} class BrowserChildMessageManager : public ContentFrameMessageManager, public nsIMessageSender, @@ -287,6 +283,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, mozilla::ipc::IPCResult RecvKeyboardHeightChanged( const mozilla::ScreenIntCoord& aHeight); + mozilla::ipc::IPCResult RecvAndroidPipModeChanged(bool aPipMode); + mozilla::ipc::IPCResult RecvActivate(uint64_t aActionId); mozilla::ipc::IPCResult RecvDeactivate(uint64_t aActionId); @@ -563,6 +561,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, }; mozilla::ScreenIntCoord GetKeyboardHeight() const { return mKeyboardHeight; } + bool InAndroidPipMode() const { return mInAndroidPipMode; } + bool IPCOpen() const { return mIPCOpen; } const mozilla::layers::CompositorOptions& GetCompositorOptions() const; @@ -848,6 +848,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, bool mTriedBrowserInit : 1; bool mHasValidInnerSize : 1; bool mDestroyed : 1; + // Whether we're in Android's PiP mode. + bool mInAndroidPipMode : 1; // Whether or not this browser is the child part of the top level PBrowser // actor in a remote browser. diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp index 25137bea3b6e..06f524e60d9c 100644 --- a/dom/ipc/BrowserParent.cpp +++ b/dom/ipc/BrowserParent.cpp @@ -1189,7 +1189,7 @@ void BrowserParent::SizeModeChanged(const nsSizeMode& aSizeMode) { } } -#if defined(MOZ_WIDGET_ANDROID) +#ifdef MOZ_WIDGET_ANDROID void BrowserParent::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) { if (!mIsDestroyed) { Unused << SendDynamicToolbarMaxHeightChanged(aHeight); @@ -1207,6 +1207,12 @@ void BrowserParent::KeyboardHeightChanged(ScreenIntCoord aHeight) { Unused << SendKeyboardHeightChanged(aHeight); } } + +void BrowserParent::AndroidPipModeChanged(bool aPipMode) { + if (!mIsDestroyed) { + Unused << SendAndroidPipModeChanged(aPipMode); + } +} #endif void BrowserParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent, diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h index 501efc92c726..3ec5cc6ea79c 100644 --- a/dom/ipc/BrowserParent.h +++ b/dom/ipc/BrowserParent.h @@ -486,10 +486,11 @@ class BrowserParent final : public PBrowserParent, void HandleAccessKey(const WidgetKeyboardEvent& aEvent, nsTArray& aCharCodes); -#if defined(MOZ_WIDGET_ANDROID) +#ifdef MOZ_WIDGET_ANDROID void DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight); void DynamicToolbarOffsetChanged(ScreenIntCoord aOffset); void KeyboardHeightChanged(ScreenIntCoord aHeight); + void AndroidPipModeChanged(bool); #endif void Activate(uint64_t aActionId); diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 6ba0c9dad738..5a376753db92 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -703,6 +703,8 @@ child: async KeyboardHeightChanged(ScreenIntCoord height); + async AndroidPipModeChanged(bool aPipMode); + /** * StopIMEStateManagement() is called when the process loses focus and * should stop managing IME state. diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl index 30a30345bb29..c43f3c9e8445 100644 --- a/dom/webidl/Document.webidl +++ b/dom/webidl/Document.webidl @@ -385,6 +385,12 @@ partial interface Document { [ChromeOnly] readonly attribute boolean loadedFromPrototype; + // Whether we're in android's Picture-in-Picture mode. + // Top level document only (for now, if we want to deal with iframes, please + // also fix bug 1959448 while at it). + [ChromeOnly] + readonly attribute boolean inAndroidPipMode; + // The principal to use for the storage area of this document [ChromeOnly] readonly attribute Principal effectiveStoragePrincipal; diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java index c351139207a7..499e12bd3958 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java @@ -410,6 +410,9 @@ public class GeckoSession { @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") public native void onSafeAreaInsetsChanged(int top, int right, int bottom, int left); + @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") + public native void onPipModeChanged(boolean enabled); + @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") public native void onKeyboardHeightChanged(int height); diff --git a/view/nsView.cpp b/view/nsView.cpp index 4d6c90a95f07..291f12c0f8f6 100644 --- a/view/nsView.cpp +++ b/view/nsView.cpp @@ -836,7 +836,7 @@ bool nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth, return false; } -#if defined(MOZ_WIDGET_ANDROID) +#ifdef MOZ_WIDGET_ANDROID void nsView::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) { MOZ_ASSERT(XRE_IsParentProcess(), "Should be only called for the browser parent process"); @@ -883,6 +883,18 @@ void nsView::KeyboardHeightChanged(ScreenIntCoord aHeight) { return CallState::Stop; }); } + +void nsView::AndroidPipModeChanged(bool aPipMode) { + MOZ_ASSERT(XRE_IsParentProcess(), + "Should be only called for the browser parent process"); + MOZ_ASSERT(this == mViewManager->GetRootView(), + "Should be called for the root view"); + CallOnAllRemoteChildren( + [aPipMode](dom::BrowserParent* aBrowserParent) -> CallState { + aBrowserParent->AndroidPipModeChanged(aPipMode); + return CallState::Continue; + }); +} #endif bool nsView::RequestWindowClose(nsIWidget* aWidget) { diff --git a/view/nsView.h b/view/nsView.h index ddabdfafef00..b04816adb110 100644 --- a/view/nsView.h +++ b/view/nsView.h @@ -390,10 +390,11 @@ class nsView final : public nsIWidgetListener { ByMoveToRect) override; bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override; -#if defined(MOZ_WIDGET_ANDROID) +#ifdef MOZ_WIDGET_ANDROID void DynamicToolbarMaxHeightChanged(mozilla::ScreenIntCoord aHeight) override; void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset) override; void KeyboardHeightChanged(mozilla::ScreenIntCoord aHeight) override; + void AndroidPipModeChanged(bool) override; #endif bool RequestWindowClose(nsIWidget* aWidget) override; MOZ_CAN_RUN_SCRIPT_BOUNDARY diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 6bcc04a9cdb1..f89d559b8252 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1382,6 +1382,21 @@ class LayerViewSupport final gkWindow->UpdateDynamicToolbarMaxHeight(ScreenIntCoord(aHeight)); } + void OnPipModeChanged(bool aPipMode) { + MOZ_ASSERT(NS_IsMainThread()); + auto win(mWindow.Access()); + if (!win) { + return; // Already shut down. + } + + nsWindow* gkWindow = win->GetNsWindow(); + if (!gkWindow) { + return; + } + + gkWindow->PipModeChanged(aPipMode); + } + void OnKeyboardHeightChanged(int32_t aHeight) { MOZ_ASSERT(NS_IsMainThread()); auto win(mWindow.Access()); @@ -3302,6 +3317,16 @@ void nsWindow::UpdateDynamicToolbarOffset(ScreenIntCoord aOffset) { } } +void nsWindow::PipModeChanged(bool aPipMode) { + if (mWidgetListener) { + mWidgetListener->AndroidPipModeChanged(aPipMode); + } + + if (mAttachedWidgetListener) { + mAttachedWidgetListener->AndroidPipModeChanged(aPipMode); + } +} + void nsWindow::KeyboardHeightChanged(ScreenIntCoord aHeight) { if (mWidgetListener) { mWidgetListener->KeyboardHeightChanged(aHeight); diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index d97064b64188..2cb3e4d7e3b1 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -257,6 +257,8 @@ class nsWindow final : public nsBaseWidget { void KeyboardHeightChanged(mozilla::ScreenIntCoord aHeight); + void PipModeChanged(bool aPipMode); + mozilla::jni::NativeWeakPtr GetNPZCSupportWeakPtr(); diff --git a/widget/nsIWidgetListener.h b/widget/nsIWidgetListener.h index 30510ceae8e3..6323d9633180 100644 --- a/widget/nsIWidgetListener.h +++ b/widget/nsIWidgetListener.h @@ -81,12 +81,13 @@ class nsIWidgetListener { */ virtual void SizeModeChanged(nsSizeMode aSizeMode) {} -#if defined(MOZ_WIDGET_ANDROID) +#ifdef MOZ_WIDGET_ANDROID virtual void DynamicToolbarMaxHeightChanged(mozilla::ScreenIntCoord aHeight) { } virtual void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset) {} /** Called when the software keyboard appears/disappears. */ virtual void KeyboardHeightChanged(mozilla::ScreenIntCoord aHeight) {} + virtual void AndroidPipModeChanged(bool) {} #endif /** Called when the macOS titlebar is shown while in fullscreen. */