diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
commit | da4c7e7ed675c3bf405668739c3012d140856109 (patch) | |
tree | cdd868dba063fecba609a1d819de271f0d51b23e /widget | |
parent | Adding upstream version 125.0.3. (diff) | |
download | firefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip |
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'widget')
80 files changed, 1386 insertions, 1906 deletions
diff --git a/widget/BasicEvents.h b/widget/BasicEvents.h index b3707f1cf4..affc58f3be 100644 --- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -440,6 +440,10 @@ class WidgetEvent : public WidgetEventTime { mFlags.mCancelable = false; mFlags.mBubbles = mFlags.mIsTrusted; break; + case eLegacyTextEventClass: + mFlags.mCancelable = mFlags.mIsTrusted && mMessage == eLegacyTextInput; + mFlags.mBubbles = mFlags.mIsTrusted && mMessage == eLegacyTextInput; + break; case eMouseEventClass: mFlags.mCancelable = (mMessage != eMouseEnter && mMessage != eMouseLeave); diff --git a/widget/EventClassList.h b/widget/EventClassList.h index c8870d84e5..937047e8ba 100644 --- a/widget/EventClassList.h +++ b/widget/EventClassList.h @@ -26,6 +26,7 @@ NS_EVENT_CLASS(Widget, CompositionEvent) NS_EVENT_CLASS(Widget, QueryContentEvent) NS_EVENT_CLASS(Widget, SelectionEvent) NS_EVENT_CLASS(Internal, EditorInputEvent) +NS_EVENT_CLASS(Internal, LegacyTextEvent) // MouseEvents.h NS_EVENT_CLASS(Widget, MouseEventBase) diff --git a/widget/EventForwards.h b/widget/EventForwards.h index e6a1ad6274..4a6d796cd1 100644 --- a/widget/EventForwards.h +++ b/widget/EventForwards.h @@ -270,9 +270,6 @@ inline bool IsCancelableBeforeInputEvent(EditorInputType aInputType) { return true; case EditorInputType::eInsertLink: return true; - case EditorInputType::eDeleteByComposition: - MOZ_ASSERT(!StaticPrefs::dom_input_events_conform_to_level_1()); - return true; case EditorInputType::eDeleteCompositionText: MOZ_ASSERT(!StaticPrefs::dom_input_events_conform_to_level_1()); return false; diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index f42c1e3b2c..4247403770 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -432,6 +432,9 @@ NS_EVENT_MESSAGE_FIRST_LAST(eGamepadEvent, eGamepadButtonDown, NS_EVENT_MESSAGE(eEditorInput) NS_EVENT_MESSAGE(eEditorBeforeInput) +// textInput event which is a default action of beforeinput +NS_EVENT_MESSAGE(eLegacyTextInput) + // selection events NS_EVENT_MESSAGE(eSelectStart) NS_EVENT_MESSAGE(eSelectionChange) @@ -450,11 +453,6 @@ NS_EVENT_MESSAGE(eToggle) NS_EVENT_MESSAGE(eClose) NS_EVENT_MESSAGE(eCancel) -// Marquee element events. -NS_EVENT_MESSAGE(eMarqueeBounce) -NS_EVENT_MESSAGE(eMarqueeStart) -NS_EVENT_MESSAGE(eMarqueeFinish) - NS_EVENT_MESSAGE(eScrollend) #ifdef UNDEF_NS_EVENT_MESSAGE_FIRST_LAST diff --git a/widget/InitData.h b/widget/InitData.h index aed5e0a551..a073362065 100644 --- a/widget/InitData.h +++ b/widget/InitData.h @@ -17,7 +17,6 @@ enum class WindowType : uint8_t { TopLevel, // default top level window Dialog, // top level window but usually handled differently // by the OS - Sheet, // MacOSX sheet (special dialog class) Popup, // used for combo boxes, etc Child, // child windows (contained inside a window on the // desktop (has no border)) diff --git a/widget/InputData.cpp b/widget/InputData.cpp index 3d1695dd8e..8f06a51c1c 100644 --- a/widget/InputData.cpp +++ b/widget/InputData.cpp @@ -303,6 +303,9 @@ MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent) case eMouseHitTest: mType = MOUSE_HITTEST; break; + case eContextMenu: + mType = MOUSE_CONTEXTMENU; + break; default: MOZ_ASSERT_UNREACHABLE("Mouse event type not supported"); break; @@ -364,6 +367,9 @@ WidgetMouseEvent MouseInput::ToWidgetEvent(nsIWidget* aWidget) const { case MOUSE_HITTEST: msg = eMouseHitTest; break; + case MOUSE_CONTEXTMENU: + msg = eContextMenu; + break; default: MOZ_ASSERT_UNREACHABLE( "Did not assign a type to WidgetMouseEvent in MouseInput"); diff --git a/widget/InputData.h b/widget/InputData.h index 855cfcd178..fa011f8451 100644 --- a/widget/InputData.h +++ b/widget/InputData.h @@ -275,7 +275,8 @@ class MouseInput : public InputData { MOUSE_WIDGET_ENTER, MOUSE_WIDGET_EXIT, MOUSE_HITTEST, - MOUSE_EXPLORE_BY_TOUCH + MOUSE_EXPLORE_BY_TOUCH, + MOUSE_CONTEXTMENU )); MOZ_DEFINE_ENUM_AT_CLASS_SCOPE( diff --git a/widget/LSBUtils.cpp b/widget/LSBUtils.cpp index 9ebffacd7e..718c43d26e 100644 --- a/widget/LSBUtils.cpp +++ b/widget/LSBUtils.cpp @@ -45,6 +45,7 @@ bool GetOSRelease(nsACString& aDistributor, nsACString& aDescription, bool seen_id = false, seen_pretty_name = false, seen_version_id = false; std::string rawline; nsAutoCString name; + nsAutoCString build_id; while (std::getline(stream, rawline)) { std::string_view line(rawline); size_t pos = line.find('='); @@ -68,6 +69,8 @@ bool GetOSRelease(nsACString& aDistributor, nsACString& aDescription, if (ExtractAndSetValue(aDescription, value)) seen_pretty_name = true; } else if (key == "VERSION_ID") { if (ExtractAndSetValue(aRelease, value)) seen_version_id = true; + } else if (key == "BUILD_ID") { + ExtractAndSetValue(build_id, value); } else if (key == "VERSION_CODENAME") { ExtractAndSetValue(aCodename, value); } @@ -77,6 +80,11 @@ bool GetOSRelease(nsACString& aDistributor, nsACString& aDescription, if (seen_id && !name.IsEmpty() && name.EqualsIgnoreCase(aDistributor)) { aDistributor = name; } + // If VERSION_ID is not set but BUILD_ID is, use BUILD_ID. + if (!seen_version_id && !build_id.IsEmpty()) { + aRelease = build_id; + seen_version_id = true; + } // Only consider our work done if we've seen at least ID, PRETTY_NAME and // VERSION_ID. return seen_id && seen_pretty_name && seen_version_id; diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 0cab187410..13219ff54b 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -53,8 +53,6 @@ class LookAndFeel { CaretBlinkCount, // pixel width of caret CaretWidth, - // show the caret when text is selected? - ShowCaretDuringSelection, // select textfields when focused via tab/accesskey? SelectTextfieldsOnKeyFocus, // delay before submenus open @@ -119,6 +117,9 @@ class LookAndFeel { */ MacRTL, + /* Native macOS titlebar height. */ + MacTitlebarHeight, + /* * AlertNotificationOrigin indicates from which corner of the * screen alerts slide in, and from which direction (horizontal/vertical). @@ -186,6 +187,7 @@ class LookAndFeel { */ ContextMenuOffsetVertical, ContextMenuOffsetHorizontal, + TooltipOffsetVertical, /* * A boolean value indicating whether client-side decorations are @@ -299,7 +301,6 @@ class LookAndFeel { * 1: High */ DynamicRange, - VideoDynamicRange, /** Whether XUL panel animations are enabled. */ PanelAnimations, @@ -321,6 +322,11 @@ class LookAndFeel { return GetInt(IntID::UseOverlayScrollbars); } + static constexpr int32_t kDefaultTooltipOffset = 21; + static int32_t TooltipOffsetVertical() { + return GetInt(IntID::TooltipOffsetVertical, kDefaultTooltipOffset); + } + // Returns keyCode value of a modifier key which is used for accesskey. // Returns 0 if the platform doesn't support access key. static uint32_t GetMenuAccessKey(); diff --git a/widget/Screen.cpp b/widget/Screen.cpp index 71a1624624..ccc78a9413 100644 --- a/widget/Screen.cpp +++ b/widget/Screen.cpp @@ -30,7 +30,7 @@ Screen::Screen(LayoutDeviceIntRect aRect, LayoutDeviceIntRect aAvailRect, uint32_t aPixelDepth, uint32_t aColorDepth, uint32_t aRefreshRate, DesktopToLayoutDeviceScale aContentsScale, CSSToLayoutDeviceScale aDefaultCssScale, float aDPI, - IsPseudoDisplay aIsPseudoDisplay, + IsPseudoDisplay aIsPseudoDisplay, IsHDR aIsHDR, hal::ScreenOrientation aOrientation, OrientationAngle aOrientationAngle) : mRect(aRect), @@ -45,7 +45,8 @@ Screen::Screen(LayoutDeviceIntRect aRect, LayoutDeviceIntRect aAvailRect, mDPI(aDPI), mScreenOrientation(EffectiveOrientation(aOrientation, aRect)), mOrientationAngle(aOrientationAngle), - mIsPseudoDisplay(aIsPseudoDisplay == IsPseudoDisplay::Yes) {} + mIsPseudoDisplay(aIsPseudoDisplay == IsPseudoDisplay::Yes), + mIsHDR(aIsHDR == IsHDR::Yes) {} Screen::Screen(const dom::ScreenDetails& aScreen) : mRect(aScreen.rect()), @@ -60,7 +61,8 @@ Screen::Screen(const dom::ScreenDetails& aScreen) mDPI(aScreen.dpi()), mScreenOrientation(aScreen.orientation()), mOrientationAngle(aScreen.orientationAngle()), - mIsPseudoDisplay(aScreen.isPseudoDisplay()) {} + mIsPseudoDisplay(aScreen.isPseudoDisplay()), + mIsHDR(aScreen.isHDR()) {} Screen::Screen(const Screen& aOther) : mRect(aOther.mRect), @@ -75,13 +77,14 @@ Screen::Screen(const Screen& aOther) mDPI(aOther.mDPI), mScreenOrientation(aOther.mScreenOrientation), mOrientationAngle(aOther.mOrientationAngle), - mIsPseudoDisplay(aOther.mIsPseudoDisplay) {} + mIsPseudoDisplay(aOther.mIsPseudoDisplay), + mIsHDR(aOther.mIsHDR) {} dom::ScreenDetails Screen::ToScreenDetails() const { return dom::ScreenDetails( mRect, mRectDisplayPix, mAvailRect, mAvailRectDisplayPix, mPixelDepth, mColorDepth, mRefreshRate, mContentsScale, mDefaultCssScale, mDPI, - mScreenOrientation, mOrientationAngle, mIsPseudoDisplay); + mScreenOrientation, mOrientationAngle, mIsPseudoDisplay, mIsHDR); } NS_IMETHODIMP diff --git a/widget/Screen.h b/widget/Screen.h index f3f8b4a628..ca2efa1f59 100644 --- a/widget/Screen.h +++ b/widget/Screen.h @@ -26,12 +26,13 @@ class Screen final : public nsIScreen { using OrientationAngle = uint16_t; enum class IsPseudoDisplay : bool { No, Yes }; + enum class IsHDR : bool { No, Yes }; Screen(LayoutDeviceIntRect aRect, LayoutDeviceIntRect aAvailRect, uint32_t aPixelDepth, uint32_t aColorDepth, uint32_t aRefreshRate, DesktopToLayoutDeviceScale aContentsScale, CSSToLayoutDeviceScale aDefaultCssScale, float aDpi, IsPseudoDisplay, - hal::ScreenOrientation = hal::ScreenOrientation::None, + IsHDR, hal::ScreenOrientation = hal::ScreenOrientation::None, OrientationAngle = 0); explicit Screen(const dom::ScreenDetails& aScreenDetails); Screen(const Screen& aOther); @@ -60,6 +61,8 @@ class Screen final : public nsIScreen { enum class IncludeOSZoom : bool { No, Yes }; CSSToLayoutDeviceScale GetCSSToLayoutDeviceScale(IncludeOSZoom) const; + bool GetIsHDR() const { return mIsHDR; } + private: virtual ~Screen() = default; @@ -76,6 +79,7 @@ class Screen final : public nsIScreen { const hal::ScreenOrientation mScreenOrientation; const OrientationAngle mOrientationAngle; const bool mIsPseudoDisplay; + const bool mIsHDR; }; } // namespace widget diff --git a/widget/ScreenManager.cpp b/widget/ScreenManager.cpp index 58e20806eb..84d7316af7 100644 --- a/widget/ScreenManager.cpp +++ b/widget/ScreenManager.cpp @@ -139,7 +139,8 @@ already_AddRefed<Screen> ScreenManager::ScreenForRect( auto screen = MakeRefPtr<Screen>( LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0, 0, DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */, - Screen::IsPseudoDisplay::No, hal::ScreenOrientation::None, 0); + Screen::IsPseudoDisplay::No, Screen::IsHDR::No, + hal::ScreenOrientation::None, 0); return screen.forget(); } @@ -219,10 +220,11 @@ already_AddRefed<Screen> ScreenManager::GetPrimaryScreen() { if (mScreenList.IsEmpty()) { MOZ_LOG(sScreenLog, LogLevel::Warning, ("No screen available. This can happen in xpcshell.")); - return MakeAndAddRef<Screen>( - LayoutDeviceIntRect(), LayoutDeviceIntRect(), 0, 0, 0, - DesktopToLayoutDeviceScale(), CSSToLayoutDeviceScale(), 96 /* dpi */, - Screen::IsPseudoDisplay::No, hal::ScreenOrientation::None, 0); + return MakeAndAddRef<Screen>(LayoutDeviceIntRect(), LayoutDeviceIntRect(), + 0, 0, 0, DesktopToLayoutDeviceScale(), + CSSToLayoutDeviceScale(), 96 /* dpi */, + Screen::IsPseudoDisplay::No, Screen::IsHDR::No, + hal::ScreenOrientation::None, 0); } return do_AddRef(mScreenList[0]); diff --git a/widget/TextEvents.h b/widget/TextEvents.h index 71d2e656e2..3f42561c75 100644 --- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -1442,13 +1442,8 @@ class WidgetSelectionEvent : public WidgetGUIEvent { ******************************************************************************/ class InternalEditorInputEvent : public InternalUIEvent { - private: - InternalEditorInputEvent() - : mData(VoidString()), - mInputType(EditorInputType::eUnknown), - mIsComposing(false) {} - public: + InternalEditorInputEvent() = delete; virtual InternalEditorInputEvent* AsEditorInputEvent() override { return this; } @@ -1457,9 +1452,7 @@ class InternalEditorInputEvent : public InternalUIEvent { nsIWidget* aWidget = nullptr, const WidgetEventTime* aTime = nullptr) : InternalUIEvent(aIsTrusted, aMessage, aWidget, eEditorInputEventClass, - aTime), - mData(VoidString()), - mInputType(EditorInputType::eUnknown) {} + aTime) {} virtual WidgetEvent* Duplicate() const override { MOZ_ASSERT(mClass == eEditorInputEventClass, @@ -1472,13 +1465,13 @@ class InternalEditorInputEvent : public InternalUIEvent { return result; } - nsString mData; + nsString mData = VoidString(); RefPtr<dom::DataTransfer> mDataTransfer; OwningNonNullStaticRangeArray mTargetRanges; - EditorInputType mInputType; + EditorInputType mInputType = EditorInputType::eUnknown; - bool mIsComposing; + bool mIsComposing = false; void AssignEditorInputEventData(const InternalEditorInputEvent& aEvent, bool aCopyTargets) { @@ -1502,10 +1495,51 @@ class InternalEditorInputEvent : public InternalUIEvent { private: static const char16_t* const kInputTypeNames[]; - typedef nsTHashMap<nsStringHashKey, EditorInputType> InputTypeHashtable; + using InputTypeHashtable = nsTHashMap<nsStringHashKey, EditorInputType>; static InputTypeHashtable* sInputTypeHashtable; }; +/****************************************************************************** + * mozilla::InternalLegacyTextEvent + ******************************************************************************/ + +class InternalLegacyTextEvent : public InternalUIEvent { + public: + InternalLegacyTextEvent() = delete; + + virtual InternalLegacyTextEvent* AsLegacyTextEvent() override { return this; } + + InternalLegacyTextEvent(bool aIsTrusted, EventMessage aMessage, + nsIWidget* aWidget = nullptr, + const WidgetEventTime* aTime = nullptr) + : InternalUIEvent(aIsTrusted, aMessage, aWidget, eLegacyTextEventClass, + aTime) {} + + virtual WidgetEvent* Duplicate() const override { + MOZ_ASSERT(mClass == eLegacyTextEventClass, + "Duplicate() must be overridden by sub class"); + // Not copying widget, it is a weak reference. + InternalLegacyTextEvent* result = + new InternalLegacyTextEvent(false, mMessage, nullptr, this); + result->AssignLegacyTextEventData(*this, true); + result->mFlags = mFlags; + return result; + } + + nsString mData; + RefPtr<dom::DataTransfer> mDataTransfer; + EditorInputType mInputType = EditorInputType::eUnknown; + + void AssignLegacyTextEventData(const InternalLegacyTextEvent& aEvent, + bool aCopyTargets) { + AssignUIEventData(aEvent, aCopyTargets); + + mData = aEvent.mData; + mDataTransfer = aEvent.mDataTransfer; + mInputType = aEvent.mInputType; + } +}; + } // namespace mozilla #endif // mozilla_TextEvents_h__ diff --git a/widget/Theme.cpp b/widget/Theme.cpp index 1fcd3d4bc3..15bf68ec01 100644 --- a/widget/Theme.cpp +++ b/widget/Theme.cpp @@ -296,6 +296,9 @@ std::pair<sRGBColor, sRGBColor> Theme::ComputeButtonColors( bool isHovered = aState.HasState(ElementState::HOVER); nscolor backgroundColor = [&] { + if (aState.HasState(ElementState::AUTOFILL)) { + return aColors.SystemNs(StyleSystemColor::MozAutofillBackground); + } if (isDisabled) { return aColors.SystemNs(StyleSystemColor::MozButtondisabledface); } @@ -308,12 +311,6 @@ std::pair<sRGBColor, sRGBColor> Theme::ComputeButtonColors( return aColors.SystemNs(StyleSystemColor::Buttonface); }(); - if (aState.HasState(ElementState::AUTOFILL)) { - backgroundColor = NS_ComposeColors( - backgroundColor, - aColors.SystemNs(StyleSystemColor::MozAutofillBackground)); - } - const sRGBColor borderColor = ComputeBorderColor(aState, aColors, OutlineCoversBorder::Yes); return std::make_pair(sRGBColor::FromABGR(backgroundColor), borderColor); @@ -323,18 +320,15 @@ std::pair<sRGBColor, sRGBColor> Theme::ComputeTextfieldColors( const ElementState& aState, const Colors& aColors, OutlineCoversBorder aOutlineCoversBorder) { nscolor backgroundColor = [&] { + if (aState.HasState(ElementState::AUTOFILL)) { + return aColors.SystemNs(StyleSystemColor::MozAutofillBackground); + } if (aState.HasState(ElementState::DISABLED)) { return aColors.SystemNs(StyleSystemColor::MozDisabledfield); } return aColors.SystemNs(StyleSystemColor::Field); }(); - if (aState.HasState(ElementState::AUTOFILL)) { - backgroundColor = NS_ComposeColors( - backgroundColor, - aColors.SystemNs(StyleSystemColor::MozAutofillBackground)); - } - const sRGBColor borderColor = ComputeBorderColor(aState, aColors, aOutlineCoversBorder); return std::make_pair(sRGBColor::FromABGR(backgroundColor), borderColor); diff --git a/widget/android/EventDispatcher.cpp b/widget/android/EventDispatcher.cpp index 6e414c11ce..2054d822d7 100644 --- a/widget/android/EventDispatcher.cpp +++ b/widget/android/EventDispatcher.cpp @@ -21,12 +21,6 @@ #include "mozilla/java/EventCallbackWrappers.h" #include "mozilla/jni/GeckoBundleUtils.h" -// Disable the C++ 2a warning. See bug #1509926 -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wc++2a-compat" -#endif - namespace mozilla { namespace widget { @@ -770,7 +764,3 @@ nsresult EventDispatcher::UnboxBundle(JSContext* aCx, jni::Object::Param aData, } // namespace widget } // namespace mozilla - -#if defined(__clang__) -# pragma clang diagnostic pop -#endif diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index 6e6d3bf47b..267beaf330 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -667,6 +667,14 @@ nsresult GfxInfo::GetFeatureStatusImpl( return NS_OK; } + if (aFeature == FEATURE_WEBGPU) { + // Ensure WebGPU is disabled by default on Android until it is better + // tested. + *aStatus = nsIGfxInfo::FEATURE_BLOCKED_DEVICE; + aFailureId = "FEATURE_FAILURE_WEBGPU_ANDROID"; + return NS_OK; + } + return GfxInfoBase::GetFeatureStatusImpl( aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, &os); } diff --git a/widget/android/ScreenHelperAndroid.cpp b/widget/android/ScreenHelperAndroid.cpp index 33c91fe6e0..141b38e01b 100644 --- a/widget/android/ScreenHelperAndroid.cpp +++ b/widget/android/ScreenHelperAndroid.cpp @@ -38,14 +38,15 @@ static already_AddRefed<Screen> MakePrimaryScreen() { uint32_t depth = java::GeckoAppShell::GetScreenDepth(); float density = java::GeckoAppShell::GetDensity(); float dpi = java::GeckoAppShell::GetDpi(); + bool isHDR = false; // Bug 1884960: report this accurately auto orientation = hal::ScreenOrientation(java::GeckoAppShell::GetScreenOrientation()); uint16_t angle = java::GeckoAppShell::GetScreenAngle(); float refreshRate = java::GeckoAppShell::GetScreenRefreshRate(); - return MakeAndAddRef<Screen>(bounds, bounds, depth, depth, refreshRate, - DesktopToLayoutDeviceScale(density), - CSSToLayoutDeviceScale(1.0f), dpi, - Screen::IsPseudoDisplay::No, orientation, angle); + return MakeAndAddRef<Screen>( + bounds, bounds, depth, depth, refreshRate, + DesktopToLayoutDeviceScale(density), CSSToLayoutDeviceScale(1.0f), dpi, + Screen::IsPseudoDisplay::No, Screen::IsHDR(isHDR), orientation, angle); } ScreenHelperAndroid::ScreenHelperAndroid() { diff --git a/widget/android/nsLookAndFeel.cpp b/widget/android/nsLookAndFeel.cpp index 6d9f8c634f..45c556f6d1 100644 --- a/widget/android/nsLookAndFeel.cpp +++ b/widget/android/nsLookAndFeel.cpp @@ -253,6 +253,7 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aColorScheme, break; case ColorID::Marktext: case ColorID::Mark: + case ColorID::MozAutofillBackground: case ColorID::SpellCheckerUnderline: aColor = GetStandinForNativeColor(aID, aColorScheme); break; @@ -298,10 +299,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; - case IntID::SelectTextfieldsOnKeyFocus: // Select textfield content when focused by kbd // used by EventStateManager::sTextfieldSelectModel diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index e9756c3f92..a654c8e848 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -609,6 +609,22 @@ class NPZCSupport final PostInputEvent([input = std::move(input), result](nsWindow* window) { WidgetMouseEvent mouseEvent = input.ToWidgetEvent(window); window->ProcessUntransformedAPZEvent(&mouseEvent, result); + if (MouseInput::SECONDARY_BUTTON == input.mButtonType) { + if ((StaticPrefs::ui_context_menus_after_mouseup() && + MouseInput::MOUSE_UP == input.mType) || + (!StaticPrefs::ui_context_menus_after_mouseup() && + MouseInput::MOUSE_DOWN == input.mType)) { + MouseInput contextMenu = input; + + // Actually we don't dispatch context menu event to APZ since we don't + // handle it on APZ yet. If handling it, we need to consider how to + // dispatch it on APZ thread. It may cause a race condition. + contextMenu.mType = MouseInput::MOUSE_CONTEXTMENU; + + WidgetMouseEvent contextMenuEvent = contextMenu.ToWidgetEvent(window); + window->ProcessUntransformedAPZEvent(&contextMenuEvent, result); + } + } }); switch (result.GetStatus()) { diff --git a/widget/cocoa/MacThemeGeometryType.h b/widget/cocoa/MacThemeGeometryType.h index 4691dbdec2..91879503e1 100644 --- a/widget/cocoa/MacThemeGeometryType.h +++ b/widget/cocoa/MacThemeGeometryType.h @@ -7,11 +7,7 @@ enum MacThemeGeometryType { eThemeGeometryTypeTitlebar = 1, - eThemeGeometryTypeToolbar, - eThemeGeometryTypeToolbox, eThemeGeometryTypeWindowButtons, - eThemeGeometryTypeMenu, - eThemeGeometryTypeTooltip, }; #endif diff --git a/widget/cocoa/ScreenHelperCocoa.mm b/widget/cocoa/ScreenHelperCocoa.mm index 57e1313320..2f1408e345 100644 --- a/widget/cocoa/ScreenHelperCocoa.mm +++ b/widget/cocoa/ScreenHelperCocoa.mm @@ -9,6 +9,7 @@ #import <Cocoa/Cocoa.h> #include "mozilla/Logging.h" +#include "nsCocoaFeatures.h" #include "nsCocoaUtils.h" #include "nsObjCExceptions.h" @@ -109,6 +110,26 @@ static already_AddRefed<Screen> MakeScreen(NSScreen* aScreen) { pixelDepth = MAX_REPORTED_PIXEL_DEPTH; } + // What's the maximum color component value this screen can display? This + // is a reasonable stand-in for measuring peak brightness. + CGFloat componentValueMax = + aScreen.maximumPotentialExtendedDynamicRangeColorComponentValue; + + // Should we treat this as HDR? Based on spec at + // https://drafts.csswg.org/mediaqueries-5/#dynamic-range, we'll consider it + // HDR if it has pixel depth greater than 24, and if has high peak brightness, + // which we measure by checking if it can represent component values greater + // than 1.0. + // + // Also, on HDR screens, users may want to force SDR by setting a different + // colorspace, for example by using the "Photography (P3 D65)" preset. In that + // case, componentValueMax will be 1.0 and we want to treat the display as + // SDR. + bool isHDR = pixelDepth > 24 && componentValueMax > 1.0; + + // Double-check HDR against the platform capabilities. + isHDR &= nsCocoaFeatures::OnBigSurOrLater(); + float dpi = 96.0f; CGDirectDisplayID displayID = [[[aScreen deviceDescription] objectForKey:@"NSScreenNumber"] intValue]; @@ -125,9 +146,10 @@ static already_AddRefed<Screen> MakeScreen(NSScreen* aScreen) { // Getting the refresh rate is a little hard on OS X. We could use // CVDisplayLinkGetNominalOutputVideoRefreshPeriod, but that's a little // involved. Ideally we could query it from vsync. For now, we leave it out. - RefPtr<Screen> screen = new Screen(rect, availRect, pixelDepth, pixelDepth, 0, - contentsScaleFactor, defaultCssScaleFactor, - dpi, Screen::IsPseudoDisplay::No); + RefPtr<Screen> screen = + new Screen(rect, availRect, pixelDepth, pixelDepth, 0, + contentsScaleFactor, defaultCssScaleFactor, dpi, + Screen::IsPseudoDisplay::No, Screen::IsHDR(isHDR)); return screen.forget(); NS_OBJC_END_TRY_BLOCK_RETURN(nullptr); diff --git a/widget/cocoa/TextInputHandler.mm b/widget/cocoa/TextInputHandler.mm index 1d96a3ce82..046f1c46be 100644 --- a/widget/cocoa/TextInputHandler.mm +++ b/widget/cocoa/TextInputHandler.mm @@ -10,6 +10,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/AutoRestore.h" +#include "mozilla/MacStringHelpers.h" #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/StaticPrefs_intl.h" @@ -321,13 +322,13 @@ static const char* GetCharacters(const nsAString& aString) { } // the result will be freed automatically by cocoa. - NSString* result = nsCocoaUtils::ToNSString(escapedStr); + NSString* result = XPCOMStringToNSString(escapedStr); return [result UTF8String]; } static const char* GetCharacters(const NSString* aString) { nsAutoString str; - nsCocoaUtils::GetStringForNSString(aString, str); + CopyNSStringToXPCOMString(aString, str); return GetCharacters(str); } @@ -835,7 +836,7 @@ bool TISInputSourceWrapper::GetStringProperty(const CFStringRef aKey, nsAString& aStr) { CFStringRef str; GetStringProperty(aKey, str); - nsCocoaUtils::GetStringForNSString((const NSString*)str, aStr); + CopyNSStringToXPCOMString((const NSString*)str, aStr); return !aStr.IsEmpty(); } @@ -883,8 +884,7 @@ bool TISInputSourceWrapper::GetPrimaryLanguage(nsAString& aPrimaryLanguage) { NS_ENSURE_TRUE(mInputSource, false); CFStringRef primaryLanguage; NS_ENSURE_TRUE(GetPrimaryLanguage(primaryLanguage), false); - nsCocoaUtils::GetStringForNSString((const NSString*)primaryLanguage, - aPrimaryLanguage); + CopyNSStringToXPCOMString((const NSString*)primaryLanguage, aPrimaryLanguage); return !aPrimaryLanguage.IsEmpty(); } @@ -985,7 +985,7 @@ void TISInputSourceWrapper::ComputeInsertStringForCharCode( } else { // If the caller isn't sure what string will be input, let's use // characters of NSEvent. - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], aResult); + CopyNSStringToXPCOMString([aNativeKeyEvent characters], aResult); } // If control key is pressed and the eventChars is a non-printable control @@ -1156,8 +1156,8 @@ void TISInputSourceWrapper::InitKeyEvent(NSEvent* aNativeKeyEvent, // non-ASCII capable layout to ASCII capable, or from Dvorak to QWERTY. // KeyboardEvent.key value should be the switched layout's character. else if (aKeyEvent.IsMeta()) { - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], - aKeyEvent.mKeyValue); + CopyNSStringToXPCOMString([aNativeKeyEvent characters], + aKeyEvent.mKeyValue); } // If control key is pressed, some keys may produce printable character via // [aNativeKeyEvent characters]. Otherwise, translate input character of @@ -1188,8 +1188,8 @@ void TISInputSourceWrapper::InitKeyEvent(NSEvent* aNativeKeyEvent, [aNativeKeyEvent modifierFlags]); aKeyEvent.mKeyValue = TranslateToChar(nativeKeyCode, state, kbType); } else { - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], - aKeyEvent.mKeyValue); + CopyNSStringToXPCOMString([aNativeKeyEvent characters], + aKeyEvent.mKeyValue); // If the key value is empty, the event may be a dead key event. // If TranslateToChar() returns non-zero value, that means that // the key may input a character with different dead key state. @@ -1207,8 +1207,8 @@ void TISInputSourceWrapper::InitKeyEvent(NSEvent* aNativeKeyEvent, if (aKeyEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING && (aKeyEvent.mKeyValue.IsEmpty() || IsControlChar(aKeyEvent.mKeyValue[0]))) { - nsCocoaUtils::GetStringForNSString( - [aNativeKeyEvent charactersIgnoringModifiers], aKeyEvent.mKeyValue); + CopyNSStringToXPCOMString([aNativeKeyEvent charactersIgnoringModifiers], + aKeyEvent.mKeyValue); // But don't expose it if it's a control character. if (!aKeyEvent.mKeyValue.IsEmpty() && IsControlChar(aKeyEvent.mKeyValue[0])) { @@ -1243,7 +1243,7 @@ void TISInputSourceWrapper::WillDispatchKeyboardEvent( if (MOZ_LOG_TEST(gKeyLog, LogLevel::Info)) { nsAutoString chars; - nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], chars); + CopyNSStringToXPCOMString([aNativeKeyEvent characters], chars); NS_ConvertUTF16toUTF8 utf8Chars(chars); char16_t uniChar = static_cast<char16_t>(aKeyEvent.mCharCode); MOZ_LOG( @@ -2428,7 +2428,7 @@ void TextInputHandler::InsertText(NSString* aString, NSRange selectedRange = SelectedRange(); nsAutoString str; - nsCocoaUtils::GetStringForNSString(aString, str); + CopyNSStringToXPCOMString(aString, str); AutoInsertStringClearer clearer(currentKeyEvent); if (currentKeyEvent) { @@ -3849,7 +3849,7 @@ bool IMEInputHandler::DispatchCompositionCommitEvent( mSelectedRange.location += aCommitString->Length(); } else if (mIMECompositionString) { nsAutoString commitString; - nsCocoaUtils::GetStringForNSString(mIMECompositionString, commitString); + CopyNSStringToXPCOMString(mIMECompositionString, commitString); mSelectedRange.location += commitString.Length(); } mSelectedRange.length = 0; @@ -4037,7 +4037,7 @@ void IMEInputHandler::InsertTextAsCommittingComposition( } nsString str; - nsCocoaUtils::GetStringForNSString(aString, str); + CopyNSStringToXPCOMString(aString, str); if (!IsIMEComposing()) { MOZ_DIAGNOSTIC_ASSERT(!str.IsEmpty()); @@ -4171,7 +4171,7 @@ void IMEInputHandler::SetMarkedText(NSAttributedString* aAttrString, } nsString str; - nsCocoaUtils::GetStringForNSString([aAttrString string], str); + CopyNSStringToXPCOMString([aAttrString string], str); mMarkedRange.length = str.Length(); @@ -4272,7 +4272,7 @@ NSAttributedString* IMEInputHandler::GetAttributedSubstringFromRange( if (MOZ_LOG_TEST(gIMELog, LogLevel::Info)) { nsAutoString str; - nsCocoaUtils::GetStringForNSString(nsstr, str); + CopyNSStringToXPCOMString(nsstr, str); MOZ_LOG(gIMELog, LogLevel::Info, ("%p IMEInputHandler::GetAttributedSubstringFromRange, " "computed with mIMECompositionString (result string=\"%s\")", @@ -5051,18 +5051,17 @@ nsresult TextInputHandlerBase::SynthesizeNativeKeyEvent( bool sendFlagsChangedEvent = IsModifierKey(aNativeKeyCode); NSEventType eventType = sendFlagsChangedEvent ? NSEventTypeFlagsChanged : NSEventTypeKeyDown; - NSEvent* downEvent = - [NSEvent keyEventWithType:eventType - location:NSMakePoint(0, 0) - modifierFlags:modifierFlags - timestamp:0 - windowNumber:windowNumber - context:nil - characters:nsCocoaUtils::ToNSString(aCharacters) - charactersIgnoringModifiers:nsCocoaUtils::ToNSString( - aUnmodifiedCharacters) - isARepeat:NO - keyCode:aNativeKeyCode]; + NSEvent* downEvent = [NSEvent + keyEventWithType:eventType + location:NSMakePoint(0, 0) + modifierFlags:modifierFlags + timestamp:0 + windowNumber:windowNumber + context:nil + characters:XPCOMStringToNSString(aCharacters) + charactersIgnoringModifiers:XPCOMStringToNSString(aUnmodifiedCharacters) + isARepeat:NO + keyCode:aNativeKeyCode]; NSEvent* upEvent = sendFlagsChangedEvent ? nil @@ -5232,7 +5231,7 @@ bool TextInputHandlerBase::SetSelection(NSRange& aRange) { return false; } nsAutoString nativeChars; - nsCocoaUtils::GetStringForNSString([aNativeEvent characters], nativeChars); + CopyNSStringToXPCOMString([aNativeEvent characters], nativeChars); // this is not character inputting event, simply. if (nativeChars.IsEmpty() || @@ -5314,7 +5313,7 @@ void TextInputHandlerBase::KeyEventState::InitKeyEvent( if (!mInsertedString.IsEmpty()) { nsAutoString unhandledString; GetUnhandledString(unhandledString); - NSString* unhandledNSString = nsCocoaUtils::ToNSString(unhandledString); + NSString* unhandledNSString = XPCOMStringToNSString(unhandledString); // If the key event's some characters were already handled by // InsertString() calls, we need to create a dummy event which doesn't // include the handled characters. @@ -5345,7 +5344,7 @@ void TextInputHandlerBase::KeyEventState::GetUnhandledString( return; } nsAutoString characters; - nsCocoaUtils::GetStringForNSString([mKeyEvent characters], characters); + CopyNSStringToXPCOMString([mKeyEvent characters], characters); if (characters.IsEmpty()) { return; } @@ -5373,8 +5372,7 @@ TextInputHandlerBase::AutoInsertStringClearer::~AutoInsertStringClearer() { // If inserting string is a part of characters of the event, // we should record it as inserted string. nsAutoString characters; - nsCocoaUtils::GetStringForNSString([mState->mKeyEvent characters], - characters); + CopyNSStringToXPCOMString([mState->mKeyEvent characters], characters); nsAutoString insertedString(mState->mInsertedString); insertedString += *mState->mInsertString; if (StringBeginsWith(characters, insertedString)) { diff --git a/widget/cocoa/VibrancyManager.h b/widget/cocoa/VibrancyManager.h index d431540830..c70ec60a3b 100644 --- a/widget/cocoa/VibrancyManager.h +++ b/widget/cocoa/VibrancyManager.h @@ -24,6 +24,7 @@ namespace mozilla { enum class VibrancyType { TOOLTIP, MENU, + TITLEBAR, }; /** diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm index e9cfcc3be0..6062acb931 100644 --- a/widget/cocoa/VibrancyManager.mm +++ b/widget/cocoa/VibrancyManager.mm @@ -9,6 +9,7 @@ #import <objc/message.h> #include "nsChildView.h" +#include "mozilla/StaticPrefs_widget.h" using namespace mozilla; @@ -30,18 +31,34 @@ static NSVisualEffectState VisualEffectStateForVibrancyType( // Tooltip and menu windows are never "key", so we need to tell the // vibrancy effect to look active regardless of window state. return NSVisualEffectStateActive; - default: - return NSVisualEffectStateFollowsWindowActiveState; + case VibrancyType::TITLEBAR: + break; } + return NSVisualEffectStateFollowsWindowActiveState; } static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType( - VibrancyType aType, BOOL* aOutIsEmphasized) { + VibrancyType aType) { switch (aType) { case VibrancyType::TOOLTIP: return (NSVisualEffectMaterial)NSVisualEffectMaterialToolTip; case VibrancyType::MENU: return NSVisualEffectMaterialMenu; + case VibrancyType::TITLEBAR: + return NSVisualEffectMaterialTitlebar; + } +} + +static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType( + VibrancyType aType) { + switch (aType) { + case VibrancyType::TOOLTIP: + case VibrancyType::MENU: + return NSVisualEffectBlendingModeBehindWindow; + case VibrancyType::TITLEBAR: + return StaticPrefs::widget_macos_titlebar_blend_mode_behind_window() + ? NSVisualEffectBlendingModeBehindWindow + : NSVisualEffectBlendingModeWithinWindow; } } @@ -53,11 +70,9 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType( self.appearance = nil; self.state = VisualEffectStateForVibrancyType(mType); - - BOOL isEmphasized = NO; - self.material = VisualEffectMaterialForVibrancyType(mType, &isEmphasized); - self.emphasized = isEmphasized; - + self.material = VisualEffectMaterialForVibrancyType(mType); + self.blendingMode = VisualEffectBlendingModeForVibrancyType(mType); + self.emphasized = NO; return self; } @@ -89,7 +104,7 @@ bool VibrancyManager::UpdateVibrantRegion( } auto& vr = *mVibrantRegions.GetOrInsertNew(uint32_t(aType)); return vr.UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() { - return this->CreateEffectView(aType); + return CreateEffectView(aType); }); } diff --git a/widget/cocoa/ViewRegion.mm b/widget/cocoa/ViewRegion.mm index 62e76d2df8..5b275a18a3 100644 --- a/widget/cocoa/ViewRegion.mm +++ b/widget/cocoa/ViewRegion.mm @@ -12,8 +12,8 @@ using namespace mozilla; ViewRegion::~ViewRegion() { - for (size_t i = 0; i < mViews.Length(); i++) { - [mViews[i] removeFromSuperview]; + for (NSView* view : mViews) { + [view removeFromSuperview]; } } @@ -33,34 +33,31 @@ bool ViewRegion::UpdateRegion(const LayoutDeviceIntRegion& aRegion, nsTArray<NSView*> viewsToRecycle = std::move(mViews); // The mViews array is now empty. - size_t i = 0; - for (auto iter = aRegion.RectIter(); - !iter.Done() || i < viewsToRecycle.Length(); i++) { - if (!iter.Done()) { - NSView* view = nil; - NSRect rect = aCoordinateConverter.DevPixelsToCocoaPoints(iter.Get()); - if (i < viewsToRecycle.Length()) { - view = viewsToRecycle[i]; - } else { - view = aViewCreationCallback(); - [aContainerView addSubview:view]; - - // Now that the view is in the view hierarchy, it'll be kept alive by - // its superview, so we can drop our reference. - [view release]; - } - if (!NSEqualRects(rect, [view frame])) { - [view setFrame:rect]; - } - [view setNeedsDisplay:YES]; - mViews.AppendElement(view); - iter.Next(); + size_t viewsRecycled = 0; + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + NSRect rect = aCoordinateConverter.DevPixelsToCocoaPoints(iter.Get()); + NSView* view = nil; + if (viewsRecycled < viewsToRecycle.Length()) { + view = viewsToRecycle[viewsRecycled++]; } else { - // Our new region is made of fewer rects than the old region, so we can - // remove this view. We only have a weak reference to it, so removing it - // from the view hierarchy will release it. - [viewsToRecycle[i] removeFromSuperview]; + view = aViewCreationCallback(); + [aContainerView addSubview:view]; + + // Now that the view is in the view hierarchy, it'll be kept alive by + // its superview, so we can drop our reference. + [view release]; } + if (!NSEqualRects(rect, view.frame)) { + view.frame = rect; + } + view.needsDisplay = YES; + mViews.AppendElement(view); + } + for (NSView* view : Span(viewsToRecycle).From(viewsRecycled)) { + // Our new region is made of fewer rects than the old region, so we can + // remove this view. We only have a weak reference to it, so removing it + // from the view hierarchy will release it. + [view removeFromSuperview]; } mRegion = aRegion; diff --git a/widget/cocoa/moz.build b/widget/cocoa/moz.build index ddb402e2cc..d0939d34c7 100644 --- a/widget/cocoa/moz.build +++ b/widget/cocoa/moz.build @@ -11,12 +11,6 @@ with Files("**"): with Files("*TextInput*"): BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling") -XPIDL_SOURCES += [ - "nsPIWidgetCocoa.idl", -] - -XPIDL_MODULE = "widget_cocoa" - EXPORTS += [ "DesktopBackgroundImage.h", "MediaHardwareKeysEventSourceMac.h", diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index 79919ba69a..d3241a983f 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -473,17 +473,20 @@ void* nsChildView::GetNativeData(uint32_t aDataType) { #pragma mark - void nsChildView::SuppressAnimation(bool aSuppress) { - GetAppWindowWidget()->SuppressAnimation(aSuppress); + if (nsCocoaWindow* widget = GetAppWindowWidget()) { + widget->SuppressAnimation(aSuppress); + } } bool nsChildView::IsVisible() const { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; if (!mVisible) { - return mVisible; + return false; } - if (!GetAppWindowWidget()->IsVisible()) { + nsCocoaWindow* widget = GetAppWindowWidget(); + if (NS_WARN_IF(!widget) || !widget->IsVisible()) { return false; } @@ -739,11 +742,11 @@ void nsChildView::Resize(double aWidth, double aHeight, bool aRepaint) { mBounds.height = height; ManipulateViewWithoutNeedingDisplay(mView, ^{ - [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; + mView.frame = DevPixelsToCocoaPoints(mBounds); }); if (mVisible && aRepaint) { - [[mView pixelHostingView] setNeedsDisplay:YES]; + mView.pixelHostingView.needsDisplay = YES; } ReportSizeEvent(); @@ -779,11 +782,11 @@ void nsChildView::Resize(double aX, double aY, double aWidth, double aHeight, } ManipulateViewWithoutNeedingDisplay(mView, ^{ - [mView setFrame:DevPixelsToCocoaPoints(mBounds)]; + mView.frame = DevPixelsToCocoaPoints(mBounds); }); if (mVisible && aRepaint) { - [[mView pixelHostingView] setNeedsDisplay:YES]; + mView.pixelHostingView.needsDisplay = YES; } if (isMoving) { @@ -1070,10 +1073,8 @@ nsresult nsChildView::SynthesizeNativeTouchpadDoubleTap( bool nsChildView::SendEventToNativeMenuSystem(NSEvent* aEvent) { bool handled = false; - nsCocoaWindow* widget = GetAppWindowWidget(); - if (widget) { - nsMenuBarX* mb = widget->GetMenuBar(); - if (mb) { + if (nsCocoaWindow* widget = GetAppWindowWidget()) { + if (nsMenuBarX* mb = widget->GetMenuBar()) { // Check if main menu wants to handle the event. handled = mb->PerformKeyEquivalent(aEvent); } @@ -1157,10 +1158,8 @@ nsresult nsChildView::ActivateNativeMenuItemAt(const nsAString& indexString) { nsresult nsChildView::ForceUpdateNativeMenuAt(const nsAString& indexString) { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - nsCocoaWindow* widget = GetAppWindowWidget(); - if (widget) { - nsMenuBarX* mb = widget->GetMenuBar(); - if (mb) { + if (nsCocoaWindow* widget = GetAppWindowWidget()) { + if (nsMenuBarX* mb = widget->GetMenuBar()) { if (indexString.IsEmpty()) mb->ForceNativeMenuReload(); else @@ -1689,33 +1688,6 @@ RefPtr<layers::NativeLayerRoot> nsChildView::GetNativeLayerRoot() { return mNativeLayerRoot; } -static int32_t FindTitlebarBottom( - const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - int32_t aWindowWidth) { - int32_t titlebarBottom = 0; - for (auto& g : aThemeGeometries) { - if (g.mType == eThemeGeometryTypeTitlebar && g.mRect.X() <= 0 && - g.mRect.XMost() >= aWindowWidth && g.mRect.Y() <= 0) { - titlebarBottom = std::max(titlebarBottom, g.mRect.YMost()); - } - } - return titlebarBottom; -} - -static int32_t FindUnifiedToolbarBottom( - const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - int32_t aWindowWidth, int32_t aTitlebarBottom) { - int32_t unifiedToolbarBottom = aTitlebarBottom; - for (uint32_t i = 0; i < aThemeGeometries.Length(); ++i) { - const nsIWidget::ThemeGeometry& g = aThemeGeometries[i]; - if ((g.mType == eThemeGeometryTypeToolbar) && g.mRect.X() <= 0 && - g.mRect.XMost() >= aWindowWidth && g.mRect.Y() <= aTitlebarBottom) { - unifiedToolbarBottom = std::max(unifiedToolbarBottom, g.mRect.YMost()); - } - } - return unifiedToolbarBottom; -} - static LayoutDeviceIntRect FindFirstRectOfType( const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, nsITheme::ThemeGeometryType aThemeGeometryType) { @@ -1730,32 +1702,17 @@ static LayoutDeviceIntRect FindFirstRectOfType( void nsChildView::UpdateThemeGeometries( const nsTArray<ThemeGeometry>& aThemeGeometries) { - if (![mView window]) return; + if (!mView.window) { + return; + } UpdateVibrancy(aThemeGeometries); - if (![[mView window] isKindOfClass:[ToolbarWindow class]]) return; - - // Update unified toolbar height and sheet attachment position. - int32_t windowWidth = mBounds.width; - int32_t titlebarBottom = FindTitlebarBottom(aThemeGeometries, windowWidth); - int32_t unifiedToolbarBottom = - FindUnifiedToolbarBottom(aThemeGeometries, windowWidth, titlebarBottom); - int32_t toolboxBottom = - FindFirstRectOfType(aThemeGeometries, eThemeGeometryTypeToolbox).YMost(); + if (![mView.window isKindOfClass:[ToolbarWindow class]]) { + return; + } ToolbarWindow* win = (ToolbarWindow*)[mView window]; - int32_t titlebarHeight = [win drawsContentsIntoWindowFrame] - ? 0 - : CocoaPointsToDevPixels([win titlebarHeight]); - int32_t devUnifiedHeight = titlebarHeight + unifiedToolbarBottom; - [win setUnifiedToolbarHeight:DevPixelsToCocoaPoints(devUnifiedHeight)]; - - int32_t sheetPositionDevPx = std::max(toolboxBottom, unifiedToolbarBottom); - NSPoint sheetPositionView = {0, DevPixelsToCocoaPoints(sheetPositionDevPx)}; - NSPoint sheetPositionWindow = [mView convertPoint:sheetPositionView - toView:nil]; - [win setSheetAttachmentPosition:sheetPositionWindow.y]; // Update titlebar control offsets. LayoutDeviceIntRect windowButtonRect = @@ -1768,10 +1725,8 @@ void nsChildView::UpdateThemeGeometries( static Maybe<VibrancyType> ThemeGeometryTypeToVibrancyType( nsITheme::ThemeGeometryType aThemeGeometryType) { switch (aThemeGeometryType) { - case eThemeGeometryTypeTooltip: - return Some(VibrancyType::TOOLTIP); - case eThemeGeometryTypeMenu: - return Some(VibrancyType::MENU); + case eThemeGeometryTypeTitlebar: + return Some(VibrancyType::TITLEBAR); default: return Nothing(); } @@ -1781,7 +1736,7 @@ static LayoutDeviceIntRegion GatherVibrantRegion( const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, VibrancyType aVibrancyType) { LayoutDeviceIntRegion region; - for (auto& geometry : aThemeGeometries) { + for (const auto& geometry : aThemeGeometries) { if (ThemeGeometryTypeToVibrancyType(geometry.mType) == Some(aVibrancyType)) { region.OrWith(geometry.mRect); @@ -1790,40 +1745,13 @@ static LayoutDeviceIntRegion GatherVibrantRegion( return region; } -template <typename Region> -static void MakeRegionsNonOverlappingImpl(Region& aOutUnion) {} - -template <typename Region, typename... Regions> -static void MakeRegionsNonOverlappingImpl(Region& aOutUnion, Region& aFirst, - Regions&... aRest) { - MakeRegionsNonOverlappingImpl(aOutUnion, aRest...); - aFirst.SubOut(aOutUnion); - aOutUnion.OrWith(aFirst); -} - -// Subtracts parts from regions in such a way that they don't have any overlap. -// Each region in the argument list will have the union of all the regions -// *following* it subtracted from itself. In other words, the arguments are -// sorted low priority to high priority. -template <typename Region, typename... Regions> -static void MakeRegionsNonOverlapping(Region& aFirst, Regions&... aRest) { - Region unionOfAll; - MakeRegionsNonOverlappingImpl(unionOfAll, aFirst, aRest...); -} - void nsChildView::UpdateVibrancy( const nsTArray<ThemeGeometry>& aThemeGeometries) { - LayoutDeviceIntRegion menuRegion = - GatherVibrantRegion(aThemeGeometries, VibrancyType::MENU); - LayoutDeviceIntRegion tooltipRegion = - GatherVibrantRegion(aThemeGeometries, VibrancyType::TOOLTIP); - - MakeRegionsNonOverlapping(menuRegion, tooltipRegion); + LayoutDeviceIntRegion titlebarRegion = + GatherVibrantRegion(aThemeGeometries, VibrancyType::TITLEBAR); auto& vm = EnsureVibrancyManager(); - bool changed = false; - changed |= vm.UpdateVibrantRegion(VibrancyType::MENU, menuRegion); - changed |= vm.UpdateVibrantRegion(VibrancyType::TOOLTIP, tooltipRegion); + bool changed = vm.UpdateVibrantRegion(VibrancyType::TITLEBAR, titlebarRegion); if (changed) { SuspendAsyncCATransactions(); @@ -1834,7 +1762,7 @@ mozilla::VibrancyManager& nsChildView::EnsureVibrancyManager() { MOZ_ASSERT(mView, "Only call this once we have a view!"); if (!mVibrancyManager) { mVibrancyManager = - MakeUnique<VibrancyManager>(*this, [mView vibrancyViewsContainer]); + MakeUnique<VibrancyManager>(*this, mView.vibrancyViewsContainer); } return *mVibrancyManager; } @@ -1897,7 +1825,7 @@ void nsChildView::UpdateWindowDraggingRegion( // Suppress calls to setNeedsDisplay during NSView geometry changes. ManipulateViewWithoutNeedingDisplay(mView, ^() { changed = mNonDraggableRegion.UpdateRegion( - nonDraggable, *this, [mView nonDraggableViewsContainer], ^() { + nonDraggable, *this, mView.nonDraggableViewsContainer, ^() { return [[NonDraggableView alloc] initWithFrame:NSZeroRect]; }); }); @@ -2198,22 +2126,22 @@ NSEvent* gLastDragMouseDownEvent = nil; // [strong] mCancelSwipeAnimation = nil; #endif + auto bounds = self.bounds; mNonDraggableViewsContainer = - [[ViewRegionContainerView alloc] initWithFrame:[self bounds]]; + [[ViewRegionContainerView alloc] initWithFrame:bounds]; mVibrancyViewsContainer = - [[ViewRegionContainerView alloc] initWithFrame:[self bounds]]; + [[ViewRegionContainerView alloc] initWithFrame:bounds]; - [mNonDraggableViewsContainer - setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - [mVibrancyViewsContainer - setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + mNonDraggableViewsContainer.autoresizingMask = + mVibrancyViewsContainer.autoresizingMask = + NSViewWidthSizable | NSViewHeightSizable; [self addSubview:mNonDraggableViewsContainer]; [self addSubview:mVibrancyViewsContainer]; - mPixelHostingView = [[PixelHostingView alloc] initWithFrame:[self bounds]]; - [mPixelHostingView - setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + mPixelHostingView = [[PixelHostingView alloc] initWithFrame:bounds]; + mPixelHostingView.autoresizingMask = + NSViewWidthSizable | NSViewHeightSizable; [self addSubview:mPixelHostingView]; @@ -2222,7 +2150,7 @@ NSEvent* gLastDragMouseDownEvent = nil; // [strong] mRootCALayer.bounds = NSZeroRect; mRootCALayer.anchorPoint = NSZeroPoint; mRootCALayer.contentsGravity = kCAGravityTopLeft; - [[mPixelHostingView layer] addSublayer:mRootCALayer]; + [mPixelHostingView.layer addSublayer:mRootCALayer]; mLastPressureStage = 0; } @@ -2398,7 +2326,7 @@ NSEvent* gLastDragMouseDownEvent = nil; // [strong] // This call will cause updateRootCALayer to be called during the upcoming // main thread CoreAnimation transaction. It will also trigger a transaction // if no transaction is currently pending. - [[mPixelHostingView layer] setNeedsDisplay]; + [mPixelHostingView.layer setNeedsDisplay]; } } @@ -5129,18 +5057,12 @@ BOOL ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, case WindowType::TopLevel: case WindowType::Dialog: - if ([aWindow attachedSheet]) return NO; + if (aWindow.attachedSheet) { + return NO; + } topLevelWindow = aWindow; break; - case WindowType::Sheet: { - nsIWidget* parentWidget = windowWidget->GetSheetWindowParent(); - if (!parentWidget) return YES; - - topLevelWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW); - break; - } - default: return YES; } diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index 621c32eb44..96e117ba26 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -12,7 +12,6 @@ #include "mozilla/RefPtr.h" #include "nsBaseWidget.h" -#include "nsPIWidgetCocoa.h" #include "nsCocoaUtils.h" #include "nsTouchBar.h" #include <dlfcn.h> @@ -27,12 +26,6 @@ namespace mozilla { enum class NativeKeyBindingsType : uint8_t; } // namespace mozilla -typedef struct _nsCocoaWindowList { - _nsCocoaWindowList() : prev(nullptr), window(nullptr) {} - struct _nsCocoaWindowList* prev; - nsCocoaWindow* window; // Weak -} nsCocoaWindowList; - // 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 @@ -169,102 +162,75 @@ typedef struct _nsCocoaWindowList { - (void)sendToplevelDeactivateEvents; @end -@interface MOZTitlebarView : NSVisualEffectView -@end - @interface FullscreenTitlebarTracker : NSTitlebarAccessoryViewController - (FullscreenTitlebarTracker*)init; @end // NSWindow subclass for handling windows with toolbars. @interface ToolbarWindow : BaseWindow { - // This window's titlebar view, if present. - // Will be nil if the window has neither a titlebar nor a unified toolbar. - // This view is a subview of the window's content view and gets created and - // destroyed by updateTitlebarView. - MOZTitlebarView* mTitlebarView; // [STRONG] // 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 mUnifiedToolbarHeight; - CGFloat mSheetAttachmentPosition; CGFloat mMenuBarHeight; - /* Store the height of the titlebar when this window is initialized. The - titlebarHeight getter returns 0 when in fullscreen, which is not useful in - some cases. */ - CGFloat mInitialTitlebarHeight; NSRect mWindowButtonsRect; } -- (void)setUnifiedToolbarHeight:(CGFloat)aHeight; -- (CGFloat)unifiedToolbarHeight; -- (CGFloat)titlebarHeight; -- (NSRect)titlebarRect; -- (void)setTitlebarNeedsDisplay; - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState; -- (void)setSheetAttachmentPosition:(CGFloat)aY; -- (CGFloat)sheetAttachmentPosition; - (void)placeWindowButtons:(NSRect)aRect; - (NSRect)windowButtonsRect; - (void)windowMainStateChanged; @end -class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { +class nsCocoaWindow final : public nsBaseWidget { private: typedef nsBaseWidget Inherited; public: nsCocoaWindow(); - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSPIWIDGETCOCOA; // semicolon for clang-format bug 1629756 - - [[nodiscard]] virtual nsresult Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const DesktopIntRect& aRect, - InitData* = nullptr) override; - - [[nodiscard]] virtual nsresult Create(nsIWidget* aParent, - nsNativeWidget aNativeParent, - const LayoutDeviceIntRect& aRect, - InitData* = nullptr) override; - - virtual void Destroy() override; - - virtual void Show(bool aState) override; - virtual bool NeedsRecreateToReshow() override; - - virtual nsIWidget* GetSheetWindowParent(void) override; - virtual void Enable(bool aState) override; - virtual bool IsEnabled() const override; - virtual void SetModal(bool aState) override; - virtual void SetFakeModal(bool aState) override; - virtual bool IsRunningAppModal() override; - virtual bool IsVisible() const override; - virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; - virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; - virtual LayoutDeviceIntPoint GetClientOffset() override; - virtual LayoutDeviceIntMargin ClientToWindowMargin() override; - - virtual void* GetNativeData(uint32_t aDataType) override; - - virtual void ConstrainPosition(DesktopIntPoint&) override; - virtual void SetSizeConstraints(const SizeConstraints& aConstraints) override; - virtual void Move(double aX, double aY) override; - virtual nsSizeMode SizeMode() override { return mSizeMode; } - virtual void SetSizeMode(nsSizeMode aMode) override; - virtual void GetWorkspaceID(nsAString& workspaceID) override; - virtual void MoveToWorkspace(const nsAString& workspaceID) override; - virtual void SuppressAnimation(bool aSuppress) override; - virtual void HideWindowChrome(bool aShouldHide) override; - - virtual bool PrepareForFullscreenTransition(nsISupports** aData) override; - virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, - uint16_t aDuration, - nsISupports* aData, - nsIRunnable* aCallback) override; - virtual void CleanupFullscreenTransition() override; + [[nodiscard]] nsresult Create(nsIWidget* aParent, + nsNativeWidget aNativeParent, + const DesktopIntRect& aRect, + InitData* = nullptr) override; + + [[nodiscard]] nsresult Create(nsIWidget* aParent, + nsNativeWidget aNativeParent, + 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 ClientToWindowMargin() override; + + 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; + + 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 { @@ -277,79 +243,79 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { mFullscreenTransitionAnimation = nil; } - virtual void Resize(double aWidth, double aHeight, bool aRepaint) override; - virtual void Resize(double aX, double aY, double aWidth, double aHeight, - bool aRepaint) override; + void Resize(double aWidth, double aHeight, bool aRepaint) override; + void Resize(double aX, double aY, double aWidth, double aHeight, + bool aRepaint) override; NSRect GetClientCocoaRect(); - virtual LayoutDeviceIntRect GetClientBounds() override; - virtual LayoutDeviceIntRect GetScreenBounds() override; + LayoutDeviceIntRect GetClientBounds() override; + LayoutDeviceIntRect GetScreenBounds() override; void ReportMoveEvent(); void ReportSizeEvent(); - virtual void SetCursor(const Cursor&) override; + void SetCursor(const Cursor&) override; CGFloat BackingScaleFactor(); void BackingScaleFactorChanged(); - virtual double GetDefaultScaleInternal() override; - virtual int32_t RoundsWidgetCoordinatesTo() override; + double GetDefaultScaleInternal() override; + int32_t RoundsWidgetCoordinatesTo() override; mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() final { return mozilla::DesktopToLayoutDeviceScale(BackingScaleFactor()); } - virtual nsresult SetTitle(const nsAString& aTitle) override; - - virtual void Invalidate(const LayoutDeviceIntRect& aRect) override; - virtual WindowRenderer* GetWindowRenderer() override; - virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, - nsEventStatus& aStatus) override; - virtual void CaptureRollupEvents(bool aDoCapture) override; - [[nodiscard]] virtual nsresult GetAttention(int32_t aCycleCount) override; - virtual bool HasPendingInputEvent() override; - virtual TransparencyMode GetTransparencyMode() override; - virtual void SetTransparencyMode(TransparencyMode aMode) override; - virtual void SetWindowShadowStyle(mozilla::WindowShadow aStyle) override; - virtual void SetWindowOpacity(float aOpacity) override; - virtual void SetWindowTransform( - const mozilla::gfx::Matrix& aTransform) override; - virtual void SetInputRegion(const InputRegion&) override; - virtual void SetColorScheme( - const mozilla::Maybe<mozilla::ColorScheme>&) override; - virtual void SetShowsToolbarButton(bool aShow) override; - virtual void SetSupportsNativeFullscreen(bool aShow) override; - virtual void SetWindowAnimationType(WindowAnimationType aType) override; - virtual void SetDrawsTitle(bool aDrawTitle) override; - virtual nsresult SetNonClientMargins(const LayoutDeviceIntMargin&) override; + nsresult SetTitle(const nsAString& aTitle) override; + + void Invalidate(const LayoutDeviceIntRect& aRect) override; + WindowRenderer* GetWindowRenderer() override; + nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, + nsEventStatus& aStatus) override; + void CaptureRollupEvents(bool aDoCapture) override; + [[nodiscard]] nsresult GetAttention(int32_t aCycleCount) override; + bool HasPendingInputEvent() 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<mozilla::ColorScheme>&) override; + void SetShowsToolbarButton(bool aShow) override; + void SetSupportsNativeFullscreen(bool aShow) override; + void SetWindowAnimationType(WindowAnimationType aType) override; + void SetDrawsTitle(bool aDrawTitle) override; + nsresult SetNonClientMargins(const LayoutDeviceIntMargin&) override; void SetDrawsInTitlebar(bool aState); - virtual void UpdateThemeGeometries( + void UpdateThemeGeometries( const nsTArray<ThemeGeometry>& aThemeGeometries) override; - virtual nsresult SynthesizeNativeMouseEvent( - LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage, - mozilla::MouseButton aButton, nsIWidget::Modifiers aModifierFlags, - nsIObserver* aObserver) override; - virtual nsresult SynthesizeNativeMouseScrollEvent( + nsresult SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint, + NativeMouseMessage aNativeMessage, + mozilla::MouseButton aButton, + nsIWidget::Modifiers aModifierFlags, + nsIObserver* aObserver) override; + nsresult SynthesizeNativeMouseScrollEvent( LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags, uint32_t aAdditionalFlags, nsIObserver* aObserver) override; - virtual void LockAspectRatio(bool aShouldLock) override; + void LockAspectRatio(bool aShouldLock) override; void DispatchSizeModeEvent(); void DispatchOcclusionEvent(); // be notified that a some form of drag event needs to go into Gecko - virtual bool DragEvent(unsigned int aMessage, - mozilla::gfx::Point aMouseGlobal, - UInt16 aKeyModifiers); + bool DragEvent(unsigned int aMessage, mozilla::gfx::Point aMouseGlobal, + UInt16 aKeyModifiers); + + bool HasModalDescendants() const { return mNumModalDescendants > 0; } + bool IsModal() const { return mModal; } - bool HasModalDescendents() { return mNumModalDescendents > 0; } NSWindow* GetCocoaWindow() { return mWindow; } void SetMenuBar(RefPtr<nsMenuBarX>&& aMenuBar); nsMenuBarX* GetMenuBar(); - virtual void SetInputContext(const InputContext& aContext, - const InputContextAction& aAction) override; - virtual InputContext GetInputContext() override { return mInputContext; } - MOZ_CAN_RUN_SCRIPT virtual bool GetEditCommands( + void SetInputContext(const InputContext& aContext, + const InputContextAction& aAction) override; + InputContext GetInputContext() override { return mInputContext; } + MOZ_CAN_RUN_SCRIPT bool GetEditCommands( mozilla::NativeKeyBindingsType aType, const mozilla::WidgetKeyboardEvent& aEvent, nsTArray<mozilla::CommandInt>& aCommands) override; @@ -400,6 +366,7 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { void DestroyNativeWindow(); void UpdateBounds(); int32_t GetWorkspaceID(); + void SendSetZLevelEvent(); void DoResize(double aX, double aY, double aWidth, double aHeight, bool aRepaint, bool aConstrainToCurrentScreen); @@ -407,7 +374,7 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { void UpdateFullscreenState(bool aFullScreen, bool aNativeMode); nsresult DoMakeFullScreen(bool aFullScreen, bool aUseSystemTransition); - virtual already_AddRefed<nsIWidget> AllocateChildPopupWidget() override { + already_AddRefed<nsIWidget> AllocateChildPopupWidget() override { return nsIWidget::CreateTopLevelWindow(); } @@ -417,8 +384,6 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { WindowDelegate* mDelegate; // our delegate for processing window msgs [STRONG] RefPtr<nsMenuBarX> mMenuBar; - NSWindow* mSheetWindowParent; // if this is a sheet, this is the NSWindow - // it's attached to nsChildView* mPopupContentView; // if this is a popup, this is its content widget // if this is a toplevel window, and there is any ongoing fullscreen @@ -432,8 +397,6 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { WindowAnimationType mAnimationType; bool mWindowMadeHere; // true if we created the window, false for embedding - bool mSheetNeedsShow; // if this is a sheet, are we waiting to be shown? - // this is used for sibling sheet contention only nsSizeMode mSizeMode; bool mInFullScreenMode; // Whether we are currently using native fullscreen. It could be false because @@ -482,19 +445,18 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { // unnecessary OcclusionStateChanged events. bool mHasStartedNativeFullscreen; - bool mModal; - bool mFakeModal; - - bool mIsAnimationSuppressed; + bool mModal = false; + bool mIsAnimationSuppressed = false; - bool mInReportMoveEvent; // true if in a call to ReportMoveEvent(). - bool mInResize; // true if in a call to DoResize(). - bool mWindowTransformIsIdentity; - bool mAlwaysOnTop; - bool mAspectRatioLocked; + 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 mNumModalDescendents; + int32_t mNumModalDescendants = 0; InputContext mInputContext; NSWindowAnimationBehavior mWindowAnimationBehavior; @@ -508,9 +470,6 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { // to EndOurNativeTransition() when the native transition is complete. bool CanStartNativeTransition(); void EndOurNativeTransition(); - - // true if Show() has been called. - bool mWasShown; }; #endif // nsCocoaWindow_h_ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 4b135e7565..8e54d9e7fd 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -66,15 +66,6 @@ using namespace mozilla::layers; using namespace mozilla::widget; using namespace mozilla; -int32_t gXULModalLevel = 0; - -// In principle there should be only one app-modal window at any given time. -// But sometimes, despite our best efforts, another window appears above the -// current app-modal window. So we need to keep a linked list of app-modal -// windows. (A non-sheet window that appears above an app-modal window is -// also made app-modal.) See nsCocoaWindow::SetModal(). -nsCocoaWindowList* gGeckoAppModalWindowList = NULL; - BOOL sTouchBarIsInitialized = NO; // defined in nsMenuBarX.mm @@ -83,6 +74,8 @@ extern NSMenu* sApplicationMenu; // Application menu shared by all menubars // defined in nsChildView.mm extern BOOL gSomeMenuBarPainted; +static uint32_t sModalWindowCount = 0; + extern "C" { // CGSPrivate.h typedef NSInteger CGSConnection; @@ -105,13 +98,6 @@ extern CGError CGSSetWindowTransform(CGSConnection cid, CGSWindow wid, #define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1" -NS_IMPL_ISUPPORTS_INHERITED(nsCocoaWindow, Inherited, nsPIWidgetCocoa) - -// A note on testing to see if your object is a sheet... -// |mWindowType == WindowType::Sheet| is true if your gecko nsIWidget is a sheet -// widget - whether or not the sheet is showing. |[mWindow isSheet]| will return -// true *only when the sheet is actually showing*. Choose your test wisely. - static void RollUpPopups(nsIRollupListener::AllowAnimations aAllowAnimations = nsIRollupListener::AllowAnimations::Yes) { if (RefPtr pm = nsXULPopupManager::GetInstance()) { @@ -139,33 +125,21 @@ nsCocoaWindow::nsCocoaWindow() mAncestorLink(nullptr), mWindow(nil), mDelegate(nil), - mSheetWindowParent(nil), mPopupContentView(nil), mFullscreenTransitionAnimation(nil), mShadowStyle(WindowShadow::None), mBackingScaleFactor(0.0), mAnimationType(nsIWidget::eGenericWindowAnimation), mWindowMadeHere(false), - mSheetNeedsShow(false), mSizeMode(nsSizeMode_Normal), mInFullScreenMode(false), mInNativeFullScreenMode(false), mIgnoreOcclusionCount(0), mHasStartedNativeFullscreen(false), - mModal(false), - mFakeModal(false), - mIsAnimationSuppressed(false), - mInReportMoveEvent(false), - mInResize(false), - mWindowTransformIsIdentity(true), - mAlwaysOnTop(false), - mAspectRatioLocked(false), - mNumModalDescendents(0), - mWindowAnimationBehavior(NSWindowAnimationBehaviorDefault), - mWasShown(false) { + mWindowAnimationBehavior(NSWindowAnimationBehaviorDefault) { // Disable automatic tabbing. We need to do this before we // orderFront any of our windows. - [NSWindow setAllowsAutomaticWindowTabbing:NO]; + NSWindow.allowsAutomaticWindowTabbing = NO; } void nsCocoaWindow::DestroyNativeWindow() { @@ -223,14 +197,6 @@ nsCocoaWindow::~nsCocoaWindow() { } NS_IF_RELEASE(mPopupContentView); - - // Deal with the possiblity that we're being destroyed while running modal. - if (mModal) { - NS_WARNING("Widget destroyed while running modal!"); - --gXULModalLevel; - NS_ASSERTION(gXULModalLevel >= 0, "Weirdness setting modality!"); - } - NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -432,15 +398,6 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect& aRect, case WindowType::Dialog: features = WindowMaskForBorderStyle(aBorderStyle); break; - case WindowType::Sheet: - if (mParent->GetWindowType() != WindowType::Invisible && - aBorderStyle & BorderStyle::ResizeH) { - features = NSWindowStyleMaskResizable; - } else { - features = NSWindowStyleMaskMiniaturizable; - } - features |= NSWindowStyleMaskTitled; - break; default: NS_ERROR("Unhandled window type!"); return NS_ERROR_FAILURE; @@ -607,14 +564,14 @@ nsresult nsCocoaWindow::CreatePopupContentView(const LayoutDeviceIntRect& aRect, } void nsCocoaWindow::Destroy() { - if (mOnDestroyCalled) return; + if (mOnDestroyCalled) { + return; + } mOnDestroyCalled = true; - // SetFakeModal(true) is called for non-modal window opened by modal window. - // On Cocoa, it needs corresponding SetFakeModal(false) on destroy to restore - // ancestor windows' state. - if (mFakeModal) { - SetFakeModal(false); + // Deal with the possiblity that we're being destroyed while running modal. + if (mModal) { + SetModal(false); } // If we don't hide here we run into problems with panels, this is not ideal. @@ -651,14 +608,6 @@ void nsCocoaWindow::Destroy() { } } -nsIWidget* nsCocoaWindow::GetSheetWindowParent(void) { - if (mWindowType != WindowType::Sheet) return nullptr; - nsCocoaWindow* parent = static_cast<nsCocoaWindow*>(mParent); - while (parent && (parent->mWindowType == WindowType::Sheet)) - parent = static_cast<nsCocoaWindow*>(parent->mParent); - return parent; -} - void* nsCocoaWindow::GetNativeData(uint32_t aDataType) { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; @@ -708,14 +657,18 @@ void* nsCocoaWindow::GetNativeData(uint32_t aDataType) { bool nsCocoaWindow::IsVisible() const { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - return (mWindow && ([mWindow isVisibleOrBeingShown] || mSheetNeedsShow)); + return mWindow && mWindow.isVisibleOrBeingShown; NS_OBJC_END_TRY_BLOCK_RETURN(false); } -void nsCocoaWindow::SetModal(bool aState) { +void nsCocoaWindow::SetModal(bool aModal) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; + if (mModal == aModal) { + return; + } + // Unlike many functions here, we explicitly *do not check* for the // existence of mWindow. This is to ensure that calls to SetModal have // no early exits and always update state. That way, if the calls are @@ -732,80 +685,47 @@ void nsCocoaWindow::SetModal(bool aState) { // objects leaking. nsAutoreleasePool localPool; - mModal = aState; - nsCocoaWindow* ancestor = static_cast<nsCocoaWindow*>(mAncestorLink); - if (aState) { - ++gXULModalLevel; - // When a non-sheet window gets "set modal", make the window(s) that it - // appears over behave as they should. We can't rely on native methods to - // do this, for the following reason: The OS runs modal non-sheet windows - // in an event loop (using [NSApplication runModalForWindow:] or similar - // methods) that's incompatible with the modal event loop in AppWindow:: - // ShowModal() (each of these event loops is "exclusive", and can't run at - // the same time as other (similar) event loops). - if (mWindowType != WindowType::Sheet) { - while (ancestor) { - if (ancestor->mNumModalDescendents++ == 0) { - NSWindow* aWindow = ancestor->GetCocoaWindow(); - if (ancestor->mWindowType != WindowType::Invisible) { - [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:NO]; - [[aWindow standardWindowButton:NSWindowMiniaturizeButton] - setEnabled:NO]; - [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:NO]; - } - } - ancestor = static_cast<nsCocoaWindow*>(ancestor->mParent); - } - [mWindow setLevel:NSModalPanelWindowLevel]; - nsCocoaWindowList* windowList = new nsCocoaWindowList; - if (windowList) { - windowList->window = this; // Don't ADDREF - windowList->prev = gGeckoAppModalWindowList; - gGeckoAppModalWindowList = windowList; - } - } + mModal = aModal; + + if (aModal) { + sModalWindowCount++; } else { - --gXULModalLevel; - NS_ASSERTION(gXULModalLevel >= 0, - "Mismatched call to nsCocoaWindow::SetModal(false)!"); - if (mWindowType != WindowType::Sheet) { - while (ancestor) { - if (--ancestor->mNumModalDescendents == 0) { - NSWindow* aWindow = ancestor->GetCocoaWindow(); - if (ancestor->mWindowType != WindowType::Invisible) { - [[aWindow standardWindowButton:NSWindowCloseButton] setEnabled:YES]; - [[aWindow standardWindowButton:NSWindowMiniaturizeButton] - setEnabled:YES]; - [[aWindow standardWindowButton:NSWindowZoomButton] setEnabled:YES]; - } - } - NS_ASSERTION(ancestor->mNumModalDescendents >= 0, - "Widget hierarchy changed while modal!"); - ancestor = static_cast<nsCocoaWindow*>(ancestor->mParent); - } - if (gGeckoAppModalWindowList) { - NS_ASSERTION(gGeckoAppModalWindowList->window == this, - "Widget hierarchy changed while modal!"); - nsCocoaWindowList* saved = gGeckoAppModalWindowList; - gGeckoAppModalWindowList = gGeckoAppModalWindowList->prev; - delete saved; // "window" not ADDREFed - } - if (mWindowType == WindowType::Popup) { - SetPopupWindowLevel(); - } else { - mWindow.level = NSNormalWindowLevel; - } + MOZ_ASSERT(sModalWindowCount); + sModalWindowCount--; + } + + // When a window gets "set modal", make the window(s) that it appears over + // behave as they should. We can't rely on native methods to do this, for the + // following reason: The OS runs modal non-sheet windows in an event loop + // (using [NSApplication runModalForWindow:] or similar methods) that's + // incompatible with the modal event loop in AppWindow::ShowModal() (each of + // these event loops is "exclusive", and can't run at the same time as other + // (similar) event loops). + for (auto* ancestor = static_cast<nsCocoaWindow*>(mAncestorLink); ancestor; + ancestor = static_cast<nsCocoaWindow*>(ancestor->mParent)) { + const bool changed = aModal ? ancestor->mNumModalDescendants++ == 0 + : --ancestor->mNumModalDescendants == 0; + NS_ASSERTION(ancestor->mNumModalDescendants >= 0, + "Widget hierarchy changed while modal!"); + if (!changed || ancestor->mWindowType == WindowType::Invisible) { + continue; } + NSWindow* win = ancestor->GetCocoaWindow(); + [[win standardWindowButton:NSWindowCloseButton] setEnabled:!aModal]; + [[win standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!aModal]; + [[win standardWindowButton:NSWindowZoomButton] setEnabled:!aModal]; + } + if (aModal) { + mWindow.level = NSModalPanelWindowLevel; + } else if (mWindowType == WindowType::Popup) { + SetPopupWindowLevel(); + } else { + mWindow.level = NSNormalWindowLevel; } NS_OBJC_END_TRY_IGNORE_BLOCK; } -void nsCocoaWindow::SetFakeModal(bool aState) { - mFakeModal = aState; - SetModal(aState); -} - bool nsCocoaWindow::IsRunningAppModal() { return [NSApp _isRunningAppModal]; } // Hide or show this window @@ -816,12 +736,10 @@ void nsCocoaWindow::Show(bool aState) { return; } - if (!mSheetNeedsShow) { - // Early exit if our current visibility state is already the requested - // state. - if (aState == mWindow.isVisibleOrBeingShown) { - return; - } + // Early exit if our current visibility state is already the requested + // state. + if (aState == mWindow.isVisibleOrBeingShown) { + return; } [mWindow setBeingShown:aState]; @@ -829,11 +747,8 @@ void nsCocoaWindow::Show(bool aState) { mWasShown = true; } - nsIWidget* parentWidget = mParent; - nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget)); NSWindow* nativeParentWindow = - parentWidget ? (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) - : nil; + mParent ? (NSWindow*)mParent->GetNativeData(NS_NATIVE_WINDOW) : nil; if (aState && !mBounds.IsEmpty()) { // If we had set the activationPolicy to accessory, then right now we won't @@ -856,82 +771,7 @@ void nsCocoaWindow::Show(bool aState) { mPopupContentView->Show(true); } - if (mWindowType == WindowType::Sheet) { - // bail if no parent window (its basically what we do in Carbon) - if (!nativeParentWindow || !piParentWidget) return; - - NSWindow* topNonSheetWindow = nativeParentWindow; - - // If this sheet is the child of another sheet, hide the parent so that - // this sheet can be displayed. Leave the parent mSheetNeedsShow alone, - // that is only used to handle sibling sheet contention. The parent will - // return once there are no more child sheets. - bool parentIsSheet = false; - if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) && - parentIsSheet) { - piParentWidget->GetSheetWindowParent(&topNonSheetWindow); -#ifdef MOZ_THUNDERBIRD - [NSApp endSheet:nativeParentWindow]; -#else - [nativeParentWindow.sheetParent endSheet:nativeParentWindow]; -#endif - } - - nsCOMPtr<nsIWidget> sheetShown; - if (NS_SUCCEEDED(piParentWidget->GetChildSheet( - true, getter_AddRefs(sheetShown))) && - (!sheetShown || sheetShown == this)) { - // If this sheet is already the sheet actually being shown, don't - // tell it to show again. Otherwise the number of calls to -#ifdef MOZ_THUNDERBIRD - // [NSApp beginSheet...] won't match up with [NSApp endSheet...]. -#else - // [NSWindow beginSheet...] won't match up with [NSWindow endSheet...]. -#endif - if (![mWindow isVisible]) { - mSheetNeedsShow = false; - mSheetWindowParent = topNonSheetWindow; -#ifdef MOZ_THUNDERBIRD - // Only set contextInfo if our parent isn't a sheet. - NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent; - [TopLevelWindowData deactivateInWindow:mSheetWindowParent]; - [NSApp beginSheet:mWindow - modalForWindow:mSheetWindowParent - modalDelegate:mDelegate - didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) - contextInfo:contextInfo]; -#else - NSWindow* sheet = mWindow; - NSWindow* nonSheetParent = parentIsSheet ? nil : mSheetWindowParent; - [TopLevelWindowData deactivateInWindow:mSheetWindowParent]; - [mSheetWindowParent beginSheet:sheet - completionHandler:^(NSModalResponse returnCode) { - // Note: 'nonSheetParent' (if it is set) is the window - // that is the parent of the sheet. If it's set, - // 'nonSheetParent' is always the top- level window, - // not another sheet itself. But 'nonSheetParent' is - // nil if our parent window is also a sheet -- in that - // case we shouldn't send the top-level window any - // activate events (because it's our parent window that - // needs to get these events, not the top-level - // window). - [TopLevelWindowData deactivateInWindow:sheet]; - [sheet orderOut:nil]; - if (nonSheetParent) { - [TopLevelWindowData activateInWindow:nonSheetParent]; - } - }]; -#endif - [TopLevelWindowData activateInWindow:mWindow]; - SendSetZLevelEvent(); - } - } else { - // A sibling of this sheet is active, don't show this sheet yet. - // When the active sheet hides, its brothers and sisters that have - // mSheetNeedsShow set will have their opportunities to display. - mSheetNeedsShow = true; - } - } else if (mWindowType == WindowType::Popup) { + if (mWindowType == WindowType::Popup) { // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000) // creating CGSWindow", which in turn triggers an internal inconsistency @@ -939,7 +779,9 @@ void nsCocoaWindow::Show(bool aState) { // calls to ...orderFront: in TRY blocks. See bmo bug 470864. NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; [[mWindow contentView] setNeedsDisplay:YES]; - [mWindow orderFront:nil]; + if (!nativeParentWindow || mPopupLevel != PopupLevel::Parent) { + [mWindow orderFront:nil]; + } NS_OBJC_END_TRY_IGNORE_BLOCK; SendSetZLevelEvent(); // If our popup window is a non-native context menu, tell the OS (and @@ -955,9 +797,7 @@ void nsCocoaWindow::Show(bool aState) { // If a parent window was supplied and this is a popup at the parent // level, set its child window. This will cause the child window to - // appear above the parent and move when the parent does. Setting this - // needs to happen after the _setWindowNumber calls above, otherwise the - // window doesn't focus properly. + // appear above the parent and move when the parent does. if (nativeParentWindow && mPopupLevel == PopupLevel::Parent) { [nativeParentWindow addChildWindow:mWindow ordered:NSWindowAbove]; } @@ -1001,120 +841,22 @@ void nsCocoaWindow::Show(bool aState) { RollUpPopups(); } - // now get rid of the window/sheet - if (mWindowType == WindowType::Sheet) { - if (mSheetNeedsShow) { - // This is an attempt to hide a sheet that never had a chance to - // be shown. There's nothing to do other than make sure that it - // won't show. - mSheetNeedsShow = false; - } else { - // get sheet's parent *before* hiding the sheet (which breaks the - // linkage) - NSWindow* sheetParent = mSheetWindowParent; - - // hide the sheet -#ifdef MOZ_THUNDERBIRD - [NSApp endSheet:mWindow]; -#else - [mSheetWindowParent endSheet:mWindow]; -#endif - [TopLevelWindowData deactivateInWindow:mWindow]; - - nsCOMPtr<nsIWidget> siblingSheetToShow; - bool parentIsSheet = false; - - if (nativeParentWindow && piParentWidget && - NS_SUCCEEDED(piParentWidget->GetChildSheet( - false, getter_AddRefs(siblingSheetToShow))) && - siblingSheetToShow) { - // First, give sibling sheets an opportunity to show. - siblingSheetToShow->Show(true); - } else if (nativeParentWindow && piParentWidget && - NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) && - parentIsSheet) { -#ifdef MOZ_THUNDERBIRD - // Only set contextInfo if the parent of the parent sheet we're about - // to restore isn't itself a sheet. - NSWindow* contextInfo = sheetParent; -#else - // Only set nonSheetGrandparent if the parent of the parent sheet - // we're about to restore isn't itself a sheet. - NSWindow* nonSheetGrandparent = sheetParent; -#endif - nsIWidget* grandparentWidget = nil; - if (NS_SUCCEEDED(piParentWidget->GetRealParent(&grandparentWidget)) && - grandparentWidget) { - nsCOMPtr<nsPIWidgetCocoa> piGrandparentWidget( - do_QueryInterface(grandparentWidget)); - bool grandparentIsSheet = false; - if (piGrandparentWidget && - NS_SUCCEEDED( - piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) && - grandparentIsSheet) { -#ifdef MOZ_THUNDERBIRD - contextInfo = nil; -#else - nonSheetGrandparent = nil; -#endif - } - } - // If there are no sibling sheets, but the parent is a sheet, restore - // it. It wasn't sent any deactivate events when it was hidden, so - // don't call through Show, just let the OS put it back up. -#ifdef MOZ_THUNDERBIRD - [NSApp beginSheet:nativeParentWindow - modalForWindow:sheetParent - modalDelegate:[nativeParentWindow delegate] - didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) - contextInfo:contextInfo]; -#else - [nativeParentWindow - beginSheet:sheetParent - completionHandler:^(NSModalResponse returnCode) { - // Note: 'nonSheetGrandparent' (if it is set) is the window that - // is the parent of sheetParent. If it's set, - // 'nonSheetGrandparent' is always the top-level window, not - // another sheet itself. But 'nonSheetGrandparent' is nil if - // our parent window is also a sheet -- in that case we - // shouldn't send the top-level window any activate events - // (because it's our parent window that needs to get these - // events, not the top-level window). - [TopLevelWindowData deactivateInWindow:sheetParent]; - [sheetParent orderOut:nil]; - if (nonSheetGrandparent) { - [TopLevelWindowData activateInWindow:nonSheetGrandparent]; - } - }]; -#endif - } else { - // Sheet, that was hard. No more siblings or parents, going back - // to a real window. - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - [sheetParent makeKeyAndOrderFront:nil]; - NS_OBJC_END_TRY_IGNORE_BLOCK; - } - SendSetZLevelEvent(); - } - } else { - // If the window is a popup window with a parent window we need to - // unhook it here before ordering it out. When you order out the child - // of a window it hides the parent window. - if (mWindowType == WindowType::Popup && nativeParentWindow) { - [nativeParentWindow removeChildWindow:mWindow]; - } - - [mWindow orderOut:nil]; + // If the window is a popup window with a parent window we need to + // unhook it here before ordering it out. When you order out the child + // of a window it hides the parent window. + if (mWindowType == WindowType::Popup && nativeParentWindow) { + [nativeParentWindow removeChildWindow:mWindow]; + } - // If our popup window is a non-native context menu, tell the OS (and - // other programs) that a menu has closed. - if ([mWindow isKindOfClass:[PopupWindow class]] && - [(PopupWindow*)mWindow isContextMenu]) { - [[NSDistributedNotificationCenter defaultCenter] - postNotificationName: - @"com.apple.HIToolbox.endMenuTrackingNotification" - object:@"org.mozilla.gecko.PopupWindow"]; - } + [mWindow orderOut:nil]; + // If our popup window is a non-native context menu, tell the OS (and + // other programs) that a menu has closed. + if ([mWindow isKindOfClass:[PopupWindow class]] && + [(PopupWindow*)mWindow isContextMenu]) { + [NSDistributedNotificationCenter.defaultCenter + postNotificationName: + @"com.apple.HIToolbox.endMenuTrackingNotification" + object:@"org.mozilla.gecko.PopupWindow"]; } } @@ -2287,53 +2029,13 @@ bool nsCocoaWindow::DragEvent(unsigned int aMessage, return false; } -NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent() { - nsWindowZ placement = nsWindowZTop; - nsCOMPtr<nsIWidget> actualBelow; +void nsCocoaWindow::SendSetZLevelEvent() { if (mWidgetListener) { + nsWindowZ placement = nsWindowZTop; + nsCOMPtr<nsIWidget> actualBelow; mWidgetListener->ZLevelChanged(true, &placement, nullptr, getter_AddRefs(actualBelow)); } - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetChildSheet(bool aShown, nsIWidget** _retval) { - nsIWidget* child = GetFirstChild(); - - while (child) { - if (child->GetWindowType() == WindowType::Sheet) { - // if it's a sheet, it must be an nsCocoaWindow - nsCocoaWindow* cocoaWindow = static_cast<nsCocoaWindow*>(child); - if (cocoaWindow->mWindow && - ((aShown && [cocoaWindow->mWindow isVisible]) || - (!aShown && cocoaWindow->mSheetNeedsShow))) { - nsCOMPtr<nsIWidget> widget = cocoaWindow; - widget.forget(_retval); - return NS_OK; - } - } - child = child->GetNextSibling(); - } - - *_retval = nullptr; - - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetRealParent(nsIWidget** parent) { - *parent = mParent; - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetIsSheet(bool* isSheet) { - mWindowType == WindowType::Sheet ? * isSheet = true : * isSheet = false; - return NS_OK; -} - -NS_IMETHODIMP nsCocoaWindow::GetSheetWindowParent( - NSWindow** sheetWindowParent) { - *sheetWindowParent = mSheetWindowParent; - return NS_OK; } // Invokes callback and ProcessEvent methods on Event Listener object @@ -3184,9 +2886,7 @@ void nsCocoaWindow::CocoaWindowDidResize() { ChildViewMouseTracker::ReEvaluateMouseEnterState(); NSWindow* window = [aNotification object]; - if ([window isSheet]) [WindowDelegate paintMenubarForWindow:window]; - - nsChildView* mainChildView = + auto* mainChildView = static_cast<nsChildView*>([[(BaseWindow*)window mainChildView] widget]); if (mainChildView) { if (mainChildView->GetInputContext().IsPasswordEditor()) { @@ -3205,13 +2905,6 @@ void nsCocoaWindow::CocoaWindowDidResize() { RollUpPopups(nsIRollupListener::AllowAnimations::No); ChildViewMouseTracker::ReEvaluateMouseEnterState(); - - // If a sheet just resigned key then we should paint the menu bar - // for whatever window is now main. - NSWindow* window = [aNotification object]; - if ([window isSheet]) - [WindowDelegate paintMenubarForWindow:[NSApp mainWindow]]; - TextInputHandler::EnsureSecureEventInputDisabled(); NS_OBJC_END_TRY_IGNORE_BLOCK; @@ -3264,36 +2957,6 @@ void nsCocoaWindow::CocoaWindowDidResize() { return YES; } -- (NSRect)window:(NSWindow*)window - willPositionSheet:(NSWindow*)sheet - usingRect:(NSRect)rect { - if ([window isKindOfClass:[ToolbarWindow class]]) { - rect.origin.y = [(ToolbarWindow*)window sheetAttachmentPosition]; - } - return rect; -} - -#ifdef MOZ_THUNDERBIRD -- (void)didEndSheet:(NSWindow*)sheet - returnCode:(int)returnCode - contextInfo:(void*)contextInfo { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // Note: 'contextInfo' (if it is set) is the window that is the parent of - // the sheet. The value of contextInfo is determined in - // nsCocoaWindow::Show(). If it's set, 'contextInfo' is always the top- - // level window, not another sheet itself. But 'contextInfo' is nil if - // our parent window is also a sheet -- in that case we shouldn't send - // the top-level window any activate events (because it's our parent - // window that needs to get these events, not the top-level window). - [TopLevelWindowData deactivateInWindow:sheet]; - [sheet orderOut:self]; - if (contextInfo) [TopLevelWindowData activateInWindow:(NSWindow*)contextInfo]; - - NS_OBJC_END_TRY_ABORT_BLOCK; -} -#endif - - (void)windowDidChangeBackingProperties:(NSNotification*)aNotification { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; @@ -3354,7 +3017,7 @@ void nsCocoaWindow::CocoaWindowDidResize() { return self.FrameView__closeButtonOrigin; } auto* win = static_cast<ToolbarWindow*>(self.window); - if (win.drawsContentsIntoWindowFrame && + if (win.drawsContentsIntoWindowFrame && !win.wantsTitleDrawn && !(win.styleMask & NSWindowStyleMaskFullScreen) && (win.styleMask & NSWindowStyleMaskTitled)) { const NSRect buttonsRect = win.windowButtonsRect; @@ -3624,7 +3287,7 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; } - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState { - bool changed = (aState != mDrawsIntoWindowFrame); + bool changed = aState != mDrawsIntoWindowFrame; mDrawsIntoWindowFrame = aState; if (changed) { [self reflowTitlebarElements]; @@ -3826,53 +3489,6 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; @end -@interface NSView (NSThemeFrame) -- (void)_drawTitleStringInClip:(NSRect)aRect; -- (void)_maskCorners:(NSUInteger)aFlags clipRect:(NSRect)aRect; -@end - -@implementation MOZTitlebarView - -- (instancetype)initWithFrame:(NSRect)aFrame { - self = [super initWithFrame:aFrame]; - - self.material = NSVisualEffectMaterialTitlebar; - self.blendingMode = NSVisualEffectBlendingModeWithinWindow; - - // Add a separator line at the bottom of the titlebar. NSBoxSeparator isn't a - // perfect match for a native titlebar separator, but it's better than - // nothing. We really want the appearance that _NSTitlebarDecorationView - // creates with the help of CoreUI, but there's no public API for that. - NSBox* separatorLine = - [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, aFrame.size.width, 1)]; - separatorLine.autoresizingMask = NSViewWidthSizable | NSViewMaxYMargin; - separatorLine.boxType = NSBoxSeparator; - [self addSubview:separatorLine]; - [separatorLine release]; - - return self; -} - -- (BOOL)mouseDownCanMoveWindow { - return YES; -} - -- (void)mouseUp:(NSEvent*)event { - if (event.clickCount == 2) { - // Handle titlebar double click. We don't get the window's default behavior - // here because the window uses NSWindowStyleMaskFullSizeContentView, and - // this view (the titlebar gradient view) is technically part of the window - // "contents" (it's a subview of the content view). - if (nsCocoaUtils::ShouldZoomOnTitlebarDoubleClick()) { - [self.window performZoom:nil]; - } else if (nsCocoaUtils::ShouldMinimizeOnTitlebarDoubleClick()) { - [self.window performMiniaturize:nil]; - } - } -} - -@end - @interface MOZTitlebarAccessoryView : NSView @end @@ -3896,44 +3512,51 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; @implementation FullscreenTitlebarTracker - (FullscreenTitlebarTracker*)init { [super init]; - self.view = - [[[MOZTitlebarAccessoryView alloc] initWithFrame:NSZeroRect] autorelease]; self.hidden = YES; return self; } +- (void)loadView { + self.view = + [[[MOZTitlebarAccessoryView alloc] initWithFrame:NSZeroRect] autorelease]; +} @end -// This class allows us to exercise control over the window's title bar. It is -// used for all windows with titlebars. -// -// ToolbarWindow supports two modes: -// - drawsContentsIntoWindowFrame mode: In this mode, the Gecko ChildView is -// sized to cover the entire window frame and manages titlebar drawing. -// - separate titlebar mode, with support for unified toolbars: In this mode, -// the Gecko ChildView does not extend into the titlebar. However, this -// window's content view (which is the ChildView's superview) *does* extend -// into the titlebar. Moreover, in this mode, we place a MOZTitlebarView -// in the content view, as a sibling of the ChildView. -// -// The "separate titlebar mode" supports the "unified toolbar" look: -// If there's a toolbar right below the titlebar, the two can "connect" and -// form a single gradient without a separator line in between. -// -// The following mechanism communicates the height of the unified toolbar to -// the ToolbarWindow: -// -// 1) In the style sheet we set the toolbar's -moz-appearance to toolbar. -// 2) When the toolbar is visible and we paint the application chrome -// window, the array that Gecko passes nsChildView::UpdateThemeGeometries -// will contain an entry for the widget type StyleAppearance::Toolbar. -// 3) nsChildView::UpdateThemeGeometries passes the toolbar's height, plus the -// titlebar height, to -[ToolbarWindow setUnifiedToolbarHeight:]. -// -// The actual drawing of the gradient happens in two parts: The titlebar part -// (i.e. the top 22 pixels of the gradient) is drawn by the MOZTitlebarView, -// which is a subview of the window's content view and a sibling of the -// ChildView. The rest of the gradient is drawn by Gecko into the ChildView, as -// part of the -moz-appearance rendering of the toolbar. +// Drop all mouse events if a modal window has appeared above us. +// This helps make us behave as if the OS were running a "real" modal event +// loop. +static bool MaybeDropEventForModalWindow(NSEvent* aEvent, id aDelegate) { + if (!sModalWindowCount) { + return false; + } + + NSEventType type = [aEvent type]; + switch (type) { + case NSEventTypeScrollWheel: + case NSEventTypeLeftMouseDown: + case NSEventTypeLeftMouseUp: + case NSEventTypeRightMouseDown: + case NSEventTypeRightMouseUp: + case NSEventTypeOtherMouseDown: + case NSEventTypeOtherMouseUp: + case NSEventTypeMouseMoved: + case NSEventTypeLeftMouseDragged: + case NSEventTypeRightMouseDragged: + case NSEventTypeOtherMouseDragged: + break; + default: + return false; + } + + if (aDelegate && [aDelegate isKindOfClass:[WindowDelegate class]]) { + if (nsCocoaWindow* widget = [(WindowDelegate*)aDelegate geckoWidget]) { + if (!widget->IsModal() || widget->HasModalDescendants()) { + return true; + } + } + } + return false; +} + @implementation ToolbarWindow - (id)initWithContentRect:(NSRect)aChildViewRect @@ -3972,17 +3595,12 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; styleMask:aStyle backing:aBufferingType defer:aFlag])) { - mTitlebarView = nil; - mUnifiedToolbarHeight = 22.0f; - mSheetAttachmentPosition = aChildViewRect.size.height; mWindowButtonsRect = NSZeroRect; - mInitialTitlebarHeight = [self titlebarHeight]; - [self setTitlebarAppearsTransparent:YES]; + self.titlebarAppearsTransparent = YES; if (@available(macOS 11.0, *)) { self.titlebarSeparatorStyle = NSTitlebarSeparatorStyleNone; } - [self updateTitlebarView]; mFullscreenTitlebarTracker = [[FullscreenTitlebarTracker alloc] init]; // revealAmount is an undocumented property of @@ -4048,8 +3666,7 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { } - (void)updateTitlebarShownAmount:(CGFloat)aShownAmount { - NSInteger styleMask = [self styleMask]; - if (!(styleMask & NSWindowStyleMaskFullScreen)) { + if (!(self.styleMask & NSWindowStyleMaskFullScreen)) { // We are not interested in the size of the titlebar unless we are in // fullscreen. return; @@ -4071,27 +3688,24 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { } if (nsIWidgetListener* listener = geckoWindow->GetWidgetListener()) { - // Use the titlebar height cached in our frame rather than - // [ToolbarWindow titlebarHeight]. titlebarHeight returns 0 when we're in - // fullscreen. - CGFloat shiftByPixels = mInitialTitlebarHeight * aShownAmount; + // titlebarHeight returns 0 when we're in fullscreen, return the default + // titlebar height. + CGFloat shiftByPixels = + LookAndFeel::GetInt(LookAndFeel::IntID::MacTitlebarHeight) * + aShownAmount; if (ShouldShiftByMenubarHeightInFullscreen(geckoWindow)) { shiftByPixels += mMenuBarHeight * aShownAmount; } - // Use mozilla::DesktopToLayoutDeviceScale rather than the - // DesktopToLayoutDeviceScale in nsCocoaWindow. The latter accounts for - // screen DPI. We don't want that because the revealAmount property - // already accounts for it, so we'd be compounding DPI scales > 1. - mozilla::DesktopCoord coord = LayoutDeviceCoord(shiftByPixels) / - mozilla::DesktopToLayoutDeviceScale(); - - listener->MacFullscreenMenubarOverlapChanged(coord); + // Use desktop pixels rather than the DesktopToLayoutDeviceScale in + // nsCocoaWindow. The latter accounts for screen DPI. We don't want that + // because the revealAmount property already accounts for it, so we'd be + // compounding DPI scales > 1. + listener->MacFullscreenMenubarOverlapChanged(DesktopCoord(shiftByPixels)); } } } - (void)dealloc { - [mTitlebarView release]; [mFullscreenTitlebarTracker removeObserver:self forKeyPath:@"revealAmount"]; [mFullscreenTitlebarTracker removeFromParentViewController]; [mFullscreenTitlebarTracker release]; @@ -4100,80 +3714,13 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { } - (NSArray<NSView*>*)contentViewContents { - NSMutableArray<NSView*>* contents = [[self.contentView subviews] mutableCopy]; - if (mTitlebarView) { - // Do not include the titlebar gradient view in the returned array. - [contents removeObject:mTitlebarView]; - } - return [contents autorelease]; -} - -- (void)updateTitlebarView { - BOOL needTitlebarView = - !self.drawsContentsIntoWindowFrame || mUnifiedToolbarHeight > 0; - if (needTitlebarView && !mTitlebarView) { - mTitlebarView = - [[MOZTitlebarView alloc] initWithFrame:self.unifiedToolbarRect]; - mTitlebarView.autoresizingMask = NSViewWidthSizable | NSViewMinYMargin; - [self.contentView addSubview:mTitlebarView - positioned:NSWindowBelow - relativeTo:nil]; - } else if (needTitlebarView && mTitlebarView) { - mTitlebarView.frame = self.unifiedToolbarRect; - } else if (!needTitlebarView && mTitlebarView) { - [mTitlebarView removeFromSuperview]; - [mTitlebarView release]; - mTitlebarView = nil; - } + return [[self.contentView.subviews copy] autorelease]; } - (void)windowMainStateChanged { - [self setTitlebarNeedsDisplay]; [[self mainChildView] ensureNextCompositeIsAtomicWithMainThreadPaint]; } -- (void)setTitlebarNeedsDisplay { - [mTitlebarView setNeedsDisplay:YES]; -} - -- (NSRect)titlebarRect { - CGFloat titlebarHeight = self.titlebarHeight; - return NSMakeRect(0, self.frame.size.height - titlebarHeight, - self.frame.size.width, titlebarHeight); -} - -// In window contentView coordinates (origin bottom left) -- (NSRect)unifiedToolbarRect { - return NSMakeRect(0, self.frame.size.height - mUnifiedToolbarHeight, - self.frame.size.width, mUnifiedToolbarHeight); -} - -// Returns the unified height of titlebar + toolbar. -- (CGFloat)unifiedToolbarHeight { - return mUnifiedToolbarHeight; -} - -- (CGFloat)titlebarHeight { - // We use the original content rect here, not what we return from - // [self contentRectForFrameRect:], because that would give us a - // titlebarHeight of zero. - NSRect frameRect = self.frame; - NSUInteger styleMask = self.styleMask; - styleMask &= ~NSWindowStyleMaskFullSizeContentView; - NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect - styleMask:styleMask]; - return NSMaxY(frameRect) - NSMaxY(originalContentRect); -} - -// Stores the complete height of titlebar + toolbar. -- (void)setUnifiedToolbarHeight:(CGFloat)aHeight { - if (aHeight == mUnifiedToolbarHeight) return; - - mUnifiedToolbarHeight = aHeight; - - [self updateTitlebarView]; -} - // Extending the content area into the title bar works by resizing the // mainChildView so that it covers the titlebar. - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState { @@ -4197,21 +3744,6 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { // we'll send a mouse move event with the correct new position. ChildViewMouseTracker::ResendLastMouseMoveEvent(); } - - [self updateTitlebarView]; -} - -- (void)setWantsTitleDrawn:(BOOL)aDrawTitle { - [super setWantsTitleDrawn:aDrawTitle]; - [self setTitlebarNeedsDisplay]; -} - -- (void)setSheetAttachmentPosition:(CGFloat)aY { - mSheetAttachmentPosition = aY; -} - -- (CGFloat)sheetAttachmentPosition { - return mSheetAttachmentPosition; } - (void)placeWindowButtons:(NSRect)aRect { @@ -4268,42 +3800,9 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { } - (void)sendEvent:(NSEvent*)anEvent { - NSEventType type = [anEvent type]; - - switch (type) { - case NSEventTypeScrollWheel: - case NSEventTypeLeftMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseDown: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseDown: - case NSEventTypeOtherMouseUp: - case NSEventTypeMouseMoved: - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: { - // Drop all mouse events if a modal window has appeared above us. - // This helps make us behave as if the OS were running a "real" modal - // event loop. - id delegate = self.delegate; - if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { - nsCocoaWindow* widget = [(WindowDelegate*)delegate geckoWidget]; - if (widget) { - if (gGeckoAppModalWindowList && - widget != gGeckoAppModalWindowList->window) { - return; - } - if (widget->HasModalDescendents()) { - return; - } - } - } - break; - } - default: - break; + if (MaybeDropEventForModalWindow(anEvent, self.delegate)) { + return; } - [super sendEvent:anEvent]; } @@ -4403,40 +3902,8 @@ static const NSUInteger kWindowShadowOptionsTooltip = 4; } - (void)sendEvent:(NSEvent*)anEvent { - NSEventType type = [anEvent type]; - - switch (type) { - case NSEventTypeScrollWheel: - case NSEventTypeLeftMouseDown: - case NSEventTypeLeftMouseUp: - case NSEventTypeRightMouseDown: - case NSEventTypeRightMouseUp: - case NSEventTypeOtherMouseDown: - case NSEventTypeOtherMouseUp: - case NSEventTypeMouseMoved: - case NSEventTypeLeftMouseDragged: - case NSEventTypeRightMouseDragged: - case NSEventTypeOtherMouseDragged: { - // Drop all mouse events if a modal window has appeared above us. - // This helps make us behave as if the OS were running a "real" modal - // event loop. - id delegate = self.delegate; - if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { - nsCocoaWindow* widget = [(WindowDelegate*)delegate geckoWidget]; - if (widget) { - if (gGeckoAppModalWindowList && - widget != gGeckoAppModalWindowList->window) { - return; - } - if (widget->HasModalDescendents()) { - return; - } - } - } - break; - } - default: - break; + if (MaybeDropEventForModalWindow(anEvent, self.delegate)) { + return; } [super sendEvent:anEvent]; diff --git a/widget/cocoa/nsLookAndFeel.h b/widget/cocoa/nsLookAndFeel.h index adce685a4e..89e4c93713 100644 --- a/widget/cocoa/nsLookAndFeel.h +++ b/widget/cocoa/nsLookAndFeel.h @@ -12,14 +12,17 @@ class nsLookAndFeel final : public nsXPLookAndFeel { nsLookAndFeel(); virtual ~nsLookAndFeel(); - void NativeInit() final; + void NativeInit() final { EnsureInit(); } + void RefreshImpl() final; + void EnsureInit(); + nsresult NativeGetColor(ColorID, ColorScheme, nscolor& aColor) override; nsresult NativeGetInt(IntID, int32_t& aResult) override; nsresult NativeGetFloat(FloatID, float& aResult) override; bool NativeGetFont(FontID aID, nsString& aFontName, gfxFontStyle& aFontStyle) override; - virtual char16_t GetPasswordCharacterImpl() override { + char16_t GetPasswordCharacterImpl() override { // unicode value for the bullet character, used for password textfields. return 0x2022; } @@ -34,10 +37,9 @@ class nsLookAndFeel final : public nsXPLookAndFeel { static void RecordAccessibilityTelemetry(); protected: - static bool SystemWantsDarkTheme(); - static bool IsSystemOrientationRTL(); - static nscolor ProcessSelectionBackground(nscolor aColor, - ColorScheme aScheme); + bool mInitialized = false; + bool mRtl = false; + int32_t mTitlebarHeight = 0; }; #endif // nsLookAndFeel_h_ diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index 5675198ff2..779984be0c 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -32,22 +32,44 @@ using namespace mozilla; + (void)startObserving; @end -nsLookAndFeel::nsLookAndFeel() = default; +nsLookAndFeel::nsLookAndFeel() { + [MOZLookAndFeelDynamicChangeObserver startObserving]; +} nsLookAndFeel::~nsLookAndFeel() = default; -void nsLookAndFeel::NativeInit() { +void nsLookAndFeel::EnsureInit() { + if (mInitialized) { + return; + } + NS_OBJC_BEGIN_TRY_ABORT_BLOCK - [MOZLookAndFeelDynamicChangeObserver startObserving]; + mInitialized = true; + NSWindow* window = + [[NSWindow alloc] initWithContentRect:NSZeroRect + styleMask:NSWindowStyleMaskTitled + backing:NSBackingStoreBuffered + defer:NO]; + auto release = MakeScopeExit([&] { [window release]; }); + + mRtl = window.windowTitlebarLayoutDirection == + NSUserInterfaceLayoutDirectionRightToLeft; + mTitlebarHeight = std::ceil(window.frame.size.height); + RecordTelemetry(); NS_OBJC_END_TRY_ABORT_BLOCK } +void nsLookAndFeel::RefreshImpl() { + mInitialized = false; + nsXPLookAndFeel::RefreshImpl(); +} + static nscolor GetColorFromNSColor(NSColor* aColor) { NSColor* deviceColor = - [aColor colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]]; + [aColor colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace]; return NS_RGBA((unsigned int)(deviceColor.redComponent * 255.0), (unsigned int)(deviceColor.greenComponent * 255.0), (unsigned int)(deviceColor.blueComponent * 255.0), @@ -77,8 +99,7 @@ static nscolor GetColorFromNSColorWithCustomAlpha(NSColor* aColor, // whereas white text on dark blue (which what you get if you mix // partially-transparent light blue with the black textbox background) has much // better contrast. -nscolor nsLookAndFeel::ProcessSelectionBackground(nscolor aColor, - ColorScheme aScheme) { +static nscolor ProcessSelectionBackground(nscolor aColor, ColorScheme aScheme) { if (aScheme == ColorScheme::Dark) { // When we use a dark selection color, we do not change alpha because we do // not use dark selection in content. The dark system color is appropriate @@ -136,6 +157,14 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::MozMenuhover: case ColorID::Selecteditem: color = GetColorFromNSColor(NSColor.selectedContentBackgroundColor); + if (aID == ColorID::MozMenuhover && + !LookAndFeel::GetInt(IntID::PrefersReducedTransparency)) { + // Wash the color a little bit with semi-transparent white to match a + // bit closer the native NSVisualEffectSelection on menus. + color = NS_ComposeColors( + color, + NS_RGBA(255, 255, 255, aScheme == ColorScheme::Light ? 51 : 25)); + } break; case ColorID::Accentcolortext: case ColorID::MozMenuhovertext: @@ -197,6 +226,7 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, : NS_RGB(0xFF, 0xFF, 0xFF); break; case ColorID::Windowtext: + case ColorID::MozDialogtext: color = GetColorFromNSColor(NSColor.windowFrameTextColor); break; case ColorID::Appworkspace: @@ -254,13 +284,14 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::Windowframe: color = GetColorFromNSColor(NSColor.windowFrameColor); break; - case ColorID::Window: { - color = GetColorFromNSColor(NSColor.windowBackgroundColor); + case ColorID::MozDialog: + case ColorID::Window: + color = GetColorFromNSColor(aScheme == ColorScheme::Light + ? NSColor.windowBackgroundColor + : NSColor.underPageBackgroundColor); break; - } case ColorID::Field: case ColorID::MozCombobox: - case ColorID::MozDialog: color = GetColorFromNSColor(NSColor.controlBackgroundColor); break; case ColorID::Fieldtext: @@ -269,7 +300,6 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::MozButtonhovertext: case ColorID::Menutext: case ColorID::Infotext: - case ColorID::MozDialogtext: case ColorID::MozCellhighlighttext: case ColorID::MozColheadertext: case ColorID::MozColheaderhovertext: @@ -323,6 +353,7 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::SpellCheckerUnderline: case ColorID::Activeborder: case ColorID::Inactiveborder: + case ColorID::MozAutofillBackground: aColor = GetStandinForNativeColor(aID, aScheme); return NS_OK; default: @@ -336,6 +367,16 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, NS_OBJC_END_TRY_ABORT_BLOCK } +static bool SystemWantsDarkTheme() { + // This returns true if the macOS system appearance is set to dark mode, + // false otherwise. + NSAppearanceName aquaOrDarkAqua = + [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[ + NSAppearanceNameAqua, NSAppearanceNameDarkAqua + ]]; + return [aquaOrDarkAqua isEqualToString:NSAppearanceNameDarkAqua]; +} + nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; @@ -355,9 +396,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::CaretWidth: aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; case IntID::SelectTextfieldsOnKeyFocus: // Select textfield content when focused by kbd // used by EventStateManager::sTextfieldSelectModel @@ -412,7 +450,12 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = nsCocoaFeatures::OnBigSurOrLater(); break; case IntID::MacRTL: - aResult = IsSystemOrientationRTL(); + EnsureInit(); + aResult = mRtl; + break; + case IntID::MacTitlebarHeight: + EnsureInit(); + aResult = mTitlebarHeight; break; case IntID::AlertNotificationOrigin: aResult = NS_ALERT_TOP; @@ -469,14 +512,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = NSWorkspace.sharedWorkspace .accessibilityDisplayShouldIncreaseContrast; break; - case IntID::VideoDynamicRange: { - // If the platform says it supports HDR, then we claim to support - // video-dynamic-range. - gfxPlatform* platform = gfxPlatform::GetPlatform(); - MOZ_ASSERT(platform); - aResult = platform->SupportsHDR(); - break; - } case IntID::PanelAnimations: aResult = 1; break; @@ -519,28 +554,6 @@ nsresult nsLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) { NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); } -bool nsLookAndFeel::SystemWantsDarkTheme() { - // This returns true if the macOS system appearance is set to dark mode, false - // otherwise. - NSAppearanceName aquaOrDarkAqua = - [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[ - NSAppearanceNameAqua, NSAppearanceNameDarkAqua - ]]; - return [aquaOrDarkAqua isEqualToString:NSAppearanceNameDarkAqua]; -} - -/*static*/ -bool nsLookAndFeel::IsSystemOrientationRTL() { - NSWindow* window = - [[NSWindow alloc] initWithContentRect:NSZeroRect - styleMask:NSWindowStyleMaskBorderless - backing:NSBackingStoreBuffered - defer:NO]; - auto direction = window.windowTitlebarLayoutDirection; - [window release]; - return direction == NSUserInterfaceLayoutDirectionRightToLeft; -} - bool nsLookAndFeel::NativeGetFont(FontID aID, nsString& aFontName, gfxFontStyle& aFontStyle) { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; diff --git a/widget/cocoa/nsNativeThemeCocoa.h b/widget/cocoa/nsNativeThemeCocoa.h index 9488e50e1e..ebbf782264 100644 --- a/widget/cocoa/nsNativeThemeCocoa.h +++ b/widget/cocoa/nsNativeThemeCocoa.h @@ -159,7 +159,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { eSpinButtonDown, // SpinButtonParams eSegment, // SegmentParams eSeparator, - eToolbar, // bool eStatusBar, // bool eGroupBox, eTextField, // TextFieldParams @@ -204,9 +203,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { static WidgetInfo Separator() { return WidgetInfo(Widget::eSeparator, false); } - static WidgetInfo Toolbar(bool aParams) { - return WidgetInfo(Widget::eToolbar, aParams); - } static WidgetInfo StatusBar(bool aParams) { return WidgetInfo(Widget::eStatusBar, aParams); } diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm index ff715ac57c..18913facea 100644 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ b/widget/cocoa/nsNativeThemeCocoa.mm @@ -399,9 +399,8 @@ static BOOL FrameIsInActiveWindow(nsIFrame* aFrame) { // Toolbar controls and content controls respond to different window // activeness states. -static BOOL IsActive(nsIFrame* aFrame, BOOL aIsToolbarControl) { - if (aIsToolbarControl) return [NativeWindowForFrame(aFrame) isMainWindow]; - return FrameIsInActiveWindow(aFrame); +static BOOL IsActiveToolbarControl(nsIFrame* aFrame) { + return NativeWindowForFrame(aFrame).isMainWindow; } NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeCocoa, nsNativeTheme, nsITheme) @@ -1021,7 +1020,6 @@ static bool IsToolbarStyleContainer(nsIFrame* aFrame) { } switch (aFrame->StyleDisplay()->EffectiveAppearance()) { - case StyleAppearance::Toolbar: case StyleAppearance::Statusbar: return true; default: @@ -2078,35 +2076,6 @@ void nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, RenderWithCoreUI(drawRect, cgContext, dict); } -void nsNativeThemeCocoa::DrawToolbar(CGContextRef cgContext, - const CGRect& inBoxRect, bool aIsMain) { - CGRect drawRect = inBoxRect; - - // top border - drawRect.size.height = 1.0f; - DrawNativeGreyColorInRect(cgContext, toolbarTopBorderGrey, drawRect, aIsMain); - - // background - drawRect.origin.y += drawRect.size.height; - drawRect.size.height = inBoxRect.size.height - 2.0f; - DrawNativeGreyColorInRect(cgContext, toolbarFillGrey, drawRect, aIsMain); - - // bottom border - drawRect.origin.y += drawRect.size.height; - drawRect.size.height = 1.0f; - DrawNativeGreyColorInRect(cgContext, toolbarBottomBorderGrey, drawRect, - aIsMain); -} - -static bool ToolbarCanBeUnified(const gfx::Rect& aRect, NSWindow* aWindow) { - if (![aWindow isKindOfClass:[ToolbarWindow class]]) return false; - - ToolbarWindow* win = (ToolbarWindow*)aWindow; - float unifiedToolbarHeight = [win unifiedToolbarHeight]; - return aRect.X() == 0 && aRect.Width() >= [win frame].size.width && - aRect.YMost() <= unifiedToolbarHeight; -} - void nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect, bool aIsMain) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; @@ -2329,25 +2298,12 @@ Maybe<nsNativeThemeCocoa::WidgetInfo> nsNativeThemeCocoa::ComputeWidgetInfo( case StyleAppearance::Separator: return Some(WidgetInfo::Separator()); - case StyleAppearance::Toolbar: { - NSWindow* win = NativeWindowForFrame(aFrame); - bool isMain = [win isMainWindow]; - if (ToolbarCanBeUnified(nativeWidgetRect, win)) { - // Unified toolbars are drawn similar to vibrancy; we communicate their - // extents via the theme geometry mechanism and then place native views - // under Gecko's rendering. So Gecko just needs to be transparent in the - // place where the toolbar should be visible. - return Nothing(); - } - return Some(WidgetInfo::Toolbar(isMain)); - } - case StyleAppearance::MozWindowTitlebar: { return Nothing(); } case StyleAppearance::Statusbar: - return Some(WidgetInfo::StatusBar(IsActive(aFrame, YES))); + return Some(WidgetInfo::StatusBar(IsActiveToolbarControl(aFrame))); case StyleAppearance::MenulistButton: case StyleAppearance::Menulist: { @@ -2594,11 +2550,6 @@ void nsNativeThemeCocoa::RenderWidget(const WidgetInfo& aWidgetInfo, HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION); break; } - case Widget::eToolbar: { - bool isMain = aWidgetInfo.Params<bool>(); - DrawToolbar(cgContext, macRect, isMain); - break; - } case Widget::eStatusBar: { bool isMain = aWidgetInfo.Params<bool>(); DrawStatusBar(cgContext, macRect, isMain); @@ -2710,7 +2661,6 @@ bool nsNativeThemeCocoa::CreateWebRenderCommandsForWidget( case StyleAppearance::SpinnerDownbutton: case StyleAppearance::Toolbarbutton: case StyleAppearance::Separator: - case StyleAppearance::Toolbar: case StyleAppearance::MozWindowTitlebar: case StyleAppearance::Statusbar: case StyleAppearance::Menulist: @@ -3091,8 +3041,6 @@ nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, // Some widget types just never change state. switch (aAppearance) { case StyleAppearance::MozWindowTitlebar: - case StyleAppearance::Toolbox: - case StyleAppearance::Toolbar: case StyleAppearance::Statusbar: case StyleAppearance::Tooltip: case StyleAppearance::Tabpanels: @@ -3178,13 +3126,11 @@ bool nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Spinner: case StyleAppearance::SpinnerUpbutton: case StyleAppearance::SpinnerDownbutton: - case StyleAppearance::Toolbar: case StyleAppearance::Statusbar: case StyleAppearance::NumberInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Searchfield: - case StyleAppearance::Toolbox: case StyleAppearance::ProgressBar: case StyleAppearance::Progresschunk: case StyleAppearance::Meter: @@ -3265,7 +3211,6 @@ bool nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus( case StyleAppearance::SpinnerUpbutton: case StyleAppearance::SpinnerDownbutton: case StyleAppearance::Separator: - case StyleAppearance::Toolbox: case StyleAppearance::NumberInput: case StyleAppearance::Textfield: case StyleAppearance::Treeview: @@ -3283,16 +3228,8 @@ nsITheme::ThemeGeometryType nsNativeThemeCocoa::ThemeGeometryTypeForWidget( switch (aAppearance) { case StyleAppearance::MozWindowTitlebar: return eThemeGeometryTypeTitlebar; - case StyleAppearance::Toolbar: - return eThemeGeometryTypeToolbar; - case StyleAppearance::Toolbox: - return eThemeGeometryTypeToolbox; case StyleAppearance::MozWindowButtonBox: return eThemeGeometryTypeWindowButtons; - case StyleAppearance::Tooltip: - return eThemeGeometryTypeTooltip; - case StyleAppearance::Menupopup: - return eThemeGeometryTypeMenu; default: return eThemeGeometryTypeUnknown; } @@ -3307,7 +3244,6 @@ nsITheme::Transparency nsNativeThemeCocoa::GetWidgetTransparency( switch (aAppearance) { case StyleAppearance::Menupopup: case StyleAppearance::Tooltip: - case StyleAppearance::Toolbar: return eTransparent; case StyleAppearance::MozMacUnifiedToolbarWindow: // We want these to be treated as opaque by Gecko. We ensure there's an diff --git a/widget/cocoa/nsNativeThemeColors.h b/widget/cocoa/nsNativeThemeColors.h index 02ec96f51a..051335e9bc 100644 --- a/widget/cocoa/nsNativeThemeColors.h +++ b/widget/cocoa/nsNativeThemeColors.h @@ -10,43 +10,6 @@ #include "mozilla/ColorScheme.h" -enum ColorName { - toolbarTopBorderGrey, - toolbarFillGrey, - toolbarBottomBorderGrey, -}; - -static const int sLionThemeColors[][2] = { - /* { active window, inactive window } */ - // toolbar: - {0xD0, 0xF0}, // top separator line - {0xB2, 0xE1}, // fill color - {0x59, 0x87}, // bottom separator line -}; - -static const int sYosemiteThemeColors[][2] = { - /* { active window, inactive window } */ - // toolbar: - {0xBD, 0xDF}, // top separator line - {0xD3, 0xF6}, // fill color - {0xB3, 0xD1}, // bottom separator line -}; - -inline int NativeGreyColorAsInt(ColorName name, BOOL isMain) { - return sYosemiteThemeColors[name][isMain ? 0 : 1]; -} - -inline float NativeGreyColorAsFloat(ColorName name, BOOL isMain) { - return NativeGreyColorAsInt(name, isMain) / 255.0f; -} - -inline void DrawNativeGreyColorInRect(CGContextRef context, ColorName name, - CGRect rect, BOOL isMain) { - float grey = NativeGreyColorAsFloat(name, isMain); - CGContextSetRGBFillColor(context, grey, grey, grey, 1.0f); - CGContextFillRect(context, rect); -} - inline NSAppearance* NSAppearanceForColorScheme(mozilla::ColorScheme aScheme) { NSAppearanceName appearanceName = aScheme == mozilla::ColorScheme::Light ? NSAppearanceNameAqua diff --git a/widget/cocoa/nsPIWidgetCocoa.idl b/widget/cocoa/nsPIWidgetCocoa.idl deleted file mode 100644 index 54aa0d5113..0000000000 --- a/widget/cocoa/nsPIWidgetCocoa.idl +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; 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/. */ - -#include "nsISupports.idl" - -interface nsIWidget; - -[ptr] native NSWindowPtr(NSWindow); - -// -// nsPIWidgetCocoa -// -// A private interface (unfrozen, private to the widget implementation) that -// gives us access to some extra features on a widget/window. -// -[uuid(f75ff69e-3a51-419e-bd29-042f804bc2ed)] -interface nsPIWidgetCocoa : nsISupports -{ - void SendSetZLevelEvent(); - - // Find the displayed child sheet (if aShown) or a child sheet that - // wants to be displayed (if !aShown) - nsIWidget GetChildSheet(in boolean aShown); - - // Get the parent widget (if any) StandardCreate() was called with. - nsIWidget GetRealParent(); - - // If the object implementing this interface is a sheet, this will return the - // native NSWindow it is attached to - readonly attribute NSWindowPtr sheetWindowParent; - - // True if window is a sheet - readonly attribute boolean isSheet; - -}; // nsPIWidgetCocoa diff --git a/widget/cocoa/nsWindowMap.mm b/widget/cocoa/nsWindowMap.mm index d93f89cb58..6f92adceaf 100644 --- a/widget/cocoa/nsWindowMap.mm +++ b/widget/cocoa/nsWindowMap.mm @@ -115,31 +115,31 @@ NS_OBJC_BEGIN_TRY_BLOCK_RETURN; if ((self = [super init])) { - [[NSNotificationCenter defaultCenter] + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowBecameKey:) name:NSWindowDidBecomeKeyNotification object:inWindow]; - [[NSNotificationCenter defaultCenter] + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowResignedKey:) name:NSWindowDidResignKeyNotification object:inWindow]; - [[NSNotificationCenter defaultCenter] + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowBecameMain:) name:NSWindowDidBecomeMainNotification object:inWindow]; - [[NSNotificationCenter defaultCenter] + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowResignedMain:) name:NSWindowDidResignMainNotification object:inWindow]; - [[NSNotificationCenter defaultCenter] + [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(windowWillClose:) name:NSWindowWillCloseNotification @@ -153,7 +153,7 @@ - (void)dealloc { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - [[NSNotificationCenter defaultCenter] removeObserver:self]; + [NSNotificationCenter.defaultCenter removeObserver:self]; [super dealloc]; NS_OBJC_END_TRY_IGNORE_BLOCK; @@ -171,7 +171,7 @@ + (void)activateInWindow:(NSWindow*)aWindow { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - WindowDelegate* delegate = (WindowDelegate*)[aWindow delegate]; + WindowDelegate* delegate = (WindowDelegate*)aWindow.delegate; if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return; if ([delegate toplevelActiveState]) return; @@ -191,7 +191,7 @@ + (void)deactivateInWindow:(NSWindow*)aWindow { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - WindowDelegate* delegate = (WindowDelegate*)[aWindow delegate]; + WindowDelegate* delegate = (WindowDelegate*)aWindow.delegate; if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) return; if (![delegate toplevelActiveState]) return; @@ -205,9 +205,10 @@ + (void)activateInWindowViews:(NSWindow*)aWindow { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - id firstResponder = [aWindow firstResponder]; - if ([firstResponder isKindOfClass:[ChildView class]]) + id firstResponder = aWindow.firstResponder; + if ([firstResponder isKindOfClass:[ChildView class]]) { [firstResponder viewsWindowDidBecomeKey]; + } NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -217,9 +218,10 @@ + (void)deactivateInWindowViews:(NSWindow*)aWindow { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - id firstResponder = [aWindow firstResponder]; - if ([firstResponder isKindOfClass:[ChildView class]]) + id firstResponder = aWindow.firstResponder; + if ([firstResponder isKindOfClass:[ChildView class]]) { [firstResponder viewsWindowDidResignKey]; + } NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -230,23 +232,23 @@ // should be sent when a native window becomes key, and the NS_DEACTIVATE // event should be sent when it resignes key. - (void)windowBecameKey:(NSNotification*)inNotification { - NSWindow* window = (NSWindow*)[inNotification object]; + NSWindow* window = inNotification.object; - id delegate = [window delegate]; + id delegate = window.delegate; if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { [TopLevelWindowData activateInWindowViews:window]; - } else if ([window isSheet] || [NSApp modalWindow]) { + } else if (window.isSheet || NSApp.modalWindow) { [TopLevelWindowData activateInWindow:window]; } } - (void)windowResignedKey:(NSNotification*)inNotification { - NSWindow* window = (NSWindow*)[inNotification object]; + NSWindow* window = inNotification.object; - id delegate = [window delegate]; + id delegate = window.delegate; if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]]) { [TopLevelWindowData deactivateInWindowViews:window]; - } else if ([window isSheet] || [NSApp modalWindow]) { + } else if (window.isSheet || NSApp.modalWindow) { [TopLevelWindowData deactivateInWindow:window]; } } @@ -255,24 +257,24 @@ // state). So (for non-embedders) we need to ensure that a top-level window // is main when an NS_ACTIVATE event is sent to Gecko for it. - (void)windowBecameMain:(NSNotification*)inNotification { - NSWindow* window = (NSWindow*)[inNotification object]; - - id delegate = [window delegate]; + NSWindow* window = inNotification.object; + id delegate = window.delegate; // Don't send events to a top-level window that has a sheet/modal-window open // above it -- as far as Gecko is concerned, it's inactive, and stays so until // the sheet/modal-window closes. if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && - ![window attachedSheet] && ![NSApp modalWindow]) + !window.attachedSheet && !NSApp.modalWindow) { [TopLevelWindowData activateInWindow:window]; + } } - (void)windowResignedMain:(NSNotification*)inNotification { - NSWindow* window = (NSWindow*)[inNotification object]; - - id delegate = [window delegate]; + NSWindow* window = inNotification.object; + id delegate = window.delegate; if (delegate && [delegate isKindOfClass:[WindowDelegate class]] && - ![window attachedSheet] && ![NSApp modalWindow]) + ![window attachedSheet] && ![NSApp modalWindow]) { [TopLevelWindowData deactivateInWindow:window]; + } } - (void)windowWillClose:(NSNotification*)inNotification { diff --git a/widget/gtk/CompositorWidgetChild.cpp b/widget/gtk/CompositorWidgetChild.cpp index b7908a43d4..3b6af872ed 100644 --- a/widget/gtk/CompositorWidgetChild.cpp +++ b/widget/gtk/CompositorWidgetChild.cpp @@ -38,13 +38,13 @@ void CompositorWidgetChild::NotifyClientSizeChanged( Unused << SendNotifyClientSizeChanged(aClientSize); } -void CompositorWidgetChild::DisableRendering() { - Unused << SendDisableRendering(); +void CompositorWidgetChild::CleanupResources() { + Unused << SendCleanupResources(); } -void CompositorWidgetChild::EnableRendering(const uintptr_t aXWindow, - const bool aShaped) { - Unused << SendEnableRendering(aXWindow, aShaped); +void CompositorWidgetChild::SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) { + Unused << SendSetRenderingSurface(aXWindow, aShaped); } } // namespace widget diff --git a/widget/gtk/CompositorWidgetChild.h b/widget/gtk/CompositorWidgetChild.h index b1cad75da3..f46cf63bfb 100644 --- a/widget/gtk/CompositorWidgetChild.h +++ b/widget/gtk/CompositorWidgetChild.h @@ -27,8 +27,9 @@ class CompositorWidgetChild final : public PCompositorWidgetChild, mozilla::ipc::IPCResult RecvUnobserveVsync() override; void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize) override; - void DisableRendering() override; - void EnableRendering(const uintptr_t aXWindow, const bool aShaped) override; + void CleanupResources() override; + void SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) override; private: RefPtr<CompositorVsyncDispatcher> mVsyncDispatcher; diff --git a/widget/gtk/CompositorWidgetParent.cpp b/widget/gtk/CompositorWidgetParent.cpp index 998614622e..7f576f35e7 100644 --- a/widget/gtk/CompositorWidgetParent.cpp +++ b/widget/gtk/CompositorWidgetParent.cpp @@ -40,14 +40,14 @@ mozilla::ipc::IPCResult CompositorWidgetParent::RecvNotifyClientSizeChanged( return IPC_OK(); } -mozilla::ipc::IPCResult CompositorWidgetParent::RecvDisableRendering() { - DisableRendering(); +mozilla::ipc::IPCResult CompositorWidgetParent::RecvCleanupResources() { + CleanupResources(); return IPC_OK(); } -mozilla::ipc::IPCResult CompositorWidgetParent::RecvEnableRendering( +mozilla::ipc::IPCResult CompositorWidgetParent::RecvSetRenderingSurface( const uintptr_t& aXWindow, const bool& aShaped) { - EnableRendering(aXWindow, aShaped); + SetRenderingSurface(aXWindow, aShaped); return IPC_OK(); } diff --git a/widget/gtk/CompositorWidgetParent.h b/widget/gtk/CompositorWidgetParent.h index 2bbc70af3e..8c0a6e8c26 100644 --- a/widget/gtk/CompositorWidgetParent.h +++ b/widget/gtk/CompositorWidgetParent.h @@ -28,9 +28,9 @@ class CompositorWidgetParent final : public PCompositorWidgetParent, mozilla::ipc::IPCResult RecvNotifyClientSizeChanged( const LayoutDeviceIntSize& aClientSize) override; - mozilla::ipc::IPCResult RecvDisableRendering() override; - mozilla::ipc::IPCResult RecvEnableRendering(const uintptr_t& aXWindow, - const bool& aShaped) override; + mozilla::ipc::IPCResult RecvCleanupResources() override; + mozilla::ipc::IPCResult RecvSetRenderingSurface(const uintptr_t& aXWindow, + const bool& aShaped) override; private: RefPtr<VsyncObserver> mVsyncObserver; diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp index 36559cfd54..073ad5248f 100644 --- a/widget/gtk/GtkCompositorWidget.cpp +++ b/widget/gtk/GtkCompositorWidget.cpp @@ -7,6 +7,7 @@ #include "mozilla/gfx/gfxVars.h" #include "mozilla/layers/CompositorThread.h" +#include "mozilla/WidgetUtilsGtk.h" #include "mozilla/widget/InProcessCompositorWidget.h" #include "mozilla/widget/PlatformWidgetTypes.h" #include "nsWindow.h" @@ -40,25 +41,22 @@ GtkCompositorWidget::GtkCompositorWidget( #if defined(MOZ_X11) if (GdkIsX11Display()) { ConfigureX11Backend((Window)aInitData.XWindow(), aInitData.Shaped()); - LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mXWindow %p " - "mIsRenderingSuspended %d\n", - (void*)mWidget.get(), (void*)aInitData.XWindow(), - !!mIsRenderingSuspended); + LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mXWindow %p\n", + (void*)mWidget.get(), (void*)aInitData.XWindow()); } #endif #if defined(MOZ_WAYLAND) if (GdkIsWaylandDisplay()) { ConfigureWaylandBackend(); - LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mWidget %p " - "mIsRenderingSuspended %d\n", - (void*)mWidget.get(), (void*)mWidget, !!mIsRenderingSuspended); + LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mWidget %p\n", + (void*)mWidget.get(), (void*)mWidget); } #endif } GtkCompositorWidget::~GtkCompositorWidget() { LOG("GtkCompositorWidget::~GtkCompositorWidget [%p]\n", (void*)mWidget.get()); - DisableRendering(); + CleanupResources(); RefPtr<nsIWidget> widget = mWidget.forget(); NS_ReleaseOnMainThread("GtkCompositorWidget::mWidget", widget.forget()); } @@ -169,57 +167,47 @@ GtkCompositorWidget::GetNativeLayerRoot() { } #endif -void GtkCompositorWidget::DisableRendering() { - LOG("GtkCompositorWidget::DisableRendering [%p]\n", (void*)mWidget.get()); - mIsRenderingSuspended = true; +void GtkCompositorWidget::CleanupResources() { + LOG("GtkCompositorWidget::CleanupResources [%p]\n", (void*)mWidget.get()); mProvider.CleanupResources(); } #if defined(MOZ_WAYLAND) -bool GtkCompositorWidget::ConfigureWaylandBackend() { +void GtkCompositorWidget::ConfigureWaylandBackend() { mProvider.Initialize(this); - return true; } #endif #if defined(MOZ_X11) -bool GtkCompositorWidget::ConfigureX11Backend(Window aXWindow, bool aShaped) { +void GtkCompositorWidget::ConfigureX11Backend(Window aXWindow, bool aShaped) { // We don't have X window yet. if (!aXWindow) { - mIsRenderingSuspended = true; - return false; + mProvider.CleanupResources(); + return; } // Initialize the window surface provider - return mProvider.Initialize(aXWindow, aShaped); + mProvider.Initialize(aXWindow, aShaped); } #endif -void GtkCompositorWidget::EnableRendering(const uintptr_t aXWindow, - const bool aShaped) { - LOG("GtkCompositorWidget::EnableRendering() [%p]\n", mWidget.get()); +void GtkCompositorWidget::SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) { + LOG("GtkCompositorWidget::SetRenderingSurface() [%p]\n", mWidget.get()); - if (!mIsRenderingSuspended) { - LOG(" quit, mIsRenderingSuspended = false\n"); - return; - } #if defined(MOZ_WAYLAND) if (GdkIsWaylandDisplay()) { LOG(" configure widget %p\n", mWidget.get()); - if (!ConfigureWaylandBackend()) { - return; - } + ConfigureWaylandBackend(); } #endif #if defined(MOZ_X11) if (GdkIsX11Display()) { LOG(" configure XWindow %p shaped %d\n", (void*)aXWindow, aShaped); - if (!ConfigureX11Backend((Window)aXWindow, aShaped)) { - return; - } + ConfigureX11Backend((Window)aXWindow, aShaped); } #endif - mIsRenderingSuspended = false; } + #ifdef MOZ_LOGGING bool GtkCompositorWidget::IsPopup() { return mWidget ? mWidget->IsPopup() : false; diff --git a/widget/gtk/GtkCompositorWidget.h b/widget/gtk/GtkCompositorWidget.h index bde88bde6c..d4834247f1 100644 --- a/widget/gtk/GtkCompositorWidget.h +++ b/widget/gtk/GtkCompositorWidget.h @@ -28,9 +28,9 @@ class PlatformCompositorWidgetDelegate : public CompositorWidgetDelegate { const LayoutDeviceIntSize& aClientSize) = 0; virtual GtkCompositorWidget* AsGtkCompositorWidget() { return nullptr; }; - virtual void DisableRendering() = 0; - virtual void EnableRendering(const uintptr_t aXWindow, - const bool aShaped) = 0; + virtual void CleanupResources() = 0; + virtual void SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) = 0; // CompositorWidgetDelegate Overrides @@ -74,10 +74,11 @@ class GtkCompositorWidget : public CompositorWidget, // Suspend rendering of this remote widget and clear all resources. // Can be used when underlying window is hidden/unmapped. - void DisableRendering() override; + void CleanupResources() override; // Resume rendering with to given aXWindow (X11) or nsWindow (Wayland). - void EnableRendering(const uintptr_t aXWindow, const bool aShaped) override; + void SetRenderingSurface(const uintptr_t aXWindow, + const bool aShaped) override; // If we fail to set window size (due to different screen scale or so) // we can't paint the frame by compositor. @@ -90,11 +91,6 @@ class GtkCompositorWidget : public CompositorWidget, RefPtr<mozilla::layers::NativeLayerRoot> GetNativeLayerRoot() override; #endif - bool PreRender(WidgetRenderingContext* aContext) override { - return !mIsRenderingSuspended; - } - bool IsHidden() const override { return mIsRenderingSuspended; } - // PlatformCompositorWidgetDelegate Overrides void NotifyClientSizeChanged(const LayoutDeviceIntSize& aClientSize) override; @@ -102,10 +98,10 @@ class GtkCompositorWidget : public CompositorWidget, private: #if defined(MOZ_WAYLAND) - bool ConfigureWaylandBackend(); + void ConfigureWaylandBackend(); #endif #if defined(MOZ_X11) - bool ConfigureX11Backend(Window aXWindow, bool aShaped); + void ConfigureX11Backend(Window aXWindow, bool aShaped); #endif #ifdef MOZ_LOGGING bool IsPopup(); @@ -129,7 +125,6 @@ class GtkCompositorWidget : public CompositorWidget, #ifdef MOZ_WAYLAND RefPtr<mozilla::layers::NativeLayerRootWayland> mNativeLayerRoot; #endif - Atomic<bool> mIsRenderingSuspended{true}; }; } // namespace widget diff --git a/widget/gtk/MPRISServiceHandler.cpp b/widget/gtk/MPRISServiceHandler.cpp index ae1ef8654d..847bf3d78d 100644 --- a/widget/gtk/MPRISServiceHandler.cpp +++ b/widget/gtk/MPRISServiceHandler.cpp @@ -439,10 +439,10 @@ const char* MPRISServiceHandler::DesktopEntry() const { bool MPRISServiceHandler::PressKey(dom::MediaControlKey aKey) const { MOZ_ASSERT(mInitialized); if (!IsMediaKeySupported(aKey)) { - LOGMPRIS("%s is not supported", ToMediaControlKeyStr(aKey)); + LOGMPRIS("%s is not supported", dom::GetEnumString(aKey).get()); return false; } - LOGMPRIS("Press %s", ToMediaControlKeyStr(aKey)); + LOGMPRIS("Press %s", dom::GetEnumString(aKey).get()); EmitEvent(aKey); return true; } @@ -861,7 +861,7 @@ bool MPRISServiceHandler::EmitSupportedKeyChanged(dom::MediaControlKey aKey, bool aSupported) const { auto it = gKeyProperty.find(aKey); if (it == gKeyProperty.end()) { - LOGMPRIS("No property for %s", ToMediaControlKeyStr(aKey)); + LOGMPRIS("No property for %s", dom::GetEnumString(aKey).get()); return false; } diff --git a/widget/gtk/MozContainer.cpp b/widget/gtk/MozContainer.cpp index 775ae0488f..446dc97a66 100644 --- a/widget/gtk/MozContainer.cpp +++ b/widget/gtk/MozContainer.cpp @@ -87,19 +87,18 @@ void moz_container_class_init(MozContainerClass* klass) { GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */ GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + widget_class->map = moz_container_map; widget_class->realize = moz_container_realize; widget_class->unrealize = moz_container_unrealize; widget_class->destroy = moz_container_destroy; #ifdef MOZ_WAYLAND if (mozilla::widget::GdkIsWaylandDisplay()) { - widget_class->map = moz_container_wayland_map; widget_class->size_allocate = moz_container_wayland_size_allocate; widget_class->map_event = moz_container_wayland_map_event; widget_class->unmap = moz_container_wayland_unmap; } else { #endif - widget_class->map = moz_container_map; widget_class->size_allocate = moz_container_size_allocate; widget_class->unmap = moz_container_unmap; #ifdef MOZ_WAYLAND @@ -130,6 +129,10 @@ void moz_container_map(GtkWidget* widget) { if (gtk_widget_get_has_window(widget)) { gdk_window_show(gtk_widget_get_window(widget)); } + + // Enable rendering to nsWindow/MozContainer + nsWindow* window = moz_container_get_nsWindow(MOZ_CONTAINER(widget)); + window->OnMap(); } void moz_container_unmap(GtkWidget* widget) { @@ -138,9 +141,9 @@ void moz_container_unmap(GtkWidget* widget) { LOGCONTAINER(("moz_container_unmap() [%p]", (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget)))); - // Disable rendering to MozContainer before we unmap it. + // Disable rendering to nsWindow/MozContainer before we really unmap it. nsWindow* window = moz_container_get_nsWindow(MOZ_CONTAINER(widget)); - window->DisableRendering(); + window->OnUnmap(); gtk_widget_set_mapped(widget, FALSE); diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp index 8490f25599..39e8a48390 100644 --- a/widget/gtk/MozContainerWayland.cpp +++ b/widget/gtk/MozContainerWayland.cpp @@ -419,21 +419,6 @@ gboolean moz_container_wayland_map_event(GtkWidget* widget, return FALSE; } -void moz_container_wayland_map(GtkWidget* widget) { - LOGCONTAINER("%s [%p]\n", __FUNCTION__, - (void*)moz_container_get_nsWindow(MOZ_CONTAINER(widget))); - - g_return_if_fail(IS_MOZ_CONTAINER(widget)); - - // We need to mark MozContainer as mapped to make sure - // moz_container_wayland_unmap() is called on hide/withdraw. - gtk_widget_set_mapped(widget, TRUE); - - if (gtk_widget_get_has_window(widget)) { - gdk_window_show(gtk_widget_get_window(widget)); - } -} - void moz_container_wayland_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { GtkAllocation tmp_allocation; diff --git a/widget/gtk/PCompositorWidget.ipdl b/widget/gtk/PCompositorWidget.ipdl index d554a33144..e083e8d4ef 100644 --- a/widget/gtk/PCompositorWidget.ipdl +++ b/widget/gtk/PCompositorWidget.ipdl @@ -22,8 +22,8 @@ parent: async __delete__(); async NotifyClientSizeChanged(LayoutDeviceIntSize aClientSize); - async DisableRendering(); - async EnableRendering(uintptr_t aXWindow, bool aShaped); + async CleanupResources(); + async SetRenderingSurface(uintptr_t aXWindow, bool aShaped); child: diff --git a/widget/gtk/ScreenHelperGTK.cpp b/widget/gtk/ScreenHelperGTK.cpp index 60be234c60..313bf54b1e 100644 --- a/widget/gtk/ScreenHelperGTK.cpp +++ b/widget/gtk/ScreenHelperGTK.cpp @@ -269,7 +269,7 @@ static already_AddRefed<Screen> MakeScreenGtk(GdkScreen* aScreen, contentsScale.scale, defaultCssScale.scale, dpi, refreshRate); return MakeAndAddRef<Screen>(rect, availRect, pixelDepth, pixelDepth, refreshRate, contentsScale, defaultCssScale, dpi, - Screen::IsPseudoDisplay::No); + Screen::IsPseudoDisplay::No, Screen::IsHDR::No); } void ScreenGetterGtk::RefreshScreens() { diff --git a/widget/gtk/WindowSurfaceProvider.cpp b/widget/gtk/WindowSurfaceProvider.cpp index 82f9029315..c8b2c5a7d6 100644 --- a/widget/gtk/WindowSurfaceProvider.cpp +++ b/widget/gtk/WindowSurfaceProvider.cpp @@ -11,6 +11,7 @@ #include "mozilla/gfx/Logging.h" #include "mozilla/layers/LayersTypes.h" #include "nsWindow.h" +#include "mozilla/ScopeExit.h" #ifdef MOZ_WAYLAND # include "mozilla/StaticPrefs_widget.h" @@ -129,13 +130,13 @@ RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() { // 2. XPutImage # ifdef MOZ_HAVE_SHMIMAGE if (!mIsShaped && nsShmImage::UseShm()) { - LOG(("Drawing to Window 0x%lx will use MIT-SHM\n", mXWindow)); + LOG(("Drawing to Window 0x%lx will use MIT-SHM\n", (Window)mXWindow)); return MakeRefPtr<WindowSurfaceX11SHM>(DefaultXDisplay(), mXWindow, mXVisual, mXDepth); } # endif // MOZ_HAVE_SHMIMAGE - LOG(("Drawing to Window 0x%lx will use XPutImage\n", mXWindow)); + LOG(("Drawing to Window 0x%lx will use XPutImage\n", (Window)mXWindow)); return MakeRefPtr<WindowSurfaceX11Image>(DefaultXDisplay(), mXWindow, mXVisual, mXDepth, mIsShaped); } @@ -143,6 +144,11 @@ RefPtr<WindowSurface> WindowSurfaceProvider::CreateWindowSurface() { MOZ_RELEASE_ASSERT(false); } +// We need to ignore thread safety checks here. We need to hold mMutex +// between StartRemoteDrawingInRegion()/EndRemoteDrawingInRegion() calls +// which confuses it. +MOZ_PUSH_IGNORE_THREAD_SAFETY + already_AddRefed<gfx::DrawTarget> WindowSurfaceProvider::StartRemoteDrawingInRegion( const LayoutDeviceIntRegion& aInvalidRegion, @@ -151,7 +157,13 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion( return nullptr; } - MutexAutoLock lock(mMutex); + // We return a reference to mWindowSurface inside draw target so we need to + // hold the mutex untill EndRemoteDrawingInRegion() call where draw target + // is returned. + // If we return null dt, EndRemoteDrawingInRegion() won't be called to + // release mutex. + mMutex.Lock(); + auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); }); if (!mWindowSurfaceValid) { mWindowSurface = nullptr; @@ -178,12 +190,20 @@ WindowSurfaceProvider::StartRemoteDrawingInRegion( dt = mWindowSurface->Lock(aInvalidRegion); } #endif + if (dt) { + // We have valid dt, mutex will be released in EndRemoteDrawingInRegion(). + unlockMutex.release(); + } + return dt.forget(); } void WindowSurfaceProvider::EndRemoteDrawingInRegion( gfx::DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) { - MutexAutoLock lock(mMutex); + // Unlock mutex from StartRemoteDrawingInRegion(). + mMutex.AssertCurrentThreadOwns(); + auto unlockMutex = MakeScopeExit([&] { mMutex.Unlock(); }); + // Commit to mWindowSurface only if we have a valid one. if (!mWindowSurface || !mWindowSurfaceValid) { return; @@ -218,5 +238,7 @@ void WindowSurfaceProvider::EndRemoteDrawingInRegion( mWindowSurface->Commit(aInvalidRegion); } +MOZ_POP_THREAD_SAFETY + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/WindowSurfaceProvider.h b/widget/gtk/WindowSurfaceProvider.h index 6aaf50e2da..44a0d78ec6 100644 --- a/widget/gtk/WindowSurfaceProvider.h +++ b/widget/gtk/WindowSurfaceProvider.h @@ -81,7 +81,7 @@ class WindowSurfaceProvider final { */ mozilla::Mutex mMutex MOZ_UNANNOTATED; // WindowSurface needs to be re-created as underlying window was changed. - mozilla::Atomic<bool> mWindowSurfaceValid; + bool mWindowSurfaceValid; #ifdef MOZ_WAYLAND RefPtr<nsWindow> mWidget; // WindowSurfaceProvider is owned by GtkCompositorWidget so we don't need @@ -91,7 +91,12 @@ class WindowSurfaceProvider final { #ifdef MOZ_X11 bool mIsShaped; int mXDepth; - Window mXWindow; + // Make mXWindow atomic to allow it read from different threads + // and make tsan happy. + // We don't care much about actual mXWindow value (it may be valid XWindow or + // nullptr) because we invalidate mXWindow at compositor/renderer thread + // before it's release in unmap handler. + Atomic<Window, Relaxed> mXWindow; Visual* mXVisual; #endif }; diff --git a/widget/gtk/nsDragService.cpp b/widget/gtk/nsDragService.cpp index 0135f97a4e..f435cdf2a0 100644 --- a/widget/gtk/nsDragService.cpp +++ b/widget/gtk/nsDragService.cpp @@ -607,6 +607,14 @@ nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) { mTargetDragContextForRemote = nullptr; mTargetWindow = nullptr; mPendingWindow = nullptr; + mPendingDragContext = nullptr; + mPendingWindowPoint = {}; + mScheduledTask = eDragTaskNone; + if (mTaskSource) { + g_source_remove(mTaskSource); + mTaskSource = 0; + } + mPendingTime = 0; mCachedDragContext = 0; return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers); @@ -1208,19 +1216,57 @@ void nsDragService::TargetDataReceived(GtkWidget* aWidget, GdkAtom target = gtk_selection_data_get_target(aSelectionData); GUniquePtr<gchar> name(gdk_atom_name(target)); nsDependentCString flavor(name.get()); - if (gtk_targets_include_uri(&target, 1)) { - GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData)); + // For the vnd.portal.filetransfer and vnd.portal.files we receive numeric + // id when it's a local file. The numeric id is then used by + // gtk_selection_data_get_uris implementation to get the actual file + // available in the flatpak environment. + // + // However due to GTK implementation also for example the uris like https + // are also provided by the vnd.portal.filetransfer target. In this case the + // call gtk_selection_data_get_uris fails. This is a bug in the gtk. + // To workaround it we try to create the valid uri and only if we fail + // we try to use the gtk_selection_data_get_uris. We ignore the valid uris + // for the vnd.portal.file* targets. + // See: https://gitlab.gnome.org/GNOME/gtk/-/issues/6563 + if (flavor.Equals(gPortalFile) || flavor.Equals(gPortalFileTransfer)) { + const guchar* data = gtk_selection_data_get_data(aSelectionData); + if (!data || data[0] == '\0') { + LOGDRAGSERVICE(" Empty data!\n"); + return; + } + nsCOMPtr<nsIURI> sourceURI; + nsresult rv = + NS_NewURI(getter_AddRefs(sourceURI), (const gchar*)data, nullptr); + if (NS_FAILED(rv)) { + // We're unable to get the URI, we'll use the + // gtk_selection_data_get_uris to get the actual file location + // accessible from the Firefox runtime. + GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData)); + uris.swap(mTargetDragUris); + } else { + LOGDRAGSERVICE( + " got valid uri for MIME %s - this is bug in GTK - expected " + "numeric value for portal, got %s\n", + flavor.get(), data); + return; + } + + } else { + GUniquePtr<gchar*> uris(gtk_selection_data_get_uris(aSelectionData)); + uris.swap(mTargetDragUris); + } #ifdef MOZ_LOGGING if (MOZ_LOG_TEST(gWidgetDragLog, mozilla::LogLevel::Debug)) { - gchar** uri = uris.get(); + gchar** uri = mTargetDragUris.get(); while (uri && *uri) { LOGDRAGSERVICE(" got uri %s, MIME %s", *uri, flavor.get()); uri++; } } + #endif - uris.swap(mTargetDragUris); + if (mTargetDragUris) { mCachedUris.InsertOrUpdate( flavor, GUniquePtr<gchar*>(g_strdupv(mTargetDragUris.get()))); @@ -2557,6 +2603,7 @@ gboolean nsDragService::RunScheduledTask() { // Nothing more to do // Returning false removes the task source from the event loop. mTaskSource = 0; + mPendingDragContext = nullptr; return FALSE; } diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 040d942cdf..8702c154d6 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -800,6 +800,7 @@ nsresult nsLookAndFeel::PerThemeData::GetColor(ColorID aID, case ColorID::SpellCheckerUnderline: case ColorID::Mark: case ColorID::Marktext: + case ColorID::MozAutofillBackground: aColor = GetStandinForNativeColor( aID, mIsDark ? ColorScheme::Dark : ColorScheme::Light); break; @@ -863,9 +864,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::CaretWidth: aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; case IntID::SelectTextfieldsOnKeyFocus: { GtkSettings* settings; gboolean select_on_focus; diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 16945349bb..78d4e925fe 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -167,9 +167,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, ElementState elementState = GetContentState(aFrame, aAppearance); if (aState) { memset(aState, 0, sizeof(GtkWidgetState)); - - // For XUL checkboxes and radio buttons, the state of the parent - // determines our state. if (aWidgetFlags) { if (elementState.HasState(ElementState::CHECKED)) { *aWidgetFlags |= MOZ_GTK_WIDGET_CHECKED; @@ -241,7 +238,8 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aAppearance == StyleAppearance::MozWindowButtonMinimize || aAppearance == StyleAppearance::MozWindowButtonMaximize || aAppearance == StyleAppearance::MozWindowButtonRestore) { - aState->backdrop = !nsWindow::GetTopLevelWindowActiveState(aFrame); + aState->backdrop = aFrame->PresContext()->Document()->State().HasState( + dom::DocumentState::WINDOW_INACTIVE); } } @@ -884,11 +882,6 @@ LayoutDeviceIntMargin nsNativeThemeGTK::GetWidgetBorder( CSSIntMargin result; GtkTextDirection direction = GetTextDirection(aFrame); switch (aAppearance) { - case StyleAppearance::Toolbox: - // gtk has no toolbox equivalent. So, although we map toolbox to - // gtk's 'toolbar' for purposes of painting the widget background, - // we don't use the toolbar border for toolbox. - break; case StyleAppearance::Dualbutton: // TOOLBAR_DUAL_BUTTON is an interesting case. We want a border to draw // around the entire button + dropdown, and also an inner border if you're @@ -1169,9 +1162,7 @@ nsNativeThemeGTK::WidgetStateChanged(nsIFrame* aFrame, } // Some widget types just never change state. - if (aAppearance == StyleAppearance::Toolbox || - aAppearance == StyleAppearance::Toolbar || - aAppearance == StyleAppearance::Progresschunk || + if (aAppearance == StyleAppearance::Progresschunk || aAppearance == StyleAppearance::ProgressBar || aAppearance == StyleAppearance::Tooltip || aAppearance == StyleAppearance::MozWindowDecorations) { @@ -1244,7 +1235,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Button: case StyleAppearance::Radio: case StyleAppearance::Checkbox: - case StyleAppearance::Toolbox: // N/A case StyleAppearance::Toolbarbutton: case StyleAppearance::Dualbutton: // so we can override the border with 0 case StyleAppearance::ToolbarbuttonDropdown: diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index e84044990c..0a78d0c8ec 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -187,8 +187,6 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor); /* callbacks from widgets */ static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr); static gboolean configure_event_cb(GtkWidget* widget, GdkEventConfigure* event); -static void widget_map_cb(GtkWidget* widget); -static void widget_unmap_cb(GtkWidget* widget); static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation); static void toplevel_window_size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation); @@ -392,11 +390,11 @@ static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) { nsWindow::nsWindow() : mTitlebarRectMutex("nsWindow::mTitlebarRectMutex"), - mDestroyMutex("nsWindow::mDestroyMutex"), + mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"), + mIsMapped(false), mIsDestroyed(false), mIsShown(false), mNeedsShow(false), - mIsMapped(false), mEnabled(true), mCreated(false), mHandleTouchEvent(false), @@ -581,7 +579,6 @@ void nsWindow::Destroy() { LOG("nsWindow::Destroy\n"); - MutexAutoLock lock(mDestroyMutex); mIsDestroyed = true; mCreated = false; @@ -612,6 +609,9 @@ void nsWindow::Destroy() { NativeShow(false); + MOZ_ASSERT(!gtk_widget_get_mapped(mShell)); + MOZ_ASSERT(!gtk_widget_get_mapped(GTK_WIDGET(mContainer))); + ClearTransparencyBitmap(); DestroyLayerManager(); @@ -3165,23 +3165,34 @@ void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) { LOG(" widget now has focus in SetFocus()"); } +void nsWindow::ResetScreenBounds() { + mGdkWindowOrigin.reset(); + mGdkWindowRootOrigin.reset(); +} + LayoutDeviceIntRect nsWindow::GetScreenBounds() { if (!mGdkWindow) { return mBounds; } const LayoutDeviceIntPoint origin = [&] { - gint x, y; - gdk_window_get_root_origin(mGdkWindow, &x, &y); + GdkPoint origin; + + if (mGdkWindowRootOrigin.isSome()) { + origin = mGdkWindowRootOrigin.value(); + } else { + gdk_window_get_root_origin(mGdkWindow, &origin.x, &origin.y); + mGdkWindowRootOrigin = Some(origin); + } // Workaround for https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4820 // Bug 1775017 Gtk < 3.24.35 returns scaled values for // override redirected window on X11. if (gtk_check_version(3, 24, 35) != nullptr && GdkIsX11Display() && gdk_window_get_window_type(mGdkWindow) == GDK_WINDOW_TEMP) { - return LayoutDeviceIntPoint(x, y); + return LayoutDeviceIntPoint(origin.x, origin.y); } - return GdkPointToDevicePixels({x, y}); + return GdkPointToDevicePixels(origin); }(); // mBounds.Size() is the window bounds, not the window-manager frame @@ -3237,6 +3248,7 @@ void nsWindow::RecomputeClientOffset(bool aNotify) { gboolean nsWindow::OnPropertyNotifyEvent(GtkWidget* aWidget, GdkEventProperty* aEvent) { if (aEvent->atom == gdk_atom_intern("_NET_FRAME_EXTENTS", FALSE)) { + ResetScreenBounds(); RecomputeClientOffset(/* aNotify = */ true); return FALSE; } @@ -3395,40 +3407,37 @@ void* nsWindow::GetNativeData(uint32_t aDataType) { case NS_NATIVE_OPENGL_CONTEXT: return nullptr; case NS_NATIVE_EGL_WINDOW: { + // On X11 we call it: + // 1) If window is mapped on OnMap() by nsWindow::ResumeCompositorImpl(), + // new EGLSurface/XWindow is created. + // 2) If window is hidden on OnUnmap(), we replace EGLSurface/XWindow + // by offline surface and release XWindow. + + // On Wayland it: + // 1) If window is mapped on OnMap(), we request frame callback + // at MozContainer. If we get frame callback at MozContainer, + // nsWindow::ResumeCompositorImpl() is called from it + // and EGLSurface/wl_surface is created. + // 2) If window is hidden on OnUnmap(), we replace EGLSurface/wl_surface + // by offline surface and release XWindow. + + // If nsWindow is already destroyed, don't try to get EGL window at all, + // we're going to be deleted anyway. + MutexAutoLock lock(mWindowVisibilityMutex); void* eglWindow = nullptr; - - // We can't block on mutex here as it leads to a deadlock: - // 1) mutex is taken at nsWindow::Destroy() - // 2) NS_NATIVE_EGL_WINDOW is called from compositor/rendering thread, - // blocking on mutex. - // 3) DestroyCompositor() is called by nsWindow::Destroy(). As a sync - // call it waits to compositor/rendering threads, - // but they're blocked at 2). - // It's fine if we return null EGL window during DestroyCompositor(), - // in such case compositor painting is skipped. - if (mDestroyMutex.TryLock()) { - if (mGdkWindow && !mIsDestroyed) { + if (mIsMapped && !mIsDestroyed) { #ifdef MOZ_X11 - if (GdkIsX11Display()) { - eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow); - } + if (GdkIsX11Display()) { + eglWindow = (void*)GDK_WINDOW_XID(mGdkWindow); + } #endif #ifdef MOZ_WAYLAND - if (GdkIsWaylandDisplay()) { - bool hiddenWindow = - mCompositorWidgetDelegate && - mCompositorWidgetDelegate->AsGtkCompositorWidget() && - mCompositorWidgetDelegate->AsGtkCompositorWidget()->IsHidden(); - if (!hiddenWindow) { - eglWindow = moz_container_wayland_get_egl_window( - mContainer, FractionalScaleFactor()); - } - } -#endif + if (GdkIsWaylandDisplay()) { + eglWindow = moz_container_wayland_get_egl_window( + mContainer, FractionalScaleFactor()); } - mDestroyMutex.Unlock(); +#endif } - LOG("Get NS_NATIVE_EGL_WINDOW mGdkWindow %p returned eglWindow %p", mGdkWindow, eglWindow); return eglWindow; @@ -3556,11 +3565,16 @@ LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() { if (IsWaylandPopup() && !mPopupUseMoveToRect) { return mBounds.TopLeft(); } - nsIntPoint origin(0, 0); - if (mGdkWindow) { - gdk_window_get_origin(mGdkWindow, &origin.x.value, &origin.y.value); + + GdkPoint origin{}; + if (mGdkWindowOrigin.isSome()) { + origin = mGdkWindowOrigin.value(); + } else if (mGdkWindow) { + gdk_window_get_origin(mGdkWindow, &origin.x, &origin.y); + mGdkWindowOrigin = Some(origin); } - return GdkPointToDevicePixels({origin.x, origin.y}); + + return GdkPointToDevicePixels(origin); } void nsWindow::CaptureRollupEvents(bool aDoCapture) { @@ -3749,6 +3763,8 @@ void nsWindow::RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion) { KnowsCompositor* knowsCompositor = renderer->AsKnowsCompositor(); if (knowsCompositor && layerManager && mCompositorSession) { + LOG("nsWindow::RequestRepaint()"); + if (!mConfiguredClearColor && !IsPopup()) { layerManager->WrBridge()->SendSetDefaultClearColor(LookAndFeel::Color( LookAndFeel::ColorID::Window, PreferenceSheet::ColorSchemeForChrome(), @@ -3764,9 +3780,13 @@ void nsWindow::RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion) { } gboolean nsWindow::OnExposeEvent(cairo_t* cr) { + LOG("nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]", mGdkWindow, + GetX11Window()); + // This might destroy us. NotifyOcclusionState(OcclusionState::VISIBLE); if (mIsDestroyed) { + LOG("destroyed after NotifyOcclusionState()"); return FALSE; } @@ -3774,26 +3794,27 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { // May run event loop and destroy us. MaybeDispatchResized(); if (mIsDestroyed) { + LOG("destroyed after MaybeDispatchResized()"); return FALSE; } // Windows that are not visible will be painted after they become visible. if (!mGdkWindow || !mHasMappedToplevel) { + LOG("quit, !mGdkWindow || !mHasMappedToplevel"); return FALSE; } #ifdef MOZ_WAYLAND if (GdkIsWaylandDisplay() && !moz_container_wayland_can_draw(mContainer)) { + LOG("quit, !moz_container_wayland_can_draw()"); return FALSE; } #endif if (!GetListener()) { + LOG("quit, !GetListener()"); return FALSE; } - LOG("nsWindow::OnExposeEvent GdkWindow [%p] XID [0x%lx]", mGdkWindow, - GetX11Window()); - LayoutDeviceIntRegion exposeRegion; if (!ExtractExposeRegion(exposeRegion, cr)) { LOG(" no rects, quit"); @@ -3816,19 +3837,24 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { // If the window has been destroyed during the will paint notification, // there is nothing left to do. if (!mGdkWindow || mIsDestroyed) { + LOG("quit, !mGdkWindow || mIsDestroyed"); return TRUE; } // Re-get all rendering components since the will paint notification // might have killed it. nsIWidgetListener* listener = GetListener(); - if (!listener) return FALSE; + if (!listener) { + LOG("quit, !listener"); + return FALSE; + } WindowRenderer* renderer = GetWindowRenderer(); WebRenderLayerManager* layerManager = renderer->AsWebRender(); KnowsCompositor* knowsCompositor = renderer->AsKnowsCompositor(); if (knowsCompositor && layerManager && layerManager->NeedsComposite()) { + LOG("needs composite, ScheduleComposite() call"); layerManager->ScheduleComposite(wr::RenderReasons::WIDGET); layerManager->SetNeedsComposite(false); } @@ -3859,11 +3885,13 @@ gboolean nsWindow::OnExposeEvent(cairo_t* cr) { } if (region.IsEmpty()) { + LOG("quit, region.IsEmpty()"); return TRUE; } // If this widget uses OMTC... if (renderer->GetBackendType() == LayersBackend::LAYERS_WR) { + LOG("redirect painting to OMTC rendering..."); listener->PaintWindow(this, region); // Re-get the listener since the will paint notification might have @@ -4048,6 +4076,8 @@ gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget, mPendingConfigures--; } + ResetScreenBounds(); + // Don't fire configure event for scale changes, we handle that // OnScaleChanged event. Skip that for toplevel windows only. if (mGdkWindow && IsTopLevelWindowType()) { @@ -4103,45 +4133,12 @@ gboolean nsWindow::OnConfigureEvent(GtkWidget* aWidget, return FALSE; } -void nsWindow::OnMap() { - LOG("nsWindow::OnMap"); - // Gtk mapped widget to screen. Configure underlying GdkWindow properly - // as our rendering target. - // This call means we have X11 (or Wayland) window we can render to by GL - // so we need to notify compositor about it. - mIsMapped = true; - ConfigureGdkWindow(); -} - -void nsWindow::OnUnmap() { - LOG("nsWindow::OnUnmap"); - - mIsMapped = false; - - if (mSourceDragContext) { - static auto sGtkDragCancel = - (void (*)(GdkDragContext*))dlsym(RTLD_DEFAULT, "gtk_drag_cancel"); - if (sGtkDragCancel) { - sGtkDragCancel(mSourceDragContext); - mSourceDragContext = nullptr; - } - } - - // We don't have valid XWindow any more, - // so clear stored ones at GtkCompositorWidget() for OMTC rendering - // and mSurfaceProvider for legacy rendering. - if (GdkIsX11Display()) { - mSurfaceProvider.CleanupResources(); - if (mCompositorWidgetDelegate) { - mCompositorWidgetDelegate->DisableRendering(); - } - } -} - void nsWindow::OnSizeAllocate(GtkAllocation* aAllocation) { LOG("nsWindow::OnSizeAllocate %d,%d -> %d x %d\n", aAllocation->x, aAllocation->y, aAllocation->width, aAllocation->height); + ResetScreenBounds(); + // Client offset are updated by _NET_FRAME_EXTENTS on X11 when system titlebar // is enabled. In either cases (Wayland or system titlebar is off on X11) // we don't get _NET_FRAME_EXTENTS X11 property notification so we derive @@ -4650,9 +4647,16 @@ bool nsWindow::DoTitlebarAction(LookAndFeel::TitlebarEvent aEvent, LOG(" action menu"); TryToShowNativeWindowMenu(aButtonEvent); break; - // Lower is part of gtk_surface1 protocol which we can't support - // as Gtk keeps it private. So emulate it by minimize. case LookAndFeel::TitlebarAction::WindowLower: + LOG(" action lower"); + // Lower is part of gtk_surface1 protocol which we can't support + // as Gtk keeps it private. So emulate it by minimize. + if (GdkIsWaylandDisplay()) { + SetSizeMode(nsSizeMode_Minimized); + } else { + gdk_window_lower(GetToplevelGdkWindow()); + } + break; case LookAndFeel::TitlebarAction::WindowMinimize: LOG(" action minimize"); SetSizeMode(nsSizeMode_Minimized); @@ -5425,8 +5429,8 @@ void nsWindow::OnScaleChanged(bool aNotify) { NotifyAPZOfDPIChange(); - LOG("OnScaleChanged %d, %f -> %d, %f\n", int(mCeiledScaleFactor), - mFractionalScaleFactor, newCeiled, newFractional); + LOG("OnScaleChanged %d, %f -> %d, %f Notify %d\n", int(mCeiledScaleFactor), + mFractionalScaleFactor, newCeiled, newFractional, aNotify); mCeiledScaleFactor = newCeiled; mFractionalScaleFactor = newFractional; @@ -5822,8 +5826,8 @@ bool nsWindow::GetShapedState() { } void nsWindow::ConfigureCompositor() { - MOZ_DIAGNOSTIC_ASSERT(mCompositorState == COMPOSITOR_ENABLED); MOZ_DIAGNOSTIC_ASSERT(mIsMapped); + MOZ_DIAGNOSTIC_ASSERT(mCompositorState == COMPOSITOR_ENABLED); LOG("nsWindow::ConfigureCompositor()"); auto startCompositing = [self = RefPtr{this}, this]() -> void { @@ -5833,7 +5837,7 @@ void nsWindow::ConfigureCompositor() { // too late if (mIsDestroyed || !mIsMapped) { LOG(" quit, mIsDestroyed = %d mIsMapped = %d", !!mIsDestroyed, - mIsMapped); + !!mIsMapped); return; } // Compositor will be resumed later by ResumeCompositorFlickering(). @@ -5860,73 +5864,6 @@ void nsWindow::ConfigureCompositor() { } } -void nsWindow::ConfigureGdkWindow() { - LOG("nsWindow::ConfigureGdkWindow()"); - - EnsureGdkWindow(); - OnScaleChanged(/* aNotify = */ false); - - if (mIsAlert) { - gdk_window_set_override_redirect(mGdkWindow, TRUE); - } - -#ifdef MOZ_X11 - if (GdkIsX11Display()) { - mSurfaceProvider.Initialize(GetX11Window(), GetShapedState()); - - // Set window manager hint to keep fullscreen windows composited. - // - // If the window were to get unredirected, there could be visible - // tearing because Gecko does not align its framebuffer updates with - // vblank. - SetCompositorHint(GTK_WIDGET_COMPOSIDED_ENABLED); - } -#endif -#ifdef MOZ_WAYLAND - if (GdkIsWaylandDisplay()) { - mSurfaceProvider.Initialize(this); - } -#endif - - if (mIsDragPopup) { - if (GdkIsWaylandDisplay()) { - // Disable painting to the widget on Wayland as we paint directly to the - // widget. Wayland compositors does not paint wl_subsurface - // of D&D widget. - if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { - GtkWidgetDisableUpdates(parent); - } - GtkWidgetDisableUpdates(mShell); - GtkWidgetDisableUpdates(GTK_WIDGET(mContainer)); - } else { - // Disable rendering of parent container on X11 to avoid flickering. - if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { - gtk_widget_set_opacity(parent, 0.0); - } - } - } - - if (mWindowType == WindowType::Popup) { - if (mNoAutoHide) { - gint wmd = ConvertBorderStyles(mBorderStyle); - if (wmd != -1) { - gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd); - } - } - // If the popup ignores mouse events, set an empty input shape. - SetInputRegion(mInputRegion); - } - - RefreshWindowClass(); - - // We're not mapped yet but we have already created compositor. - if (mCompositorWidgetDelegate) { - ConfigureCompositor(); - } - - LOG(" finished, new GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window()); -} - nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, const LayoutDeviceIntRect& aRect, widget::InitData* aInitData) { @@ -6355,8 +6292,6 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, G_CALLBACK(settings_xft_dpi_changed_cb), this); // Widget signals - g_signal_connect(mContainer, "map", G_CALLBACK(widget_map_cb), nullptr); - g_signal_connect(mContainer, "unmap", G_CALLBACK(widget_unmap_cb), nullptr); g_signal_connect_after(mContainer, "size_allocate", G_CALLBACK(size_allocate_cb), nullptr); g_signal_connect(mContainer, "hierarchy-changed", @@ -6567,6 +6502,10 @@ void nsWindow::NativeMoveResize(bool aMoved, bool aResized) { LOG("nsWindow::NativeMoveResize move %d resize %d to %d,%d -> %d x %d\n", aMoved, aResized, topLeft.x, topLeft.y, size.width, size.height); + if (aMoved) { + ResetScreenBounds(); + } + if (aResized && !AreBoundsSane()) { LOG(" bounds are insane, hidding the window"); // We have been resized but to incorrect size. @@ -6648,8 +6587,8 @@ void nsWindow::PauseCompositorFlickering() { CompositorBridgeChild* remoteRenderer = GetRemoteRenderer(); if (remoteRenderer) { - remoteRenderer->SendPause(); mCompositorState = COMPOSITOR_PAUSED_FLICKERING; + remoteRenderer->SendPause(); mCompositorPauseTimeoutID = (int)g_timeout_add( COMPOSITOR_PAUSE_TIMEOUT, [](void* data) -> gint { @@ -6703,7 +6642,8 @@ void nsWindow::ResumeCompositorImpl() { LOG("nsWindow::ResumeCompositorImpl()\n"); MOZ_DIAGNOSTIC_ASSERT(mCompositorWidgetDelegate); - mCompositorWidgetDelegate->EnableRendering(GetX11Window(), GetShapedState()); + mCompositorWidgetDelegate->SetRenderingSurface(GetX11Window(), + GetShapedState()); // As WaylandStartVsync needs mCompositorWidgetDelegate this is the right // time to start it. @@ -8287,25 +8227,6 @@ static gboolean configure_event_cb(GtkWidget* widget, return window->OnConfigureEvent(widget, event); } -// Some Gtk widget code may call gtk_widget_unrealize() which destroys -// mGdkWindow. We need to listen on this signal and re-create -// mGdkWindow when we're already mapped. -static void widget_map_cb(GtkWidget* widget) { - RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); - if (!window) { - return; - } - window->OnMap(); -} - -static void widget_unmap_cb(GtkWidget* widget) { - RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); - if (!window) { - return; - } - window->OnUnmap(); -} - static void size_allocate_cb(GtkWidget* widget, GtkAllocation* allocation) { RefPtr<nsWindow> window = get_window_for_gtk_widget(widget); if (!window) { @@ -9124,7 +9045,7 @@ void nsWindow::DidGetNonBlankPaint() { void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) { LOG("nsWindow::SetCompositorWidgetDelegate %p mIsMapped %d " "mCompositorWidgetDelegate %p\n", - delegate, mIsMapped, mCompositorWidgetDelegate); + delegate, !!mIsMapped, mCompositorWidgetDelegate); MOZ_RELEASE_ASSERT(NS_IsMainThread()); if (delegate) { @@ -9962,26 +9883,6 @@ void nsWindow::UnlockNativePointer() { } #endif -bool nsWindow::GetTopLevelWindowActiveState(nsIFrame* aFrame) { - // Used by window frame and button box rendering. We can end up in here in - // the content process when rendering one of these moz styles freely in a - // page. Fail in this case, there is no applicable window focus state. - if (!XRE_IsParentProcess()) { - return false; - } - // All headless windows are considered active so they are painted. - if (gfxPlatform::IsHeadless()) { - return true; - } - // Get the widget. nsIFrame's GetNearestWidget walks up the view chain - // until it finds a real window. - nsWindow* window = static_cast<nsWindow*>(aFrame->GetNearestWidget()); - if (!window) { - return false; - } - return !window->mTitlebarBackdropState; -} - static nsIFrame* FindTitlebarFrame(nsIFrame* aFrame) { for (nsIFrame* childFrame : aFrame->PrincipalChildList()) { StyleAppearance appearance = @@ -10072,41 +9973,34 @@ nsWindow* nsWindow::GetFocusedWindow() { return gFocusWindow; } #ifdef MOZ_WAYLAND bool nsWindow::SetEGLNativeWindowSize( const LayoutDeviceIntSize& aEGLWindowSize) { - if (!GdkIsWaylandDisplay()) { + if (!GdkIsWaylandDisplay() || !mIsMapped) { return true; } - // SetEGLNativeWindowSize() is called from renderer/compositor thread, - // make sure nsWindow is not destroyed. - bool paint = false; + if (mCompositorState == COMPOSITOR_PAUSED_FLICKERING) { + LOG("nsWindow::SetEGLNativeWindowSize() return, " + "COMPOSITOR_PAUSED_FLICKERING is set"); + return false; + } - // See NS_NATIVE_EGL_WINDOW why we can't block here. - if (mDestroyMutex.TryLock()) { - if (!mIsDestroyed) { - gint scale = GdkCeiledScaleFactor(); + gint scale = GdkCeiledScaleFactor(); # ifdef MOZ_LOGGING - if (LOG_ENABLED()) { - static uintptr_t lastSizeLog = 0; - uintptr_t sizeLog = uintptr_t(this) + aEGLWindowSize.width + - aEGLWindowSize.height + scale + - aEGLWindowSize.width / scale + - aEGLWindowSize.height / scale; - if (lastSizeLog != sizeLog) { - lastSizeLog = sizeLog; - LOG("nsWindow::SetEGLNativeWindowSize() %d x %d scale %d (unscaled " - "%d x " - "%d)", - aEGLWindowSize.width, aEGLWindowSize.height, scale, - aEGLWindowSize.width / scale, aEGLWindowSize.height / scale); - } - } -# endif - paint = moz_container_wayland_egl_window_set_size( - mContainer, aEGLWindowSize.ToUnknownSize(), scale); + if (LOG_ENABLED()) { + static uintptr_t lastSizeLog = 0; + uintptr_t sizeLog = + uintptr_t(this) + aEGLWindowSize.width + aEGLWindowSize.height + scale + + aEGLWindowSize.width / scale + aEGLWindowSize.height / scale; + if (lastSizeLog != sizeLog) { + lastSizeLog = sizeLog; + LOG("nsWindow::SetEGLNativeWindowSize() %d x %d scale %d (unscaled " + "%d x %d)", + aEGLWindowSize.width, aEGLWindowSize.height, scale, + aEGLWindowSize.width / scale, aEGLWindowSize.height / scale); } - mDestroyMutex.Unlock(); } - return paint; +# endif + return moz_container_wayland_egl_window_set_size( + mContainer, aEGLWindowSize.ToUnknownSize(), scale); } #endif @@ -10123,45 +10017,138 @@ void nsWindow::ClearRenderingQueue() { DestroyLayerManager(); } -void nsWindow::DisableRendering() { - LOG("nsWindow::DisableRendering()"); +// nsWindow::OnMap() / nsWindow::OnUnmap() is called from map/unmap mContainer +// handlers directly as we paint to mContainer. +void nsWindow::OnMap() { + LOG("nsWindow::OnMap"); - if (mGdkWindow) { - g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); - mGdkWindow = nullptr; + { + MutexAutoLock lock(mWindowVisibilityMutex); + mIsMapped = true; + + EnsureGdkWindow(); + OnScaleChanged(/* aNotify = */ false); + + if (mIsAlert) { + gdk_window_set_override_redirect(mGdkWindow, TRUE); + } + +#ifdef MOZ_X11 + if (GdkIsX11Display()) { + mSurfaceProvider.Initialize(GetX11Window(), GetShapedState()); + + // Set window manager hint to keep fullscreen windows composited. + // + // If the window were to get unredirected, there could be visible + // tearing because Gecko does not align its framebuffer updates with + // vblank. + SetCompositorHint(GTK_WIDGET_COMPOSITED_ENABLED); + } +#endif +#ifdef MOZ_WAYLAND + if (GdkIsWaylandDisplay()) { + mSurfaceProvider.Initialize(this); + } +#endif + } + + if (mIsDragPopup) { + if (GdkIsWaylandDisplay()) { + // Disable painting to the widget on Wayland as we paint directly to the + // widget. Wayland compositors does not paint wl_subsurface + // of D&D widget. + if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { + GtkWidgetDisableUpdates(parent); + } + GtkWidgetDisableUpdates(mShell); + GtkWidgetDisableUpdates(GTK_WIDGET(mContainer)); + } else { + // Disable rendering of parent container on X11 to avoid flickering. + if (GtkWidget* parent = gtk_widget_get_parent(mShell)) { + gtk_widget_set_opacity(parent, 0.0); + } + } + } + + if (mWindowType == WindowType::Popup) { + if (mNoAutoHide) { + gint wmd = ConvertBorderStyles(mBorderStyle); + if (wmd != -1) { + gdk_window_set_decorations(mGdkWindow, (GdkWMDecoration)wmd); + } + } + // If the popup ignores mouse events, set an empty input shape. + SetInputRegion(mInputRegion); + } + + RefreshWindowClass(); + + // We're not mapped yet but we have already created compositor. + if (mCompositorWidgetDelegate) { + ConfigureCompositor(); + } + + LOG(" finished, new GdkWindow %p XID 0x%lx\n", mGdkWindow, GetX11Window()); +} + +void nsWindow::OnUnmap() { + LOG("nsWindow::OnUnmap"); + + { + MutexAutoLock lock(mWindowVisibilityMutex); + mIsMapped = false; + + if (mSourceDragContext) { + static auto sGtkDragCancel = + (void (*)(GdkDragContext*))dlsym(RTLD_DEFAULT, "gtk_drag_cancel"); + if (sGtkDragCancel) { + sGtkDragCancel(mSourceDragContext); + mSourceDragContext = nullptr; + } + } + + if (mGdkWindow) { + g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); + mGdkWindow = nullptr; + } + + // Clear resources (mainly XWindow) stored at GtkCompositorWidget. + // It makes sure we don't paint to it when nsWindow becomes hiden/deleted + // and XWindow is released. + if (mCompositorWidgetDelegate) { + mCompositorWidgetDelegate->CleanupResources(); + } + + // Clear nsWindow resources used for old (in-thread) rendering. + mSurfaceProvider.CleanupResources(); } // Until Bug 1654938 is fixed we delete layer manager for hidden popups, // otherwise it can easily hold 1GB+ memory for long time. if (mWindowType == WindowType::Popup) { DestroyLayerManager(); - mSurfaceProvider.CleanupResources(); } else { -#ifdef MOZ_WAYLAND - // Widget is backed by OpenGL EGLSurface created over wl_surface - // owned by mContainer. - // RenderCompositorEGL::Resume() deletes recent EGLSurface based on - // wl_surface owned by mContainer and creates a new fallback EGLSurface. - // Then we can delete wl_surface in moz_container_wayland_unmap(). + // Widget is backed by OpenGL EGLSurface created over wl_surface/XWindow. + // + // RenderCompositorEGL::Resume() deletes recent EGLSurface, + // calls nsWindow::GetNativeData(NS_NATIVE_EGL_WINDOW) from compositor + // thread to get new native rendering surface. + // + // For hidden/unmapped windows we return nullptr NS_NATIVE_EGL_WINDOW at + // nsWindow::GetNativeData() so RenderCompositorEGL::Resume() creates + // offscreen fallback EGLSurface to avoid compositor pause. + // // We don't want to pause compositor as it may lead to whole // browser freeze (Bug 1777664). - /// - // We don't need to do such operation for SW backend as - // WindowSurfaceWaylandMB::Commit() gets wl_surface from - // MozContainer every commit. - if (moz_container_wayland_has_egl_window(mContainer) && - mCompositorWidgetDelegate) { - if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { - // Call DisableRendering() to make GtkCompositorWidget hidden. - // Then SendResume() will create fallback EGLSurface, see - // GLContextEGL::CreateEGLSurfaceForCompositorWidget(). - mCompositorWidgetDelegate->DisableRendering(); - remoteRenderer->SendResume(); - mCompositorWidgetDelegate->EnableRendering(GetX11Window(), - GetShapedState()); - } + // + // If RenderCompositorSWGL compositor is used (SW fallback) + // RenderCompositorSWGL::Resume() only requests full render for next paint + // as wl_surface/XWindow is managed by WindowSurfaceProvider owned + // directly by GtkCompositorWidget and that's covered by + // mCompositorWidgetDelegate->CleanupResources() call above. + if (CompositorBridgeChild* remoteRenderer = GetRemoteRenderer()) { + remoteRenderer->SendResume(); } -#endif } } diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index f8fe344f09..25d68129d8 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -175,6 +175,7 @@ class nsWindow final : public nsBaseWidget { void MoveToWorkspace(const nsAString& workspaceID) override; void Enable(bool aState) override; void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; + void ResetScreenBounds(); LayoutDeviceIntRect GetScreenBounds() override; LayoutDeviceIntRect GetClientBounds() override; LayoutDeviceIntSize GetClientSize() override; @@ -411,7 +412,6 @@ class nsWindow final : public nsBaseWidget { */ static GtkWindowDecoration GetSystemGtkWindowDecoration(); - static bool GetTopLevelWindowActiveState(nsIFrame* aFrame); static bool TitlebarUseShapeMask(); bool IsRemoteContent() { return HasRemoteContent(); } void NativeMoveResizeWaylandPopupCallback(const GdkRectangle* aFinalSize, @@ -461,8 +461,6 @@ class nsWindow final : public nsBaseWidget { // rendering queue blocking (see Bug 1782948). void ClearRenderingQueue(); - void DisableRendering(); - bool ApplyEnterLeaveMutterWorkaround(); void NotifyOcclusionState(mozilla::widget::OcclusionState aState) override; @@ -553,6 +551,9 @@ class nsWindow final : public nsBaseWidget { GtkWidget* mShell = nullptr; MozContainer* mContainer = nullptr; GdkWindow* mGdkWindow = nullptr; + mozilla::Maybe<GdkPoint> mGdkWindowOrigin; + mozilla::Maybe<GdkPoint> mGdkWindowRootOrigin; + PlatformCompositorWidgetDelegate* mCompositorWidgetDelegate = nullptr; mozilla::Atomic<WindowCompositorState, mozilla::Relaxed> mCompositorState{ COMPOSITOR_ENABLED}; @@ -633,10 +634,14 @@ class nsWindow final : public nsBaseWidget { mozilla::Mutex mTitlebarRectMutex; LayoutDeviceIntRect mTitlebarRect MOZ_GUARDED_BY(mTitlebarRectMutex); - mozilla::Mutex mDestroyMutex; + // This mutex protect window visibility changes. + mozilla::Mutex mWindowVisibilityMutex; + // This track real window visibility from OS perspective. + // It's set by OnMap/OnUnmap which is based on Gtk events. + mozilla::Atomic<bool, mozilla::Relaxed> mIsMapped; // Has this widget been destroyed yet? - bool mIsDestroyed : 1; + mozilla::Atomic<bool, mozilla::Relaxed> mIsDestroyed; // mIsShown tracks requested visible status from browser perspective, i.e. // if the window should be visible or now. bool mIsShown : 1; @@ -646,9 +651,6 @@ class nsWindow final : public nsBaseWidget { // that the window is not actually visible but we report to browser that // it is visible (mIsShown == true). bool mNeedsShow : 1; - // This track real window visibility from OS perspective. - // It's set by OnMap/OnUnmap which is based on Gtk events. - bool mIsMapped : 1; // is this widget enabled? bool mEnabled : 1; // has the native window for this been created yet? @@ -802,11 +804,6 @@ class nsWindow final : public nsBaseWidget { void DispatchMissedButtonReleases(GdkEventCrossing* aGdkEvent); - // When window widget gets mapped/unmapped we need to configure - // underlying GdkWindow properly. Otherwise we'll end up with - // rendering to released window. - void ConfigureGdkWindow(); - void ReleaseGdkWindow(); void ConfigureCompositor(); bool IsAlwaysUndecoratedWindow() const; @@ -996,9 +993,9 @@ class nsWindow final : public nsBaseWidget { void RequestRepaint(LayoutDeviceIntRegion& aRepaintRegion); #ifdef MOZ_X11 - typedef enum {GTK_WIDGET_COMPOSIDED_DEFAULT = 0, - GTK_WIDGET_COMPOSIDED_DISABLED = 1, - GTK_WIDGET_COMPOSIDED_ENABLED = 2} WindowComposeRequest; + typedef enum {GTK_WIDGET_COMPOSITED_DEFAULT = 0, + GTK_WIDGET_COMPOSITED_DISABLED = 1, + GTK_WIDGET_COMPOSITED_ENABLED = 2} WindowComposeRequest; void SetCompositorHint(WindowComposeRequest aState); bool ConfigureX11GLVisual(); #endif diff --git a/widget/headless/HeadlessLookAndFeelGTK.cpp b/widget/headless/HeadlessLookAndFeelGTK.cpp index 462a877f34..aa55bcc347 100644 --- a/widget/headless/HeadlessLookAndFeelGTK.cpp +++ b/widget/headless/HeadlessLookAndFeelGTK.cpp @@ -32,9 +32,6 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::CaretWidth: aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; case IntID::SelectTextfieldsOnKeyFocus: aResult = 1; break; diff --git a/widget/headless/HeadlessScreenHelper.cpp b/widget/headless/HeadlessScreenHelper.cpp index 4d8dbfa0d1..5a9643c572 100644 --- a/widget/headless/HeadlessScreenHelper.cpp +++ b/widget/headless/HeadlessScreenHelper.cpp @@ -32,9 +32,10 @@ LayoutDeviceIntRect HeadlessScreenHelper::GetScreenRect() { HeadlessScreenHelper::HeadlessScreenHelper() { AutoTArray<RefPtr<Screen>, 1> screenList; LayoutDeviceIntRect rect = GetScreenRect(); - auto ret = MakeRefPtr<Screen>( - rect, rect, 24, 24, 0, DesktopToLayoutDeviceScale(), - CSSToLayoutDeviceScale(), 96.0f, Screen::IsPseudoDisplay::No); + auto ret = + MakeRefPtr<Screen>(rect, rect, 24, 24, 0, DesktopToLayoutDeviceScale(), + CSSToLayoutDeviceScale(), 96.0f, + Screen::IsPseudoDisplay::No, Screen::IsHDR::No); screenList.AppendElement(ret.forget()); ScreenManager::Refresh(std::move(screenList)); } diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp index 083d026d3c..419b3bf940 100644 --- a/widget/headless/HeadlessWidget.cpp +++ b/widget/headless/HeadlessWidget.cpp @@ -160,10 +160,9 @@ void HeadlessWidget::GetCompositorWidgetInitData( nsIWidget* HeadlessWidget::GetTopLevelWidget() { return mTopLevel; } void HeadlessWidget::RaiseWindow() { - MOZ_ASSERT(mWindowType == WindowType::TopLevel || - mWindowType == WindowType::Dialog || - mWindowType == WindowType::Sheet, - "Raising a non-toplevel window."); + MOZ_ASSERT( + mWindowType == WindowType::TopLevel || mWindowType == WindowType::Dialog, + "Raising a non-toplevel window."); // Do nothing if this is the currently active window. RefPtr<HeadlessWidget> activeWindow = GetActiveWindow(); @@ -204,7 +203,7 @@ void HeadlessWidget::Show(bool aState) { // so we don't focus them by default. if (aState && !mAlwaysOnTop && (mWindowType == WindowType::TopLevel || - mWindowType == WindowType::Dialog || mWindowType == WindowType::Sheet)) { + mWindowType == WindowType::Dialog)) { RaiseWindow(); } diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp index 333d154e70..9ffc53b4c2 100644 --- a/widget/nsBaseClipboard.cpp +++ b/widget/nsBaseClipboard.cpp @@ -340,7 +340,7 @@ class SafeContentAnalysisResultCallback final NS_IMETHODIMP Error(nsresult aError) override { using namespace mozilla::contentanalysis; Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ERROR_OTHER)); + NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); return NS_OK; } @@ -379,13 +379,18 @@ CheckClipboardContentAnalysisAsText( } nsString text; if (NS_FAILED(textData->GetData(text))) { - return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER); + return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); + } + if (text.IsEmpty()) { + // Content Analysis doesn't expect to analyze an empty string. + // Just approve it. + return true; } RefPtr<mozilla::dom::WindowGlobalParent> window = mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId); if (!window) { // The window has gone away in the meantime - return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER); + return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); } nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest = new ContentAnalysisRequest( @@ -395,7 +400,7 @@ CheckClipboardContentAnalysisAsText( nsresult rv = aContentAnalysis->AnalyzeContentRequestCallback( contentAnalysisRequest, /* aAutoAcknowledge */ true, aResolver); if (NS_FAILED(rv)) { - return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER); + return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); } return true; } @@ -420,7 +425,7 @@ CheckClipboardContentAnalysisAsFile( rv = file->GetPath(filePath); } else { MOZ_ASSERT_UNREACHABLE("clipboard data had kFileMime but no nsIFile!"); - return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER); + return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); } } if (NS_FAILED(rv) || filePath.IsEmpty()) { @@ -430,7 +435,7 @@ CheckClipboardContentAnalysisAsFile( mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId); if (!window) { // The window has gone away in the meantime - return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER); + return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); } // Let the content analysis code calculate the digest nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest = @@ -443,7 +448,7 @@ CheckClipboardContentAnalysisAsFile( contentAnalysisRequest, /* aAutoAcknowledge */ true, aResolver); if (NS_FAILED(rv)) { - return mozilla::Err(NoContentAnalysisResult::ERROR_OTHER); + return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); } return true; } @@ -463,14 +468,15 @@ static void CheckClipboardContentAnalysis( if (!aWindow || aWindow->GetBrowsingContext()->IsChrome() || aWindow->IsInProcess()) { aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS)); + NoContentAnalysisResult:: + ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS)); return; } nsCOMPtr<nsIContentAnalysis> contentAnalysis = mozilla::components::nsIContentAnalysis::Service(); if (!contentAnalysis) { aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ERROR_OTHER)); + NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); return; } @@ -478,7 +484,7 @@ static void CheckClipboardContentAnalysis( nsresult rv = contentAnalysis->GetIsActive(&contentAnalysisIsActive); if (MOZ_LIKELY(NS_FAILED(rv) || !contentAnalysisIsActive)) { aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::CONTENT_ANALYSIS_NOT_ACTIVE)); + NoContentAnalysisResult::ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE)); return; } @@ -488,7 +494,7 @@ static void CheckClipboardContentAnalysis( rv = aTransferable->FlavorsTransferableCanExport(flavors); if (NS_WARN_IF(NS_FAILED(rv))) { aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ERROR_OTHER)); + NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); return; } bool keepChecking = true; @@ -515,7 +521,7 @@ static void CheckClipboardContentAnalysis( if (!textResult.unwrap()) { // Couldn't get file or text data from this aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ERROR_COULD_NOT_GET_DATA)); + NoContentAnalysisResult::ALLOW_DUE_TO_COULD_NOT_GET_DATA)); return; } } diff --git a/widget/nsIDragSession.idl b/widget/nsIDragSession.idl index e96e6fc1c7..8dbf93e719 100644 --- a/widget/nsIDragSession.idl +++ b/widget/nsIDragSession.idl @@ -135,7 +135,7 @@ interface nsIDragSession : nsISupports /** * Returns true if current session was started with synthesized drag start. */ - [notxpcom, nostdcall] bool isSynthesizedForTests(); + [notxpcom, nostdcall] boolean isSynthesizedForTests(); /** * Sets drag end point of synthesized session when the test does not dispatch @@ -147,5 +147,5 @@ interface nsIDragSession : nsISupports * Returns true if the session is for dragging text in a text in text control * element. */ - [notxpcom, nostdcall] bool isDraggingTextInTextControl(); + [notxpcom, nostdcall] boolean isDraggingTextInTextControl(); }; diff --git a/widget/nsIMacDockSupport.idl b/widget/nsIMacDockSupport.idl index 04d6dca774..6f0f1adbae 100644 --- a/widget/nsIMacDockSupport.idl +++ b/widget/nsIMacDockSupport.idl @@ -41,7 +41,7 @@ interface nsIMacDockSupport : nsISupports * True if this app is in the list of apps that are persisted to the macOS * Dock (as if the user selected "Keep in Dock"). */ - readonly attribute bool isAppInDock; + readonly attribute boolean isAppInDock; /** * Ensure that there is a tile for this app in the list of apps that are @@ -66,6 +66,6 @@ interface nsIMacDockSupport : nsISupports * @return true if the app was already in the list of persisted apps or if it * was successfully added, else returns false. */ - bool ensureAppIsPinnedToDock([optional] in AString aAppPath, - [optional] in AString aAppToReplacePath); + boolean ensureAppIsPinnedToDock([optional] in AString aAppPath, + [optional] in AString aAppToReplacePath); }; diff --git a/widget/nsIPrintSettings.idl b/widget/nsIPrintSettings.idl index efed9efce6..daf0e145f4 100644 --- a/widget/nsIPrintSettings.idl +++ b/widget/nsIPrintSettings.idl @@ -203,7 +203,7 @@ interface nsIPrintSettings : nsISupports * to make best use of the space on the sheet. Specifically, this returns * true IFF `numPagesPerSheet` is set to 2 or 6 pages-per-sheet. */ - [noscript, notxpcom, nostdcall] bool HasOrthogonalPagesPerSheet(); + [noscript, notxpcom, nostdcall] boolean HasOrthogonalPagesPerSheet(); /** * Makes a new copy @@ -220,7 +220,7 @@ interface nsIPrintSettings : nsISupports * therefore print. The printer name is ignored and it allows for a small * delta in sizes to allow for rounding differences. */ - bool equivalentTo(in nsIPrintSettings aPrintSettings); + boolean equivalentTo(in nsIPrintSettings aPrintSettings); /** * The edge measurements define the positioning of the headers @@ -335,7 +335,7 @@ interface nsIPrintSettings : nsISupports */ attribute AString toFileName; - attribute nsIOutputStream outputStream; /* for kOutputDestinationPrinter */ + attribute nsIOutputStream outputStream; /* for kOutputDestinationStream */ [infallible] attribute long printPageDelay; /* in milliseconds */ diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 81e952a8c6..06eab558eb 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -668,12 +668,6 @@ class nsIWidget : public nsISupports { virtual void SetModal(bool aModal) = 0; /** - * Make the non-modal window opened by modal window fake-modal, that will - * call SetFakeModal(false) on destroy on Cocoa. - */ - virtual void SetFakeModal(bool aModal) { SetModal(aModal); } - - /** * Are we app modal. Currently only implemented on Cocoa. */ virtual bool IsRunningAppModal() { return false; } @@ -784,10 +778,6 @@ class nsIWidget : public nsISupports { virtual void Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) = 0; - virtual mozilla::Maybe<bool> IsResizingNativeWidget() { - return mozilla::Nothing(); - } - /** * Resize the widget so that the inner client area has the given size. * diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index 9eabbd84b6..0c5b5207f2 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -128,7 +128,6 @@ static const char sIntPrefs[][45] = { "ui.caretBlinkTime", "ui.caretBlinkCount", "ui.caretWidth", - "ui.caretVisibleWithSelection", "ui.selectTextfieldsOnKeyFocus", "ui.submenuDelay", "ui.menusCanOverlapOSBar", @@ -152,6 +151,7 @@ static const char sIntPrefs[][45] = { "ui.windowsAccentColorInTitlebar", "ui.macBigSurTheme", "ui.macRTL", + "ui.macTitlebarHeight", "ui.alertNotificationOrigin", "ui.scrollToClick", "ui.IMERawInputUnderlineStyle", @@ -167,6 +167,7 @@ static const char sIntPrefs[][45] = { "ui.scrollbarFadeDuration", "ui.contextMenuOffsetVertical", "ui.contextMenuOffsetHorizontal", + "ui.tooltipOffsetVertical", "ui.GtkCSDAvailable", "ui.GtkCSDMinimizeButton", "ui.GtkCSDMaximizeButton", @@ -186,7 +187,6 @@ static const char sIntPrefs[][45] = { "ui.titlebarRadius", "ui.titlebarButtonSpacing", "ui.dynamicRange", - "ui.videoDynamicRange", "ui.panelAnimations", "ui.hideCursorWhileTyping", "ui.gtkThemeFamily", @@ -711,6 +711,7 @@ nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID, // Seems to be the default color (hardcoded because of bug 1065998) COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC) COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B) + COLOR(MozAutofillBackground, 0xff, 0xfc, 0xc8) default: break; } @@ -855,6 +856,11 @@ Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) { case ColorID::Inactivecaption: color = NS_RGB(28, 27, 34); break; + case ColorID::MozAutofillBackground: + // This is the light version of this color, but darkened to have good + // contrast with our white-ish FieldText. + color = NS_RGB(0x72, 0x6c, 0x00); + break; default: return Nothing(); } diff --git a/widget/tests/mochitest.toml b/widget/tests/mochitest.toml index 4a4a9d2729..93e3adf733 100644 --- a/widget/tests/mochitest.toml +++ b/widget/tests/mochitest.toml @@ -36,7 +36,10 @@ skip-if = ["display == 'wayland'"] # Bug 1879835 support-files = "file_test_clipboard_getDataSnapshotSync.js" ["test_contextmenu_by_mouse_on_unix.html"] -run-if = ["os == 'linux'"] +run-if = [ + "os == 'linux'", + "os == 'android'", +] skip-if = ["headless"] # headless widget doesn't dispatch contextmenu event by mouse event. ["test_keypress_event_with_alt_on_mac.html"] diff --git a/widget/tests/test_assign_event_data.html b/widget/tests/test_assign_event_data.html index 1da9bb535f..26d214edce 100644 --- a/widget/tests/test_assign_event_data.html +++ b/widget/tests/test_assign_event_data.html @@ -59,6 +59,7 @@ <body> <div id="display"> <input id="input-text"> + <div contenteditable id="contenteditable"><br></div> <button id="button">button</button> <a id="a" href="about:blank">hyper link</a> <span id="pointer-target">span</span> @@ -352,6 +353,44 @@ const kTests = [ }, todoMismatch: [ ], }, + { description: "InternalLegacyTextEvent (input at key input)", + targetID: "input-text", eventType: "textInput", + dispatchEvent() { + const input = document.getElementById(this.targetID); + input.value = ""; + input.focus(); + synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_B : MAC_VK_ANSI_B, + { shiftKey: true }, "B", "B"); + observeKeyUpOnContent(KeyboardEvent.DOM_VK_B, runNextTest); + return true; + }, + canRun() { + return (kIsMac || kIsWin); + }, + todoMismatch: [], + }, + { description: "InternalLegacyTextEvent (paste)", + targetID: "contenteditable", eventType: "textInput", + async dispatchEvent() { + const editingHost = document.getElementById(this.targetID); + editingHost.innerHTML = "abc"; + editingHost.focus(); + getSelection().selectAllChildren(editingHost); + synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_C : MAC_VK_ANSI_C, + { accelKey: true }, "", "c"); + const waitForInput = new Promise(resolve => { + editingHost.addEventListener("input", resolve, {once: true}); + }); + // In this case, TextEvent.data is stored with a dataTransfer. + synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, kIsWin ? WIN_VK_V : MAC_VK_ANSI_V, + { accelKey: true }, "", "v"); + await waitForInput; + }, + canRun() { + return (kIsMac || kIsWin); + }, + todoMismatch: [], + }, { description: "WidgetMouseScrollEvent (DOMMouseScroll, vertical)", targetID: "input-text", eventType: "DOMMouseScroll", dispatchEvent() { @@ -615,7 +654,7 @@ const kTests = [ }, ]; -function doTest(aTest) { +async function doTest(aTest) { if (!aTest.canRun()) { SimpleTest.executeSoon(runNextTest); return; @@ -650,7 +689,7 @@ function doTest(aTest) { runNextTest(); } }; - var testWillCallRunNextTest = aTest.dispatchEvent(); + var testWillCallRunNextTest = await aTest.dispatchEvent(); } var gIndex = -1; @@ -695,7 +734,8 @@ function init() { ["mousewheel.with_alt.action", 0], ["mousewheel.with_alt.action.override_x", -1], ["mousewheel.with_meta.action", 0], - ["mousewheel.with_meta.action.override_x", -1]]}, runNextTest); + ["mousewheel.with_meta.action.override_x", -1], + ["dom.events.textevent.enabled", true]]}, runNextTest); } function finish() { diff --git a/widget/tests/window_composition_text_querycontent.xhtml b/widget/tests/window_composition_text_querycontent.xhtml index 4806d0d187..ea224a1636 100644 --- a/widget/tests/window_composition_text_querycontent.xhtml +++ b/widget/tests/window_composition_text_querycontent.xhtml @@ -806,6 +806,16 @@ function checkInputEvent(aEvent, aIsComposing, aInputType, aData, aTargetRanges, } } +function checkTextInputEvent(aEvent, aData, aDescription) { + if (aEvent.type !== "textInput") { + throw new Error(`${aDescription}: "${aEvent.type}" is not TextEvent`); + } + ok(TextEvent.isInstance(aEvent), `"${aEvent.type}" event should be dispatched with TextEvent interface: ${aDescription}`); + is(aEvent.cancelable, true, `"${aEvent.type}" event should be cancelable: ${aDescription}`); + is(aEvent.bubbles, true, `"${aEvent.type}" event should always bubble: ${aDescription}`); + is(aEvent.data, aData, `data of "${aEvent.type}" event should be ${aData}: ${aDescription}`); +} + function runCompositionCommitAsIsTest() { textarea.focus(); @@ -824,6 +834,7 @@ function runCompositionCommitAsIsTest() textarea.addEventListener("compositionupdate", handler, true); textarea.addEventListener("compositionend", handler, true); textarea.addEventListener("beforeinput", handler, true); + textarea.addEventListener("textInput", handler, true); textarea.addEventListener("input", handler, true); textarea.addEventListener("text", handler, true); @@ -842,22 +853,29 @@ function runCompositionCommitAsIsTest() }); is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #1"); + is(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitAsIsTest: no textInput event should be fired before commit #1"); + clearResult(); synthesizeComposition({ type: "compositioncommitasis", key: { key: "Enter" } }); - is(result.length, 4, - "runCompositionCommitAsIsTest: 4 events should be fired after dispatching compositioncommitasis #1"); + is(result.length, 5, + "runCompositionCommitAsIsTest: 5 events should be fired after dispatching compositioncommitasis #1"); is(result[0].type, "text", "runCompositionCommitAsIsTest: text should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1"); is(result[1].type, "beforeinput", "runCompositionCommitAsIsTest: beforeinput should be fired after dispatching compositioncommitasis because it's dispatched when there is composing string #1"); checkInputEvent(result[1], true, "insertCompositionText", "\u3042", [], "runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1"); - is(result[2].type, "compositionend", + is(result[2].type, "textInput", + "runCompositionCommitAsIsText: textInput should be fired after dispatching compositioncommitasis because it's after the last beforeinput for the composition #1"); + checkTextInputEvent(result[2], "\u3042", + "runCompositionCommitAsIsText: after dispatching compositioncommitasis #1"); + is(result[3].type, "compositionend", "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #1"); - is(result[3].type, "input", + is(result[4].type, "input", "runCompositionCommitAsIsTest: input should be fired after dispatching compositioncommitasis #1"); - checkInputEvent(result[3], false, "insertCompositionText", "\u3042", [], + checkInputEvent(result[4], false, "insertCompositionText", "\u3042", [], "runCompositionCommitAsIsTest: after dispatching compositioncommitasis #1"); is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #1"); @@ -887,6 +905,8 @@ function runCompositionCommitAsIsTest() "key": { key: "KEY_Enter", type: "keydown" }, }); is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2"); + isnot(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitAsIsTest: a textInput event should be fired before commit #2"); clearResult(); synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter", type: "keyup" } }); @@ -928,6 +948,8 @@ function runCompositionCommitAsIsTest() "key": { key: "KEY_Escape", type: "keydown" }, }); is(textarea.value, "", "runCompositionCommitAsIsTest: textarea has non-empty composition string #3"); + todo_isnot(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitAsIsTest: a textInput event should be fired immediately before commit #3"); clearResult(); synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape", type: "keyup" } }); @@ -946,6 +968,7 @@ function runCompositionCommitAsIsTest() textarea.removeEventListener("compositionupdate", handler, true); textarea.removeEventListener("compositionend", handler, true); textarea.removeEventListener("beforeinput", handler, true); + textarea.removeEventListener("textInput", handler, true); textarea.removeEventListener("input", handler, true); textarea.removeEventListener("text", handler, true); } @@ -968,6 +991,7 @@ function runCompositionCommitTest() textarea.addEventListener("compositionupdate", handler, true); textarea.addEventListener("compositionend", handler, true); textarea.addEventListener("beforeinput", handler, true); + textarea.addEventListener("textInput", handler, true); textarea.addEventListener("input", handler, true); textarea.addEventListener("text", handler, true); @@ -985,12 +1009,14 @@ function runCompositionCommitTest() "key": { key: "a", type: "keydown" }, }); is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #1"); + is(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitTest: no textInput event should be fired before commit #1"); clearResult(); synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "a", type: "keyup" } }); - is(result.length, 5, - "runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #1"); + is(result.length, 6, + "runCompositionCommitTest: 6 events should be fired after dispatching compositioncommit #1"); is(result[0].type, "compositionupdate", "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit because it's dispatched when there is composing string #1"); is(result[1].type, "text", @@ -999,16 +1025,21 @@ function runCompositionCommitTest() "runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit because it's dispatched when there is composing string #1"); checkInputEvent(result[2], true, "insertCompositionText", "\u3043", [], "runCompositionCommitTest: after dispatching compositioncommit #1"); - is(result[3].type, "compositionend", + is(result[3].type, "textInput", + "runCompositionCommitTest: textInput should be fired after dispatching compositioncommit because the preceding beforeinput is last one for the composition #1"); + checkTextInputEvent(result[3], "\u3043", + "runCompositionCommitTest: after dispatching compositioncommit #1"); + is(result[4].type, "compositionend", "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #1"); - is(result[4].type, "input", + is(result[5].type, "input", "runCompositionCommitTest: input should be fired after dispatching compositioncommit #1"); - checkInputEvent(result[4], false, "insertCompositionText", "\u3043", [], + checkInputEvent(result[5], false, "insertCompositionText", "\u3043", [], "runCompositionCommitTest: after dispatching compositioncommit #1"); is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #1"); // compositioncommit with different committed string when there is already committed string textarea.value = ""; + clearResult(); synthesizeCompositionChange( { "composition": { "string": "\u3042", @@ -1033,12 +1064,14 @@ function runCompositionCommitTest() "key": { key: "KEY_Enter", type: "keydown" }, }); is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have committed string #2"); + is(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitTest: no textInput event should be fired before commit #2"); clearResult(); synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } }); - is(result.length, 5, - "runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #2"); + is(result.length, 6, + "runCompositionCommitTest: 6 events should be fired after dispatching compositioncommit #2"); is(result[0].type, "compositionupdate", "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #2"); is(result[1].type, "text", @@ -1047,16 +1080,21 @@ function runCompositionCommitTest() "runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #2"); checkInputEvent(result[2], true, "insertCompositionText", "\u3043", [], "runCompositionCommitTest: after dispatching compositioncommit #2"); - is(result[3].type, "compositionend", + is(result[3].type, "textInput", + "runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #2"); + checkTextInputEvent(result[3], "\u3043", + "runCompositionCommitTest: after dispatching compositioncommit #2"); + is(result[4].type, "compositionend", "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #2"); - is(result[4].type, "input", + is(result[5].type, "input", "runCompositionCommitTest: input should be fired after dispatching compositioncommit #2"); - checkInputEvent(result[4], false, "insertCompositionText", "\u3043", [], + checkInputEvent(result[5], false, "insertCompositionText", "\u3043", [], "runCompositionCommitTest: after dispatching compositioncommit #2"); is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #2"); // compositioncommit with empty composition string. textarea.value = ""; + clearResult(); synthesizeCompositionChange( { "composition": { "string": "\u3042", @@ -1081,12 +1119,14 @@ function runCompositionCommitTest() "key": { key: "KEY_Enter", type: "keydown" }, }); is(textarea.value, "", "runCompositionCommitTest: textarea has non-empty composition string #3"); + is(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitTest: no textInput event should be fired before commit #3"); clearResult(); synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } }); - is(result.length, 5, - "runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #3"); + is(result.length, 6, + "runCompositionCommitTest: 6 events should be fired after dispatching compositioncommit #3"); is(result[0].type, "compositionupdate", "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #3"); is(result[1].type, "text", @@ -1095,11 +1135,15 @@ function runCompositionCommitTest() "runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #3"); checkInputEvent(result[2], true, "insertCompositionText", "\u3043", [], "runCompositionCommitTest: after dispatching compositioncommit #3"); - is(result[3].type, "compositionend", + is(result[3].type, "textInput", + "runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #3"); + checkTextInputEvent(result[3], "\u3043", + "runCompositionCommitTest: after dispatching compositioncommit #3"); + is(result[4].type, "compositionend", "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #3"); - is(result[4].type, "input", + is(result[5].type, "input", "runCompositionCommitTest: input should be fired after dispatching compositioncommit #3"); - checkInputEvent(result[4], false, "insertCompositionText", "\u3043", [], + checkInputEvent(result[5], false, "insertCompositionText", "\u3043", [], "runCompositionCommitTest: after dispatching compositioncommit #3"); is(textarea.value, "\u3043", "runCompositionCommitTest: textarea doesn't have committed string #3"); @@ -1111,19 +1155,23 @@ function runCompositionCommitTest() clearResult(); synthesizeComposition({ type: "compositioncommit", data: "" }); - is(result.length, 4, - "runCompositionCommitTest: 4 events should be fired when inserting empty string with composition"); + is(result.length, 5, + "runCompositionCommitTest: 5 events should be fired when inserting empty string with composition"); is(result[0].type, "text", "runCompositionCommitTest: text should be fired when inserting empty string with composition"); is(result[1].type, "beforeinput", "runCompositionCommitTest: beforeinput should be fired when inserting empty string with composition"); checkInputEvent(result[1], true, "insertCompositionText", "", [], "runCompositionCommitTest: when inserting empty string with composition"); - is(result[2].type, "compositionend", + is(result[2].type, "textInput", + "runCompositionCommitTest: textInput should be fired when inserting empty string with composition"); + checkTextInputEvent(result[2], "", + "runCompositionCommitTest: when inserting empty string with composition"); + is(result[3].type, "compositionend", "runCompositionCommitTest: compositionend should be fired when inserting empty string with composition"); - is(result[3].type, "input", + is(result[4].type, "input", "runCompositionCommitTest: input should be fired when inserting empty string with composition"); - checkInputEvent(result[3], false, "insertCompositionText", "", [], + checkInputEvent(result[4], false, "insertCompositionText", "", [], "runCompositionCommitTest: when inserting empty string with composition"); is(textarea.value, "abc", "runCompositionCommitTest: textarea should keep original value when inserting empty string with composition"); @@ -1136,19 +1184,23 @@ function runCompositionCommitTest() clearResult(); synthesizeComposition({ type: "compositioncommit", data: "" }); - is(result.length, 4, - "runCompositionCommitTest: 4 events should be fired when replacing with empty string with composition"); + is(result.length, 5, + "runCompositionCommitTest: 5 events should be fired when replacing with empty string with composition"); is(result[0].type, "text", "runCompositionCommitTest: text should be fired when replacing with empty string with composition"); is(result[1].type, "beforeinput", "runCompositionCommitTest: beforeinput should be fired when replacing with empty string with composition"); checkInputEvent(result[1], true, "insertCompositionText", "", [], "runCompositionCommitTest: when replacing with empty string with composition"); - is(result[2].type, "compositionend", + is(result[2].type, "textInput", + "runCompositionCommitTest: textInput should be fired when replacing with empty string with composition"); + checkTextInputEvent(result[2], "", + "runCompositionCommitTest: when replacing with empty string with composition"); + is(result[3].type, "compositionend", "runCompositionCommitTest: compositionend should be fired when replacing with empty string with composition"); - is(result[3].type, "input", + is(result[4].type, "input", "runCompositionCommitTest: input should be fired when replacing with empty string with composition"); - checkInputEvent(result[3], false, "insertCompositionText", "", [], + checkInputEvent(result[4], false, "insertCompositionText", "", [], "runCompositionCommitTest: when replacing with empty string with composition"); is(textarea.value, "", "runCompositionCommitTest: textarea should become empty when replacing selection with empty string with composition"); @@ -1161,8 +1213,8 @@ function runCompositionCommitTest() clearResult(); synthesizeComposition({ type: "compositioncommit", data: "abc" }); - is(result.length, 5, - "runCompositionCommitTest: 5 events should be fired when replacing selection with same string with composition"); + is(result.length, 6, + "runCompositionCommitTest: 6 events should be fired when replacing selection with same string with composition"); is(result[0].type, "compositionupdate", "runCompositionCommitTest: compositionupdate should be fired when replacing selection with same string with composition"); is(result[1].type, "text", @@ -1171,11 +1223,15 @@ function runCompositionCommitTest() "runCompositionCommitTest: beforeinput should be fired when replacing selection with same string with composition"); checkInputEvent(result[2], true, "insertCompositionText", "abc", [], "runCompositionCommitTest: when replacing selection with same string with composition"); - is(result[3].type, "compositionend", + is(result[3].type, "textInput", + "runCompositionCommitTest: textInput should be fired when replacing selection with same string with composition"); + checkTextInputEvent(result[3], "abc", + "runCompositionCommitTest: when replacing selection with same string with composition"); + is(result[4].type, "compositionend", "runCompositionCommitTest: compositionend should be fired when replacing selection with same string with composition"); - is(result[4].type, "input", + is(result[5].type, "input", "runCompositionCommitTest: input should be fired when replacing selection with same string with composition"); - checkInputEvent(result[4], false, "insertCompositionText", "abc", [], + checkInputEvent(result[5], false, "insertCompositionText", "abc", [], "runCompositionCommitTest: when replacing selection with same string with composition"); is(textarea.value, "abc", "runCompositionCommitTest: textarea should keep same value when replacing selection with same string with composition"); @@ -1198,8 +1254,8 @@ function runCompositionCommitTest() clearResult(); synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Enter" } }); - is(result.length, 5, - "runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #4"); + is(result.length, 6, + "runCompositionCommitTest: 6 events should be fired after dispatching compositioncommit #4"); is(result[0].type, "compositionupdate", "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #4"); is(result[1].type, "text", @@ -1208,11 +1264,15 @@ function runCompositionCommitTest() "runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #4"); checkInputEvent(result[2], true, "insertCompositionText", "", [], "runCompositionCommitTest: after dispatching compositioncommit #4"); - is(result[3].type, "compositionend", + is(result[3].type, "textInput", + "runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #4"); + checkTextInputEvent(result[3], "", + "runCompositionCommitTest: after dispatching compositioncommit #4"); + is(result[4].type, "compositionend", "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #4"); - is(result[4].type, "input", + is(result[5].type, "input", "runCompositionCommitTest: input should be fired after dispatching compositioncommit #4"); - checkInputEvent(result[4], false, "insertCompositionText", "", [], + checkInputEvent(result[5], false, "insertCompositionText", "", [], "runCompositionCommitTest: after dispatching compositioncommit #4"); is(textarea.value, "", "runCompositionCommitTest: textarea should be empty #4"); @@ -1222,8 +1282,8 @@ function runCompositionCommitTest() clearResult(); synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "a" } }); - is(result.length, 5, - "runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #5"); + is(result.length, 6, + "runCompositionCommitTest: 6 events should be fired after dispatching compositioncommit #5"); is(result[0].type, "compositionupdate", "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #5"); is(result[1].type, "text", @@ -1232,11 +1292,15 @@ function runCompositionCommitTest() "runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #5"); checkInputEvent(result[2], true, "insertCompositionText", "\u3042", [], "runCompositionCommitTest: after dispatching compositioncommit #5"); - is(result[3].type, "compositionend", + is(result[3].type, "textInput", + "runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #5"); + checkTextInputEvent(result[3], "\u3042", + "runCompositionCommitTest: after dispatching compositioncommit #5"); + is(result[4].type, "compositionend", "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5"); - is(result[4].type, "input", + is(result[5].type, "input", "runCompositionCommitTest: input should be fired after dispatching compositioncommit #5"); - checkInputEvent(result[4], false, "insertCompositionText", "\u3042", [], + checkInputEvent(result[5], false, "insertCompositionText", "\u3042", [], "runCompositionCommitTest: after dispatching compositioncommit #5"); is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should be empty #5"); @@ -1258,24 +1322,29 @@ function runCompositionCommitTest() clearResult(); synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "KEY_Enter" } }); - is(result.length, 4, - "runCompositionCommitTest: 4 events should be fired after dispatching compositioncommit #6"); + is(result.length, 5, + "runCompositionCommitTest: 5 events should be fired after dispatching compositioncommit #6"); is(result[0].type, "text", "runCompositionCommitTest: text should be fired after dispatching compositioncommit #6"); is(result[1].type, "beforeinput", "runCompositionCommitTest: beforeinput should be fired after dispatching compositioncommit #6"); checkInputEvent(result[1], true, "insertCompositionText", "\u3042", [], "runCompositionCommitTest: after dispatching compositioncommit #6"); - is(result[2].type, "compositionend", + is(result[2].type, "textInput", + "runCompositionCommitTest: textInput should be fired after dispatching compositioncommit #6"); + checkTextInputEvent(result[2], "\u3042", + "runCompositionCommitTest: after dispatching compositioncommit #6"); + is(result[3].type, "compositionend", "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #6"); - is(result[3].type, "input", + is(result[4].type, "input", "runCompositionCommitTest: input should be fired after dispatching compositioncommit #6"); - checkInputEvent(result[3], false, "insertCompositionText", "\u3042", [], + checkInputEvent(result[4], false, "insertCompositionText", "\u3042", [], "runCompositionCommitTest: after dispatching compositioncommit #6"); is(textarea.value, "\u3042", "runCompositionCommitTest: textarea should have committed string #6"); // compositioncommit with same composition string when there is committed string textarea.value = ""; + clearResult(); synthesizeCompositionChange( { "composition": { "string": "\u3042", @@ -1301,6 +1370,8 @@ function runCompositionCommitTest() "key": { key: "KEY_Enter", type: "keydown" }, }); is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6"); + todo_isnot(result.findIndex(value => value.type == "textInput"), -1, + "runCompositionCommitTest: a textInput event should be fired before immediately commit #6"); clearResult(); synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "KEY_Enter", type: "keyup" } }); @@ -1319,6 +1390,7 @@ function runCompositionCommitTest() textarea.removeEventListener("compositionupdate", handler, true); textarea.removeEventListener("compositionend", handler, true); textarea.removeEventListener("beforeinput", handler, true); + textarea.removeEventListener("textInput", handler, true); textarea.removeEventListener("input", handler, true); textarea.removeEventListener("text", handler, true); } @@ -10860,6 +10932,10 @@ async function runInputModeTest() async function runTest() { + await SpecialPowers.pushPrefEnv({ + set: [["dom.events.textevent.enabled", true]], + }); + window.addEventListener("unload", window.arguments[0].SimpleTest.finish, {once: true, capture: true}); contenteditable = document.getElementById("iframe4").contentDocument.getElementById("contenteditable"); diff --git a/widget/uikit/TextInputHandler.mm b/widget/uikit/TextInputHandler.mm index 9a4a6ae226..3ee3573b83 100644 --- a/widget/uikit/TextInputHandler.mm +++ b/widget/uikit/TextInputHandler.mm @@ -10,6 +10,7 @@ #include "mozilla/EventForwards.h" #include "mozilla/Logging.h" +#include "mozilla/MacStringHelpers.h" #include "mozilla/MiscEvents.h" #include "mozilla/TextEventDispatcher.h" #include "mozilla/TextEvents.h" @@ -23,21 +24,6 @@ mozilla::LazyLogModule gIMELog("TextInputHandler"); namespace mozilla::widget { -static void GetStringForNSString(const NSString* aSrc, nsAString& aDist) { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - if (!aSrc) { - aDist.Truncate(); - return; - } - - aDist.SetLength([aSrc length]); - [aSrc getCharacters:reinterpret_cast<unichar*>(aDist.BeginWriting()) - range:NSMakeRange(0, [aSrc length])]; - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - NS_IMPL_ISUPPORTS(TextInputHandler, TextEventDispatcherListener, nsISupportsWeakReference) @@ -63,7 +49,7 @@ void TextInputHandler::WillDispatchKeyboardEvent( bool TextInputHandler::InsertText(NSString* aText) { nsString str; - GetStringForNSString(aText, str); + CopyNSStringToXPCOMString(aText, str); MOZ_LOG(gIMELog, LogLevel::Info, ("%p TextInputHandler::InsertText(aText=%s)", this, diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm index b420cc9a1d..51a9a95b52 100644 --- a/widget/uikit/nsLookAndFeel.mm +++ b/widget/uikit/nsLookAndFeel.mm @@ -232,9 +232,6 @@ nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::CaretWidth: aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; case IntID::SelectTextfieldsOnKeyFocus: // Select textfield content when focused by kbd // used by nsEventStateManager::sTextfieldSelectModel diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 0e1b4002d3..6d8429f1cc 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -310,7 +310,7 @@ uint32_t ParseIDFromDeviceID(const nsAString& key, const nsAString& prefix, return 0x5143; } nsresult err; - return id.ToInteger(&err, 16); + return id.ToUnsignedInteger(&err, 16); } // OS version in 16.16 major/minor form diff --git a/widget/windows/ScreenHelperWin.cpp b/widget/windows/ScreenHelperWin.cpp index 8a0ec3b608..945b8d1895 100644 --- a/widget/windows/ScreenHelperWin.cpp +++ b/widget/windows/ScreenHelperWin.cpp @@ -7,9 +7,12 @@ #include "ScreenHelperWin.h" #include "mozilla/Logging.h" +#include "mozilla/gfx/DeviceManagerDx.h" #include "nsTArray.h" #include "WinUtils.h" +#include <dxgi.h> + static mozilla::LazyLogModule sScreenLog("WidgetScreen"); namespace mozilla { @@ -74,8 +77,13 @@ static void GetDisplayInfo(const char16ptr_t aName, } } +struct CollectMonitorsParam { + nsTArray<RefPtr<Screen>> screens; +}; + BOOL CALLBACK CollectMonitors(HMONITOR aMon, HDC, LPRECT, LPARAM ioParam) { - auto screens = reinterpret_cast<nsTArray<RefPtr<Screen>>*>(ioParam); + CollectMonitorsParam* cmParam = + reinterpret_cast<CollectMonitorsParam*>(ioParam); BOOL success = FALSE; MONITORINFOEX info; info.cbSize = sizeof(MONITORINFOEX); @@ -123,6 +131,9 @@ BOOL CALLBACK CollectMonitors(HMONITOR aMon, HDC, LPRECT, LPARAM ioParam) { GetDisplayInfo(info.szDevice, orientation, angle, isPseudoDisplay, refreshRate); + auto* manager = gfx::DeviceManagerDx::Get(); + bool isHDR = manager ? manager->MonitorHDREnabled(aMon) : false; + MOZ_LOG(sScreenLog, LogLevel::Debug, ("New screen [%s (%s) %d %u %f %f %f %d %d %d]", ToString(rect).c_str(), ToString(availRect).c_str(), pixelDepth, @@ -131,26 +142,32 @@ BOOL CALLBACK CollectMonitors(HMONITOR aMon, HDC, LPRECT, LPARAM ioParam) { auto screen = MakeRefPtr<Screen>( rect, availRect, pixelDepth, pixelDepth, refreshRate, contentsScaleFactor, defaultCssScaleFactor, dpi, Screen::IsPseudoDisplay(isPseudoDisplay), - orientation, angle); + Screen::IsHDR(isHDR), orientation, angle); if (info.dwFlags & MONITORINFOF_PRIMARY) { // The primary monitor must be the first element of the screen list. - screens->InsertElementAt(0, std::move(screen)); + cmParam->screens.InsertElementAt(0, std::move(screen)); } else { - screens->AppendElement(std::move(screen)); + cmParam->screens.AppendElement(std::move(screen)); } return TRUE; } +/* static */ void ScreenHelperWin::RefreshScreens() { MOZ_LOG(sScreenLog, LogLevel::Debug, ("Refreshing screens")); - AutoTArray<RefPtr<Screen>, 4> screens; + auto* manager = gfx::DeviceManagerDx::Get(); + if (XRE_IsParentProcess() && manager) { + manager->UpdateMonitorInfo(); + } + + CollectMonitorsParam cmParam; BOOL result = ::EnumDisplayMonitors( - nullptr, nullptr, (MONITORENUMPROC)CollectMonitors, (LPARAM)&screens); + nullptr, nullptr, (MONITORENUMPROC)CollectMonitors, (LPARAM)&cmParam); if (!result) { NS_WARNING("Unable to EnumDisplayMonitors"); } - ScreenManager::Refresh(std::move(screens)); + ScreenManager::Refresh(std::move(cmParam.screens)); } } // namespace widget diff --git a/widget/windows/WindowsSMTCProvider.cpp b/widget/windows/WindowsSMTCProvider.cpp index 69915cafd8..3ddd26b6cc 100644 --- a/widget/windows/WindowsSMTCProvider.cpp +++ b/widget/windows/WindowsSMTCProvider.cpp @@ -356,7 +356,7 @@ bool WindowsSMTCProvider::RegisterEvents() { void WindowsSMTCProvider::OnButtonPressed( mozilla::dom::MediaControlKey aKey) const { if (!IsKeySupported(aKey)) { - LOG("key: %s is not supported", ToMediaControlKeyStr(aKey)); + LOG("key: %s is not supported", dom::GetEnumString(aKey).get()); return; } @@ -383,7 +383,7 @@ bool WindowsSMTCProvider::UpdateButtons() { for (const mozilla::dom::MediaControlKey& key : kKeys) { if (!EnableKey(key, IsKeySupported(key))) { success = false; - LOG("Failed to set %s=%s", ToMediaControlKeyStr(key), + LOG("Failed to set %s=%s", dom::GetEnumString(key).get(), IsKeySupported(key) ? "true" : "false"); } } @@ -414,7 +414,7 @@ bool WindowsSMTCProvider::EnableKey(mozilla::dom::MediaControlKey aKey, // The callback for the event checks if the key is supported return mSeekRegistrationToken.value != 0; default: - LOG("No button for %s", ToMediaControlKeyStr(aKey)); + LOG("No button for %s", dom::GetEnumString(aKey).get()); return false; } } diff --git a/widget/windows/moz.build b/widget/windows/moz.build index f19a46caf1..eeda8d1257 100644 --- a/widget/windows/moz.build +++ b/widget/windows/moz.build @@ -39,6 +39,7 @@ EXPORTS += [ ] EXPORTS.mozilla += [ + "ScreenHelperWin.h", "ShellHeaderOnlyUtils.h", "ToastNotificationHeaderOnlyUtils.h", "UrlmonHeaderOnlyUtils.h", diff --git a/widget/windows/nsFilePicker.cpp b/widget/windows/nsFilePicker.cpp index fb4c6e80b5..2a45937988 100644 --- a/widget/windows/nsFilePicker.cpp +++ b/widget/windows/nsFilePicker.cpp @@ -735,7 +735,11 @@ nsFilePicker::CheckContentAnalysisService() { auto contentAnalysisCallback = mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysisCallback>( [promise](nsIContentAnalysisResponse* aResponse) { - promise->Resolve(aResponse->GetShouldAllowContent(), __func__); + bool shouldAllow = false; + mozilla::DebugOnly<nsresult> rv = + aResponse->GetShouldAllowContent(&shouldAllow); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + promise->Resolve(shouldAllow, __func__); }, [promise](nsresult aError) { promise->Reject(aError, __func__); }); @@ -840,7 +844,7 @@ nsresult nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) { auto promise = mMode == modeGetFolder ? ShowFolderPicker(initialDir) : ShowFilePicker(initialDir); - auto p2 = promise->Then( + promise->Then( mozilla::GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__, [self = RefPtr(this), callback = RefPtr(aCallback)](bool selectionMade) -> void { diff --git a/widget/windows/nsLookAndFeel.cpp b/widget/windows/nsLookAndFeel.cpp index 6122000c9e..b6b6877857 100644 --- a/widget/windows/nsLookAndFeel.cpp +++ b/widget/windows/nsLookAndFeel.cpp @@ -36,6 +36,17 @@ static int32_t GetSystemParam(long flag, int32_t def) { return ::SystemParametersInfo(flag, 0, &value, 0) ? value : def; } +static int32_t GetTooltipOffsetVertical() { + static constexpr DWORD kDefaultCursorSize = 32; + const DWORD cursorSize = + GetSystemParam(MOZ_SPI_CURSORSIZE, kDefaultCursorSize); + if (cursorSize == kDefaultCursorSize) { + return LookAndFeel::kDefaultTooltipOffset; + } + return std::ceilf(float(LookAndFeel::kDefaultTooltipOffset) * + float(cursorSize) / float(kDefaultCursorSize)); +} + static bool SystemWantsDarkTheme() { if (nsUXThemeData::IsHighContrastOn()) { return LookAndFeel::IsDarkColor( @@ -353,6 +364,7 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::Marktext: case ColorID::Mark: case ColorID::SpellCheckerUnderline: + case ColorID::MozAutofillBackground: aColor = GetStandinForNativeColor(aID, aScheme); return NS_OK; default: @@ -392,13 +404,9 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = std::ceil(float(timeout) / (2.0f * float(blinkTime))); break; } - case IntID::CaretWidth: aResult = 1; break; - case IntID::ShowCaretDuringSelection: - aResult = 0; - break; case IntID::SelectTextfieldsOnKeyFocus: // Select textfield content when focused by kbd // used by EventStateManager::sTextfieldSelectModel @@ -523,6 +531,9 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::ContextMenuOffsetHorizontal: aResult = 2; break; + case IntID::TooltipOffsetVertical: + aResult = GetTooltipOffsetVertical(); + break; case IntID::SystemUsesDarkTheme: aResult = SystemWantsDarkTheme(); break; diff --git a/widget/windows/nsLookAndFeel.h b/widget/windows/nsLookAndFeel.h index d19aa91329..0ef38bfda5 100644 --- a/widget/windows/nsLookAndFeel.h +++ b/widget/windows/nsLookAndFeel.h @@ -40,6 +40,10 @@ #define SYS_COLOR_MAX 30 #define SYS_COLOR_COUNT (SYS_COLOR_MAX - SYS_COLOR_MIN + 1) +// Undocumented SPI, see bug 1712669 comment 4. +#define MOZ_SPI_CURSORSIZE 0x2028 +#define MOZ_SPI_SETCURSORSIZE 0x2029 + namespace mozilla::widget::WinRegistry { class KeyWatcher; } diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 04883a833f..7ef968baf6 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -484,9 +484,6 @@ mozilla::Maybe<nsUXThemeClass> nsNativeThemeWin::GetThemeClass( case StyleAppearance::Textfield: case StyleAppearance::Textarea: return Some(eUXEdit); - case StyleAppearance::Toolbox: - return Some(eUXRebar); - case StyleAppearance::Toolbar: case StyleAppearance::Toolbarbutton: case StyleAppearance::Separator: return Some(eUXToolbar); @@ -719,27 +716,6 @@ nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, } return NS_OK; } - case StyleAppearance::Toolbox: { - aState = 0; - aPart = RP_BACKGROUND; - return NS_OK; - } - case StyleAppearance::Toolbar: { - // Use -1 to indicate we don't wish to have the theme background drawn - // for this item. We will pass any nessessary information via aState, - // and will render the item using separate code. - aPart = -1; - aState = 0; - if (aFrame) { - nsIContent* content = aFrame->GetContent(); - nsIContent* parent = content->GetParent(); - // XXXzeniko hiding the first toolbar will result in an unwanted margin - if (parent && parent->GetFirstChild() == content) { - aState = 1; - } - } - return NS_OK; - } case StyleAppearance::Treeview: case StyleAppearance::Listbox: { aPart = TREEVIEW_BODY; @@ -1031,16 +1007,6 @@ RENDER_AGAIN: ::DeleteObject(hPen); } } - } else if (aAppearance == StyleAppearance::Toolbar && state == 0) { - // Draw toolbar separator lines above all toolbars except the first one. - // The lines are part of the Rebar theme, which is loaded for - // StyleAppearance::Toolbox. - theme = GetTheme(StyleAppearance::Toolbox); - if (!theme) return NS_ERROR_FAILURE; - - widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT; - DrawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, - nullptr); } nativeDrawing.EndNativeDrawing(); @@ -1101,7 +1067,6 @@ LayoutDeviceIntMargin nsNativeThemeWin::GetWidgetBorder( } if (!WidgetIsContainer(aAppearance) || - aAppearance == StyleAppearance::Toolbox || aAppearance == StyleAppearance::Tabpanel) return result; // Don't worry about it. @@ -1109,12 +1074,6 @@ LayoutDeviceIntMargin nsNativeThemeWin::GetWidgetBorder( nsresult rv = GetThemePartAndState(aFrame, aAppearance, part, state); if (NS_FAILED(rv)) return result; - if (aAppearance == StyleAppearance::Toolbar) { - // make space for the separator line above all toolbars but the first - if (state == 0) result.top = TB_SEPARATOR_HEIGHT; - return result; - } - result = GetCachedWidgetBorder(theme, themeClass.value(), aAppearance, part, state); @@ -1278,8 +1237,6 @@ LayoutDeviceIntSize nsNativeThemeWin::GetMinimumWidgetSize( switch (aAppearance) { case StyleAppearance::NumberInput: case StyleAppearance::Textfield: - case StyleAppearance::Toolbox: - case StyleAppearance::Toolbar: case StyleAppearance::Progresschunk: case StyleAppearance::Tabpanels: case StyleAppearance::Tabpanel: @@ -1352,9 +1309,7 @@ nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, nsAtom* aAttribute, bool* aShouldRepaint, const nsAttrValue* aOldValue) { // Some widget types just never change state. - if (aAppearance == StyleAppearance::Toolbox || - aAppearance == StyleAppearance::Toolbar || - aAppearance == StyleAppearance::Progresschunk || + if (aAppearance == StyleAppearance::Progresschunk || aAppearance == StyleAppearance::ProgressBar || aAppearance == StyleAppearance::Tabpanels || aAppearance == StyleAppearance::Tabpanel || diff --git a/widget/windows/nsUXThemeData.cpp b/widget/windows/nsUXThemeData.cpp index b3f9e6fce9..ce8b0f3479 100644 --- a/widget/windows/nsUXThemeData.cpp +++ b/widget/windows/nsUXThemeData.cpp @@ -62,8 +62,6 @@ const wchar_t* nsUXThemeData::GetClassName(nsUXThemeClass cls) { return L"Button"; case eUXEdit: return L"Edit"; - case eUXRebar: - return L"Rebar"; case eUXToolbar: return L"Toolbar"; case eUXProgress: diff --git a/widget/windows/nsUXThemeData.h b/widget/windows/nsUXThemeData.h index 38be8b4484..24fe07d128 100644 --- a/widget/windows/nsUXThemeData.h +++ b/widget/windows/nsUXThemeData.h @@ -19,7 +19,6 @@ enum nsUXThemeClass { eUXButton = 0, eUXEdit, - eUXRebar, eUXToolbar, eUXProgress, eUXTab, diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index b304d2efac..22d20d099e 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -78,6 +78,7 @@ #include <limits> #include "mozilla/widget/WinMessages.h" +#include "nsLookAndFeel.h" #include "nsWindow.h" #include "nsWindowTaskbarConcealer.h" #include "nsAppRunner.h" @@ -181,11 +182,13 @@ # endif # include "mozilla/a11y/Compatibility.h" # include "oleidl.h" +# include <uiautomation.h> # include <winuser.h> # include "nsAccessibilityService.h" # include "mozilla/a11y/DocAccessible.h" # include "mozilla/a11y/LazyInstantiator.h" # include "mozilla/a11y/Platform.h" +# include "mozilla/StaticPrefs_accessibility.h" # if !defined(WINABLEAPI) # include <winable.h> # endif // !defined(WINABLEAPI) @@ -1335,8 +1338,6 @@ DWORD nsWindow::WindowExStyle() { } return extendedStyle; } - case WindowType::Sheet: - MOZ_FALLTHROUGH_ASSERT("Sheets are macOS specific"); case WindowType::Dialog: case WindowType::TopLevel: case WindowType::Invisible: @@ -1583,13 +1584,6 @@ void nsWindow::Show(bool bState) { // SetWindowPos would get the correct answer. mIsVisible = bState; - // We may have cached an out of date visible state. This can happen - // when session restore sets the full screen mode. - if (mIsVisible) - mOldStyle |= WS_VISIBLE; - else - mOldStyle &= ~WS_VISIBLE; - if (mWnd) { if (bState) { if (!wasVisible && mWindowType == WindowType::TopLevel) { @@ -2043,13 +2037,6 @@ void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, if (aRepaint) Invalidate(); } -mozilla::Maybe<bool> nsWindow::IsResizingNativeWidget() { - if (mResizeState == RESIZING) { - return Some(true); - } - return Some(false); -} - /************************************************************** * * SECTION: Window Z-order and state. @@ -3070,34 +3057,85 @@ void nsWindow::HideWindowChrome(bool aShouldHide) { if (mHideChrome == aShouldHide) return; - DWORD_PTR style, exStyle; - mHideChrome = aShouldHide; - if (aShouldHide) { - DWORD_PTR tempStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE); - DWORD_PTR tempExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE); + // Data manipulation: styles + ex-styles, and bitmasking operations thereupon. + struct Styles { + LONG_PTR style, ex; + constexpr Styles operator|(Styles const& that) const { + return Styles{.style = style | that.style, .ex = ex | that.ex}; + } + constexpr Styles operator&(Styles const& that) const { + return Styles{.style = style & that.style, .ex = ex & that.ex}; + } + constexpr Styles operator~() const { + return Styles{.style = ~style, .ex = ~ex}; + } - style = tempStyle & ~(WS_CAPTION | WS_THICKFRAME); - exStyle = tempExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | - WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); + // Compute a style-set which matches `zero` where the bits of `this` are 0 + // and `one` where the bits of `this` are 1. + constexpr Styles merge(Styles zero, Styles one) const { + Styles const& mask = *this; + return (~mask & zero) | (mask & one); + } - mOldStyle = tempStyle; - mOldExStyle = tempExStyle; - } else { - if (!mOldStyle || !mOldExStyle) { - mOldStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE); - mOldExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE); + // The dual of `merge`, above: returns a pair [zero, one] satisfying + // `a.merge(a.split(b)...) == b`. (Or its equivalent in valid C++.) + constexpr std::tuple<Styles, Styles> split(Styles data) const { + Styles const& mask = *this; + return {~mask & data, mask & data}; } + }; - style = mOldStyle; - exStyle = mOldExStyle; + // Get styles from an HWND. + constexpr auto const GetStyles = [](HWND hwnd) { + return Styles{.style = ::GetWindowLongPtrW(hwnd, GWL_STYLE), + .ex = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE)}; + }; + constexpr auto const SetStyles = [](HWND hwnd, Styles styles) { + VERIFY_WINDOW_STYLE(styles.style); + ::SetWindowLongPtrW(hwnd, GWL_STYLE, styles.style); + ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, styles.ex); + }; + + // Get styles from *this. + auto const GetCachedStyles = [&]() { + return mOldStyles.map([](auto const& m) { + return Styles{.style = m.style, .ex = m.exStyle}; + }); + }; + auto const SetCachedStyles = [&](Styles styles) { + using WStyles = nsWindow::WindowStyles; + mOldStyles = Some(WStyles{.style = styles.style, .exStyle = styles.ex}); + }; + + // The mask describing the "chrome" which this function is supposed to remove + // (or restore, as the case may be). Other style-flags will be left untouched. + constexpr static const Styles kChromeMask{ + .style = WS_CAPTION | WS_THICKFRAME, + .ex = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | + WS_EX_STATICEDGE}; + + // The desired style-flagset for fullscreen windows. (This happens to be all + // zeroes, but we don't need to rely on that.) + constexpr static const Styles kFullscreenChrome{.style = 0, .ex = 0}; + + auto const [chromeless, currentChrome] = kChromeMask.split(GetStyles(hwnd)); + Styles newChrome{}, oldChrome{}; + + mHideChrome = aShouldHide; + if (aShouldHide) { + newChrome = kFullscreenChrome; + oldChrome = currentChrome; + } else { + // if there's nothing to "restore" it to, just use what's there now + oldChrome = GetCachedStyles().refOr(currentChrome); + newChrome = oldChrome; if (mFutureMarginsToUse) { SetNonClientMargins(mFutureMarginsOnceChromeShows); } } - VERIFY_WINDOW_STYLE(style); - ::SetWindowLongPtrW(hwnd, GWL_STYLE, style); - ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle); + SetCachedStyles(oldChrome); + SetStyles(hwnd, kChromeMask.merge(chromeless, newChrome)); } /************************************************************** @@ -4941,10 +4979,12 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, case WM_SETTINGCHANGE: { if (wParam == SPI_SETCLIENTAREAANIMATION || - wParam == SPI_SETKEYBOARDDELAY || wParam == SPI_SETMOUSEVANISH) { + wParam == SPI_SETKEYBOARDDELAY || wParam == SPI_SETMOUSEVANISH || + wParam == MOZ_SPI_SETCURSORSIZE) { // These need to update LookAndFeel cached values. // They affect reduced motion settings / caret blink count / show - // pointer while typing, so no need to invalidate style / layout. + // pointer while typing / tooltip offset, so no need to invalidate style + // / layout. NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly); break; } @@ -5885,6 +5925,20 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, a11y::LazyInstantiator::EnableBlindAggregation(mWnd); result = true; } + } else if (objId == UiaRootObjectId && + StaticPrefs::accessibility_uia_enable()) { + if (a11y::LocalAccessible* acc = GetAccessible()) { + RefPtr<IAccessible> ia; + acc->GetNativeInterface(getter_AddRefs(ia)); + MOZ_ASSERT(ia); + RefPtr<IRawElementProviderSimple> uia; + ia->QueryInterface(IID_IRawElementProviderSimple, + getter_AddRefs(uia)); + if (uia) { + *aRetValue = UiaReturnRawElementProvider(mWnd, wParam, lParam, uia); + result = true; + } + } } } break; #endif diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 3a521fb978..a2b2a701bd 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -175,7 +175,6 @@ class nsWindow final : public nsBaseWidget { void Resize(double aWidth, double aHeight, bool aRepaint) override; void Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override; - mozilla::Maybe<bool> IsResizingNativeWidget() override; void PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget* aWidget, bool aActivate) override; void SetSizeMode(nsSizeMode aMode) override; @@ -755,8 +754,12 @@ class nsWindow final : public nsBaseWidget { bool mIsAlert = false; bool mIsPerformingDwmFlushHack = false; bool mDraggingWindowWithMouse = false; - DWORD_PTR mOldStyle = 0; - DWORD_PTR mOldExStyle = 0; + // Partial cached window-styles, for when going fullscreen. (Only window- + // decoration-related flags are saved here.) + struct WindowStyles { + LONG_PTR style, exStyle; + }; + mozilla::Maybe<WindowStyles> mOldStyles; nsNativeDragTarget* mNativeDragTarget = nullptr; HKL mLastKeyboardLayout = 0; mozilla::CheckInvariantWrapper<FrameState> mFrameState; |