/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsCocoaWindow_h_ #define nsCocoaWindow_h_ #undef DARWIN #import #include "mozilla/RefPtr.h" #include "nsBaseWidget.h" #include "nsCocoaUtils.h" #include "nsTouchBar.h" #include "ViewRegion.h" #include "mozView.h" #include #include class nsCocoaWindow; class nsChildView; class nsMenuBarX; @class ChildView; namespace mozilla { enum class NativeKeyBindingsType : uint8_t; class VibrancyManager; namespace widget { class TextInputHandler; } } // namespace mozilla // NSWindow subclass that is the base class for all of our own window classes. // Among other things, this class handles the storage of those settings that // need to be persisted across window destruction and reconstruction, i.e. when // switching to and from fullscreen mode. // We don't save shadow, transparency mode or background color because it's not // worth the hassle - Gecko will reset them anyway as soon as the window is // resized. @interface BaseWindow : NSWindow { // Data Storage NSMutableDictionary* mState; BOOL mDrawsIntoWindowFrame; // Invalidation disabling BOOL mDisabledNeedsDisplay; NSTrackingArea* mTrackingArea; NSView* mViewWithTrackingArea; NSRect mDirtyRect; BOOL mBeingShown; BOOL mDrawTitle; BOOL mIsAnimationSuppressed; nsTouchBar* mTouchBar; } - (void)importState:(NSDictionary*)aState; - (NSMutableDictionary*)exportState; - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState; - (BOOL)drawsContentsIntoWindowFrame; - (NSRect)childViewFrameRectForCurrentBounds; - (void)updateChildViewFrameRect; // These two methods are like contentRectForFrameRect and // frameRectForContentRect, but they deal with the rect of the window's "main // ChildView" instead of the rect of the window's content view. The two are // sometimes sized differently: The window's content view always covers the // entire window, whereas the ChildView only covers the full window when // drawsContentsIntoWindowFrame is YES. When drawsContentsIntoWindowFrame is NO, // there's a titlebar-sized gap above the ChildView within the content view. - (NSRect)childViewRectForFrameRect:(NSRect)aFrameRect; - (NSRect)frameRectForChildViewRect:(NSRect)aChildViewRect; - (void)mouseEntered:(NSEvent*)aEvent; - (void)mouseExited:(NSEvent*)aEvent; - (void)mouseMoved:(NSEvent*)aEvent; - (NSView*)trackingAreaView; - (void)createTrackingArea; - (void)removeTrackingArea; - (void)setBeingShown:(BOOL)aValue; - (BOOL)isBeingShown; - (BOOL)isVisibleOrBeingShown; - (void)setIsAnimationSuppressed:(BOOL)aValue; - (BOOL)isAnimationSuppressed; // Returns an autoreleased NSArray containing the NSViews that we consider the // "contents" of this window. All views in the returned array are subviews of // this window's content view. However, the array may not include all of the // content view's subviews; concretely, the ToolbarWindow implementation will // exclude its MOZTitlebarView from the array that is returned here. // In the vast majority of cases, the array will only have a single element: // this window's mainChildView. - (NSArray*)contentViewContents; - (ChildView*)mainChildView; - (void)setWantsTitleDrawn:(BOOL)aDrawTitle; - (BOOL)wantsTitleDrawn; - (void)disableSetNeedsDisplay; - (void)enableSetNeedsDisplay; - (NSRect)getAndResetNativeDirtyRect; - (void)setEffectViewWrapperForStyle:(mozilla::WindowShadow)aStyle; @property(nonatomic) mozilla::WindowShadow shadowStyle; - (void)releaseJSObjects; @end @interface NSWindow (Undocumented) - (NSDictionary*)shadowParameters; // Present in the same form on OS X since at least OS X 10.5. - (NSRect)contentRectForFrameRect:(NSRect)windowFrame styleMask:(NSUInteger)windowStyle; - (NSRect)frameRectForContentRect:(NSRect)windowContentRect styleMask:(NSUInteger)windowStyle; // Present since at least OS X 10.5. The OS calls this method on NSWindow // (and its subclasses) to find out which NSFrameView subclass to instantiate // to create its "frame view". + (Class)frameViewClassForStyleMask:(NSUInteger)styleMask; @end @interface PopupWindow : BaseWindow { @private BOOL mIsContextMenu; } - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation; - (BOOL)isContextMenu; - (void)setIsContextMenu:(BOOL)flag; - (BOOL)canBecomeMainWindow; @end @interface BorderlessWindow : BaseWindow { } - (BOOL)canBecomeKeyWindow; - (BOOL)canBecomeMainWindow; @end @interface WindowDelegate : NSObject { nsCocoaWindow* mGeckoWindow; // [WEAK] (we are owned by the window) // Used to avoid duplication when we send NS_ACTIVATE and // NS_DEACTIVATE to Gecko for toplevel widgets. Starts out // false. bool mToplevelActiveState; BOOL mHasEverBeenZoomed; } + (void)paintMenubarForWindow:(NSWindow*)aWindow; - (id)initWithGeckoWindow:(nsCocoaWindow*)geckoWind; - (void)windowDidResize:(NSNotification*)aNotification; - (nsCocoaWindow*)geckoWidget; - (bool)toplevelActiveState; - (void)sendToplevelActivateEvents; - (void)sendToplevelDeactivateEvents; @end @interface FullscreenTitlebarTracker : NSTitlebarAccessoryViewController - (FullscreenTitlebarTracker*)init; @end // NSWindow subclass for handling windows with toolbars. @interface ToolbarWindow : BaseWindow { // mFullscreenTitlebarTracker attaches an invisible rectangle to the system // title bar. This allows us to detect when the title bar is showing in // fullscreen. FullscreenTitlebarTracker* mFullscreenTitlebarTracker; CGFloat mMenuBarHeight; NSRect mWindowButtonsRect; } - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState; - (void)placeWindowButtons:(NSRect)aRect; - (NSRect)windowButtonsRect; - (void)windowMainStateChanged; @end class nsCocoaWindow final : public nsBaseWidget { private: friend class nsChildView; typedef nsBaseWidget Inherited; public: nsCocoaWindow(); [[nodiscard]] nsresult Create(nsIWidget* aParent, const DesktopIntRect& aRect, InitData* = nullptr) override; [[nodiscard]] nsresult Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, InitData* = nullptr) override; void Destroy() override; void Show(bool aState) override; bool NeedsRecreateToReshow() override; void Enable(bool aState) override; bool IsEnabled() const override; void SetModal(bool aState) override; bool IsRunningAppModal() override; bool IsVisible() const override; void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; LayoutDeviceIntPoint WidgetToScreenOffset() override; LayoutDeviceIntPoint GetClientOffset() override; LayoutDeviceIntMargin NormalSizeModeClientToWindowMargin() override; bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) override { return false; } void* GetNativeData(uint32_t aDataType) override; void ConstrainPosition(DesktopIntPoint&) override; void SetSizeConstraints(const SizeConstraints& aConstraints) override; void Move(double aX, double aY) override; nsSizeMode SizeMode() override { return mSizeMode; } void SetSizeMode(nsSizeMode aMode) override; void GetWorkspaceID(nsAString& workspaceID) override; void MoveToWorkspace(const nsAString& workspaceID) override; void SuppressAnimation(bool aSuppress) override; void HideWindowChrome(bool aShouldHide) override; NSView* GetEditorView(); nsresult SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode, uint32_t aModifierFlags, const nsAString& aCharacters, const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) override; nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage, mozilla::MouseButton aButton, nsIWidget::Modifiers aModifierFlags, nsIObserver* aObserver) override; nsresult SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint, nsIObserver* aObserver) override { return SynthesizeNativeMouseEvent( aPoint, NativeMouseMessage::Move, mozilla::MouseButton::eNotPressed, nsIWidget::Modifiers::NO_MODIFIERS, aObserver); } nsresult SynthesizeNativeMouseScrollEvent( LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags, uint32_t aAdditionalFlags, nsIObserver* aObserver) override; nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, TouchPointerState aPointerState, LayoutDeviceIntPoint aPoint, double aPointerPressure, uint32_t aPointerOrientation, nsIObserver* aObserver) override; nsresult SynthesizeNativeTouchpadDoubleTap(LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) override; static bool DoHasPendingInputEvent(); static uint32_t GetCurrentInputEventCount(); static void UpdateCurrentInputEventCount(); // unit conversion convenience functions int32_t CocoaPointsToDevPixels(CGFloat aPts) const { return nsCocoaUtils::CocoaPointsToDevPixels(aPts, BackingScaleFactor()); } LayoutDeviceIntPoint CocoaPointsToDevPixels(const NSPoint& aPt) const { return nsCocoaUtils::CocoaPointsToDevPixels(aPt, BackingScaleFactor()); } LayoutDeviceIntPoint CocoaPointsToDevPixelsRoundDown( const NSPoint& aPt) const { return nsCocoaUtils::CocoaPointsToDevPixelsRoundDown(aPt, BackingScaleFactor()); } LayoutDeviceIntRect CocoaPointsToDevPixels(const NSRect& aRect) const { return nsCocoaUtils::CocoaPointsToDevPixels(aRect, BackingScaleFactor()); } CGFloat DevPixelsToCocoaPoints(int32_t aPixels) const { return nsCocoaUtils::DevPixelsToCocoaPoints(aPixels, BackingScaleFactor()); } NSRect DevPixelsToCocoaPoints(const LayoutDeviceIntRect& aRect) const { return nsCocoaUtils::DevPixelsToCocoaPoints(aRect, BackingScaleFactor()); } // Called by nsCocoaWindow when the window's fullscreen state changes. void UpdateFullscreen(bool aFullscreen); void DispatchAPZWheelInputEvent(mozilla::InputData& aEvent); nsEventStatus DispatchAPZInputEvent(mozilla::InputData& aEvent); void DispatchDoubleTapGesture(mozilla::TimeStamp aEventTimeStamp, LayoutDeviceIntPoint aScreenPosition, mozilla::Modifiers aModifiers); // Called when the main thread enters a phase during which visual changes // are imminent and any layer updates on the compositor thread would interfere // with visual atomicity. // "Async" CATransactions are CATransactions which happen on a thread that's // not the main thread. void SuspendAsyncCATransactions(); // Called when we know that the current main thread paint will be completed // once the main thread goes back to the event loop. void MaybeScheduleUnsuspendAsyncCATransactions(); // Called from the runnable dispatched by // MaybeScheduleUnsuspendAsyncCATransactions(). At this point we know that the // main thread is done handling the visual change (such as a window resize) // and we can start modifying CALayers from the compositor thread again. void UnsuspendAsyncCATransactions(); #ifdef DEBUG // test only. nsresult SetHiDPIMode(bool aHiDPI) override; nsresult RestoreHiDPIMode() override; #endif void UpdateVibrancy(const nsTArray& aThemeGeometries); mozilla::VibrancyManager& EnsureVibrancyManager(); void TearDownView(); bool PrepareForFullscreenTransition(nsISupports** aData) override; void PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration, nsISupports* aData, nsIRunnable* aCallback) override; void CleanupFullscreenTransition() override; nsresult MakeFullScreen(bool aFullScreen) final; nsresult MakeFullScreenWithNativeTransition(bool aFullScreen) final; NSAnimation* FullscreenTransitionAnimation() const { return mFullscreenTransitionAnimation; } void ReleaseFullscreenTransitionAnimation() { MOZ_ASSERT(mFullscreenTransitionAnimation, "Should only be called when there is animation"); [mFullscreenTransitionAnimation release]; mFullscreenTransitionAnimation = nil; } void Resize(double aWidth, double aHeight, bool aRepaint) override; void Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override; NSRect GetClientCocoaRect(); LayoutDeviceIntRect GetClientBounds() override; LayoutDeviceIntRect GetScreenBounds() override; void ReportMoveEvent(); void ReportSizeEvent(); bool WidgetTypeSupportsAcceleration() override { return true; } bool ShouldUseOffMainThreadCompositing() override; void SetCursor(const Cursor&) override; void UpdateBoundsFromView(); void EnsureContentLayerForMainThreadPainting(); CGFloat BackingScaleFactor() const; void BackingScaleFactorChanged(); double GetDefaultScaleInternal() override; int32_t RoundsWidgetCoordinatesTo() override; // Mac specific methods void WillPaintWindow(); bool PaintWindow(LayoutDeviceIntRegion aRegion); bool PaintWindowInDrawTarget(mozilla::gfx::DrawTarget* aDT, const LayoutDeviceIntRegion& aRegion, const mozilla::gfx::IntSize& aSurfaceSize); void PaintWindowInContentLayer(); void HandleMainThreadCATransaction(); #ifdef ACCESSIBILITY already_AddRefed GetDocumentAccessible(); #endif bool WidgetPaintsBackground() override { return true; } bool PreRender(mozilla::widget::WidgetRenderingContext* aContext) override; void PostRender(mozilla::widget::WidgetRenderingContext* aContext) override; RefPtr GetNativeLayerRoot() override; void UpdateWindowDraggingRegion( const LayoutDeviceIntRegion& aRegion) override; LayoutDeviceIntRegion GetNonDraggableRegion() { return mNonDraggableRegion.Region(); } void LookUpDictionary(const nsAString& aText, const nsTArray& aFontRangeArray, const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) override; mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final { return mozilla::DesktopToLayoutDeviceScale(BackingScaleFactor()); } nsresult SetTitle(const nsAString& aTitle) override; void Invalidate(const LayoutDeviceIntRect& aRect) override; nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override; void CaptureRollupEvents(bool aDoCapture) override; [[nodiscard]] nsresult GetAttention(int32_t aCycleCount) override; bool HasPendingInputEvent() override; bool SendEventToNativeMenuSystem(NSEvent* aEvent); void PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) override; nsresult ActivateNativeMenuItemAt(const nsAString& indexString) override; nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) override; [[nodiscard]] nsresult GetSelectionAsPlaintext(nsAString& aResult) override; TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override; [[nodiscard]] nsresult AttachNativeKeyEvent( mozilla::WidgetKeyboardEvent& aEvent) override; TransparencyMode GetTransparencyMode() override; void SetTransparencyMode(TransparencyMode aMode) override; void SetWindowShadowStyle(mozilla::WindowShadow aStyle) override; void SetWindowOpacity(float aOpacity) override; void SetWindowTransform(const mozilla::gfx::Matrix& aTransform) override; void SetInputRegion(const InputRegion&) override; void SetColorScheme(const mozilla::Maybe&) override; void SetShowsToolbarButton(bool aShow) override; bool GetSupportsNativeFullscreen(); void SetSupportsNativeFullscreen(bool aShow) override; void SetWindowAnimationType(WindowAnimationType aType) override; void SetDrawsTitle(bool aDrawTitle) override; void SetCustomTitlebar(bool) override; void UpdateThemeGeometries( const nsTArray& aThemeGeometries) override; void LockAspectRatio(bool aShouldLock) override; void DispatchSizeModeEvent(); void DispatchOcclusionEvent(); // be notified that a some form of drag event needs to go into Gecko bool DragEvent(unsigned int aMessage, mozilla::gfx::Point aMouseGlobal, UInt16 aKeyModifiers); bool HasModalDescendants() const { return mNumModalDescendants > 0; } bool IsModal() const { return mModal; } NSWindow* GetCocoaWindow() { return mWindow; } void SetMenuBar(RefPtr&& aMenuBar); nsMenuBarX* GetMenuBar(); void SetInputContext(const InputContext& aContext, const InputContextAction& aAction) override; InputContext GetInputContext() override; MOZ_CAN_RUN_SCRIPT bool GetEditCommands( mozilla::NativeKeyBindingsType aType, const mozilla::WidgetKeyboardEvent& aEvent, nsTArray& aCommands) override; void SetPopupWindowLevel(); bool InFullScreenMode() const { return mInFullScreenMode; } // Class method versions of NSWindow/Delegate callbacks which need to // access object state. void CocoaWindowWillEnterFullscreen(bool aFullscreen); void CocoaWindowDidEnterFullscreen(bool aFullscreen); void CocoaWindowDidResize(); void CocoaSendToplevelActivateEvents(); void CocoaSendToplevelDeactivateEvents(); nsIWidgetListener* GetPaintListener() const { return mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; } enum class TransitionType { Windowed, Fullscreen, EmulatedFullscreen, Miniaturize, Deminiaturize, Zoom, }; void FinishCurrentTransitionIfMatching(const TransitionType& aTransition); // Called when something has happened that might cause us to update our // fullscreen state. Returns true if we updated state. We'll call this // on window resize, and we'll call it when we enter or exit fullscreen, // since fullscreen to-and-from zoomed windows won't necessarily trigger // a resize. bool HandleUpdateFullscreenOnResize(); protected: virtual ~nsCocoaWindow(); nsresult CreateNativeWindow(const NSRect& aRect, BorderStyle aBorderStyle, bool aRectIsFrameRect, bool aIsPrivateBrowsing); void DestroyNativeWindow(); void UpdateBounds(); int32_t GetWorkspaceID(); void MoveVisibleWindowToWorkspace(int32_t workspaceID); void DoResize(double aX, double aY, double aWidth, double aHeight, bool aRepaint, bool aConstrainToCurrentScreen); void UpdateFullscreenState(bool aFullScreen, bool aNativeMode); nsresult DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition); already_AddRefed AllocateChildPopupWidget() override { return nsIWidget::CreateTopLevelWindow(); } BaseWindow* mWindow; // our cocoa window [STRONG] BaseWindow* mClosedRetainedWindow; // a second strong reference to our // window upon closing it, held through our destructor. This is useful // to ensure that macOS run loops which reference the window will still // have something to point to even if they don't use proper retain and // release patterns. WindowDelegate* mDelegate = nullptr; // our delegate for processing window msgs [STRONG] RefPtr mMenuBar; ChildView* mChildView = nullptr; // Cocoa content view, [STRONG] #ifdef ACCESSIBILITY // weak ref to this childview's associated mozAccessible for speed reasons // (we get queried for it *a lot* but don't want to own it) nsWeakPtr mAccessible; #endif // Held while the compositor (or WR renderer) thread is compositing. // Protects from tearing down the view during compositing and from presenting // half-composited layers to the screen. mozilla::Mutex mCompositingLock MOZ_UNANNOTATED{"ChildViewCompositing"}; mozilla::ViewRegion mNonDraggableRegion; // Cached value of [mChildView backingScaleFactor], to avoid sending two obj-c // messages (respondsToSelector, backingScaleFactor) every time we need to // use it. // ** We'll need to reinitialize this if the backing resolution changes. ** mutable CGFloat mBackingScaleFactor; RefPtr mNativeLayerRoot; // In BasicLayers mode, this is the CoreAnimation layer that contains the // rendering from Gecko. It is a sublayer of mNativeLayerRoot's underlying // wrapper layer. // Lazily created by EnsureContentLayerForMainThreadPainting(). RefPtr mContentLayer; RefPtr mPoolHandle; // In BasicLayers mode, this is the invalid region of mContentLayer. LayoutDeviceIntRegion mContentLayerInvalidRegion; mozilla::UniquePtr mVibrancyManager; RefPtr mUnsuspendAsyncCATransactionsRunnable; // if this is a toplevel window, and there is any ongoing fullscreen // transition, it is the animation object. NSAnimation* mFullscreenTransitionAnimation; mozilla::WindowShadow mShadowStyle; CGFloat mAspectRatio; WindowAnimationType mAnimationType; bool mWindowMadeHere; // true if we created the window, false for embedding nsSizeMode mSizeMode; bool mInFullScreenMode; // Whether we are currently using native fullscreen. It could be false because // we are in the emulated fullscreen where we do not use the native // fullscreen. bool mInNativeFullScreenMode; mozilla::Maybe mTransitionCurrent; std::queue mTransitionsPending; static uint32_t sLastInputEventCount; // This is used by SynthesizeNativeTouchPoint to maintain state between // multiple synthesized points mozilla::UniquePtr mSynthesizedTouchInput; // A runnable we might assign to run ProcessTransitions at a later event loop. // Cancelable so we can cancel it in CancelAllTransitions(), if needed. RefPtr mProcessTransitionsPending; // Sometimes we add a transition that wasn't requested by a caller. We do this // to manage transitions between states that otherwise would be rejected by // Cocoa. When we do this, it's useful to know when we are handling an added // transition because we don't want to send size mode events when they // execute. bool mIsTransitionCurrentAdded = false; // Whether we are treating the next resize as the start of a fullscreen // transition. If we are, which direction are we going: Fullscreen or // Windowed. mozilla::Maybe mUpdateFullscreenOnResize; bool IsInTransition() { return mTransitionCurrent.isSome(); } void QueueTransition(const TransitionType& aTransition); void ProcessTransitions(); // Call this to stop all transition processing, which is useful during // window closing and shutdown. void CancelAllTransitions(); bool mInProcessTransitions = false; // While running an emulated fullscreen transition, we want to suppress // sending size mode events due to window resizing. We fix it up at the end // when the transition is complete. bool mSuppressSizeModeEvents = false; // Ignore occlusion events caused by displaying the temporary fullscreen // window during the fullscreen transition animation because only focused // contexts are permitted to enter DOM fullscreen. int mIgnoreOcclusionCount; // Set to true when a native fullscreen transition is initiated -- either to // or from fullscreen -- and set to false when it is complete. During this // period, we presume the window is visible, which prevents us from sending // unnecessary OcclusionStateChanged events. bool mHasStartedNativeFullscreen; bool mModal = false; bool mIsAnimationSuppressed = false; bool mInReportMoveEvent = false; // true if in a call to ReportMoveEvent(). bool mInResize = false; // true if in a call to DoResize(). bool mWindowTransformIsIdentity = true; bool mAlwaysOnTop = false; bool mAspectRatioLocked = false; bool mIsAlert = false; // True if this is an non-native alert window. bool mWasShown = false; int32_t mNumModalDescendants = 0; // The workspaceID to move to once the window becomes visible. A value of 0 // is a no-op. int32_t mDeferredWorkspaceID = 0; RefPtr mTextInputHandler; InputContext mInputContext; NSWindowAnimationBehavior mWindowAnimationBehavior; private: // This is class state for tracking which nsCocoaWindow, if any, is in the // middle of a native fullscreen transition. static nsCocoaWindow* sWindowInNativeTransition; // This function returns true if the caller has been able to claim the sole // permission to start a native transition. It must be followed by a call // to EndOurNativeTransition() when the native transition is complete. bool CanStartNativeTransition(); void EndOurNativeTransition(); }; #endif // nsCocoaWindow_h_