Commit 0fe78799 authored by Emilio Cobos Álvarez's avatar Emilio Cobos Álvarez
Browse files

Bug 1746955 - Make macOS context menus respect the color-scheme CSS property....

Bug 1746955 - Make macOS context menus respect the color-scheme CSS property. r=mac-reviewers,mstange

This is consistent with other platforms, and with non-native popups.

Differential Revision: https://phabricator.services.mozilla.com/D134336
parent 37a1a70a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -32,7 +32,8 @@ class Runnable;
// cancelAsynchronousOpening:. Can only be called on the main thread.
- (NSInteger)asynchronouslyOpenMenu:(NSMenu*)aMenu
                   atScreenPosition:(NSPoint)aPosition
                            forView:(NSView*)aView;
                            forView:(NSView*)aView
                     withAppearance:(NSAppearance*)aAppearance;

// If the menu opening request for aHandle hasn't been processed yet, cancel it.
// Can only be called on the main thread.
+20 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ static BOOL sNeedToUnwindForMenuClosing = NO;
@property(retain) NSMenu* menu;
@property NSPoint position;
@property(retain) NSView* view;
@property(retain) NSAppearance* appearance;
@end

@implementation MOZMenuOpeningInfo
@@ -69,7 +70,8 @@ static BOOL sNeedToUnwindForMenuClosing = NO;

- (NSInteger)asynchronouslyOpenMenu:(NSMenu*)aMenu
                   atScreenPosition:(NSPoint)aPosition
                            forView:(NSView*)aView {
                            forView:(NSView*)aView
                     withAppearance:(NSAppearance*)aAppearance {
  MOZ_RELEASE_ASSERT(!mPendingOpening,
                     "A menu is already waiting to open. Before opening the next one, either wait "
                     "for this one to open or cancel the request.");
@@ -81,6 +83,7 @@ static BOOL sNeedToUnwindForMenuClosing = NO;
  info.menu = aMenu;
  info.position = aPosition;
  info.view = aView;
  info.appearance = aAppearance;
  mPendingOpening = [info retain];
  [info release];

@@ -103,7 +106,7 @@ static BOOL sNeedToUnwindForMenuClosing = NO;
    mPendingOpening = nil;

    @try {
      [self _openMenu:info.menu atScreenPosition:info.position forView:info.view];
      [self _openMenu:info.menu atScreenPosition:info.position forView:info.view withAppearance:info.appearance];
    } @catch (NSException* exception) {
      nsObjCExceptionLog(exception);
    }
@@ -139,7 +142,7 @@ static BOOL sNeedToUnwindForMenuClosing = NO;
  }
}

- (void)_openMenu:(NSMenu*)aMenu atScreenPosition:(NSPoint)aPosition forView:(NSView*)aView {
- (void)_openMenu:(NSMenu*)aMenu atScreenPosition:(NSPoint)aPosition forView:(NSView*)aView withAppearance:(NSAppearance*)aAppearance {
  // There are multiple ways to display an NSMenu as a context menu.
  //
  //  1. We can return the NSMenu from -[ChildView menuForEvent:] and the NSView will open it for
@@ -164,6 +167,20 @@ static BOOL sNeedToUnwindForMenuClosing = NO;
  // a later time.
  // The code below uses option 4 as the preferred option because it's the simplest: It works in all
  // scenarios and it doesn't have the positioning drawbacks of option 5.

  if (aAppearance) {
#if !defined(MAC_OS_VERSION_11_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_11_0
    if (nsCocoaFeatures::OnBigSurOrLater()) {
#else
    if (@available(macOS 11.0, *)) {
#endif
      // By default, NSMenu inherits its appearance from the opening NSEvent's
      // window. If CSS has overridden it, on Big Sur + we can respect it with
      // -[NSMenu setAppearance].
      aMenu.appearance = aAppearance;
    }
  }

  if (aView) {
    NSWindow* window = aView.window;
    // Create a synthetic event at the right location and open the menu [option 4].
+7 −7
Original line number Diff line number Diff line
@@ -26,10 +26,10 @@ class NativeMenuMac : public NativeMenu,
                      public nsMenuItemIconX::Listener,
                      public nsMenuX::Observer {
 public:
  explicit NativeMenuMac(mozilla::dom::Element* aElement);
  explicit NativeMenuMac(dom::Element* aElement);

  // NativeMenu
  void ShowAsContextMenu(const mozilla::DesktopPoint& aPosition) override;
  void ShowAsContextMenu(const DesktopPoint& aPosition) override;
  bool Close() override;
  void ActivateItem(dom::Element* aItemElement, Modifiers aModifiers, int16_t aButton,
                    ErrorResult& aRv) override;
@@ -47,11 +47,11 @@ class NativeMenuMac : public NativeMenu,
  void IconUpdated() override;

  // nsMenuX::Observer
  void OnMenuWillOpen(mozilla::dom::Element* aPopupElement) override;
  void OnMenuDidOpen(mozilla::dom::Element* aPopupElement) override;
  void OnMenuWillActivateItem(mozilla::dom::Element* aPopupElement,
                              mozilla::dom::Element* aMenuItemElement) override;
  void OnMenuClosed(mozilla::dom::Element* aPopupElement) override;
  void OnMenuWillOpen(dom::Element* aPopupElement) override;
  void OnMenuDidOpen(dom::Element* aPopupElement) override;
  void OnMenuWillActivateItem(dom::Element* aPopupElement,
                              dom::Element* aMenuItemElement) override;
  void OnMenuClosed(dom::Element* aPopupElement) override;

  NSMenu* NativeNSMenu() { return mMenu ? mMenu->NativeNSMenu() : nil; }
  void MenuWillOpen();
+18 −7
Original line number Diff line number Diff line
@@ -9,16 +9,17 @@

#include "mozilla/Assertions.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"

#include "MOZMenuOpeningCoordinator.h"
#include "nsISupports.h"
#include "nsGkAtoms.h"
#include "nsGkAtoms.h"
#include "nsMenuGroupOwnerX.h"
#include "nsMenuItemX.h"
#include "nsMenuUtilsX.h"
#include "nsNativeThemeColors.h"
#include "nsObjCExceptions.h"
#include "nsThreadUtils.h"
#include "PresShell.h"
@@ -32,7 +33,7 @@ using dom::Element;

namespace widget {

NativeMenuMac::NativeMenuMac(mozilla::dom::Element* aElement)
NativeMenuMac::NativeMenuMac(dom::Element* aElement)
    : mElement(aElement), mContainerStatusBarItem(nil) {
  MOZ_RELEASE_ASSERT(aElement->IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menupopup));
  mMenuGroupOwner = new nsMenuGroupOwnerX(aElement, nullptr);
@@ -192,8 +193,8 @@ void NativeMenuMac::OnMenuDidOpen(dom::Element* aPopupElement) {
  }
}

void NativeMenuMac::OnMenuWillActivateItem(mozilla::dom::Element* aPopupElement,
                                           mozilla::dom::Element* aMenuItemElement) {
void NativeMenuMac::OnMenuWillActivateItem(dom::Element* aPopupElement,
                                           dom::Element* aMenuItemElement) {
  // Our caller isn't keeping us alive, so make sure we stay alive throughout this function in case
  // one of the observer notifications destroys us.
  RefPtr<NativeMenuMac> kungFuDeathGrip(this);
@@ -218,7 +219,7 @@ void NativeMenuMac::OnMenuClosed(dom::Element* aPopupElement) {
}

static NSView* NativeViewForContent(nsIContent* aContent) {
  mozilla::dom::Document* doc = aContent->GetUncomposedDoc();
  dom::Document* doc = aContent->GetUncomposedDoc();
  if (!doc) {
    return nil;
  }
@@ -237,18 +238,28 @@ static NSView* NativeViewForContent(nsIContent* aContent) {
  return (NSView*)widget->GetNativeData(NS_NATIVE_WIDGET);
}

void NativeMenuMac::ShowAsContextMenu(const mozilla::DesktopPoint& aPosition) {
static NSAppearance* NativeAppearanceForContent(nsIContent* aContent) {
  nsIFrame* f = aContent->GetPrimaryFrame();
  if (!f) {
    return nil;
  }
  return NSAppearanceForColorScheme(LookAndFeel::ColorSchemeForFrame(f));
}

void NativeMenuMac::ShowAsContextMenu(const DesktopPoint& aPosition) {
  mMenu->PopupShowingEventWasSentAndApprovedExternally();

  NSMenu* menu = mMenu->NativeNSMenu();
  NSView* view = NativeViewForContent(mMenu->Content());
  NSAppearance* appearance = NativeAppearanceForContent(mMenu->Content());
  NSPoint locationOnScreen = nsCocoaUtils::GeckoPointToCocoaPoint(aPosition);

  // Let the MOZMenuOpeningCoordinator do the actual opening, so that this ShowAsContextMenu call
  // does not spawn a nested event loop, which would be surprising to our callers.
  mOpeningHandle = [MOZMenuOpeningCoordinator.sharedInstance asynchronouslyOpenMenu:menu
                                                                   atScreenPosition:locationOnScreen
                                                                            forView:view];
                                                                            forView:view
                                                                     withAppearance:appearance];
}

bool NativeMenuMac::Close() {
+3 −4
Original line number Diff line number Diff line
@@ -8,9 +8,9 @@

#import <Cocoa/Cocoa.h>

#include "LookAndFeel.h"
#include "nsCocoaFeatures.h"
#include "SDKDeclarations.h"
#include "mozilla/ColorScheme.h"

enum ColorName {
  toolbarTopBorderGrey,
@@ -66,11 +66,10 @@ inline NSColor* ControlAccentColor() {
             : [NSColor colorWithSRGBRed:0.247 green:0.584 blue:0.965 alpha:1.0];
}

inline NSAppearance* NSAppearanceForColorScheme(mozilla::LookAndFeel::ColorScheme aScheme) {
  using ColorScheme = mozilla::LookAndFeel::ColorScheme;
inline NSAppearance* NSAppearanceForColorScheme(mozilla::ColorScheme aScheme) {
  if (@available(macOS 10.14, *)) {
    NSAppearanceName appearanceName =
        aScheme == ColorScheme::Light ? NSAppearanceNameAqua : NSAppearanceNameDarkAqua;
        aScheme == mozilla::ColorScheme::Light ? NSAppearanceNameAqua : NSAppearanceNameDarkAqua;
    return [NSAppearance appearanceNamed:appearanceName];
  }
  return [NSAppearance appearanceNamed:NSAppearanceNameAqua];