diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
commit | fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch) | |
tree | 4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /widget | |
parent | Releasing progress-linux version 124.0.1-1~progress7.99u1. (diff) | |
download | firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'widget')
143 files changed, 4329 insertions, 1802 deletions
diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index ae02d04676..f42c1e3b2c 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -209,6 +209,10 @@ NS_EVENT_MESSAGE(eLegacyDOMFocusOut) NS_EVENT_MESSAGE(ePageShow) NS_EVENT_MESSAGE(ePageHide) +// Canvas events +NS_EVENT_MESSAGE(eContextLost) +NS_EVENT_MESSAGE(eContextRestored) + // SVG events NS_EVENT_MESSAGE(eSVGLoad) NS_EVENT_MESSAGE(eSVGScroll) diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index 3e32ffbe15..251b46ce66 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -279,6 +279,12 @@ static const char* GetPrefNameForFeature(int32_t aFeature) { case nsIGfxInfo::FEATURE_WEBGL_USE_HARDWARE: name = BLOCKLIST_PREF_BRANCH "webgl-use-hardware"; break; + case nsIGfxInfo::FEATURE_OVERLAY_VP_AUTO_HDR: + name = BLOCKLIST_PREF_BRANCH "overlay-vp-auto-hdr"; + break; + case nsIGfxInfo::FEATURE_OVERLAY_VP_SUPER_RESOLUTION: + name = BLOCKLIST_PREF_BRANCH "overlay-vp-super-resolution"; + break; default: MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!"); break; @@ -555,6 +561,12 @@ static int32_t BlocklistFeatureToGfxFeature(const nsAString& aFeature) { if (aFeature.EqualsLiteral("ACCELERATED_CANVAS2D")) { return nsIGfxInfo::FEATURE_ACCELERATED_CANVAS2D; } + if (aFeature.EqualsLiteral("FEATURE_OVERLAY_VP_AUTO_HDR")) { + return nsIGfxInfo::FEATURE_OVERLAY_VP_AUTO_HDR; + } + if (aFeature.EqualsLiteral("FEATURE_OVERLAY_VP_SUPER_RESOLUTION")) { + return nsIGfxInfo::FEATURE_OVERLAY_VP_SUPER_RESOLUTION; + } if (aFeature.EqualsLiteral("ALL")) { return GfxDriverInfo::allFeatures; } @@ -1276,8 +1288,7 @@ bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor, } bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const { - return aFeature == nsIGfxInfo::FEATURE_VIDEO_OVERLAY || - aFeature == nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY; + return aFeature == nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY; } nsresult GfxInfoBase::GetFeatureStatusImpl( @@ -1925,7 +1936,7 @@ using Device = nsIGfxInfo::FontVisibilityDeviceDetermination; static StaticAutoPtr<std::pair<Device, nsString>> ret; std::pair<Device, nsString>* GfxInfoBase::GetFontVisibilityDeterminationPair() { - if(!ret) { + if (!ret) { ret = new std::pair<Device, nsString>(); ret->first = Device::Unassigned; ret->second = u""_ns; @@ -2150,6 +2161,18 @@ GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) { return NS_OK; } +NS_IMETHODIMP +GfxInfoBase::GetUsingRemoteCanvas(bool* aOutValue) { + *aOutValue = gfx::gfxVars::RemoteCanvasEnabled(); + return NS_OK; +} + +NS_IMETHODIMP +GfxInfoBase::GetUsingAcceleratedCanvas(bool* aOutValue) { + *aOutValue = gfx::gfxVars::UseAcceleratedCanvas2D(); + return NS_OK; +} + NS_IMETHODIMP_(int32_t) GfxInfoBase::GetMaxRefreshRate(bool* aMixed) { if (aMixed) { diff --git a/widget/GfxInfoBase.h b/widget/GfxInfoBase.h index 4d4b1a2def..c2bab66400 100644 --- a/widget/GfxInfoBase.h +++ b/widget/GfxInfoBase.h @@ -73,6 +73,8 @@ class GfxInfoBase : public nsIGfxInfo, NS_IMETHOD GetAzureCanvasBackend(nsAString& aBackend) override; NS_IMETHOD GetAzureContentBackend(nsAString& aBackend) override; NS_IMETHOD GetUsingGPUProcess(bool* aOutValue) override; + NS_IMETHOD GetUsingRemoteCanvas(bool* aOutValue) override; + NS_IMETHOD GetUsingAcceleratedCanvas(bool* aOutValue) override; NS_IMETHOD GetIsHeadless(bool* aIsHeadless) override; NS_IMETHOD GetTargetFrameRate(uint32_t* aTargetFrameRate) override; NS_IMETHOD GetCodecSupportInfo(nsACString& aCodecSupportInfo) override; diff --git a/widget/IMEData.h b/widget/IMEData.h index 7fb1241333..1ea33e18b9 100644 --- a/widget/IMEData.h +++ b/widget/IMEData.h @@ -451,18 +451,20 @@ struct InputContext final { bool IsInputAttributeChanged(const InputContext& aOldContext) const { return mIMEState.mEnabled != aOldContext.mIMEState.mEnabled || -#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN) +#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_WIN) || \ + defined(XP_IOS) // input type and inputmode are supported by Windows IME API, GTK - // IME API and Android IME API + // IME API, Android IME API and iOS API. mHTMLInputType != aOldContext.mHTMLInputType || mHTMLInputMode != aOldContext.mHTMLInputMode || #endif -#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) - // autocapitalize is supported by Android IME API and GTK IME API +#if defined(ANDROID) || defined(MOZ_WIDGET_GTK) || defined(XP_IOS) + // autocapitalize is supported by Android IME API, GTK IME API, and + // iOS API mAutocapitalize != aOldContext.mAutocapitalize || #endif -#if defined(ANDROID) - // enterkeyhint is only supported by Android IME API. +#if defined(ANDROID) || defined(XP_IOS) + // enterkeyhint is only supported by Android IME API and iOS API. mActionHint != aOldContext.mActionHint || #endif false; diff --git a/widget/IconLoader.cpp b/widget/IconLoader.cpp index 5c0488e3e2..41d2a29bc7 100644 --- a/widget/IconLoader.cpp +++ b/widget/IconLoader.cpp @@ -9,6 +9,7 @@ #include "imgLoader.h" #include "imgRequestProxy.h" #include "mozilla/dom/Document.h" +#include "mozilla/dom/FetchPriority.h" #include "nsContentUtils.h" #include "nsIContent.h" #include "nsIContentPolicy.h" @@ -63,7 +64,7 @@ nsresult IconLoader::LoadIcon(nsIURI* aIconURI, nsINode* aNode, nullptr, nsIRequest::LOAD_NORMAL, nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE, u""_ns, /* aUseUrgentStartForChannel */ false, /* aLinkPreload */ false, 0, - getter_AddRefs(mIconRequest)); + dom::FetchPriority::Auto, getter_AddRefs(mIconRequest)); } else { // TODO: nsIContentPolicy::TYPE_INTERNAL_IMAGE may not be the correct // policy. See bug 1691868 for more details. @@ -72,7 +73,8 @@ nsresult IconLoader::LoadIcon(nsIURI* aIconURI, nsINode* aNode, aNode, document, nsIRequest::LOAD_NORMAL, nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE, u""_ns, /* aUseUrgentStartForChannel */ false, - /* aLinkPreload */ false, 0, getter_AddRefs(mIconRequest)); + /* aLinkPreload */ false, 0, dom::FetchPriority::Auto, + getter_AddRefs(mIconRequest)); } if (NS_FAILED(rv)) { return rv; diff --git a/widget/InitData.h b/widget/InitData.h index 1498feed9a..aed5e0a551 100644 --- a/widget/InitData.h +++ b/widget/InitData.h @@ -37,11 +37,6 @@ enum class PopupLevel : uint8_t { // The popup appears just above its parent and maintains its position // relative to the parent. Parent, - // The popup is a floating popup used for tool palettes. A parent window must - // be specified, but a platform implementation need not use this. On Windows, - // floating is generally equivalent to parent. On Mac, floating puts the - // popup at toplevel, but it will hide when the application is deactivated. - Floating, // The popup appears on top of other windows, including those of other // applications. Top, diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 2ab61df304..0cab187410 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -163,10 +163,6 @@ class LookAndFeel { * 1: scrollbar button repeats to scroll even if cursor is outside of it. */ ScrollbarButtonAutoRepeatBehavior, - /** - * Delay before showing a tooltip. - */ - TooltipDelay, /* * A Boolean value to determine whether swipe animations should be used. */ @@ -293,6 +289,9 @@ class LookAndFeel { /** GTK titlebar radius */ TitlebarRadius, + /** GTK button-to-button spacing in the inline axis */ + TitlebarButtonSpacing, + /** * Corresponding to dynamic-range. * https://drafts.csswg.org/mediaqueries-5/#dynamic-range diff --git a/widget/Theme.cpp b/widget/Theme.cpp index 35c53240e5..1fcd3d4bc3 100644 --- a/widget/Theme.cpp +++ b/widget/Theme.cpp @@ -1562,7 +1562,7 @@ UniquePtr<ScrollbarDrawing> Theme::ScrollbarStyle() { return MakeUnique<ScrollbarDrawingWin11>(); } return MakeUnique<ScrollbarDrawingWin>(); -#elif MOZ_WIDGET_COCOA +#elif defined(MOZ_WIDGET_COCOA) || defined(MOZ_WIDGET_UIKIT) return MakeUnique<ScrollbarDrawingCocoa>(); #elif MOZ_WIDGET_GTK return MakeUnique<ScrollbarDrawingGTK>(); diff --git a/widget/ThemeCocoa.cpp b/widget/ThemeCocoa.cpp index 733c215991..a41d41409b 100644 --- a/widget/ThemeCocoa.cpp +++ b/widget/ThemeCocoa.cpp @@ -4,27 +4,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ThemeCocoa.h" - -#include "cocoa/MacThemeGeometryType.h" #include "gfxPlatform.h" -#include "mozilla/ClearOnShutdown.h" -#include "mozilla/gfx/Helpers.h" #include "mozilla/LookAndFeel.h" #include "mozilla/ServoStyleConsts.h" namespace mozilla::widget { -LayoutDeviceIntSize ThemeCocoa::GetMinimumWidgetSize( - nsPresContext* aPresContext, nsIFrame* aFrame, - StyleAppearance aAppearance) { - if (aAppearance == StyleAppearance::MozMenulistArrowButton) { - auto size = - GetScrollbarSize(aPresContext, StyleScrollbarWidth::Auto, Overlay::No); - return {size, size}; - } - return Theme::GetMinimumWidgetSize(aPresContext, aFrame, aAppearance); -} - NS_IMETHODIMP ThemeCocoa::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance, diff --git a/widget/ThemeCocoa.h b/widget/ThemeCocoa.h index f846766f37..d5d84ee5f0 100644 --- a/widget/ThemeCocoa.h +++ b/widget/ThemeCocoa.h @@ -9,8 +9,6 @@ #include "Theme.h" -#include "ScrollbarDrawingCocoa.h" - namespace mozilla::widget { class ThemeCocoa : public Theme { @@ -18,10 +16,6 @@ class ThemeCocoa : public Theme { explicit ThemeCocoa(UniquePtr<ScrollbarDrawing>&& aScrollbarDrawing) : Theme(std::move(aScrollbarDrawing)) {} - LayoutDeviceIntSize GetMinimumWidgetSize( - nsPresContext* aPresContext, nsIFrame* aFrame, - StyleAppearance aAppearance) override; - NS_IMETHOD DrawWidgetBackground(gfxContext* aContext, nsIFrame*, StyleAppearance, const nsRect& aRect, const nsRect& aDirtyRect, diff --git a/widget/android/EventDispatcher.cpp b/widget/android/EventDispatcher.cpp index ab876de136..6e414c11ce 100644 --- a/widget/android/EventDispatcher.cpp +++ b/widget/android/EventDispatcher.cpp @@ -129,9 +129,9 @@ nsresult UnboxBundle(JSContext* aCx, const jni::Object::LocalRef& aData, nsresult rv = UnboxValue(aCx, values->GetElement(i), &value); if (rv == NS_ERROR_INVALID_ARG && !JS_IsExceptionPending(aCx)) { JS_ReportErrorUTF8( - aCx, u8"Invalid event data property %s", - NS_ConvertUTF16toUTF8( - nsString(reinterpret_cast<const char16_t*>(keyChars), keyLen)) + aCx, "Invalid event data property %s", + NS_ConvertUTF16toUTF8(reinterpret_cast<const char16_t*>(keyChars), + keyLen) .get()); } NS_ENSURE_SUCCESS(rv, rv); diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index 0e78b187bf..6e6d3bf47b 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -350,11 +350,11 @@ GfxInfo::GetDrmRenderDevice(nsACString& aDrmRenderDevice) { } void GfxInfo::AddCrashReportAnnotations() { - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID, - mGLStrings->Vendor()); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID, - mGLStrings->Renderer()); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationNSCString( + CrashReporter::Annotation::AdapterVendorID, mGLStrings->Vendor()); + CrashReporter::RecordAnnotationNSCString( + CrashReporter::Annotation::AdapterDeviceID, mGLStrings->Renderer()); + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::AdapterDriverVersion, mGLStrings->Version()); } diff --git a/widget/android/jni/Utils.cpp b/widget/android/jni/Utils.cpp index 08164e4950..8d3800fd03 100644 --- a/widget/android/jni/Utils.cpp +++ b/widget/android/jni/Utils.cpp @@ -221,7 +221,7 @@ bool HandleUncaughtException(JNIEnv* aEnv) { bool ReportException(JNIEnv* aEnv, jthrowable aExc, jstring aStack) { bool result = true; - result &= NS_SUCCEEDED(CrashReporter::AnnotateCrashReport( + result &= NS_SUCCEEDED(CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::JavaStackTrace, String::Ref::From(aStack)->ToCString())); diff --git a/widget/android/nsLookAndFeel.cpp b/widget/android/nsLookAndFeel.cpp index 02919e0dbf..6d9f8c634f 100644 --- a/widget/android/nsLookAndFeel.cpp +++ b/widget/android/nsLookAndFeel.cpp @@ -312,10 +312,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = 200; break; - case IntID::TooltipDelay: - aResult = 500; - break; - case IntID::MenusCanOverlapOSBar: // we want XUL popups to be able to overlap the task bar. aResult = 1; diff --git a/widget/cocoa/CFTypeRefPtr.h b/widget/cocoa/CFTypeRefPtr.h deleted file mode 100644 index 185355777e..0000000000 --- a/widget/cocoa/CFTypeRefPtr.h +++ /dev/null @@ -1,194 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef CFTypeRefPtr_h -#define CFTypeRefPtr_h - -#include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" -#include "mozilla/DbgMacro.h" -#include "mozilla/HashFunctions.h" - -// A smart pointer for CoreFoundation classes which does reference counting. -// -// Manual reference counting: -// -// UInt32 someNumber = 10; -// CFNumberRef numberObject = -// CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &someNumber); -// // do something with numberObject -// CFRelease(numberObject); -// -// Automatic reference counting using CFTypeRefPtr: -// -// UInt32 someNumber = 10; -// auto numberObject = -// CFTypeRefPtr<CFNumberRef>::WrapUnderCreateRule( -// CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &someNumber)); -// // do something with numberObject -// // no CFRelease - -template <class PtrT> -class CFTypeRefPtr { - private: - void assign_with_CFRetain(PtrT aRawPtr) { - CFRetain(aRawPtr); - assign_assuming_CFRetain(aRawPtr); - } - - void assign_assuming_CFRetain(PtrT aNewPtr) { - PtrT oldPtr = mRawPtr; - mRawPtr = aNewPtr; - if (oldPtr) { - CFRelease(oldPtr); - } - } - - private: - PtrT mRawPtr; - - public: - ~CFTypeRefPtr() { - if (mRawPtr) { - CFRelease(mRawPtr); - } - } - - // Constructors - - CFTypeRefPtr() : mRawPtr(nullptr) {} - - CFTypeRefPtr(const CFTypeRefPtr<PtrT>& aSmartPtr) - : mRawPtr(aSmartPtr.mRawPtr) { - if (mRawPtr) { - CFRetain(mRawPtr); - } - } - - CFTypeRefPtr(CFTypeRefPtr<PtrT>&& aRefPtr) : mRawPtr(aRefPtr.mRawPtr) { - aRefPtr.mRawPtr = nullptr; - } - - MOZ_IMPLICIT CFTypeRefPtr(decltype(nullptr)) : mRawPtr(nullptr) {} - - // There is no constructor from a raw pointer value. - // Use one of the static WrapUnder*Rule methods below instead. - - static CFTypeRefPtr<PtrT> WrapUnderCreateRule(PtrT aRawPtr) { - CFTypeRefPtr<PtrT> ptr; - ptr.AssignUnderCreateRule(aRawPtr); - return ptr; - } - - static CFTypeRefPtr<PtrT> WrapUnderGetRule(PtrT aRawPtr) { - CFTypeRefPtr<PtrT> ptr; - ptr.AssignUnderGetRule(aRawPtr); - return ptr; - } - - // Assignment operators - - CFTypeRefPtr<PtrT>& operator=(decltype(nullptr)) { - assign_assuming_CFRetain(nullptr); - return *this; - } - - CFTypeRefPtr<PtrT>& operator=(const CFTypeRefPtr<PtrT>& aRhs) { - assign_with_CFRetain(aRhs.mRawPtr); - return *this; - } - - CFTypeRefPtr<PtrT>& operator=(CFTypeRefPtr<PtrT>&& aRefPtr) { - assign_assuming_CFRetain(aRefPtr.mRawPtr); - aRefPtr.mRawPtr = nullptr; - return *this; - } - - // There is no operator= for a raw pointer value. - // Use one of the AssignUnder*Rule methods below instead. - - CFTypeRefPtr<PtrT>& AssignUnderCreateRule(PtrT aRawPtr) { - // Freshly-created objects come with a retain count of 1. - assign_assuming_CFRetain(aRawPtr); - return *this; - } - - CFTypeRefPtr<PtrT>& AssignUnderGetRule(PtrT aRawPtr) { - assign_with_CFRetain(aRawPtr); - return *this; - } - - // Other pointer operators - - // This is the only way to get the raw pointer out of the smart pointer. - // There is no implicit conversion to a raw pointer. - PtrT get() const { return mRawPtr; } - - // Don't allow implicit conversion of temporary CFTypeRefPtr to raw pointer, - // because the refcount might be one and the pointer will immediately become - // invalid. - operator PtrT() const&& = delete; - // Also don't allow implicit conversion of non-temporary CFTypeRefPtr. - operator PtrT() const& = delete; - - // These let you null-check a pointer without calling get(). - explicit operator bool() const { return !!mRawPtr; } -}; - -template <class PtrT> -inline bool operator==(const CFTypeRefPtr<PtrT>& aLhs, - const CFTypeRefPtr<PtrT>& aRhs) { - return aLhs.get() == aRhs.get(); -} - -template <class PtrT> -inline bool operator!=(const CFTypeRefPtr<PtrT>& aLhs, - const CFTypeRefPtr<PtrT>& aRhs) { - return !(aLhs == aRhs); -} - -// Comparing an |CFTypeRefPtr| to |nullptr| - -template <class PtrT> -inline bool operator==(const CFTypeRefPtr<PtrT>& aLhs, decltype(nullptr)) { - return aLhs.get() == nullptr; -} - -template <class PtrT> -inline bool operator==(decltype(nullptr), const CFTypeRefPtr<PtrT>& aRhs) { - return nullptr == aRhs.get(); -} - -template <class PtrT> -inline bool operator!=(const CFTypeRefPtr<PtrT>& aLhs, decltype(nullptr)) { - return aLhs.get() != nullptr; -} - -template <class PtrT> -inline bool operator!=(decltype(nullptr), const CFTypeRefPtr<PtrT>& aRhs) { - return nullptr != aRhs.get(); -} - -// MOZ_DBG support - -template <class PtrT> -std::ostream& operator<<(std::ostream& aOut, const CFTypeRefPtr<PtrT>& aObj) { - return mozilla::DebugValue(aOut, aObj.get()); -} - -// std::hash support (e.g. for unordered_map) -namespace std { -template <class PtrT> -struct hash<CFTypeRefPtr<PtrT>> { - typedef CFTypeRefPtr<PtrT> argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& aPtr) const { - return mozilla::HashGeneric(reinterpret_cast<uintptr_t>(aPtr.get())); - } -}; -} // namespace std - -#endif /* CFTypeRefPtr_h */ diff --git a/widget/cocoa/GfxInfo.mm b/widget/cocoa/GfxInfo.mm index 1476cc27db..f4477cef4a 100644 --- a/widget/cocoa/GfxInfo.mm +++ b/widget/cocoa/GfxInfo.mm @@ -419,21 +419,17 @@ GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) { return NS_ERROR_FAILURE; } void GfxInfo::AddCrashReportAnnotations() { nsString deviceID, vendorID, driverVersion; - nsAutoCString narrowDeviceID, narrowVendorID, narrowDriverVersion; GetAdapterDeviceID(deviceID); - CopyUTF16toUTF8(deviceID, narrowDeviceID); GetAdapterVendorID(vendorID); - CopyUTF16toUTF8(vendorID, narrowVendorID); GetAdapterDriverVersion(driverVersion); - CopyUTF16toUTF8(driverVersion, narrowDriverVersion); - - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID, - narrowVendorID); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID, - narrowDeviceID); - CrashReporter::AnnotateCrashReport( - CrashReporter::Annotation::AdapterDriverVersion, narrowDriverVersion); + + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterVendorID, vendorID); + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterDeviceID, deviceID); + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterDriverVersion, driverVersion); } // We don't support checking driver versions on Mac. diff --git a/widget/cocoa/MOZIconHelper.mm b/widget/cocoa/MOZIconHelper.mm index e15407b797..f9c8f02406 100644 --- a/widget/cocoa/MOZIconHelper.mm +++ b/widget/cocoa/MOZIconHelper.mm @@ -32,11 +32,11 @@ if (aScaleFactor != 0.0f) { rv = nsCocoaUtils::CreateNSImageFromImageContainer( aImage, imgIContainer::FRAME_CURRENT, aPresContext, aComputedStyle, - &retainedImage, aScaleFactor, &isEntirelyBlack); + aSize, &retainedImage, aScaleFactor, &isEntirelyBlack); } else { rv = nsCocoaUtils::CreateDualRepresentationNSImageFromImageContainer( aImage, imgIContainer::FRAME_CURRENT, aPresContext, aComputedStyle, - &retainedImage, &isEntirelyBlack); + aSize, &retainedImage, &isEntirelyBlack); } NSImage* image = [retainedImage autorelease]; @@ -45,10 +45,6 @@ return nil; } - int32_t origWidth = 0, origHeight = 0; - aImage->GetWidth(&origWidth); - aImage->GetHeight(&origHeight); - // If all the color channels in the image are black, treat the image as a // template. This will cause macOS to use the image's alpha channel as a mask // and it will fill it with a color that looks good in the context that it's diff --git a/widget/cocoa/OSXNotificationCenter.mm b/widget/cocoa/OSXNotificationCenter.mm index 07cb026f1f..f7d507a947 100644 --- a/widget/cocoa/OSXNotificationCenter.mm +++ b/widget/cocoa/OSXNotificationCenter.mm @@ -544,9 +544,11 @@ OSXNotificationCenter::OnImageReady(nsISupports* aUserData, NSImage* cocoaImage = nil; // TODO: Pass pres context / ComputedStyle here to support context paint - // properties + // properties. + // TODO: Do we have a reasonable size to pass around here? nsCocoaUtils::CreateDualRepresentationNSImageFromImageContainer( - image, imgIContainer::FRAME_FIRST, nullptr, nullptr, &cocoaImage); + image, imgIContainer::FRAME_FIRST, nullptr, nullptr, NSMakeSize(0, 0), + &cocoaImage); (osxni->mPendingNotification).contentImage = cocoaImage; [cocoaImage release]; ShowPendingNotification(osxni); diff --git a/widget/cocoa/TextRecognition.mm b/widget/cocoa/TextRecognition.mm index c365f51982..31a170466a 100644 --- a/widget/cocoa/TextRecognition.mm +++ b/widget/cocoa/TextRecognition.mm @@ -70,8 +70,8 @@ auto TextRecognition::DoFindText(gfx::DataSourceSurface& aSurface, // https://developer.apple.com/documentation/vision/vnrecognizedtext?language=objc auto& quad = *pResult->quads().AppendElement(); - CopyCocoaStringToXPCOMString(recognizedText.string, - quad.string()); + CopyNSStringToXPCOMString(recognizedText.string, + quad.string()); quad.confidence() = recognizedText.confidence; auto ToImagePoint = [](CGPoint aPoint) -> ImagePoint { diff --git a/widget/cocoa/moz.build b/widget/cocoa/moz.build index ebee9439e0..ddb402e2cc 100644 --- a/widget/cocoa/moz.build +++ b/widget/cocoa/moz.build @@ -18,7 +18,6 @@ XPIDL_SOURCES += [ XPIDL_MODULE = "widget_cocoa" EXPORTS += [ - "CFTypeRefPtr.h", "DesktopBackgroundImage.h", "MediaHardwareKeysEventSourceMac.h", "MediaHardwareKeysEventSourceMacMediaCenter.h", diff --git a/widget/cocoa/nsCocoaUtils.h b/widget/cocoa/nsCocoaUtils.h index 9e3b76a920..9b36c1192d 100644 --- a/widget/cocoa/nsCocoaUtils.h +++ b/widget/cocoa/nsCocoaUtils.h @@ -18,6 +18,7 @@ #include "nsObjCExceptions.h" #include "mozilla/EventForwards.h" +#include "mozilla/MacStringHelpers.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPtr.h" #include "nsIWidget.h" @@ -298,8 +299,9 @@ class nsCocoaUtils { static nsresult CreateNSImageFromImageContainer( imgIContainer* aImage, uint32_t aWhichFrame, const nsPresContext* aPresContext, - const mozilla::ComputedStyle* aComputedStyle, NSImage** aResult, - CGFloat scaleFactor, bool* aIsEntirelyBlack = nullptr); + const mozilla::ComputedStyle* aComputedStyle, + const NSSize& aPreferredSize, NSImage** aResult, CGFloat scaleFactor, + bool* aIsEntirelyBlack = nullptr); /** Creates a Cocoa <code>NSImage</code> from a frame of an <code>imgIContainer</code>. The new <code>NSImage</code> will have both a @@ -317,18 +319,23 @@ class nsCocoaUtils { static nsresult CreateDualRepresentationNSImageFromImageContainer( imgIContainer* aImage, uint32_t aWhichFrame, const nsPresContext* aPresContext, - const mozilla::ComputedStyle* aComputedStyle, NSImage** aResult, + const mozilla::ComputedStyle* aComputedStyle, + const NSSize& aPreferredSize, NSImage** aResult, bool* aIsEntirelyBlack = nullptr); /** * Returns nsAString for aSrc. */ - static void GetStringForNSString(const NSString* aSrc, nsAString& aDist); + static void GetStringForNSString(const NSString* aSrc, nsAString& aDist) { + mozilla::CopyNSStringToXPCOMString(aSrc, aDist); + } /** * Makes NSString instance for aString. */ - static NSString* ToNSString(const nsAString& aString); + static NSString* ToNSString(const nsAString& aString) { + return mozilla::XPCOMStringToNSString(aString); + } /** * Returns an NSURL instance for the provided string. @@ -338,7 +345,9 @@ class nsCocoaUtils { /** * Makes NSString instance for aCString. */ - static NSString* ToNSString(const nsACString& aCString); + static NSString* ToNSString(const nsACString& aCString) { + return mozilla::XPCOMStringToNSString(aCString); + } /** * Returns NSRect for aGeckoRect. diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm index f3a7604762..769eb05a85 100644 --- a/widget/cocoa/nsCocoaUtils.mm +++ b/widget/cocoa/nsCocoaUtils.mm @@ -515,11 +515,27 @@ nsresult nsCocoaUtils::CreateNSImageFromCGImage(CGImageRef aInputImage, nsresult nsCocoaUtils::CreateNSImageFromImageContainer( imgIContainer* aImage, uint32_t aWhichFrame, const nsPresContext* aPresContext, const ComputedStyle* aComputedStyle, - NSImage** aResult, CGFloat scaleFactor, bool* aIsEntirelyBlack) { + const NSSize& aPreferredSize, NSImage** aResult, CGFloat scaleFactor, + bool* aIsEntirelyBlack) { RefPtr<SourceSurface> surface; - int32_t width = 0, height = 0; - aImage->GetWidth(&width); - aImage->GetHeight(&height); + int32_t width = 0; + int32_t height = 0; + { + const bool gotWidth = NS_SUCCEEDED(aImage->GetWidth(&width)); + const bool gotHeight = NS_SUCCEEDED(aImage->GetHeight(&height)); + if (auto ratio = aImage->GetIntrinsicRatio()) { + if (gotWidth != gotHeight) { + if (gotWidth) { + height = ratio->Inverted().ApplyTo(width); + } else { + width = ratio->ApplyTo(height); + } + } else if (!gotWidth) { + height = std::ceil(aPreferredSize.height); + width = ratio->ApplyTo(height); + } + } + } // Render a vector image at the correct resolution on a retina display if (aImage->GetType() == imgIContainer::TYPE_VECTOR) { @@ -582,31 +598,28 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer( nsresult nsCocoaUtils::CreateDualRepresentationNSImageFromImageContainer( imgIContainer* aImage, uint32_t aWhichFrame, const nsPresContext* aPresContext, const ComputedStyle* aComputedStyle, - NSImage** aResult, bool* aIsEntirelyBlack) { - int32_t width = 0, height = 0; - aImage->GetWidth(&width); - aImage->GetHeight(&height); - NSSize size = NSMakeSize(width, height); - *aResult = [[NSImage alloc] init]; - [*aResult setSize:size]; - + const NSSize& aPreferredSize, NSImage** aResult, bool* aIsEntirelyBlack) { NSImage* newRepresentation = nil; nsresult rv = CreateNSImageFromImageContainer( - aImage, aWhichFrame, aPresContext, aComputedStyle, &newRepresentation, - 1.0f, aIsEntirelyBlack); + aImage, aWhichFrame, aPresContext, aComputedStyle, aPreferredSize, + &newRepresentation, 1.0f, aIsEntirelyBlack); if (NS_FAILED(rv) || !newRepresentation) { return NS_ERROR_FAILURE; } + NSSize size = newRepresentation.size; + *aResult = [[NSImage alloc] init]; + [*aResult setSize:size]; + [[[newRepresentation representations] objectAtIndex:0] setSize:size]; [*aResult addRepresentation:[[newRepresentation representations] objectAtIndex:0]]; [newRepresentation release]; newRepresentation = nil; - rv = CreateNSImageFromImageContainer(aImage, aWhichFrame, aPresContext, - aComputedStyle, &newRepresentation, 2.0f, - aIsEntirelyBlack); + rv = CreateNSImageFromImageContainer( + aImage, aWhichFrame, aPresContext, aComputedStyle, aPreferredSize, + &newRepresentation, 2.0f, aIsEntirelyBlack); if (NS_FAILED(rv) || !newRepresentation) { return NS_ERROR_FAILURE; } @@ -619,43 +632,6 @@ nsresult nsCocoaUtils::CreateDualRepresentationNSImageFromImageContainer( } // static -void nsCocoaUtils::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; -} - -// static -NSString* nsCocoaUtils::ToNSString(const nsAString& aString) { - if (aString.IsEmpty()) { - return [NSString string]; - } - return [NSString stringWithCharacters:reinterpret_cast<const unichar*>( - aString.BeginReading()) - length:aString.Length()]; -} - -// static -NSString* nsCocoaUtils::ToNSString(const nsACString& aCString) { - if (aCString.IsEmpty()) { - return [NSString string]; - } - return [[[NSString alloc] initWithBytes:aCString.BeginReading() - length:aCString.Length() - encoding:NSUTF8StringEncoding] autorelease]; -} - -// static NSURL* nsCocoaUtils::ToNSURL(const nsAString& aURLString) { nsAutoCString encodedURLString; nsresult rv = NS_GetSpecWithNSURLEncoding(encodedURLString, diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h index c2e595677f..621c32eb44 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h @@ -492,6 +492,7 @@ class nsCocoaWindow final : public nsBaseWidget, public nsPIWidgetCocoa { bool mWindowTransformIsIdentity; bool mAlwaysOnTop; bool mAspectRatioLocked; + bool mIsAlert = false; // True if this is an non-native alert window. int32_t mNumModalDescendents; InputContext mInputContext; diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index e5abaa5a87..4b135e7565 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -187,7 +187,7 @@ void nsCocoaWindow::DestroyNativeWindow() { [mWindow releaseJSObjects]; // We want to unhook the delegate here because we don't want events // sent to it after this object has been destroyed. - [mWindow setDelegate:nil]; + mWindow.delegate = nil; [mWindow close]; mWindow = nil; [mDelegate autorelease]; @@ -260,7 +260,7 @@ DesktopToLayoutDeviceScale ParentBackingScaleFactor(nsIWidget* aParent, } NSWindow* parentWindow = [aParentView window]; if (parentWindow) { - return DesktopToLayoutDeviceScale([parentWindow backingScaleFactor]); + return DesktopToLayoutDeviceScale(parentWindow.backingScaleFactor); } return DesktopToLayoutDeviceScale(1.0); } @@ -322,6 +322,7 @@ nsresult nsCocoaWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, mParent = aParent; mAncestorLink = aParent; mAlwaysOnTop = aInitData->mAlwaysOnTop; + mIsAlert = aInitData->mIsAlert; // If we have a parent widget, the new widget will be offset from the // parent widget by aRect.{x,y}. Otherwise, we'll use aRect for the @@ -477,29 +478,31 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect& aRect, contentRect = aRect; contentRect.origin.y -= (newWindowFrame.size.height - aRect.size.height); - if (mWindowType != WindowType::Popup) - contentRect.origin.y -= [[NSApp mainMenu] menuBarHeight]; + if (mWindowType != WindowType::Popup) { + contentRect.origin.y -= NSApp.mainMenu.menuBarHeight; + } } // NSLog(@"Top-level window being created at Cocoa rect: %f, %f, %f, %f\n", // rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); Class windowClass = [BaseWindow class]; - // If we have a titlebar on a top-level window, we want to be able to control - // the titlebar color (for unified windows), so use the special ToolbarWindow - // class. Note that we need to check the window type because we mark sheets as - // having titlebars. if ((mWindowType == WindowType::TopLevel || mWindowType == WindowType::Dialog) && - (features & NSWindowStyleMaskTitled)) + (features & NSWindowStyleMaskTitled)) { + // If we have a titlebar on a top-level window, we want to be able to + // control the titlebar color (for unified windows), so use the special + // ToolbarWindow class. Note that we need to check the window type because + // we mark sheets as having titlebars. windowClass = [ToolbarWindow class]; - // If we're a popup window we need to use the PopupWindow class. - else if (mWindowType == WindowType::Popup) + } else if (mWindowType == WindowType::Popup) { windowClass = [PopupWindow class]; - // If we're a non-popup borderless window we need to use the - // BorderlessWindow class. - else if (features == NSWindowStyleMaskBorderless) + // If we're a popup window we need to use the PopupWindow class. + } else if (features == NSWindowStyleMaskBorderless) { + // If we're a non-popup borderless window we need to use the + // BorderlessWindow class. windowClass = [BorderlessWindow class]; + } // Create the window mWindow = [[windowClass alloc] initWithContentRect:contentRect @@ -509,65 +512,60 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect& aRect, // Make sure that window titles don't leak to disk in private browsing mode // due to macOS' resume feature. - [mWindow setRestorable:!aIsPrivateBrowsing]; + mWindow.restorable = !aIsPrivateBrowsing; if (aIsPrivateBrowsing) { [mWindow disableSnapshotRestoration]; } // setup our notification delegate. Note that setDelegate: does NOT retain. mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this]; - [mWindow setDelegate:mDelegate]; + mWindow.delegate = mDelegate; // Make sure that the content rect we gave has been honored. NSRect wantedFrame = [mWindow frameRectForChildViewRect:contentRect]; - if (!NSEqualRects([mWindow frame], wantedFrame)) { + if (!NSEqualRects(mWindow.frame, wantedFrame)) { // This can happen when the window is not on the primary screen. [mWindow setFrame:wantedFrame display:NO]; } UpdateBounds(); if (mWindowType == WindowType::Invisible) { - [mWindow setLevel:kCGDesktopWindowLevelKey]; + mWindow.level = kCGDesktopWindowLevelKey; } if (mWindowType == WindowType::Popup) { SetPopupWindowLevel(); - [mWindow setBackgroundColor:NSColor.clearColor]; - [mWindow setOpaque:NO]; + mWindow.backgroundColor = NSColor.clearColor; + mWindow.opaque = NO; // When multiple spaces are in use and the browser is assigned to a // particular space, override the "Assign To" space and display popups on // the active space. Does not work with multiple displays. See // NeedsRecreateToReshow() for multi-display with multi-space workaround. - if (!mAlwaysOnTop) { - NSWindowCollectionBehavior behavior = [mWindow collectionBehavior]; - behavior |= NSWindowCollectionBehaviorMoveToActiveSpace; - [mWindow setCollectionBehavior:behavior]; - } + mWindow.collectionBehavior = mWindow.collectionBehavior | + NSWindowCollectionBehaviorMoveToActiveSpace; } else { // Non-popup windows are always opaque. - [mWindow setOpaque:YES]; + mWindow.opaque = YES; } - NSWindowCollectionBehavior newBehavior = [mWindow collectionBehavior]; - if (mAlwaysOnTop) { - [mWindow setLevel:NSFloatingWindowLevel]; - newBehavior |= NSWindowCollectionBehaviorCanJoinAllSpaces; + if (mAlwaysOnTop || mIsAlert) { + mWindow.level = NSFloatingWindowLevel; + mWindow.collectionBehavior = + mWindow.collectionBehavior | NSWindowCollectionBehaviorCanJoinAllSpaces; } - [mWindow setCollectionBehavior:newBehavior]; - - [mWindow setContentMinSize:NSMakeSize(60, 60)]; + mWindow.contentMinSize = NSMakeSize(60, 60); [mWindow disableCursorRects]; // Make the window use CoreAnimation from the start, so that we don't // switch from a non-CA window to a CA-window in the middle. - [[mWindow contentView] setWantsLayer:YES]; + mWindow.contentView.wantsLayer = YES; // Make sure the window starts out not draggable by the background. // We will turn it on as necessary. - [mWindow setMovableByWindowBackground:NO]; + mWindow.movableByWindowBackground = NO; - [[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow]; + [WindowDataMap.sharedWindowDataMap ensureDataForWindow:mWindow]; mWindowMadeHere = true; // Make the window respect the global appearance, which follows the @@ -596,11 +594,11 @@ nsresult nsCocoaWindow::CreatePopupContentView(const LayoutDeviceIntRect& aRect, return rv; } - NSView* contentView = [mWindow contentView]; - ChildView* childView = - (ChildView*)mPopupContentView->GetNativeData(NS_NATIVE_WIDGET); - [childView setFrame:[contentView bounds]]; - [childView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + NSView* contentView = mWindow.contentView; + auto* childView = static_cast<ChildView*>( + mPopupContentView->GetNativeData(NS_NATIVE_WIDGET)); + childView.frame = contentView.bounds; + childView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; [contentView addSubview:childView]; return NS_OK; @@ -670,7 +668,7 @@ void* nsCocoaWindow::GetNativeData(uint32_t aDataType) { // to emulate how windows works, we always have to return a NSView // for NS_NATIVE_WIDGET case NS_NATIVE_WIDGET: - retVal = [mWindow contentView]; + retVal = mWindow.contentView; break; case NS_NATIVE_WINDOW: @@ -687,9 +685,9 @@ void* nsCocoaWindow::GetNativeData(uint32_t aDataType) { if (retVal) { break; } - NSView* view = mWindow ? [mWindow contentView] : nil; + NSView* view = mWindow ? mWindow.contentView : nil; if (view) { - retVal = [view inputContext]; + retVal = view.inputContext; } // If inputContext isn't available on this window, return this window's // pointer instead of nullptr since if this returns nullptr, @@ -792,10 +790,11 @@ void nsCocoaWindow::SetModal(bool aState) { gGeckoAppModalWindowList = gGeckoAppModalWindowList->prev; delete saved; // "window" not ADDREFed } - if (mWindowType == WindowType::Popup) + if (mWindowType == WindowType::Popup) { SetPopupWindowLevel(); - else - [mWindow setLevel:NSNormalWindowLevel]; + } else { + mWindow.level = NSNormalWindowLevel; + } } } @@ -810,43 +809,44 @@ void nsCocoaWindow::SetFakeModal(bool aState) { bool nsCocoaWindow::IsRunningAppModal() { return [NSApp _isRunningAppModal]; } // Hide or show this window -void nsCocoaWindow::Show(bool bState) { +void nsCocoaWindow::Show(bool aState) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - if (!mWindow) return; + if (!mWindow) { + return; + } if (!mSheetNeedsShow) { // Early exit if our current visibility state is already the requested // state. - if (bState == ([mWindow isVisible] || [mWindow isBeingShown])) { + if (aState == mWindow.isVisibleOrBeingShown) { return; } } - [mWindow setBeingShown:bState]; - if (bState && !mWasShown) { + [mWindow setBeingShown:aState]; + if (aState && !mWasShown) { mWasShown = true; } nsIWidget* parentWidget = mParent; nsCOMPtr<nsPIWidgetCocoa> piParentWidget(do_QueryInterface(parentWidget)); NSWindow* nativeParentWindow = - (parentWidget) ? (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) - : nil; + parentWidget ? (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW) + : nil; - if (bState && !mBounds.IsEmpty()) { + if (aState && !mBounds.IsEmpty()) { // If we had set the activationPolicy to accessory, then right now we won't // have a dock icon. Make sure that we undo that and show a dock icon now // that we're going to show a window. - if ([NSApp activationPolicy] != NSApplicationActivationPolicyRegular) { - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + if (NSApp.activationPolicy != NSApplicationActivationPolicyRegular) { + NSApp.activationPolicy = NSApplicationActivationPolicyRegular; PR_SetEnv("MOZ_APP_NO_DOCK="); } // Don't try to show a popup when the parent isn't visible or is minimized. if (mWindowType == WindowType::Popup && nativeParentWindow) { - if (![nativeParentWindow isVisible] || - [nativeParentWindow isMiniaturized]) { + if (!nativeParentWindow.isVisible || nativeParentWindow.isMiniaturized) { return; } } @@ -947,7 +947,7 @@ void nsCocoaWindow::Show(bool bState) { // close other programs' context menus when ours open. if ([mWindow isKindOfClass:[PopupWindow class]] && [(PopupWindow*)mWindow isContextMenu]) { - [[NSDistributedNotificationCenter defaultCenter] + [NSDistributedNotificationCenter.defaultCenter postNotificationName: @"com.apple.HIToolbox.beginMenuTrackingNotification" object:@"org.mozilla.gecko.PopupWindow"]; @@ -958,8 +958,9 @@ void nsCocoaWindow::Show(bool bState) { // 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. - if (nativeParentWindow && mPopupLevel == PopupLevel::Parent) + if (nativeParentWindow && mPopupLevel == PopupLevel::Parent) { [nativeParentWindow addChildWindow:mWindow ordered:NSWindowAbove]; + } } else { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; if (mWindowType == WindowType::TopLevel && @@ -983,9 +984,9 @@ void nsCocoaWindow::Show(bool bState) { mWindowAnimationBehavior = behavior; } - // We don't want alwaysontop windows to pull focus when they're opened, - // as these tend to be for peripheral indicators and displays. - if (mAlwaysOnTop) { + // We don't want alwaysontop / alert windows to pull focus when they're + // opened, as these tend to be for peripheral indicators and displays. + if (mAlwaysOnTop || mIsAlert) { [mWindow orderFront:nil]; } else { [mWindow makeKeyAndOrderFront:nil]; @@ -1099,8 +1100,9 @@ void nsCocoaWindow::Show(bool bState) { // 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) + if (mWindowType == WindowType::Popup && nativeParentWindow) { [nativeParentWindow removeChildWindow:mWindow]; + } [mWindow orderOut:nil]; @@ -1129,8 +1131,8 @@ void nsCocoaWindow::Show(bool bState) { bool nsCocoaWindow::NeedsRecreateToReshow() { // Limit the workaround to popup windows because only they need to override // the "Assign To" setting. i.e., to display where the parent window is. - return (mWindowType == WindowType::Popup) && mWasShown && - ([[NSScreen screens] count] > 1); + return mWindowType == WindowType::Popup && mWasShown && + NSScreen.screens.count > 1; } WindowRenderer* nsCocoaWindow::GetWindowRenderer() { @@ -1143,8 +1145,8 @@ WindowRenderer* nsCocoaWindow::GetWindowRenderer() { TransparencyMode nsCocoaWindow::GetTransparencyMode() { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - return (!mWindow || [mWindow isOpaque]) ? TransparencyMode::Opaque - : TransparencyMode::Transparent; + return !mWindow || mWindow.isOpaque ? TransparencyMode::Opaque + : TransparencyMode::Transparent; NS_OBJC_END_TRY_BLOCK_RETURN(TransparencyMode::Opaque); } @@ -1162,9 +1164,9 @@ void nsCocoaWindow::SetTransparencyMode(TransparencyMode aMode) { if (isTransparent == currentTransparency) { return; } - [mWindow setOpaque:!isTransparent]; - [mWindow setBackgroundColor:(isTransparent ? NSColor.clearColor - : NSColor.whiteColor)]; + mWindow.opaque = !isTransparent; + mWindow.backgroundColor = + isTransparent ? NSColor.clearColor : NSColor.whiteColor; NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -1184,7 +1186,7 @@ void nsCocoaWindow::ConstrainPosition(DesktopIntPoint& aPoint) { int32_t width, height; - NSRect frame = [mWindow frame]; + NSRect frame = mWindow.frame; // zero size rects confuse the screen manager width = std::max<int32_t>(frame.size.width, 1); @@ -1243,7 +1245,7 @@ void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints) { NSSize minSize = { nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.width, c.mScale.scale), nsCocoaUtils::DevPixelsToCocoaPoints(c.mMinSize.height, c.mScale.scale)}; - [mWindow setMinSize:minSize]; + mWindow.minSize = minSize; c.mMaxSize.width = std::max( nsCocoaUtils::CocoaPointsToDevPixels(c.mMaxSize.width, c.mScale.scale), @@ -1259,8 +1261,7 @@ void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints) { c.mMaxSize.height == NS_MAXSIZE ? FLT_MAX : nsCocoaUtils::DevPixelsToCocoaPoints( c.mMaxSize.height, c.mScale.scale)}; - [mWindow setMaxSize:maxSize]; - + mWindow.maxSize = maxSize; nsBaseWidget::SetSizeConstraints(c); NS_OBJC_END_TRY_IGNORE_BLOCK; @@ -1280,7 +1281,7 @@ void nsCocoaWindow::Move(double aX, double aY) { static_cast<float>(aX), static_cast<float>(nsCocoaUtils::FlippedScreenY(NSToIntRound(aY)))}; - NSRect frame = [mWindow frame]; + NSRect frame = mWindow.frame; if (frame.origin.x != coord.x || frame.origin.y + frame.size.height != coord.y) { [mWindow setFrameTopLeftPoint:coord]; @@ -1488,13 +1489,9 @@ void nsCocoaWindow::MoveToWorkspace(const nsAString& workspaceIDStr) { void nsCocoaWindow::SuppressAnimation(bool aSuppress) { if ([mWindow respondsToSelector:@selector(setAnimationBehavior:)]) { - if (aSuppress) { - [mWindow setIsAnimationSuppressed:YES]; - [mWindow setAnimationBehavior:NSWindowAnimationBehaviorNone]; - } else { - [mWindow setIsAnimationSuppressed:NO]; - [mWindow setAnimationBehavior:mWindowAnimationBehavior]; - } + mWindow.isAnimationSuppressed = aSuppress; + mWindow.animationBehavior = + aSuppress ? NSWindowAnimationBehaviorNone : mWindowAnimationBehavior; } } @@ -1506,10 +1503,11 @@ void nsCocoaWindow::HideWindowChrome(bool aShouldHide) { if (!mWindow || !mWindowMadeHere || (mWindowType != WindowType::TopLevel && - mWindowType != WindowType::Dialog)) + mWindowType != WindowType::Dialog)) { return; + } - BOOL isVisible = [mWindow isVisible]; + const BOOL isVisible = mWindow.isVisible; // Remove child windows. NSArray* childWindows = [mWindow childWindows]; @@ -1530,7 +1528,7 @@ void nsCocoaWindow::HideWindowChrome(bool aShouldHide) { NSMutableDictionary* state = [mWindow exportState]; // Recreate the window with the right border style. - NSRect frameRect = [mWindow frame]; + NSRect frameRect = mWindow.frame; DestroyNativeWindow(); nsresult rv = CreateNativeWindow( frameRect, aShouldHide ? BorderStyle::None : mBorderStyle, true, @@ -1629,7 +1627,7 @@ static bool AlwaysUsesNativeFullScreen() { NSScreen* cocoaScreen = ScreenHelperCocoa::CocoaScreenForScreen(widgetScreen); NSWindow* win = - [[NSWindow alloc] initWithContentRect:[cocoaScreen frame] + [[NSWindow alloc] initWithContentRect:cocoaScreen.frame styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]; @@ -2075,7 +2073,7 @@ void nsCocoaWindow::DoResize(double aX, double aY, double aWidth, // convert requested bounds into Cocoa coordinate system NSRect newFrame = nsCocoaUtils::GeckoRectToCocoaRect(newBounds); - NSRect frame = [mWindow frame]; + NSRect frame = mWindow.frame; BOOL isMoving = newFrame.origin.x != frame.origin.x || newFrame.origin.y != frame.origin.y; BOOL isResizing = newFrame.size.width != frame.size.width || @@ -2114,7 +2112,7 @@ NSRect nsCocoaWindow::GetClientCocoaRect() { return NSZeroRect; } - return [mWindow childViewRectForFrameRect:[mWindow frame]]; + return [mWindow childViewRectForFrameRect:mWindow.frame]; } LayoutDeviceIntRect nsCocoaWindow::GetClientBounds() { @@ -2130,7 +2128,7 @@ LayoutDeviceIntRect nsCocoaWindow::GetClientBounds() { void nsCocoaWindow::UpdateBounds() { NSRect frame = NSZeroRect; if (mWindow) { - frame = [mWindow frame]; + frame = mWindow.frame; } mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, BackingScaleFactor()); @@ -2145,7 +2143,7 @@ LayoutDeviceIntRect nsCocoaWindow::GetScreenBounds() { #ifdef DEBUG LayoutDeviceIntRect r = nsCocoaUtils::CocoaRectToGeckoRectDevPix( - [mWindow frame], BackingScaleFactor()); + mWindow.frame, BackingScaleFactor()); NS_ASSERTION(mWindow && mBounds == r, "mBounds out of sync!"); #endif @@ -2157,7 +2155,7 @@ LayoutDeviceIntRect nsCocoaWindow::GetScreenBounds() { double nsCocoaWindow::GetDefaultScaleInternal() { return BackingScaleFactor(); } static CGFloat GetBackingScaleFactor(NSWindow* aWindow) { - NSRect frame = [aWindow frame]; + NSRect frame = aWindow.frame; if (frame.size.width > 0 && frame.size.height > 0) { return nsCocoaUtils::GetBackingScaleFactor(aWindow); } @@ -2220,6 +2218,7 @@ void nsCocoaWindow::BackingScaleFactorChanged() { } mBackingScaleFactor = newScale; + NotifyAPZOfDPIChange(); if (!mWidgetListener || mWidgetListener->GetAppWindow()) { return; @@ -2255,7 +2254,7 @@ nsresult nsCocoaWindow::SetTitle(const nsAString& aTitle) { const unichar* uniTitle = reinterpret_cast<const unichar*>(strTitle.get()); NSString* title = [NSString stringWithCharacters:uniTitle length:strTitle.Length()]; - if ([mWindow drawsContentsIntoWindowFrame] && ![mWindow wantsTitleDrawn]) { + if (mWindow.drawsContentsIntoWindowFrame && !mWindow.wantsTitleDrawn) { // Don't cause invalidations when the title isn't displayed. [mWindow disableSetNeedsDisplay]; [mWindow setTitle:title]; @@ -2291,9 +2290,10 @@ bool nsCocoaWindow::DragEvent(unsigned int aMessage, NS_IMETHODIMP nsCocoaWindow::SendSetZLevelEvent() { nsWindowZ placement = nsWindowZTop; nsCOMPtr<nsIWidget> actualBelow; - if (mWidgetListener) + if (mWidgetListener) { mWidgetListener->ZLevelChanged(true, &placement, nullptr, getter_AddRefs(actualBelow)); + } return NS_OK; } @@ -2344,8 +2344,9 @@ nsresult nsCocoaWindow::DispatchEvent(WidgetGUIEvent* event, nsCOMPtr<nsIWidget> kungFuDeathGrip(event->mWidget); mozilla::Unused << kungFuDeathGrip; // Not used within this function - if (mWidgetListener) + if (mWidgetListener) { aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents); + } return NS_OK; } @@ -2355,10 +2356,15 @@ nsresult nsCocoaWindow::DispatchEvent(WidgetGUIEvent* event, // canonical indicator that a window is currently full screen and it makes sense // to keep all sizemode logic here. static nsSizeMode GetWindowSizeMode(NSWindow* aWindow, bool aFullScreen) { - if (aFullScreen) return nsSizeMode_Fullscreen; - if ([aWindow isMiniaturized]) return nsSizeMode_Minimized; - if (([aWindow styleMask] & NSWindowStyleMaskResizable) && [aWindow isZoomed]) + if (aFullScreen) { + return nsSizeMode_Fullscreen; + } + if (aWindow.isMiniaturized) { + return nsSizeMode_Minimized; + } + if ((aWindow.styleMask & NSWindowStyleMaskResizable) && aWindow.isZoomed) { return nsSizeMode_Maximized; + } return nsSizeMode_Normal; } @@ -2379,7 +2385,7 @@ void nsCocoaWindow::ReportMoveEvent() { // The zoomed state can change when we're moving, in which case we need to // update our internal mSizeMode. This can happen either if we're maximized // and then moved, or if we're not maximized and moved back to zoomed state. - if (mWindow && ((mSizeMode == nsSizeMode_Maximized) ^ [mWindow isZoomed])) { + if (mWindow && (mSizeMode == nsSizeMode_Maximized) ^ mWindow.isZoomed) { DispatchSizeModeEvent(); } @@ -2462,8 +2468,9 @@ void nsCocoaWindow::SetMenuBar(RefPtr<nsMenuBarX>&& aMenuBar) { // displayed when the app starts up. if (mMenuBar && ((!gSomeMenuBarPainted && nsMenuUtilsX::GetHiddenWindowMenuBar() == mMenuBar) || - [mWindow isMainWindow])) + mWindow.isMainWindow)) { mMenuBar->Paint(); + } } void nsCocoaWindow::SetFocus(Raise aRaise, @@ -2474,12 +2481,10 @@ void nsCocoaWindow::SetFocus(Raise aRaise, return mPopupContentView->SetFocus(aRaise, aCallerType); } - if (aRaise == Raise::Yes && - ([mWindow isVisible] || [mWindow isMiniaturized])) { - if ([mWindow isMiniaturized]) { + if (aRaise == Raise::Yes && (mWindow.isVisible || mWindow.isMiniaturized)) { + if (mWindow.isMiniaturized) { [mWindow deminiaturize:nil]; } - [mWindow makeKeyAndOrderFront:nil]; SendSetZLevelEvent(); } @@ -2533,7 +2538,7 @@ void nsCocoaWindow::CaptureRollupEvents(bool aDoCapture) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; if (aDoCapture) { - if (![NSApp isActive]) { + if (!NSApp.isActive) { // We need to capture mouse event if we aren't // the active application. We only set this up when needed // because they cause spurious mouse event after crash @@ -2553,14 +2558,17 @@ void nsCocoaWindow::CaptureRollupEvents(bool aDoCapture) { // So here we fiddle with a non-native popup window's level to make sure the // "active" one is always above any other non-native popup windows that // may be visible. - if (mWindow && (mWindowType == WindowType::Popup)) SetPopupWindowLevel(); + if (mWindowType == WindowType::Popup) { + SetPopupWindowLevel(); + } } else { nsToolkit::GetToolkit()->StopMonitoringAllProcessMouseEvents(); // XXXndeakin this doesn't make sense. // Why is the new window assumed to be a modal panel? - if (mWindow && (mWindowType == WindowType::Popup)) - [mWindow setLevel:NSModalPanelWindowLevel]; + if (mWindow && mWindowType == WindowType::Popup) { + mWindow.level = NSModalPanelWindowLevel; + } } NS_OBJC_END_TRY_IGNORE_BLOCK; @@ -2644,7 +2652,7 @@ void nsCocoaWindow::SetWindowTransform(const gfx::Matrix& aTransform) { // Calling CGSSetWindowTransform when the window is not visible results in // misplacing the window into doubled x,y coordinates (see bug 1448132). - if (![mWindow isVisible] || NSIsEmptyRect([mWindow frame])) { + if (!mWindow.isVisible || NSIsEmptyRect(mWindow.frame)) { return; } @@ -2753,13 +2761,9 @@ void nsCocoaWindow::SetWindowAnimationType( void nsCocoaWindow::SetDrawsTitle(bool aDrawTitle) { NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - if (![mWindow drawsContentsIntoWindowFrame]) { - // If we don't draw into the window frame, we always want to display window - // titles. - [mWindow setWantsTitleDrawn:YES]; - } else { - [mWindow setWantsTitleDrawn:aDrawTitle]; - } + // If we don't draw into the window frame, we always want to display window + // titles. + mWindow.wantsTitleDrawn = aDrawTitle || !mWindow.drawsContentsIntoWindowFrame; NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -2853,19 +2857,13 @@ void nsCocoaWindow::UpdateThemeGeometries( } void nsCocoaWindow::SetPopupWindowLevel() { - if (!mWindow) return; - - // Floating popups are at the floating level and hide when the window is - // deactivated. - if (mPopupLevel == PopupLevel::Floating) { - [mWindow setLevel:NSFloatingWindowLevel]; - [mWindow setHidesOnDeactivate:YES]; - } else { - // Otherwise, this is a top-level or parent popup. Parent popups always - // appear just above their parent and essentially ignore the level. - [mWindow setLevel:NSPopUpMenuWindowLevel]; - [mWindow setHidesOnDeactivate:NO]; + if (!mWindow) { + return; } + // Otherwise, this is a top-level or parent popup. Parent popups always + // appear just above their parent and essentially ignore the level. + mWindow.level = NSPopUpMenuWindowLevel; + mWindow.hidesOnDeactivate = NO; } void nsCocoaWindow::SetInputContext(const InputContext& aContext, @@ -2897,7 +2895,7 @@ bool nsCocoaWindow::GetEditCommands(NativeKeyBindingsType aType, void nsCocoaWindow::PauseOrResumeCompositor(bool aPause) { if (auto* mainChildView = - static_cast<nsIWidget*>([[mWindow mainChildView] widget])) { + static_cast<nsIWidget*>(mWindow.mainChildView.widget)) { mainChildView->PauseOrResumeCompositor(aPause); } } @@ -2951,16 +2949,17 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() { nsCocoaWindow* geckoWidget = [windowDelegate geckoWidget]; NS_ASSERTION(geckoWidget, "Window delegate not returning a gecko widget!"); - nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar(); - if (geckoMenuBar) { + if (nsMenuBarX* geckoMenuBar = geckoWidget->GetMenuBar()) { geckoMenuBar->Paint(); } else { // sometimes we don't have a native application menu early in launching - if (!sApplicationMenu) return; + if (!sApplicationMenu) { + return; + } - NSMenu* mainMenu = [NSApp mainMenu]; + NSMenu* mainMenu = NSApp.mainMenu; NS_ASSERTION( - [mainMenu numberOfItems] > 0, + mainMenu.numberOfItems > 0, "Main menu does not have any items, something is terribly wrong!"); // Create a new menu bar. @@ -2976,7 +2975,7 @@ already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() { [firstMenuItem release]; // set our new menu bar as the main menu - [NSApp setMainMenu:newMenuBar]; + NSApp.mainMenu = newMenuBar; [newMenuBar release]; } @@ -3097,8 +3096,8 @@ void nsCocoaWindow::CocoaWindowDidResize() { // in fullscreen mode. In Safari they're not transparent. But in Firefox // for some reason they are, which causes bug 1069658. The following code // works around this Apple bug or design flaw. - NSWindow* window = (NSWindow*)[notification object]; - NSView* frameView = [[window contentView] superview]; + NSWindow* window = notification.object; + NSView* frameView = window.contentView.superview; NSView* titlebarView = nil; NSView* titlebarContainerView = nil; if ([frameView respondsToSelector:@selector(titlebarView)]) { @@ -3142,9 +3141,13 @@ void nsCocoaWindow::CocoaWindowDidResize() { // [NSApp _isRunningAppModal] will return true if we're running an OS dialog // app modally. If one of those is up then we want it to retain its menu bar. - if ([NSApp _isRunningAppModal]) return; - NSWindow* window = [aNotification object]; - if (window) [WindowDelegate paintMenubarForWindow:window]; + if (NSApp._isRunningAppModal) { + return; + } + NSWindow* window = aNotification.object; + if (window) { + [WindowDelegate paintMenubarForWindow:window]; + } if ([window isKindOfClass:[ToolbarWindow class]]) { [(ToolbarWindow*)window windowMainStateChanged]; @@ -3254,8 +3257,9 @@ void nsCocoaWindow::CocoaWindowDidResize() { } - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)proposedFrame { - if (!mHasEverBeenZoomed && [window isZoomed]) return NO; // See bug 429954. - + if (!mHasEverBeenZoomed && window.isZoomed) { + return NO; // See bug 429954. + } mHasEverBeenZoomed = YES; return YES; } @@ -3298,7 +3302,7 @@ void nsCocoaWindow::CocoaWindowDidResize() { if ([window respondsToSelector:@selector(backingScaleFactor)]) { CGFloat oldFactor = [[[aNotification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; - if ([window backingScaleFactor] != oldFactor) { + if (window.backingScaleFactor != oldFactor) { mGeckoWindow->BackingScaleFactorChanged(); } } @@ -3349,7 +3353,7 @@ void nsCocoaWindow::CocoaWindowDidResize() { if (![self.window isKindOfClass:[ToolbarWindow class]]) { return self.FrameView__closeButtonOrigin; } - ToolbarWindow* win = (ToolbarWindow*)[self window]; + auto* win = static_cast<ToolbarWindow*>(self.window); if (win.drawsContentsIntoWindowFrame && !(win.styleMask & NSWindowStyleMaskFullScreen) && (win.styleMask & NSWindowStyleMaskTitled)) { @@ -3360,8 +3364,9 @@ void nsCocoaWindow::CocoaWindowDidResize() { // for the vertical coordinate will move the buttons above the window, // making them invisible. return NSMakePoint(buttonsRect.origin.x, win.frame.size.height); - } else if (win.windowTitlebarLayoutDirection == - NSUserInterfaceLayoutDirectionRightToLeft) { + } + if (win.windowTitlebarLayoutDirection == + NSUserInterfaceLayoutDirectionRightToLeft) { // We're in RTL mode, which means that the close button is the rightmost // button of the three window buttons. and buttonsRect.origin is the // bottom left corner of the green (zoom) button. The close button is 40px @@ -3375,13 +3380,14 @@ void nsCocoaWindow::CocoaWindowDidResize() { } - (CGFloat)FrameView__titlebarHeight { + // XXX: Shouldn't this be [super FrameView__titlebarHeight]? CGFloat height = [self FrameView__titlebarHeight]; - if ([[self window] isKindOfClass:[ToolbarWindow class]]) { + if ([self.window isKindOfClass:[ToolbarWindow class]]) { // Make sure that the titlebar height includes our shifted buttons. // The following coordinates are in window space, with the origin being at // the bottom left corner of the window. - ToolbarWindow* win = (ToolbarWindow*)[self window]; - CGFloat frameHeight = [self frame].size.height; + auto* win = static_cast<ToolbarWindow*>(self.window); + CGFloat frameHeight = self.frame.size.height; CGFloat windowButtonY = frameHeight; if (!NSIsEmptyRect(win.windowButtonsRect) && win.drawsContentsIntoWindowFrame && @@ -3506,8 +3512,8 @@ static NSImage* GetMenuMaskImage() { } - (void)swapOutChildViewWrapper:(NSView*)aNewWrapper { - [aNewWrapper setFrame:[[self contentView] frame]]; - NSView* childView = [[self mainChildView] retain]; + aNewWrapper.frame = self.contentView.frame; + NSView* childView = [self.mainChildView retain]; [childView removeFromSuperview]; [aNewWrapper addSubview:childView]; [childView release]; @@ -3603,16 +3609,16 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; - (NSMutableDictionary*)exportState { NSMutableDictionary* state = [NSMutableDictionary dictionaryWithCapacity:10]; - if (NSString* title = [self title]) { + if (NSString* title = self.title) { [state setObject:title forKey:kStateTitleKey]; } - [state setObject:[NSNumber numberWithBool:[self drawsContentsIntoWindowFrame]] + [state setObject:[NSNumber numberWithBool:self.drawsContentsIntoWindowFrame] forKey:kStateDrawsContentsIntoWindowFrameKey]; - [state setObject:[NSNumber numberWithBool:[self showsToolbarButton]] + [state setObject:[NSNumber numberWithBool:self.showsToolbarButton] forKey:kStateShowsToolbarButton]; - [state setObject:[NSNumber numberWithUnsignedInt:[self collectionBehavior]] + [state setObject:[NSNumber numberWithUnsignedInt:self.collectionBehavior] forKey:kStateCollectionBehavior]; - [state setObject:[NSNumber numberWithBool:[self wantsTitleDrawn]] + [state setObject:[NSNumber numberWithBool:self.wantsTitleDrawn] forKey:kStateWantsTitleDrawn]; return state; } @@ -3667,17 +3673,17 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; } - (NSView*)trackingAreaView { - NSView* contentView = [self contentView]; - return [contentView superview] ? [contentView superview] : contentView; + NSView* contentView = self.contentView; + return contentView.superview ? contentView.superview : contentView; } - (NSArray<NSView*>*)contentViewContents { - return [[[[self contentView] subviews] copy] autorelease]; + return [[self.contentView.subviews copy] autorelease]; } - (ChildView*)mainChildView { - NSView* contentView = [self contentView]; - NSView* lastView = [[contentView subviews] lastObject]; + NSView* contentView = self.contentView; + NSView* lastView = contentView.subviews.lastObject; if ([lastView isKindOfClass:[ChildView class]]) { return (ChildView*)lastView; } @@ -3686,7 +3692,7 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; - (void)removeTrackingArea { if (mTrackingArea) { - [[self trackingAreaView] removeTrackingArea:mTrackingArea]; + [self.trackingAreaView removeTrackingArea:mTrackingArea]; [mTrackingArea release]; mTrackingArea = nil; } @@ -3695,7 +3701,7 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; - (void)updateTrackingArea { [self removeTrackingArea]; - NSView* view = [self trackingAreaView]; + NSView* view = self.trackingAreaView; const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways; @@ -3741,7 +3747,7 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; // Possibly move the titlebar buttons. - (void)reflowTitlebarElements { - NSView* frameView = [[self contentView] superview]; + NSView* frameView = self.contentView.superview; if ([frameView respondsToSelector:@selector(_tileTitlebarAndRedisplay:)]) { [frameView _tileTitlebarAndRedisplay:NO]; } @@ -3852,15 +3858,15 @@ static const NSString* kStateWantsTitleDrawn = @"wantsTitleDrawn"; } - (void)mouseUp:(NSEvent*)event { - if ([event clickCount] == 2) { + 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]; + [self.window performZoom:nil]; } else if (nsCocoaUtils::ShouldMinimizeOnTitlebarDoubleClick()) { - [[self window] performMiniaturize:nil]; + [self.window performMiniaturize:nil]; } } } @@ -4094,8 +4100,7 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { } - (NSArray<NSView*>*)contentViewContents { - NSMutableArray<NSView*>* contents = - [[[self contentView] subviews] mutableCopy]; + NSMutableArray<NSView*>* contents = [[self.contentView subviews] mutableCopy]; if (mTitlebarView) { // Do not include the titlebar gradient view in the returned array. [contents removeObject:mTitlebarView]; @@ -4105,16 +4110,16 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { - (void)updateTitlebarView { BOOL needTitlebarView = - ![self drawsContentsIntoWindowFrame] || mUnifiedToolbarHeight > 0; + !self.drawsContentsIntoWindowFrame || mUnifiedToolbarHeight > 0; if (needTitlebarView && !mTitlebarView) { mTitlebarView = - [[MOZTitlebarView alloc] initWithFrame:[self unifiedToolbarRect]]; + [[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]; + mTitlebarView.frame = self.unifiedToolbarRect; } else if (!needTitlebarView && mTitlebarView) { [mTitlebarView removeFromSuperview]; [mTitlebarView release]; @@ -4132,15 +4137,15 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { } - (NSRect)titlebarRect { - CGFloat titlebarHeight = [self titlebarHeight]; - return NSMakeRect(0, [self frame].size.height - titlebarHeight, - [self frame].size.width, titlebarHeight); + 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); + return NSMakeRect(0, self.frame.size.height - mUnifiedToolbarHeight, + self.frame.size.width, mUnifiedToolbarHeight); } // Returns the unified height of titlebar + toolbar. @@ -4152,8 +4157,8 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { // 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]; + NSRect frameRect = self.frame; + NSUInteger styleMask = self.styleMask; styleMask &= ~NSWindowStyleMaskFullSizeContentView; NSRect originalContentRect = [NSWindow contentRectForFrameRect:frameRect styleMask:styleMask]; @@ -4172,16 +4177,15 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { // Extending the content area into the title bar works by resizing the // mainChildView so that it covers the titlebar. - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState { - BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState); + BOOL stateChanged = self.drawsContentsIntoWindowFrame != aState; [super setDrawsContentsIntoWindowFrame:aState]; - if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) { + if (stateChanged && [self.delegate isKindOfClass:[WindowDelegate class]]) { // Here we extend / shrink our mainChildView. We do that by firing a resize // event which will cause the ChildView to be resized to the rect returned // by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return // value on what we return from drawsContentsIntoWindowFrame. - WindowDelegate* windowDelegate = (WindowDelegate*)[self delegate]; - nsCocoaWindow* geckoWindow = [windowDelegate geckoWidget]; - if (geckoWindow) { + auto* windowDelegate = static_cast<WindowDelegate*>(self.delegate); + if (nsCocoaWindow* geckoWindow = windowDelegate.geckoWidget) { // Re-layout our contents. geckoWindow->ReportSizeEvent(); } @@ -4233,13 +4237,16 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { RollUpPopups(); - if ([[self delegate] isKindOfClass:[WindowDelegate class]]) { - WindowDelegate* windowDelegate = (WindowDelegate*)[self delegate]; - nsCocoaWindow* geckoWindow = [windowDelegate geckoWidget]; - if (!geckoWindow) return; + if ([self.delegate isKindOfClass:[WindowDelegate class]]) { + auto* windowDelegate = static_cast<WindowDelegate*>(self.delegate); + nsCocoaWindow* geckoWindow = windowDelegate.geckoWidget; + if (!geckoWindow) { + return; + } - nsIWidgetListener* listener = geckoWindow->GetWidgetListener(); - if (listener) listener->OSToolbarButtonPressed(); + if (nsIWidgetListener* listener = geckoWindow->GetWidgetListener()) { + listener->OSToolbarButtonPressed(); + } } NS_OBJC_END_TRY_IGNORE_BLOCK; @@ -4278,14 +4285,17 @@ static bool ShouldShiftByMenubarHeightInFullscreen(nsCocoaWindow* aWindow) { // 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]; + id delegate = self.delegate; if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { nsCocoaWindow* widget = [(WindowDelegate*)delegate geckoWidget]; if (widget) { if (gGeckoAppModalWindowList && - (widget != gGeckoAppModalWindowList->window)) + widget != gGeckoAppModalWindowList->window) { + return; + } + if (widget->HasModalDescendents()) { return; - if (widget->HasModalDescendents()) return; + } } } break; @@ -4410,14 +4420,17 @@ static const NSUInteger kWindowShadowOptionsTooltip = 4; // 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]; + id delegate = self.delegate; if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) { nsCocoaWindow* widget = [(WindowDelegate*)delegate geckoWidget]; if (widget) { if (gGeckoAppModalWindowList && - (widget != gGeckoAppModalWindowList->window)) + widget != gGeckoAppModalWindowList->window) { return; - if (widget->HasModalDescendents()) return; + } + if (widget->HasModalDescendents()) { + return; + } } } break; @@ -4436,8 +4449,7 @@ static const NSUInteger kWindowShadowOptionsTooltip = 4; - (BOOL)canBecomeMainWindow { NS_OBJC_BEGIN_TRY_BLOCK_RETURN; - if (![self isVisible]) return NO; - return YES; + return self.isVisible; NS_OBJC_END_TRY_BLOCK_RETURN(NO); } diff --git a/widget/cocoa/nsCursorManager.mm b/widget/cocoa/nsCursorManager.mm index 2db20681ad..11a37f59b7 100644 --- a/widget/cocoa/nsCursorManager.mm +++ b/widget/cocoa/nsCursorManager.mm @@ -304,19 +304,17 @@ static constexpr nsCursor kCustomCursor = eCursorCount; return NS_ERROR_FAILURE; } + const NSSize cocoaSize = NSMakeSize(size.width, size.height); NSImage* cursorImage; nsresult rv = nsCocoaUtils::CreateNSImageFromImageContainer( aCursor.mContainer, imgIContainer::FRAME_FIRST, nullptr, nullptr, - &cursorImage, scaleFactor); + cocoaSize, &cursorImage, scaleFactor); if (NS_FAILED(rv) || !cursorImage) { return NS_ERROR_FAILURE; } - { - NSSize cocoaSize = NSMakeSize(size.width, size.height); - [cursorImage setSize:cocoaSize]; - [[[cursorImage representations] objectAtIndex:0] setSize:cocoaSize]; - } + [cursorImage setSize:cocoaSize]; + [[[cursorImage representations] objectAtIndex:0] setSize:cocoaSize]; // if the hotspot is nonsensical, make it 0,0 uint32_t hotspotX = diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index 73fd6b689c..5675198ff2 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -366,9 +366,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::SubmenuDelay: aResult = 200; break; - case IntID::TooltipDelay: - aResult = 500; - break; case IntID::MenusCanOverlapOSBar: // xul popups are not allowed to overlap the menubar. aResult = 0; diff --git a/widget/cocoa/nsMacSharingService.mm b/widget/cocoa/nsMacSharingService.mm index 694617ba65..c8509af00f 100644 --- a/widget/cocoa/nsMacSharingService.mm +++ b/widget/cocoa/nsMacSharingService.mm @@ -98,7 +98,7 @@ static NSString* NSImageToBase64(const NSImage* aImage) { static void SetStrAttribute(JSContext* aCx, JS::Rooted<JSObject*>& aObj, const char* aKey, NSString* aVal) { nsAutoString strVal; - mozilla::CopyCocoaStringToXPCOMString(aVal, strVal); + mozilla::CopyNSStringToXPCOMString(aVal, strVal); JS::Rooted<JSString*> title(aCx, JS_NewUCStringCopyZ(aCx, strVal.get())); JS::Rooted<JS::Value> attVal(aCx, JS::StringValue(title)); JS_SetProperty(aCx, aObj, aKey, attVal); diff --git a/widget/gtk/DBusMenu.cpp b/widget/gtk/DBusMenu.cpp new file mode 100644 index 0000000000..8672de40ef --- /dev/null +++ b/widget/gtk/DBusMenu.cpp @@ -0,0 +1,67 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ +/* 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 "DBusMenu.h" +#include "prlink.h" +#include "nsThreadUtils.h" +#include "mozilla/ArrayUtils.h" + +namespace mozilla::widget { + +#define FUNC(name, type, params) \ + DBusMenuFunctions::_##name##_fn DBusMenuFunctions::s_##name; +DBUSMENU_GLIB_FUNCTIONS +DBUSMENU_GTK_FUNCTIONS +#undef FUNC + +static PRLibrary* gDbusmenuGlib = nullptr; +static PRLibrary* gDbusmenuGtk = nullptr; + +using DBusMenuFunc = void (*)(); +struct DBusMenuDynamicFunction { + const char* functionName; + DBusMenuFunc* function; +}; + +static bool sInitialized; +static bool sLibPresent; + +/* static */ bool DBusMenuFunctions::Init() { + MOZ_ASSERT(NS_IsMainThread()); + if (sInitialized) { + return sLibPresent; + } + sInitialized = true; +#define FUNC(name, type, params) \ + {#name, (DBusMenuFunc*)&DBusMenuFunctions::s_##name}, + static const DBusMenuDynamicFunction kDbusmenuGlibSymbols[] = { + DBUSMENU_GLIB_FUNCTIONS}; + static const DBusMenuDynamicFunction kDbusmenuGtkSymbols[] = { + DBUSMENU_GTK_FUNCTIONS}; + +#define LOAD_LIBRARY(symbol, name) \ + if (!g##symbol) { \ + g##symbol = PR_LoadLibrary(name); \ + if (!g##symbol) { \ + return false; \ + } \ + } \ + for (uint32_t i = 0; i < mozilla::ArrayLength(k##symbol##Symbols); ++i) { \ + *k##symbol##Symbols[i].function = \ + PR_FindFunctionSymbol(g##symbol, k##symbol##Symbols[i].functionName); \ + if (!*k##symbol##Symbols[i].function) { \ + return false; \ + } \ + } + + LOAD_LIBRARY(DbusmenuGlib, "libdbusmenu-glib.so.4") + LOAD_LIBRARY(DbusmenuGtk, "libdbusmenu-gtk3.so.4") +#undef LOAD_LIBRARY + + sLibPresent = true; + return true; +} + +} // namespace mozilla::widget diff --git a/widget/gtk/DBusMenu.h b/widget/gtk/DBusMenu.h new file mode 100644 index 0000000000..d288d02322 --- /dev/null +++ b/widget/gtk/DBusMenu.h @@ -0,0 +1,137 @@ +/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_widget_DBusMenu_h +#define mozilla_widget_DBusMenu_h + +#include <glib.h> +#include <gdk/gdk.h> + +namespace mozilla { + +namespace dom { +class Element; +} + +namespace widget { + +#define DBUSMENU_GLIB_FUNCTIONS \ + FUNC(dbusmenu_menuitem_child_add_position, gboolean, \ + (DbusmenuMenuitem * mi, DbusmenuMenuitem * child, guint position)) \ + FUNC(dbusmenu_menuitem_set_root, void, \ + (DbusmenuMenuitem * mi, gboolean root)) \ + FUNC(dbusmenu_menuitem_child_append, gboolean, \ + (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)) \ + FUNC(dbusmenu_menuitem_child_delete, gboolean, \ + (DbusmenuMenuitem * mi, DbusmenuMenuitem * child)) \ + FUNC(dbusmenu_menuitem_get_children, GList*, (DbusmenuMenuitem * mi)) \ + FUNC(dbusmenu_menuitem_new, DbusmenuMenuitem*, (void)) \ + FUNC(dbusmenu_menuitem_property_get, const gchar*, \ + (DbusmenuMenuitem * mi, const gchar* property)) \ + FUNC(dbusmenu_menuitem_property_get_bool, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property)) \ + FUNC(dbusmenu_menuitem_property_remove, void, \ + (DbusmenuMenuitem * mi, const gchar* property)) \ + FUNC(dbusmenu_menuitem_property_set, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property, const gchar* value)) \ + FUNC(dbusmenu_menuitem_property_set_bool, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property, const gboolean value)) \ + FUNC(dbusmenu_menuitem_property_set_int, gboolean, \ + (DbusmenuMenuitem * mi, const gchar* property, const gint value)) \ + FUNC(dbusmenu_menuitem_show_to_user, void, \ + (DbusmenuMenuitem * mi, guint timestamp)) \ + FUNC(dbusmenu_menuitem_take_children, GList*, (DbusmenuMenuitem * mi)) \ + FUNC(dbusmenu_server_new, DbusmenuServer*, (const gchar* object)) \ + FUNC(dbusmenu_server_set_root, void, \ + (DbusmenuServer * server, DbusmenuMenuitem * root)) \ + FUNC(dbusmenu_server_set_status, void, \ + (DbusmenuServer * server, DbusmenuStatus status)) + +#define DBUSMENU_GTK_FUNCTIONS \ + FUNC(dbusmenu_menuitem_property_set_image, gboolean, \ + (DbusmenuMenuitem * menuitem, const gchar* property, \ + const GdkPixbuf* data)) \ + FUNC(dbusmenu_menuitem_property_set_shortcut, gboolean, \ + (DbusmenuMenuitem * menuitem, guint key, GdkModifierType modifier)) + +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef struct _DbusmenuServer DbusmenuServer; + +enum DbusmenuStatus { DBUSMENU_STATUS_NORMAL, DBUSMENU_STATUS_NOTICE }; + +#define DBUSMENU_MENUITEM_CHILD_DISPLAY_SUBMENU "submenu" +#define DBUSMENU_MENUITEM_PROP_CHILD_DISPLAY "children-display" +#define DBUSMENU_MENUITEM_PROP_ENABLED "enabled" +#define DBUSMENU_MENUITEM_PROP_ICON_DATA "icon-data" +#define DBUSMENU_MENUITEM_PROP_LABEL "label" +#define DBUSMENU_MENUITEM_PROP_SHORTCUT "shortcut" +#define DBUSMENU_MENUITEM_PROP_TYPE "type" +#define DBUSMENU_MENUITEM_PROP_TOGGLE_STATE "toggle-state" +#define DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE "toggle-type" +#define DBUSMENU_MENUITEM_PROP_VISIBLE "visible" +#define DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW "about-to-show" +#define DBUSMENU_MENUITEM_SIGNAL_EVENT "event" +#define DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED "item-activated" +#define DBUSMENU_MENUITEM_TOGGLE_CHECK "checkmark" +#define DBUSMENU_MENUITEM_TOGGLE_RADIO "radio" +#define DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED 1 +#define DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED 0 +#define DBUSMENU_SERVER_PROP_DBUS_OBJECT "dbus-object" + +class DBusMenuFunctions { + public: + DBusMenuFunctions() = delete; + + static bool Init(); + +#define FUNC(name, type, params) \ + typedef type(*_##name##_fn) params; \ + static _##name##_fn s_##name; + DBUSMENU_GLIB_FUNCTIONS + DBUSMENU_GTK_FUNCTIONS +#undef FUNC +}; + +#define dbusmenu_menuitem_set_root \ + DBusMenuFunctions::s_dbusmenu_menuitem_set_root +#define dbusmenu_menuitem_child_add_position \ + DBusMenuFunctions::s_dbusmenu_menuitem_child_add_position +#define dbusmenu_menuitem_child_append \ + DBusMenuFunctions::s_dbusmenu_menuitem_child_append +#define dbusmenu_menuitem_child_delete \ + DBusMenuFunctions::s_dbusmenu_menuitem_child_delete +#define dbusmenu_menuitem_get_children \ + DBusMenuFunctions::s_dbusmenu_menuitem_get_children +#define dbusmenu_menuitem_new DBusMenuFunctions::s_dbusmenu_menuitem_new +#define dbusmenu_menuitem_property_get \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_get +#define dbusmenu_menuitem_property_get_bool \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_get_bool +#define dbusmenu_menuitem_property_remove \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_remove +#define dbusmenu_menuitem_property_set \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set +#define dbusmenu_menuitem_property_set_bool \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_bool +#define dbusmenu_menuitem_property_set_int \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_int +#define dbusmenu_menuitem_show_to_user \ + DBusMenuFunctions::s_dbusmenu_menuitem_show_to_user +#define dbusmenu_menuitem_take_children \ + DBusMenuFunctions::s_dbusmenu_menuitem_take_children +#define dbusmenu_server_new DBusMenuFunctions::s_dbusmenu_server_new +#define dbusmenu_server_set_root DBusMenuFunctions::s_dbusmenu_server_set_root +#define dbusmenu_server_set_status \ + DBusMenuFunctions::s_dbusmenu_server_set_status + +#define dbusmenu_menuitem_property_set_image \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_image +#define dbusmenu_menuitem_property_set_shortcut \ + DBusMenuFunctions::s_dbusmenu_menuitem_property_set_shortcut + +} // namespace widget +} // namespace mozilla + +#endif diff --git a/widget/gtk/GRefPtr.h b/widget/gtk/GRefPtr.h index 1970008811..071164c06f 100644 --- a/widget/gtk/GRefPtr.h +++ b/widget/gtk/GRefPtr.h @@ -13,6 +13,9 @@ #include <gtk/gtk.h> #include "mozilla/RefPtr.h" +typedef struct _DbusmenuMenuitem DbusmenuMenuitem; +typedef struct _DbusmenuServer DbusmenuServer; + namespace mozilla { template <typename T> @@ -25,6 +28,8 @@ struct GObjectRefPtrTraits { template <> \ struct RefPtrTraits<type_> : public GObjectRefPtrTraits<type_> {}; +GOBJECT_TRAITS(DbusmenuMenuitem) +GOBJECT_TRAITS(DbusmenuServer) GOBJECT_TRAITS(GtkWidget) GOBJECT_TRAITS(GFile) GOBJECT_TRAITS(GFileMonitor) diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp index 4dadbc81b5..457b2c72ce 100644 --- a/widget/gtk/GfxInfo.cpp +++ b/widget/gtk/GfxInfo.cpp @@ -75,17 +75,17 @@ nsresult GfxInfo::Init() { } void GfxInfo::AddCrashReportAnnotations() { - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID, - mVendorId); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID, - mDeviceId); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationNSCString( + CrashReporter::Annotation::AdapterVendorID, mVendorId); + CrashReporter::RecordAnnotationNSCString( + CrashReporter::Annotation::AdapterDeviceID, mDeviceId); + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::AdapterDriverVendor, mDriverVendor); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::AdapterDriverVersion, mDriverVersion); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::IsWayland, - mIsWayland); - CrashReporter::AnnotateCrashReport( + CrashReporter::RecordAnnotationBool(CrashReporter::Annotation::IsWayland, + mIsWayland); + CrashReporter::RecordAnnotationNSCString( CrashReporter::Annotation::DesktopEnvironment, GetDesktopEnvironmentIdentifier()); diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp index a3443b9828..36559cfd54 100644 --- a/widget/gtk/GtkCompositorWidget.cpp +++ b/widget/gtk/GtkCompositorWidget.cpp @@ -39,11 +39,11 @@ GtkCompositorWidget::GtkCompositorWidget( "GtkCompositorWidget::mClientSize") { #if defined(MOZ_X11) if (GdkIsX11Display()) { - mXWindow = (Window)aInitData.XWindow(); - ConfigureX11Backend(mXWindow, aInitData.Shaped()); + ConfigureX11Backend((Window)aInitData.XWindow(), aInitData.Shaped()); LOG("GtkCompositorWidget::GtkCompositorWidget() [%p] mXWindow %p " "mIsRenderingSuspended %d\n", - (void*)mWidget.get(), (void*)mXWindow, !!mIsRenderingSuspended); + (void*)mWidget.get(), (void*)aInitData.XWindow(), + !!mIsRenderingSuspended); } #endif #if defined(MOZ_WAYLAND) @@ -123,8 +123,8 @@ EGLNativeWindowType GtkCompositorWidget::GetEGLNativeWindow() { window = (EGLNativeWindowType)mWidget->GetNativeData(NS_NATIVE_EGL_WINDOW); } #if defined(MOZ_X11) - if (mXWindow) { - window = (EGLNativeWindowType)mXWindow; + else { + window = (EGLNativeWindowType)mProvider.GetXWindow(); } #endif LOG("GtkCompositorWidget::GetEGLNativeWindow [%p] window %p\n", mWidget.get(), @@ -173,9 +173,6 @@ void GtkCompositorWidget::DisableRendering() { LOG("GtkCompositorWidget::DisableRendering [%p]\n", (void*)mWidget.get()); mIsRenderingSuspended = true; mProvider.CleanupResources(); -#if defined(MOZ_X11) - mXWindow = {}; -#endif } #if defined(MOZ_WAYLAND) @@ -187,27 +184,13 @@ bool GtkCompositorWidget::ConfigureWaylandBackend() { #if defined(MOZ_X11) bool GtkCompositorWidget::ConfigureX11Backend(Window aXWindow, bool aShaped) { - mXWindow = aXWindow; - // We don't have X window yet. - if (!mXWindow) { + if (!aXWindow) { mIsRenderingSuspended = true; return false; } - - // Grab the window's visual and depth - XWindowAttributes windowAttrs; - if (!XGetWindowAttributes(DefaultXDisplay(), mXWindow, &windowAttrs)) { - NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!"); - return false; - } - - Visual* visual = windowAttrs.visual; - int depth = windowAttrs.depth; - // Initialize the window surface provider - mProvider.Initialize(mXWindow, visual, depth, aShaped); - return true; + return mProvider.Initialize(aXWindow, aShaped); } #endif diff --git a/widget/gtk/GtkCompositorWidget.h b/widget/gtk/GtkCompositorWidget.h index 5bf89835d7..bde88bde6c 100644 --- a/widget/gtk/GtkCompositorWidget.h +++ b/widget/gtk/GtkCompositorWidget.h @@ -84,7 +84,7 @@ class GtkCompositorWidget : public CompositorWidget, bool SetEGLNativeWindowSize(const LayoutDeviceIntSize& aEGLWindowSize); #if defined(MOZ_X11) - Window XWindow() const { return mXWindow; } + Window XWindow() const { return mProvider.GetXWindow(); } #endif #if defined(MOZ_WAYLAND) RefPtr<mozilla::layers::NativeLayerRoot> GetNativeLayerRoot() override; @@ -123,11 +123,9 @@ class GtkCompositorWidget : public CompositorWidget, // of the two. DataMutex<LayoutDeviceIntSize> mClientSize; + // Holds rendering resources WindowSurfaceProvider mProvider; -#if defined(MOZ_X11) - Window mXWindow = {}; -#endif #ifdef MOZ_WAYLAND RefPtr<mozilla::layers::NativeLayerRootWayland> mNativeLayerRoot; #endif diff --git a/widget/gtk/IMContextWrapper.cpp b/widget/gtk/IMContextWrapper.cpp index fc87acbf86..2e438bbc5c 100644 --- a/widget/gtk/IMContextWrapper.cpp +++ b/widget/gtk/IMContextWrapper.cpp @@ -232,11 +232,18 @@ class SelectionStyleProvider final { sHasShutDown = true; } - // mContainer associated with an IM context. - void AttachTo(MozContainer* aContainer) { - gtk_style_context_add_provider( - gtk_widget_get_style_context(GTK_WIDGET(aContainer)), - GTK_STYLE_PROVIDER(mProvider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + // aGDKWindow is a GTK window which will be associated with an IM context. + void AttachTo(GdkWindow* aGDKWindow) { + GtkWidget* widget = nullptr; + // gdk_window_get_user_data() typically returns pointer to widget that + // window belongs to. If it's widget, fcitx retrieves selection colors + // of them. So, we need to overwrite its style. + gdk_window_get_user_data(aGDKWindow, (gpointer*)&widget); + if (GTK_IS_WIDGET(widget)) { + gtk_style_context_add_provider(gtk_widget_get_style_context(widget), + GTK_STYLE_PROVIDER(mProvider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } } void OnThemeChanged() { @@ -410,17 +417,21 @@ nsDependentCSubstring IMContextWrapper::GetIMName() const { } void IMContextWrapper::Init() { + MozContainer* container = mOwnerWindow->GetMozContainer(); + MOZ_ASSERT(container, "container is null"); + GdkWindow* gdkWindow = gtk_widget_get_window(GTK_WIDGET(container)); + // Overwrite selection colors of the window before associating the window // with IM context since IME may look up selection colors via IM context // to support any colored widgets. - SelectionStyleProvider::GetInstance()->AttachTo( - mOwnerWindow->GetMozContainer()); + SelectionStyleProvider::GetInstance()->AttachTo(gdkWindow); // NOTE: gtk_im_*_new() abort (kill) the whole process when it fails. // So, we don't need to check the result. // Normal context. mContext = gtk_im_multicontext_new(); + gtk_im_context_set_client_window(mContext, gdkWindow); g_signal_connect(mContext, "preedit_changed", G_CALLBACK(IMContextWrapper::OnChangeCompositionCallback), this); @@ -492,6 +503,7 @@ void IMContextWrapper::Init() { // Simple context if (sUseSimpleContext) { mSimpleContext = gtk_im_context_simple_new(); + gtk_im_context_set_client_window(mSimpleContext, gdkWindow); g_signal_connect(mSimpleContext, "preedit_changed", G_CALLBACK(&IMContextWrapper::OnChangeCompositionCallback), this); @@ -514,6 +526,7 @@ void IMContextWrapper::Init() { // Dummy context mDummyContext = gtk_im_multicontext_new(); + gtk_im_context_set_client_window(mDummyContext, gdkWindow); MOZ_LOG(gIMELog, LogLevel::Info, ("0x%p Init(), mOwnerWindow=%p, mContext=%p (im=\"%s\"), " @@ -540,17 +553,6 @@ IMContextWrapper::~IMContextWrapper() { MOZ_LOG(gIMELog, LogLevel::Info, ("0x%p ~IMContextWrapper()", this)); } -void IMContextWrapper::SetGdkWindow(GdkWindow* aGdkWindow) { - MOZ_LOG(gIMELog, LogLevel::Info, - ("0x%p GdkWindowChanged(%p)", this, aGdkWindow)); - MOZ_ASSERT(!aGdkWindow || mOwnerWindow->GetGdkWindow() == aGdkWindow); - gtk_im_context_set_client_window(mContext, aGdkWindow); - if (mSimpleContext) { - gtk_im_context_set_client_window(mSimpleContext, aGdkWindow); - } - gtk_im_context_set_client_window(mDummyContext, aGdkWindow); -} - NS_IMETHODIMP IMContextWrapper::NotifyIME(TextEventDispatcher* aTextEventDispatcher, const IMENotification& aNotification) { diff --git a/widget/gtk/IMContextWrapper.h b/widget/gtk/IMContextWrapper.h index 213c5ce8d3..cf1d2638e0 100644 --- a/widget/gtk/IMContextWrapper.h +++ b/widget/gtk/IMContextWrapper.h @@ -117,10 +117,6 @@ class IMContextWrapper final : public TextEventDispatcherListener { void OnUpdateComposition(); void OnLayoutChange(); - // Set GdkWindow associated with IM context. - // It can be null which disables context operations. - void SetGdkWindow(GdkWindow* aGdkWindow); - TextEventDispatcher* GetTextEventDispatcher(); // TODO: Typically, new IM comes every several years. And now, our code diff --git a/widget/gtk/MozContainer.cpp b/widget/gtk/MozContainer.cpp index 95d32b57b4..775ae0488f 100644 --- a/widget/gtk/MozContainer.cpp +++ b/widget/gtk/MozContainer.cpp @@ -35,27 +35,6 @@ static void moz_container_size_allocate(GtkWidget* widget, static void moz_container_realize(GtkWidget* widget); static void moz_container_unrealize(GtkWidget* widget); -/* container class methods */ -static void moz_container_remove(GtkContainer* container, - GtkWidget* child_widget); -static void moz_container_forall(GtkContainer* container, - gboolean include_internals, - GtkCallback callback, gpointer callback_data); -static void moz_container_add(GtkContainer* container, GtkWidget* widget); - -typedef struct _MozContainerChild MozContainerChild; - -struct _MozContainerChild { - GtkWidget* widget; - gint x; - gint y; -}; - -static void moz_container_allocate_child(MozContainer* container, - MozContainerChild* child); -static MozContainerChild* moz_container_get_child(MozContainer* container, - GtkWidget* child); - /* public methods */ GType moz_container_get_type(void) { @@ -92,26 +71,6 @@ GtkWidget* moz_container_new(void) { return GTK_WIDGET(container); } -void moz_container_put(MozContainer* container, GtkWidget* child_widget, gint x, - gint y) { - MozContainerChild* child; - - child = g_new(MozContainerChild, 1); - - child->widget = child_widget; - child->x = x; - child->y = y; - - /* printf("moz_container_put %p %p %d %d\n", (void *)container, - (void *)child_widget, x, y); */ - - container->data.children = g_list_append(container->data.children, child); - - /* we assume that the caller of this function will have already set - the parent GdkWindow because we can have many anonymous children. */ - gtk_widget_set_parent(child_widget, GTK_WIDGET(container)); -} - static void moz_container_destroy(GtkWidget* widget) { auto* container = MOZ_CONTAINER(widget); if (container->destroyed) { @@ -126,7 +85,6 @@ static void moz_container_destroy(GtkWidget* widget) { void moz_container_class_init(MozContainerClass* klass) { /*GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass); */ - GtkContainerClass* container_class = GTK_CONTAINER_CLASS(klass); GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); widget_class->realize = moz_container_realize; @@ -147,10 +105,6 @@ void moz_container_class_init(MozContainerClass* klass) { #ifdef MOZ_WAYLAND } #endif - - container_class->remove = moz_container_remove; - container_class->forall = moz_container_forall; - container_class->add = moz_container_add; } void moz_container_init(MozContainer* container) { @@ -164,8 +118,6 @@ void moz_container_init(MozContainer* container) { void moz_container_map(GtkWidget* widget) { MozContainer* container; - GList* tmp_list; - GtkWidget* tmp_child; g_return_if_fail(IS_MOZ_CONTAINER(widget)); container = MOZ_CONTAINER(widget); @@ -175,16 +127,6 @@ void moz_container_map(GtkWidget* widget) { gtk_widget_set_mapped(widget, TRUE); - tmp_list = container->data.children; - while (tmp_list) { - tmp_child = ((MozContainerChild*)tmp_list->data)->widget; - - if (gtk_widget_get_visible(tmp_child)) { - if (!gtk_widget_get_mapped(tmp_child)) gtk_widget_map(tmp_child); - } - tmp_list = tmp_list->next; - } - if (gtk_widget_get_has_window(widget)) { gdk_window_show(gtk_widget_get_window(widget)); } @@ -257,8 +199,6 @@ void moz_container_unrealize(GtkWidget* widget) { } void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { - MozContainer* container; - GList* tmp_list; GtkAllocation tmp_allocation; g_return_if_fail(IS_MOZ_CONTAINER(widget)); @@ -269,10 +209,8 @@ void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { allocation->height)); /* short circuit if you can */ - container = MOZ_CONTAINER(widget); gtk_widget_get_allocation(widget, &tmp_allocation); - if (!container->data.children && tmp_allocation.x == allocation->x && - tmp_allocation.y == allocation->y && + if (tmp_allocation.x == allocation->x && tmp_allocation.y == allocation->y && tmp_allocation.width == allocation->width && tmp_allocation.height == allocation->height) { return; @@ -280,16 +218,6 @@ void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { gtk_widget_set_allocation(widget, allocation); - tmp_list = container->data.children; - - while (tmp_list) { - MozContainerChild* child = static_cast<MozContainerChild*>(tmp_list->data); - - moz_container_allocate_child(container, child); - - tmp_list = tmp_list->next; - } - if (gtk_widget_get_has_window(widget) && gtk_widget_get_realized(widget)) { gdk_window_move_resize(gtk_widget_get_window(widget), allocation->x, allocation->y, allocation->width, @@ -297,99 +225,6 @@ void moz_container_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { } } -void moz_container_remove(GtkContainer* container, GtkWidget* child_widget) { - MozContainerChild* child; - MozContainer* moz_container; - GdkWindow* parent_window; - - g_return_if_fail(IS_MOZ_CONTAINER(container)); - g_return_if_fail(GTK_IS_WIDGET(child_widget)); - - moz_container = MOZ_CONTAINER(container); - - child = moz_container_get_child(moz_container, child_widget); - g_return_if_fail(child); - - /* gtk_widget_unparent will remove the parent window (as well as the - * parent widget), but, in Mozilla's window hierarchy, the parent window - * may need to be kept because it may be part of a GdkWindow sub-hierarchy - * that is being moved to another MozContainer. - * - * (In a conventional GtkWidget hierarchy, GdkWindows being reparented - * would have their own GtkWidget and that widget would be the one being - * reparented. In Mozilla's hierarchy, the parent_window needs to be - * retained so that the GdkWindow sub-hierarchy is maintained.) - */ - parent_window = gtk_widget_get_parent_window(child_widget); - if (parent_window) g_object_ref(parent_window); - - gtk_widget_unparent(child_widget); - - if (parent_window) { - /* The child_widget will always still exist because g_signal_emit, - * which invokes this function, holds a reference. - * - * If parent_window is the container's root window then it will not be - * the parent_window if the child_widget is placed in another - * container. - */ - if (parent_window != gtk_widget_get_window(GTK_WIDGET(container))) { - gtk_widget_set_parent_window(child_widget, parent_window); - } - - g_object_unref(parent_window); - } - - moz_container->data.children = - g_list_remove(moz_container->data.children, child); - g_free(child); -} - -void moz_container_forall(GtkContainer* container, gboolean include_internals, - GtkCallback callback, gpointer callback_data) { - g_return_if_fail(IS_MOZ_CONTAINER(container)); - g_return_if_fail(callback); - - MozContainer* moz_container = MOZ_CONTAINER(container); - - GList* tmp_list = moz_container->data.children; - while (tmp_list) { - MozContainerChild* child; - child = static_cast<MozContainerChild*>(tmp_list->data); - tmp_list = tmp_list->next; - (*callback)(child->widget, callback_data); - } -} - -static void moz_container_allocate_child(MozContainer* container, - MozContainerChild* child) { - GtkAllocation allocation; - - gtk_widget_get_allocation(child->widget, &allocation); - allocation.x = child->x; - allocation.y = child->y; - - gtk_widget_size_allocate(child->widget, &allocation); -} - -MozContainerChild* moz_container_get_child(MozContainer* container, - GtkWidget* child_widget) { - GList* tmp_list = container->data.children; - while (tmp_list) { - MozContainerChild* child; - - child = static_cast<MozContainerChild*>(tmp_list->data); - tmp_list = tmp_list->next; - - if (child->widget == child_widget) return child; - } - return nullptr; -} - -static void moz_container_add(GtkContainer* container, GtkWidget* widget) { - moz_container_put(MOZ_CONTAINER(container), widget, 0, 0); -} - void moz_container_force_default_visual(MozContainer* container) { container->data.force_default_visual = true; } diff --git a/widget/gtk/MozContainer.h b/widget/gtk/MozContainer.h index 27fa2a701f..e6f9b4e992 100644 --- a/widget/gtk/MozContainer.h +++ b/widget/gtk/MozContainer.h @@ -18,35 +18,12 @@ /* * MozContainer * - * This class serves three purposes in the nsIWidget implementation. + * This class serves two purposes in the nsIWidget implementation. * * - It provides objects to receive signals from GTK for events on native * windows. * - * - It provides GdkWindow to draw content on Wayland or when Gtk+ renders - * client side decorations to mShell. - * - * - It provides a container parent for GtkWidgets. The only GtkWidgets - * that need this in Mozilla are the GtkSockets for windowed plugins (Xt - * and XEmbed). - * - * Note that the window hierarchy in Mozilla differs from conventional - * GtkWidget hierarchies. - * - * Mozilla's hierarchy exists through the GdkWindow hierarchy, and all child - * GdkWindows (within a child nsIWidget hierarchy) belong to one MozContainer - * GtkWidget. If the MozContainer is unrealized or its GdkWindows are - * destroyed for some other reason, then the hierarchy no longer exists. (In - * conventional GTK clients, the hierarchy is recorded by the GtkWidgets, and - * so can be re-established after destruction of the GdkWindows.) - * - * One consequence of this is that the MozContainer does not know which of its - * GdkWindows should parent child GtkWidgets. (Conventional GtkContainers - * determine which GdkWindow to assign child GtkWidgets.) - * - * Therefore, when adding a child GtkWidget to a MozContainer, - * gtk_widget_set_parent_window should be called on the child GtkWidget before - * it is realized. + * - It provides GdkWindow to draw content. */ #define MOZ_CONTAINER_TYPE (moz_container_get_type()) @@ -71,7 +48,6 @@ struct _MozContainer { GtkContainer container; gboolean destroyed; struct Data { - GList* children = nullptr; gboolean force_default_visual = false; #ifdef MOZ_WAYLAND MozContainerWayland wl_container; diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp index 0e50a3f27c..8490f25599 100644 --- a/widget/gtk/MozContainerWayland.cpp +++ b/widget/gtk/MozContainerWayland.cpp @@ -436,7 +436,6 @@ void moz_container_wayland_map(GtkWidget* widget) { void moz_container_wayland_size_allocate(GtkWidget* widget, GtkAllocation* allocation) { - MozContainer* container; GtkAllocation tmp_allocation; g_return_if_fail(IS_MOZ_CONTAINER(widget)); @@ -447,10 +446,8 @@ void moz_container_wayland_size_allocate(GtkWidget* widget, allocation->height); /* short circuit if you can */ - container = MOZ_CONTAINER(widget); gtk_widget_get_allocation(widget, &tmp_allocation); - if (!container->data.children && tmp_allocation.x == allocation->x && - tmp_allocation.y == allocation->y && + if (tmp_allocation.x == allocation->x && tmp_allocation.y == allocation->y && tmp_allocation.width == allocation->width && tmp_allocation.height == allocation->height) { return; @@ -464,6 +461,7 @@ void moz_container_wayland_size_allocate(GtkWidget* widget, // We need to position our subsurface according to GdkWindow // when offset changes (GdkWindow is maximized for instance). // see gtk-clutter-embed.c for reference. + MozContainer* container = MOZ_CONTAINER(widget); MutexAutoLock lock(container->data.wl_container.container_lock); if (!container->data.wl_container.surface) { if (!moz_container_wayland_surface_create_locked(lock, container)) { diff --git a/widget/gtk/NativeMenuGtk.cpp b/widget/gtk/NativeMenuGtk.cpp index 9d413d475e..3f8aeb1940 100644 --- a/widget/gtk/NativeMenuGtk.cpp +++ b/widget/gtk/NativeMenuGtk.cpp @@ -4,6 +4,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "NativeMenuGtk.h" +#include "AsyncDBus.h" +#include "gdk/gdkkeysyms-compat.h" +#include "mozilla/BasicEvents.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/XULCommandEvent.h" @@ -15,6 +18,10 @@ #include "nsStubMutationObserver.h" #include "mozilla/dom/Element.h" #include "mozilla/StaticPrefs_widget.h" +#include "DBusMenu.h" +#include "nsLayoutUtils.h" +#include "nsGtkUtils.h" +#include "nsGtkKeyUtils.h" #include <dlfcn.h> #include <gtk/gtk.h> @@ -35,7 +42,8 @@ static bool IsDisabled(const dom::Element& aElement) { } static bool NodeIsRelevant(const nsINode& aNode) { return aNode.IsAnyOfXULElements(nsGkAtoms::menu, nsGkAtoms::menuseparator, - nsGkAtoms::menuitem, nsGkAtoms::menugroup); + nsGkAtoms::menuitem, nsGkAtoms::menugroup, + nsGkAtoms::menubar); } // If this is a radio / checkbox menuitem, get the current value. @@ -155,7 +163,7 @@ void Actions::Clear() { mNextActionIndex = 0; } -class MenuModel final : public nsStubMutationObserver { +class MenuModel : public nsStubMutationObserver { NS_DECL_ISUPPORTS NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED @@ -166,41 +174,60 @@ class MenuModel final : public nsStubMutationObserver { public: explicit MenuModel(dom::Element* aElement) : mElement(aElement) { mElement->AddMutationObserver(this); - mGMenu = dont_AddRef(g_menu_new()); - mActions.mGroup = dont_AddRef(g_simple_action_group_new()); - } - - GMenuModel* GetModel() { return G_MENU_MODEL(mGMenu.get()); } - GActionGroup* GetActionGroup() { - return G_ACTION_GROUP(mActions.mGroup.get()); } dom::Element* Element() { return mElement; } - void RecomputeModelIfNeeded(); + void RecomputeModelIfNeeded() { + if (!mDirty) { + return; + } + RecomputeModel(); + mDirty = false; + } - bool IsShowing() { return mPoppedUp; } + bool IsShowing() { return mShowing; } void WillShow() { - mPoppedUp = true; + mShowing = true; RecomputeModelIfNeeded(); } - void DidHide() { mPoppedUp = false; } + void DidHide() { mShowing = false; } - private: + protected: + virtual void RecomputeModel() = 0; virtual ~MenuModel() { mElement->RemoveMutationObserver(this); } void DirtyModel() { mDirty = true; - if (mPoppedUp) { + if (mShowing) { RecomputeModelIfNeeded(); } } RefPtr<dom::Element> mElement; + bool mDirty = true; + bool mShowing = false; +}; + +class MenuModelGMenu final : public MenuModel { + public: + explicit MenuModelGMenu(dom::Element* aElement) : MenuModel(aElement) { + mGMenu = dont_AddRef(g_menu_new()); + mActions.mGroup = dont_AddRef(g_simple_action_group_new()); + } + + GMenuModel* GetModel() { return G_MENU_MODEL(mGMenu.get()); } + GActionGroup* GetActionGroup() { + return G_ACTION_GROUP(mActions.mGroup.get()); + } + + protected: + void RecomputeModel() override; + static void RecomputeModelFor(GMenu* aMenu, Actions& aActions, + const dom::Element& aElement); + RefPtr<GMenu> mGMenu; Actions mActions; - bool mDirty = true; - bool mPoppedUp = false; }; NS_IMPL_ISUPPORTS(MenuModel, nsIMutationObserver) @@ -243,8 +270,8 @@ static const dom::Element* GetMenuPopupChild(const dom::Element& aElement) { return nullptr; } -static void RecomputeModelFor(GMenu* aMenu, Actions& aActions, - const dom::Element& aElement) { +void MenuModelGMenu::RecomputeModelFor(GMenu* aMenu, Actions& aActions, + const dom::Element& aElement) { RefPtr<GMenu> sectionMenu; auto FlushSectionMenu = [&] { if (sectionMenu) { @@ -305,10 +332,7 @@ static void RecomputeModelFor(GMenu* aMenu, Actions& aActions, FlushSectionMenu(); } -void MenuModel::RecomputeModelIfNeeded() { - if (!mDirty) { - return; - } +void MenuModelGMenu::RecomputeModel() { mActions.Clear(); g_menu_remove_all(mGMenu.get()); RecomputeModelFor(mGMenu.get(), mActions, *mElement); @@ -341,7 +365,7 @@ METHOD_SIGNAL(Unmap); #undef METHOD_SIGNAL NativeMenuGtk::NativeMenuGtk(dom::Element* aElement) - : mMenuModel(MakeRefPtr<MenuModel>(aElement)) { + : mMenuModel(MakeRefPtr<MenuModelGMenu>(aElement)) { // Floating, so no need to dont_AddRef. mNativeMenu = gtk_menu_new_from_model(mMenuModel->GetModel()); gtk_widget_insert_action_group(mNativeMenu.get(), "menu", @@ -421,4 +445,383 @@ void NativeMenuGtk::CloseSubmenu(dom::Element*) { // TODO: For testing mostly. } +#ifdef MOZ_ENABLE_DBUS + +class MenubarModelDBus final : public MenuModel { + public: + explicit MenubarModelDBus(dom::Element* aElement) : MenuModel(aElement) { + mRoot = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_set_root(mRoot.get(), true); + mShowing = true; + } + + DbusmenuMenuitem* Root() const { return mRoot.get(); } + + protected: + void RecomputeModel() override; + static void AppendMenuItem(DbusmenuMenuitem* aParent, + const dom::Element* aElement); + static void AppendSeparator(DbusmenuMenuitem* aParent); + static void AppendSubmenu(DbusmenuMenuitem* aParent, + const dom::Element* aMenu, + const dom::Element* aPopup); + static uint RecomputeModelFor(DbusmenuMenuitem* aParent, + const dom::Element& aElement); + + RefPtr<DbusmenuMenuitem> mRoot; +}; + +void MenubarModelDBus::RecomputeModel() { + while (GList* children = dbusmenu_menuitem_get_children(mRoot.get())) { + auto* first = static_cast<DbusmenuMenuitem*>(children->data); + if (!first) { + break; + } + dbusmenu_menuitem_child_delete(mRoot.get(), first); + } + RecomputeModelFor(mRoot, *Element()); +} + +static const dom::Element* RelevantElementForKeys( + const dom::Element* aElement) { + nsAutoString key; + aElement->GetAttr(nsGkAtoms::key, key); + if (!key.IsEmpty()) { + dom::Document* document = aElement->OwnerDoc(); + dom::Element* element = document->GetElementById(key); + if (element) { + return element; + } + } + return aElement; +} + +static uint32_t ParseKey(const nsAString& aKey, const nsAString& aKeyCode) { + guint key = 0; + if (!aKey.IsEmpty()) { + key = gdk_unicode_to_keyval(*aKey.BeginReading()); + } + + if (key == 0 && !aKeyCode.IsEmpty()) { + key = KeymapWrapper::ConvertGeckoKeyCodeToGDKKeyval(aKeyCode); + } + + return key; +} + +static uint32_t KeyFrom(const dom::Element* aElement) { + const auto* element = RelevantElementForKeys(aElement); + + nsAutoString key; + nsAutoString keycode; + element->GetAttr(nsGkAtoms::key, key); + element->GetAttr(nsGkAtoms::keycode, keycode); + + return ParseKey(key, keycode); +} + +// TODO(emilio): Unify with nsMenuUtilsX::GeckoModifiersForNodeAttribute (or +// at least switch to strtok_r). +static uint32_t ParseModifiers(const nsAString& aModifiers) { + if (aModifiers.IsEmpty()) { + return 0; + } + + uint32_t modifier = 0; + char* str = ToNewUTF8String(aModifiers); + char* token = strtok(str, ", \t"); + while (token) { + if (nsCRT::strcmp(token, "shift") == 0) { + modifier |= GDK_SHIFT_MASK; + } else if (nsCRT::strcmp(token, "alt") == 0) { + modifier |= GDK_MOD1_MASK; + } else if (nsCRT::strcmp(token, "meta") == 0) { + modifier |= GDK_META_MASK; + } else if (nsCRT::strcmp(token, "control") == 0) { + modifier |= GDK_CONTROL_MASK; + } else if (nsCRT::strcmp(token, "accel") == 0) { + auto accel = WidgetInputEvent::AccelModifier(); + if (accel == MODIFIER_META) { + modifier |= GDK_META_MASK; + } else if (accel == MODIFIER_ALT) { + modifier |= GDK_MOD1_MASK; + } else if (accel == MODIFIER_CONTROL) { + modifier |= GDK_CONTROL_MASK; + } + } + + token = strtok(nullptr, ", \t"); + } + + free(str); + + return modifier; +} + +static uint32_t ModifiersFrom(const dom::Element* aContent) { + const auto* element = RelevantElementForKeys(aContent); + + nsAutoString modifiers; + element->GetAttr(nsGkAtoms::modifiers, modifiers); + + return ParseModifiers(modifiers); +} + +static void UpdateAccel(DbusmenuMenuitem* aItem, const nsIContent* aContent) { + uint32_t key = KeyFrom(aContent->AsElement()); + if (key != 0) { + dbusmenu_menuitem_property_set_shortcut( + aItem, key, + static_cast<GdkModifierType>(ModifiersFrom(aContent->AsElement()))); + } +} + +static void UpdateRadioOrCheck(DbusmenuMenuitem* aItem, + const dom::Element* aContent) { + static mozilla::dom::Element::AttrValuesArray attrs[] = { + nsGkAtoms::checkbox, nsGkAtoms::radio, nullptr}; + int32_t type = aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, + attrs, eCaseMatters); + + if (type < 0 || type >= 2) { + return; + } + + if (type == 0) { + dbusmenu_menuitem_property_set(aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + DBUSMENU_MENUITEM_TOGGLE_CHECK); + } else { + dbusmenu_menuitem_property_set(aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE, + DBUSMENU_MENUITEM_TOGGLE_RADIO); + } + + bool isChecked = aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::checked, + nsGkAtoms::_true, eCaseMatters); + dbusmenu_menuitem_property_set_int( + aItem, DBUSMENU_MENUITEM_PROP_TOGGLE_STATE, + isChecked ? DBUSMENU_MENUITEM_TOGGLE_STATE_CHECKED + : DBUSMENU_MENUITEM_TOGGLE_STATE_UNCHECKED); +} + +static void UpdateEnabled(DbusmenuMenuitem* aItem, const nsIContent* aContent) { + bool disabled = aContent->AsElement()->AttrValueIs( + kNameSpaceID_None, nsGkAtoms::disabled, nsGkAtoms::_true, eCaseMatters); + + dbusmenu_menuitem_property_set_bool(aItem, DBUSMENU_MENUITEM_PROP_ENABLED, + !disabled); +} + +// we rebuild the dbus model when elements are removed from the DOM, +// so this isn't going to trigger for asynchronous +static MOZ_CAN_RUN_SCRIPT void DBusActivationCallback( + DbusmenuMenuitem* aMenuitem, guint aTimestamp, gpointer aUserData) { + RefPtr element = static_cast<dom::Element*>(aUserData); + ActivateItem(*element); +} + +static void ConnectActivated(DbusmenuMenuitem* aItem, + const dom::Element* aContent) { + g_signal_connect(G_OBJECT(aItem), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, + G_CALLBACK(DBusActivationCallback), + const_cast<dom::Element*>(aContent)); +} + +static MOZ_CAN_RUN_SCRIPT void DBusAboutToShowCallback( + DbusmenuMenuitem* aMenuitem, gpointer aUserData) { + RefPtr element = static_cast<dom::Element*>(aUserData); + FireEvent(element, eXULPopupShowing); + FireEvent(element, eXULPopupShown); +} + +static void ConnectAboutToShow(DbusmenuMenuitem* aItem, + const dom::Element* aContent) { + g_signal_connect(G_OBJECT(aItem), DBUSMENU_MENUITEM_SIGNAL_ABOUT_TO_SHOW, + G_CALLBACK(DBusAboutToShowCallback), + const_cast<dom::Element*>(aContent)); +} + +void MenubarModelDBus::AppendMenuItem(DbusmenuMenuitem* aParent, + const dom::Element* aChild) { + nsAutoString label; + aChild->GetAttr(nsGkAtoms::label, label); + if (label.IsEmpty()) { + aChild->GetAttr(nsGkAtoms::aria_label, label); + } + RefPtr<DbusmenuMenuitem> child = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_LABEL, + NS_ConvertUTF16toUTF8(label).get()); + dbusmenu_menuitem_child_append(aParent, child); + UpdateAccel(child, aChild); + UpdateRadioOrCheck(child, aChild); + UpdateEnabled(child, aChild); + ConnectActivated(child, aChild); + // TODO: icons +} + +void MenubarModelDBus::AppendSeparator(DbusmenuMenuitem* aParent) { + RefPtr<DbusmenuMenuitem> child = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_property_set(child, DBUSMENU_MENUITEM_PROP_TYPE, + "separator"); + dbusmenu_menuitem_child_append(aParent, child); +} + +void MenubarModelDBus::AppendSubmenu(DbusmenuMenuitem* aParent, + const dom::Element* aMenu, + const dom::Element* aPopup) { + RefPtr<DbusmenuMenuitem> submenu = dont_AddRef(dbusmenu_menuitem_new()); + if (RecomputeModelFor(submenu, *aPopup) == 0) { + RefPtr<DbusmenuMenuitem> placeholder = dont_AddRef(dbusmenu_menuitem_new()); + dbusmenu_menuitem_child_append(submenu, placeholder); + } + nsAutoString label; + aMenu->GetAttr(nsGkAtoms::label, label); + ConnectAboutToShow(submenu, aPopup); + dbusmenu_menuitem_property_set(submenu, DBUSMENU_MENUITEM_PROP_LABEL, + NS_ConvertUTF16toUTF8(label).get()); + dbusmenu_menuitem_child_append(aParent, submenu); +} + +uint MenubarModelDBus::RecomputeModelFor(DbusmenuMenuitem* aParent, + const dom::Element& aElement) { + uint childCount = 0; + for (const nsIContent* child = aElement.GetFirstChild(); child; + child = child->GetNextSibling()) { + if (child->IsXULElement(nsGkAtoms::menuitem) && + !IsDisabled(*child->AsElement())) { + AppendMenuItem(aParent, child->AsElement()); + childCount++; + continue; + } + if (child->IsXULElement(nsGkAtoms::menuseparator)) { + AppendSeparator(aParent); + childCount++; + continue; + } + if (child->IsXULElement(nsGkAtoms::menu) && + !IsDisabled(*child->AsElement())) { + if (const auto* popup = GetMenuPopupChild(*child->AsElement())) { + childCount++; + AppendSubmenu(aParent, child->AsElement(), popup); + } + } + } + return childCount; +} + +void DBusMenuBar::NameOwnerChangedCallback(GObject*, GParamSpec*, + gpointer user_data) { + static_cast<DBusMenuBar*>(user_data)->OnNameOwnerChanged(); +} + +void DBusMenuBar::OnNameOwnerChanged() { + GUniquePtr<gchar> nameOwner(g_dbus_proxy_get_name_owner(mProxy)); + if (!nameOwner) { + return; + } + + RefPtr win = mMenuModel->Element()->OwnerDoc()->GetInnerWindow(); + if (NS_WARN_IF(!win)) { + return; + } + nsIWidget* widget = nsGlobalWindowInner::Cast(win.get())->GetNearestWidget(); + if (NS_WARN_IF(!widget)) { + return; + } + auto* gdkWin = + static_cast<GdkWindow*>(widget->GetNativeData(NS_NATIVE_WINDOW)); + if (NS_WARN_IF(!gdkWin)) { + return; + } + +# ifdef MOZ_WAYLAND + if (auto* display = widget::WaylandDisplayGet()) { + if (!StaticPrefs::widget_gtk_global_menu_wayland_enabled()) { + return; + } + xdg_dbus_annotation_manager_v1* annotationManager = + display->GetXdgDbusAnnotationManager(); + if (NS_WARN_IF(!annotationManager)) { + return; + } + + wl_surface* surface = gdk_wayland_window_get_wl_surface(gdkWin); + if (NS_WARN_IF(!surface)) { + return; + } + + GDBusConnection* connection = g_dbus_proxy_get_connection(mProxy); + const char* myServiceName = g_dbus_connection_get_unique_name(connection); + if (NS_WARN_IF(!myServiceName)) { + return; + } + + // FIXME(emilio, bug 1883209): Nothing deletes this as of right now. + mAnnotation = xdg_dbus_annotation_manager_v1_create_surface( + annotationManager, "com.canonical.dbusmenu", surface); + + xdg_dbus_annotation_v1_set_address(mAnnotation, myServiceName, + mObjectPath.get()); + return; + } +# endif +# ifdef MOZ_X11 + // legacy path + auto xid = GDK_WINDOW_XID(gdkWin); + widget::DBusProxyCall(mProxy, "RegisterWindow", + g_variant_new("(uo)", xid, mObjectPath.get()), + G_DBUS_CALL_FLAGS_NONE) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self = RefPtr{this}](RefPtr<GVariant>&& aResult) { + self->mMenuModel->Element()->SetBoolAttr(nsGkAtoms::hidden, true); + }, + [self = RefPtr{this}](GUniquePtr<GError>&& aError) { + g_printerr("Failed to register window menubar: %s\n", + aError->message); + self->mMenuModel->Element()->SetBoolAttr(nsGkAtoms::hidden, false); + }); +# endif +} + +static unsigned sID = 0; + +DBusMenuBar::DBusMenuBar(dom::Element* aElement) + : mObjectPath(nsPrintfCString("/com/canonical/menu/%u", sID++)), + mMenuModel(MakeRefPtr<MenubarModelDBus>(aElement)), + mServer(dont_AddRef(dbusmenu_server_new(mObjectPath.get()))) { + mMenuModel->RecomputeModelIfNeeded(); + dbusmenu_server_set_root(mServer.get(), mMenuModel->Root()); +} + +RefPtr<DBusMenuBar> DBusMenuBar::Create(dom::Element* aElement) { + RefPtr<DBusMenuBar> self = new DBusMenuBar(aElement); + widget::CreateDBusProxyForBus( + G_BUS_TYPE_SESSION, + GDBusProxyFlags(G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START), + nullptr, "com.canonical.AppMenu.Registrar", + "/com/canonical/AppMenu/Registrar", "com.canonical.AppMenu.Registrar") + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self](RefPtr<GDBusProxy>&& aProxy) { + self->mProxy = std::move(aProxy); + g_signal_connect(self->mProxy, "notify::g-name-owner", + G_CALLBACK(NameOwnerChangedCallback), self.get()); + self->OnNameOwnerChanged(); + }, + [](GUniquePtr<GError>&& aError) { + g_printerr("Failed to create DBUS proxy for menubar: %s\n", + aError->message); + }); + return self; +} + +DBusMenuBar::~DBusMenuBar() { +# ifdef MOZ_WAYLAND + MozClearPointer(mAnnotation, xdg_dbus_annotation_v1_destroy); +# endif +} +#endif + } // namespace mozilla::widget diff --git a/widget/gtk/NativeMenuGtk.h b/widget/gtk/NativeMenuGtk.h index 3f1f3213c1..ca0f64c8ff 100644 --- a/widget/gtk/NativeMenuGtk.h +++ b/widget/gtk/NativeMenuGtk.h @@ -1,4 +1,3 @@ - /* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ /* 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 @@ -7,10 +6,13 @@ #ifndef mozilla_widget_NativeMenuGtk_h #define mozilla_widget_NativeMenuGtk_h +#include "mozilla/RefCounted.h" #include "mozilla/widget/NativeMenu.h" #include "mozilla/EventForwards.h" #include "GRefPtr.h" +struct xdg_dbus_annotation_v1; + namespace mozilla { namespace dom { @@ -19,7 +21,8 @@ class Element; namespace widget { -class MenuModel; +class MenuModelGMenu; +class MenubarModelDBus; class NativeMenuGtk : public NativeMenu { public: @@ -54,10 +57,35 @@ class NativeMenuGtk : public NativeMenu { bool mPoppedUp = false; RefPtr<GtkWidget> mNativeMenu; - RefPtr<MenuModel> mMenuModel; + RefPtr<MenuModelGMenu> mMenuModel; nsTArray<NativeMenu::Observer*> mObservers; }; +#ifdef MOZ_ENABLE_DBUS + +class DBusMenuBar final : public RefCounted<DBusMenuBar> { + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME(DBusMenuBar) + static RefPtr<DBusMenuBar> Create(dom::Element*); + ~DBusMenuBar(); + + protected: + explicit DBusMenuBar(dom::Element* aElement); + + static void NameOwnerChangedCallback(GObject*, GParamSpec*, gpointer); + void OnNameOwnerChanged(); + + nsCString mObjectPath; + RefPtr<MenubarModelDBus> mMenuModel; + RefPtr<DbusmenuServer> mServer; + RefPtr<GDBusProxy> mProxy; +# ifdef MOZ_WAYLAND + xdg_dbus_annotation_v1* mAnnotation = nullptr; +# endif +}; + +#endif + } // namespace widget } // namespace mozilla diff --git a/widget/gtk/NativeMenuSupport.cpp b/widget/gtk/NativeMenuSupport.cpp index 4360867fff..60517cd526 100644 --- a/widget/gtk/NativeMenuSupport.cpp +++ b/widget/gtk/NativeMenuSupport.cpp @@ -5,8 +5,11 @@ #include "mozilla/widget/NativeMenuSupport.h" +#include "mozilla/StaticPrefs_widget.h" #include "MainThreadUtils.h" #include "NativeMenuGtk.h" +#include "DBusMenu.h" +#include "nsWindow.h" namespace mozilla::widget { @@ -14,7 +17,14 @@ void NativeMenuSupport::CreateNativeMenuBar(nsIWidget* aParent, dom::Element* aMenuBarElement) { MOZ_RELEASE_ASSERT(NS_IsMainThread(), "Attempting to create native menu bar on wrong thread!"); - // TODO + +#ifdef MOZ_ENABLE_DBUS + if (aMenuBarElement && StaticPrefs::widget_gtk_global_menu_enabled() && + DBusMenuFunctions::Init()) { + static_cast<nsWindow*>(aParent)->SetDBusMenuBar( + DBusMenuBar::Create(aMenuBarElement)); + } +#endif } already_AddRefed<NativeMenu> NativeMenuSupport::CreateNativeContextMenu( diff --git a/widget/gtk/WakeLockListener.cpp b/widget/gtk/WakeLockListener.cpp index b2c43cb485..33bb374cef 100644 --- a/widget/gtk/WakeLockListener.cpp +++ b/widget/gtk/WakeLockListener.cpp @@ -137,6 +137,7 @@ class WakeLockTopic { bool UninhibitWaylandIdle(); #endif + bool IsNativeWakeLock(int aWakeLockType); bool IsWakeLockTypeAvailable(int aWakeLockType); bool SwitchToNextWakeLockType(); @@ -779,8 +780,14 @@ nsresult WakeLockTopic::InhibitScreensaver() { mShouldInhibit = true; // Iterate through wake lock types in case of failure. - while (!SendInhibit() && SwitchToNextWakeLockType()) { - ; + while (!SendInhibit()) { + // We don't switch away from native locks. Just try again. + if (IsNativeWakeLock(sWakeLockType)) { + return NS_ERROR_FAILURE; + } + if (!SwitchToNextWakeLockType()) { + return NS_ERROR_FAILURE; + } } return (sWakeLockType != Unsupported) ? NS_OK : NS_ERROR_FAILURE; @@ -850,6 +857,21 @@ bool WakeLockTopic::IsWakeLockTypeAvailable(int aWakeLockType) { } } +bool WakeLockTopic::IsNativeWakeLock(int aWakeLockType) { + switch (aWakeLockType) { +#if defined(MOZ_X11) + case XScreenSaver: + return true; +#endif +#if defined(MOZ_WAYLAND) + case WaylandIdleInhibit: + return true; +#endif + default: + return false; + } +} + bool WakeLockTopic::SwitchToNextWakeLockType() { WAKE_LOCK_LOG("WakeLockTopic::SwitchToNextWakeLockType() WakeLockType %s", WakeLockTypeNames[sWakeLockType]); diff --git a/widget/gtk/WidgetStyleCache.cpp b/widget/gtk/WidgetStyleCache.cpp index b7c1cf8a9f..13b194a64e 100644 --- a/widget/gtk/WidgetStyleCache.cpp +++ b/widget/gtk/WidgetStyleCache.cpp @@ -644,10 +644,7 @@ static void CreateHeaderBarButtons() { GtkWidget* headerBar = sWidgetStorage[MOZ_GTK_HEADER_BAR]; MOZ_ASSERT(headerBar, "We're missing header bar widget!"); - gint buttonSpacing = 6; - g_object_get(headerBar, "spacing", &buttonSpacing, nullptr); - - GtkWidget* buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, buttonSpacing); + GtkWidget* buttonBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_add(GTK_CONTAINER(headerBar), buttonBox); // We support only LTR headerbar layout for now. gtk_style_context_add_class(gtk_widget_get_style_context(buttonBox), diff --git a/widget/gtk/WindowSurfaceProvider.cpp b/widget/gtk/WindowSurfaceProvider.cpp index b346f0783d..82f9029315 100644 --- a/widget/gtk/WindowSurfaceProvider.cpp +++ b/widget/gtk/WindowSurfaceProvider.cpp @@ -63,24 +63,34 @@ WindowSurfaceProvider::~WindowSurfaceProvider() { } #ifdef MOZ_WAYLAND -void WindowSurfaceProvider::Initialize(RefPtr<nsWindow> aWidget) { +bool WindowSurfaceProvider::Initialize(RefPtr<nsWindow> aWidget) { mWindowSurfaceValid = false; mWidget = std::move(aWidget); + return true; } -void WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { +bool WindowSurfaceProvider::Initialize(GtkCompositorWidget* aCompositorWidget) { mWindowSurfaceValid = false; mCompositorWidget = aCompositorWidget; mWidget = static_cast<nsWindow*>(aCompositorWidget->RealWidget()); + return true; } #endif #ifdef MOZ_X11 -void WindowSurfaceProvider::Initialize(Window aWindow, Visual* aVisual, - int aDepth, bool aIsShaped) { +bool WindowSurfaceProvider::Initialize(Window aWindow, bool aIsShaped) { mWindowSurfaceValid = false; + + // Grab the window's visual and depth + XWindowAttributes windowAttrs; + if (!XGetWindowAttributes(DefaultXDisplay(), aWindow, &windowAttrs)) { + NS_WARNING("GtkCompositorWidget(): XGetWindowAttributes() failed!"); + return false; + } + mXWindow = aWindow; - mXVisual = aVisual; - mXDepth = aDepth; + mXVisual = windowAttrs.visual; + mXDepth = windowAttrs.depth; mIsShaped = aIsShaped; + return true; } #endif diff --git a/widget/gtk/WindowSurfaceProvider.h b/widget/gtk/WindowSurfaceProvider.h index a040bbe395..6aaf50e2da 100644 --- a/widget/gtk/WindowSurfaceProvider.h +++ b/widget/gtk/WindowSurfaceProvider.h @@ -45,11 +45,12 @@ class WindowSurfaceProvider final { * while WindowSurfaceProvider is used. */ #ifdef MOZ_WAYLAND - void Initialize(RefPtr<nsWindow> aWidget); - void Initialize(GtkCompositorWidget* aCompositorWidget); + bool Initialize(RefPtr<nsWindow> aWidget); + bool Initialize(GtkCompositorWidget* aCompositorWidget); #endif #ifdef MOZ_X11 - void Initialize(Window aWindow, Visual* aVisual, int aDepth, bool aIsShaped); + bool Initialize(Window aWindow, bool aIsShaped); + Window GetXWindow() const { return mXWindow; } #endif /** diff --git a/widget/gtk/gtk3drawing.cpp b/widget/gtk/gtk3drawing.cpp index 1fa8b95606..122b43d688 100644 --- a/widget/gtk/gtk3drawing.cpp +++ b/widget/gtk/gtk3drawing.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- 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/. */ @@ -264,7 +264,6 @@ static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance, gint iconWidth, iconHeight; if (!gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &iconWidth, &iconHeight)) { NS_WARNING("Failed to get Gtk+ icon size for titlebar button!"); - // Use some reasonable fallback size iconWidth = 16; iconHeight = 16; @@ -292,37 +291,7 @@ static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance, // Place icon at button center. aMetrics->iconXPosition = (width - iconWidth) / 2; aMetrics->iconYPosition = (height - iconHeight) / 2; - - aMetrics->minSizeWithBorderMargin.width = width; - aMetrics->minSizeWithBorderMargin.height = height; -} - -// We support LTR layout only here for now. -static void CalculateToolbarButtonSpacing(WidgetNodeType aAppearance, - ToolbarButtonGTKMetrics* aMetrics) { - GtkStyleContext* style = GetStyleContext(aAppearance); - gtk_style_context_get_margin(style, gtk_style_context_get_state(style), - &aMetrics->buttonMargin); - - // Get titlebar spacing, a default one is 6 pixels (gtk/gtkheaderbar.c) - gint buttonSpacing = 6; - g_object_get(GetWidget(MOZ_GTK_HEADER_BAR), "spacing", &buttonSpacing, - nullptr); - - // We apply spacing as a margin equally to both adjacent buttons. - buttonSpacing /= 2; - - if (!aMetrics->firstButton) { - aMetrics->buttonMargin.left += buttonSpacing; - } - if (!aMetrics->lastButton) { - aMetrics->buttonMargin.right += buttonSpacing; - } - - aMetrics->minSizeWithBorderMargin.width += - aMetrics->buttonMargin.right + aMetrics->buttonMargin.left; - aMetrics->minSizeWithBorderMargin.height += - aMetrics->buttonMargin.top + aMetrics->buttonMargin.bottom; + aMetrics->minSizeWithBorder = {width, height}; } size_t GetGtkHeaderBarButtonLayout(Span<ButtonLayout> aButtonLayout, @@ -388,26 +357,14 @@ static void EnsureToolbarMetrics() { memset(&sToolbarMetrics, 0, sizeof(sToolbarMetrics)); // Calculate titlebar button visibility and positions. - ButtonLayout aButtonLayout[TOOLBAR_BUTTONS]; + ButtonLayout buttonLayout[TOOLBAR_BUTTONS]; size_t activeButtonNums = - GetGtkHeaderBarButtonLayout(Span(aButtonLayout), nullptr); - - for (size_t i = 0; i < activeButtonNums; i++) { - int buttonIndex = - (aButtonLayout[i].mType - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE); - ToolbarButtonGTKMetrics* metrics = sToolbarMetrics.button + buttonIndex; - metrics->visible = true; - // Mark first button - if (!i) { - metrics->firstButton = true; - } - // Mark last button. - if (i == (activeButtonNums - 1)) { - metrics->lastButton = true; - } + GetGtkHeaderBarButtonLayout(Span(buttonLayout), nullptr); - CalculateToolbarButtonMetrics(aButtonLayout[i].mType, metrics); - CalculateToolbarButtonSpacing(aButtonLayout[i].mType, metrics); + for (const auto& layout : Span(buttonLayout, activeButtonNums)) { + int buttonIndex = layout.mType - MOZ_GTK_HEADER_BAR_BUTTON_CLOSE; + ToolbarButtonGTKMetrics* metrics = &sToolbarMetrics.button[buttonIndex]; + CalculateToolbarButtonMetrics(layout.mType, metrics); } sToolbarMetrics.initialized = true; @@ -506,26 +463,27 @@ static gint moz_gtk_button_paint(cairo_t* cr, const GdkRectangle* rect, return MOZ_GTK_SUCCESS; } -static gint moz_gtk_header_bar_button_paint(cairo_t* cr, - const GdkRectangle* aRect, +static gint moz_gtk_header_bar_button_paint(cairo_t* cr, GdkRectangle* aRect, GtkWidgetState* state, GtkReliefStyle relief, WidgetNodeType aIconWidgetType, GtkTextDirection direction) { - GdkRectangle rect = *aRect; - // We need to inset our calculated margin because it also - // contains titlebar button spacing. + GtkWidget* buttonWidget = GetWidget(aIconWidgetType); + if (!buttonWidget) { + return MOZ_GTK_UNKNOWN_WIDGET; + } + const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics( aIconWidgetType == MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE ? MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE : aIconWidgetType); - Inset(&rect, metrics->buttonMargin); - - GtkWidget* buttonWidget = GetWidget(aIconWidgetType); - if (!buttonWidget) { - return MOZ_GTK_UNKNOWN_WIDGET; + // Vertically center and clamp the rect to the desired size. + if (aRect->height > metrics->minSizeWithBorder.height) { + gint diff = aRect->height - metrics->minSizeWithBorder.height; + aRect->y += diff / 2; + aRect->height = metrics->minSizeWithBorder.height; } - moz_gtk_button_paint(cr, &rect, state, relief, buttonWidget, direction); + moz_gtk_button_paint(cr, aRect, state, relief, buttonWidget, direction); GtkWidget* iconWidget = gtk_bin_get_child(GTK_BIN(GetWidget(aIconWidgetType))); @@ -544,8 +502,9 @@ static gint moz_gtk_header_bar_button_paint(cairo_t* cr, gtk_style_context_set_state(style, state_flags); /* This is available since Gtk+ 3.10 as well as GtkHeaderBar */ - gtk_render_icon_surface(style, cr, surface, rect.x + metrics->iconXPosition, - rect.y + metrics->iconYPosition); + gtk_render_icon_surface(style, cr, surface, + aRect->x + metrics->iconXPosition, + aRect->y + metrics->iconYPosition); gtk_style_context_restore(style); } @@ -1700,22 +1659,6 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, return MOZ_GTK_SUCCESS; } - case MOZ_GTK_HEADER_BAR_BUTTON_BOX: { - style = GetStyleContext(MOZ_GTK_HEADER_BAR); - moz_gtk_add_border_padding(style, left, top, right, bottom); - *top = *bottom = 0; - bool leftButtonsPlacement = false; - GetGtkHeaderBarButtonLayout({}, &leftButtonsPlacement); - if (direction == GTK_TEXT_DIR_RTL) { - leftButtonsPlacement = !leftButtonsPlacement; - } - if (leftButtonsPlacement) { - *right = 0; - } else { - *left = 0; - } - return MOZ_GTK_SUCCESS; - } /* These widgets have no borders, since they are not containers. */ case MOZ_GTK_SPLITTER_HORIZONTAL: case MOZ_GTK_SPLITTER_VERTICAL: diff --git a/widget/gtk/gtkdrawing.h b/widget/gtk/gtkdrawing.h index e751dc38c9..4ca226d9c7 100644 --- a/widget/gtk/gtkdrawing.h +++ b/widget/gtk/gtkdrawing.h @@ -76,13 +76,9 @@ typedef struct { } ToggleGTKMetrics; typedef struct { - MozGtkSize minSizeWithBorderMargin; - GtkBorder buttonMargin; + MozGtkSize minSizeWithBorder; gint iconXPosition; gint iconYPosition; - bool visible; - bool firstButton; - bool lastButton; } ToolbarButtonGTKMetrics; #define TOOLBAR_BUTTONS 3 @@ -269,8 +265,6 @@ enum WidgetNodeType : int { MOZ_GTK_HEADER_BAR, /* Paints a GtkHeaderBar in maximized state */ MOZ_GTK_HEADER_BAR_MAXIMIZED, - /* Container for GtkHeaderBar buttons */ - MOZ_GTK_HEADER_BAR_BUTTON_BOX, /* Paints GtkHeaderBar title buttons. * Keep the order here as MOZ_GTK_HEADER_BAR_BUTTON_* are processed * as an array from MOZ_GTK_HEADER_BAR_BUTTON_CLOSE to the last one. diff --git a/widget/gtk/moz.build b/widget/gtk/moz.build index 0d3916853c..87866b1a9b 100644 --- a/widget/gtk/moz.build +++ b/widget/gtk/moz.build @@ -173,6 +173,7 @@ if CONFIG["MOZ_ENABLE_DBUS"]: ] UNIFIED_SOURCES += [ "AsyncDBus.cpp", + "DBusMenu.cpp", ] CXXFLAGS += CONFIG["MOZ_DBUS_CFLAGS"] diff --git a/widget/gtk/nsGtkKeyUtils.cpp b/widget/gtk/nsGtkKeyUtils.cpp index 7157a09664..e49f64a64b 100644 --- a/widget/gtk/nsGtkKeyUtils.cpp +++ b/widget/gtk/nsGtkKeyUtils.cpp @@ -2125,6 +2125,212 @@ guint KeymapWrapper::GetGDKKeyvalWithoutModifier( return keyval; } +struct KeyCodeData { + const char* str; + size_t strlength; + uint32_t keycode; +}; + +static struct KeyCodeData gKeyCodes[] = { +#define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) \ + {#aDOMKeyName, sizeof(#aDOMKeyName) - 1, aDOMKeyCode}, +#include "mozilla/VirtualKeyCodeList.h" +#undef NS_DEFINE_VK + {nullptr, 0, 0}}; + +struct KeyPair { + uint32_t DOMKeyCode; + guint GDKKeyval; +}; + +// +// Netscape keycodes are defined in widget/public/nsGUIEvent.h +// GTK keycodes are defined in <gdk/gdkkeysyms.h> +// +static const KeyPair gKeyPairs[] = { + {NS_VK_CANCEL, GDK_Cancel}, + {NS_VK_BACK, GDK_BackSpace}, + {NS_VK_TAB, GDK_Tab}, + {NS_VK_CLEAR, GDK_Clear}, + {NS_VK_RETURN, GDK_Return}, + {NS_VK_SHIFT, GDK_Shift_L}, + {NS_VK_CONTROL, GDK_Control_L}, + {NS_VK_ALT, GDK_Alt_L}, + {NS_VK_META, GDK_Meta_L}, + + // Assume that Super or Hyper is always mapped to physical Win key. + {NS_VK_WIN, GDK_Super_L}, + + // GTK's AltGraph key is similar to Mac's Option (Alt) key. However, + // unfortunately, browsers on Mac are using NS_VK_ALT for it even though + // it's really different from Alt key on Windows. + // On the other hand, GTK's AltGrapsh keys are really different from + // Alt key. However, there is no AltGrapsh key on Windows. On Windows, + // both Ctrl and Alt keys are pressed internally when AltGr key is pressed. + // For some languages' users, AltGraph key is important, so, web + // applications on such locale may want to know AltGraph key press. + // Therefore, we should map AltGr keycode for them only on GTK. + {NS_VK_ALTGR, GDK_ISO_Level3_Shift}, + + {NS_VK_PAUSE, GDK_Pause}, + {NS_VK_CAPS_LOCK, GDK_Caps_Lock}, + {NS_VK_ESCAPE, GDK_Escape}, + // { NS_VK_ACCEPT, GDK_XXX }, + // { NS_VK_MODECHANGE, GDK_XXX }, + {NS_VK_SPACE, GDK_space}, + {NS_VK_PAGE_UP, GDK_Page_Up}, + {NS_VK_PAGE_DOWN, GDK_Page_Down}, + {NS_VK_END, GDK_End}, + {NS_VK_HOME, GDK_Home}, + {NS_VK_LEFT, GDK_Left}, + {NS_VK_UP, GDK_Up}, + {NS_VK_RIGHT, GDK_Right}, + {NS_VK_DOWN, GDK_Down}, + {NS_VK_SELECT, GDK_Select}, + {NS_VK_PRINT, GDK_Print}, + {NS_VK_EXECUTE, GDK_Execute}, + {NS_VK_PRINTSCREEN, GDK_Print}, + {NS_VK_INSERT, GDK_Insert}, + {NS_VK_DELETE, GDK_Delete}, + {NS_VK_HELP, GDK_Help}, + + {NS_VK_NUM_LOCK, GDK_Num_Lock}, + {NS_VK_SCROLL_LOCK, GDK_Scroll_Lock}, + + // Function keys + {NS_VK_F1, GDK_F1}, + {NS_VK_F2, GDK_F2}, + {NS_VK_F3, GDK_F3}, + {NS_VK_F4, GDK_F4}, + {NS_VK_F5, GDK_F5}, + {NS_VK_F6, GDK_F6}, + {NS_VK_F7, GDK_F7}, + {NS_VK_F8, GDK_F8}, + {NS_VK_F9, GDK_F9}, + {NS_VK_F10, GDK_F10}, + {NS_VK_F11, GDK_F11}, + {NS_VK_F12, GDK_F12}, + {NS_VK_F13, GDK_F13}, + {NS_VK_F14, GDK_F14}, + {NS_VK_F15, GDK_F15}, + {NS_VK_F16, GDK_F16}, + {NS_VK_F17, GDK_F17}, + {NS_VK_F18, GDK_F18}, + {NS_VK_F19, GDK_F19}, + {NS_VK_F20, GDK_F20}, + {NS_VK_F21, GDK_F21}, + {NS_VK_F22, GDK_F22}, + {NS_VK_F23, GDK_F23}, + {NS_VK_F24, GDK_F24}, + + // context menu key, keysym 0xff67, typically keycode 117 on 105-key + // (Microsoft) x86 keyboards, located between right 'Windows' key and right + // Ctrl key + {NS_VK_CONTEXT_MENU, GDK_Menu}, + {NS_VK_SLEEP, GDK_Sleep}, + + {NS_VK_ATTN, GDK_3270_Attn}, + {NS_VK_CRSEL, GDK_3270_CursorSelect}, + {NS_VK_EXSEL, GDK_3270_ExSelect}, + {NS_VK_EREOF, GDK_3270_EraseEOF}, + {NS_VK_PLAY, GDK_3270_Play}, + //{ NS_VK_ZOOM, GDK_XXX }, + {NS_VK_PA1, GDK_3270_PA1}, + + {NS_VK_MULTIPLY, GDK_KP_Multiply}, + {NS_VK_ADD, GDK_KP_Add}, + {NS_VK_SEPARATOR, GDK_KP_Separator}, + {NS_VK_SUBTRACT, GDK_KP_Subtract}, + {NS_VK_DECIMAL, GDK_KP_Decimal}, + {NS_VK_DIVIDE, GDK_KP_Divide}, + {NS_VK_NUMPAD0, GDK_KP_0}, + {NS_VK_NUMPAD1, GDK_KP_1}, + {NS_VK_NUMPAD2, GDK_KP_2}, + {NS_VK_NUMPAD3, GDK_KP_3}, + {NS_VK_NUMPAD4, GDK_KP_4}, + {NS_VK_NUMPAD5, GDK_KP_5}, + {NS_VK_NUMPAD6, GDK_KP_6}, + {NS_VK_NUMPAD7, GDK_KP_7}, + {NS_VK_NUMPAD8, GDK_KP_8}, + {NS_VK_NUMPAD9, GDK_KP_9}, + {NS_VK_SPACE, GDK_space}, + {NS_VK_COLON, GDK_colon}, + {NS_VK_SEMICOLON, GDK_semicolon}, + {NS_VK_LESS_THAN, GDK_less}, + {NS_VK_EQUALS, GDK_equal}, + {NS_VK_GREATER_THAN, GDK_greater}, + {NS_VK_QUESTION_MARK, GDK_question}, + {NS_VK_AT, GDK_at}, + {NS_VK_CIRCUMFLEX, GDK_asciicircum}, + {NS_VK_EXCLAMATION, GDK_exclam}, + {NS_VK_DOUBLE_QUOTE, GDK_quotedbl}, + {NS_VK_HASH, GDK_numbersign}, + {NS_VK_DOLLAR, GDK_dollar}, + {NS_VK_PERCENT, GDK_percent}, + {NS_VK_AMPERSAND, GDK_ampersand}, + {NS_VK_UNDERSCORE, GDK_underscore}, + {NS_VK_OPEN_PAREN, GDK_parenleft}, + {NS_VK_CLOSE_PAREN, GDK_parenright}, + {NS_VK_ASTERISK, GDK_asterisk}, + {NS_VK_PLUS, GDK_plus}, + {NS_VK_PIPE, GDK_bar}, + {NS_VK_HYPHEN_MINUS, GDK_minus}, + {NS_VK_OPEN_CURLY_BRACKET, GDK_braceleft}, + {NS_VK_CLOSE_CURLY_BRACKET, GDK_braceright}, + {NS_VK_TILDE, GDK_asciitilde}, + {NS_VK_COMMA, GDK_comma}, + {NS_VK_PERIOD, GDK_period}, + {NS_VK_SLASH, GDK_slash}, + {NS_VK_BACK_QUOTE, GDK_grave}, + {NS_VK_OPEN_BRACKET, GDK_bracketleft}, + {NS_VK_BACK_SLASH, GDK_backslash}, + {NS_VK_CLOSE_BRACKET, GDK_bracketright}, + {NS_VK_QUOTE, GDK_apostrophe}, +}; + +/* static */ +guint KeymapWrapper::ConvertGeckoKeyCodeToGDKKeyval(const nsAString& aKeyCode) { + NS_ConvertUTF16toUTF8 keyName(aKeyCode); + ToUpperCase(keyName); // We want case-insensitive comparison with data + // stored as uppercase. + + uint32_t keyCode = 0; + + uint32_t keyNameLength = keyName.Length(); + const char* keyNameStr = keyName.get(); + for (const auto& code : gKeyCodes) { + if (keyNameLength == code.strlength && + !nsCRT::strcmp(code.str, keyNameStr)) { + keyCode = code.keycode; + break; + } + } + + // First, try to handle alphanumeric input, not listed in nsKeycodes: + // most likely, more letters will be getting typed in than things in + // the key list, so we will look through these first. + + if (keyCode >= NS_VK_A && keyCode <= NS_VK_Z) { + // gdk and DOM both use the ASCII codes for these keys. + return keyCode; + } + + // numbers + if (keyCode >= NS_VK_0 && keyCode <= NS_VK_9) { + // gdk and DOM both use the ASCII codes for these keys. + return keyCode - NS_VK_0 + GDK_0; + } + + // misc other things + for (const auto& pair : gKeyPairs) { + if (pair.DOMKeyCode == keyCode) { + return pair.GDKKeyval; + } + } + + return 0; +} + /* static */ uint32_t KeymapWrapper::GetDOMKeyCodeFromKeyPairs(guint aGdkKeyval) { switch (aGdkKeyval) { diff --git a/widget/gtk/nsGtkKeyUtils.h b/widget/gtk/nsGtkKeyUtils.h index aac9d446f3..b1f1fae138 100644 --- a/widget/gtk/nsGtkKeyUtils.h +++ b/widget/gtk/nsGtkKeyUtils.h @@ -55,6 +55,8 @@ class KeymapWrapper { */ static CodeNameIndex ComputeDOMCodeNameIndex(const GdkEventKey* aGdkKeyEvent); + static guint ConvertGeckoKeyCodeToGDKKeyval(const nsAString& aKeyCode); + /** * We need to translate modifiers masks from Gdk to Gecko. * MappedModifier is a table of mapped modifiers, we ignore other diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index c4b430d2eb..040d942cdf 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -905,10 +905,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = (int32_t)delay; break; } - case IntID::TooltipDelay: { - aResult = 500; - break; - } case IntID::MenusCanOverlapOSBar: // we want XUL popups to be able to overlap the task bar. aResult = 1; @@ -1035,6 +1031,11 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { aResult = EffectiveTheme().mTitlebarRadius; break; } + case IntID::TitlebarButtonSpacing: { + EnsureInit(); + aResult = EffectiveTheme().mTitlebarButtonSpacing; + break; + } case IntID::AllowOverlayScrollbarsOverlap: { aResult = 1; break; @@ -1976,6 +1977,10 @@ void nsLookAndFeel::PerThemeData::Init() { mTitlebar = GetColorPair(style, GTK_STATE_FLAG_NORMAL); mTitlebarInactive = GetColorPair(style, GTK_STATE_FLAG_BACKDROP); mTitlebarRadius = IsSolidCSDStyleUsed() ? 0 : GetBorderRadius(style); + // Get titlebar spacing, a default one is 6 pixels (gtk/gtkheaderbar.c) + mTitlebarButtonSpacing = 6; + g_object_get(GetWidget(MOZ_GTK_HEADER_BAR), "spacing", + &mTitlebarButtonSpacing, nullptr); } // We special-case the header bar color in Adwaita, Yaru and Breeze to be the diff --git a/widget/gtk/nsLookAndFeel.h b/widget/gtk/nsLookAndFeel.h index 56608d331f..1ef28afe21 100644 --- a/widget/gtk/nsLookAndFeel.h +++ b/widget/gtk/nsLookAndFeel.h @@ -137,6 +137,7 @@ class nsLookAndFeel final : public nsXPLookAndFeel { float mCaretRatio = 0.0f; int32_t mTitlebarRadius = 0; + int32_t mTitlebarButtonSpacing = 0; char16_t mInvisibleCharacter = 0; bool mMenuSupportsDrag = false; diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 06d9b48007..16945349bb 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -191,6 +191,10 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aAppearance == StyleAppearance::Toolbarbutton || aAppearance == StyleAppearance::Dualbutton || aAppearance == StyleAppearance::ToolbarbuttonDropdown || + aAppearance == StyleAppearance::MozWindowButtonMinimize || + aAppearance == StyleAppearance::MozWindowButtonRestore || + aAppearance == StyleAppearance::MozWindowButtonMaximize || + aAppearance == StyleAppearance::MozWindowButtonClose || aAppearance == StyleAppearance::Menulist || aAppearance == StyleAppearance::MenulistButton) { aState->active &= aState->inHover; @@ -392,9 +396,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, case StyleAppearance::MozWindowTitlebarMaximized: aGtkWidgetType = MOZ_GTK_HEADER_BAR_MAXIMIZED; break; - case StyleAppearance::MozWindowButtonBox: - aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_BOX; - break; case StyleAppearance::MozWindowButtonClose: aGtkWidgetType = MOZ_GTK_HEADER_BAR_BUTTON_CLOSE; break; @@ -676,16 +677,6 @@ CSSIntMargin nsNativeThemeGTK::GetExtraSizeForWidget( return extra; } -bool nsNativeThemeGTK::IsWidgetVisible(StyleAppearance aAppearance) { - switch (aAppearance) { - case StyleAppearance::MozWindowButtonBox: - return false; - default: - break; - } - return true; -} - NS_IMETHODIMP nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance, @@ -702,8 +693,7 @@ nsNativeThemeGTK::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame, GtkTextDirection direction = GetTextDirection(aFrame); gint flags; - if (!IsWidgetVisible(aAppearance) || - !GetGtkWidgetAndState(aAppearance, aFrame, gtkWidgetType, &state, + if (!GetGtkWidgetAndState(aAppearance, aFrame, gtkWidgetType, &state, &flags)) { return NS_OK; } @@ -937,7 +927,6 @@ bool nsNativeThemeGTK::GetWidgetPadding(nsDeviceContext* aContext, switch (aAppearance) { case StyleAppearance::Toolbarbutton: case StyleAppearance::Tooltip: - case StyleAppearance::MozWindowButtonBox: case StyleAppearance::MozWindowButtonClose: case StyleAppearance::MozWindowButtonMinimize: case StyleAppearance::MozWindowButtonMaximize: @@ -1072,23 +1061,23 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize( case StyleAppearance::MozWindowButtonClose: { const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_CLOSE); - result.width = metrics->minSizeWithBorderMargin.width; - result.height = metrics->minSizeWithBorderMargin.height; + result.width = metrics->minSizeWithBorder.width; + result.height = metrics->minSizeWithBorder.height; break; } case StyleAppearance::MozWindowButtonMinimize: { const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MINIMIZE); - result.width = metrics->minSizeWithBorderMargin.width; - result.height = metrics->minSizeWithBorderMargin.height; + result.width = metrics->minSizeWithBorder.width; + result.height = metrics->minSizeWithBorder.height; break; } case StyleAppearance::MozWindowButtonMaximize: case StyleAppearance::MozWindowButtonRestore: { const ToolbarButtonGTKMetrics* metrics = GetToolbarButtonMetrics(MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE); - result.width = metrics->minSizeWithBorderMargin.width; - result.height = metrics->minSizeWithBorderMargin.height; + result.width = metrics->minSizeWithBorder.width; + result.height = metrics->minSizeWithBorder.height; break; } case StyleAppearance::Button: @@ -1288,7 +1277,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Range: case StyleAppearance::RangeThumb: case StyleAppearance::Splitter: - case StyleAppearance::MozWindowButtonBox: case StyleAppearance::MozWindowButtonClose: case StyleAppearance::MozWindowButtonMinimize: case StyleAppearance::MozWindowButtonMaximize: diff --git a/widget/gtk/nsNativeThemeGTK.h b/widget/gtk/nsNativeThemeGTK.h index 2d0878290e..62a046c959 100644 --- a/widget/gtk/nsNativeThemeGTK.h +++ b/widget/gtk/nsNativeThemeGTK.h @@ -93,7 +93,6 @@ class nsNativeThemeGTK final : public mozilla::widget::Theme { WidgetNodeType& aGtkWidgetType, GtkWidgetState* aState, gint* aWidgetFlags); mozilla::CSSIntMargin GetExtraSizeForWidget(nsIFrame*, StyleAppearance); - bool IsWidgetVisible(StyleAppearance aAppearance); void RefreshWidgetWindow(nsIFrame* aFrame); WidgetNodeType NativeThemeToGtkTheme(StyleAppearance aAppearance, diff --git a/widget/gtk/nsWaylandDisplay.cpp b/widget/gtk/nsWaylandDisplay.cpp index 2a1021457a..1ea87b3bc5 100644 --- a/widget/gtk/nsWaylandDisplay.cpp +++ b/widget/gtk/nsWaylandDisplay.cpp @@ -93,6 +93,11 @@ void nsWaylandDisplay::SetXdgActivation(xdg_activation_v1* aXdgActivation) { mXdgActivation = aXdgActivation; } +void nsWaylandDisplay::SetXdgDbusAnnotationManager( + xdg_dbus_annotation_manager_v1* aXdgDbusAnnotationManager) { + mXdgDbusAnnotationManager = aXdgDbusAnnotationManager; +} + static void global_registry_handler(void* data, wl_registry* registry, uint32_t id, const char* interface, uint32_t version) { @@ -140,6 +145,11 @@ static void global_registry_handler(void* data, wl_registry* registry, auto* activation = WaylandRegistryBind<xdg_activation_v1>( registry, id, &xdg_activation_v1_interface, 1); display->SetXdgActivation(activation); + } else if (iface.EqualsLiteral("xdg_dbus_annotation_manager_v1")) { + auto* annotationManager = + WaylandRegistryBind<xdg_dbus_annotation_manager_v1>( + registry, id, &xdg_dbus_annotation_manager_v1_interface, 1); + display->SetXdgDbusAnnotationManager(annotationManager); } else if (iface.EqualsLiteral("wl_seat")) { // Install keyboard handlers for main thread only auto* seat = diff --git a/widget/gtk/nsWaylandDisplay.h b/widget/gtk/nsWaylandDisplay.h index cd8124d97f..40250c2bf2 100644 --- a/widget/gtk/nsWaylandDisplay.h +++ b/widget/gtk/nsWaylandDisplay.h @@ -19,6 +19,7 @@ #include "mozilla/widget/linux-dmabuf-unstable-v1-client-protocol.h" #include "mozilla/widget/viewporter-client-protocol.h" #include "mozilla/widget/xdg-activation-v1-client-protocol.h" +#include "mozilla/widget/xdg-dbus-annotation-v1-client-protocol.h" #include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h" namespace mozilla::widget { @@ -48,6 +49,9 @@ class nsWaylandDisplay { } zwp_linux_dmabuf_v1* GetDmabuf() { return mDmabuf; }; xdg_activation_v1* GetXdgActivation() { return mXdgActivation; }; + xdg_dbus_annotation_manager_v1* GetXdgDbusAnnotationManager() { + return mXdgDbusAnnotationManager; + } wp_fractional_scale_manager_v1* GetFractionalScaleManager() { return mFractionalScaleManager; } @@ -64,6 +68,8 @@ class nsWaylandDisplay { void SetPointerConstraints(zwp_pointer_constraints_v1* aPointerConstraints); void SetDmabuf(zwp_linux_dmabuf_v1* aDmabuf); void SetXdgActivation(xdg_activation_v1* aXdgActivation); + void SetXdgDbusAnnotationManager( + xdg_dbus_annotation_manager_v1* aXdgDbusAnnotationManager); void SetFractionalScaleManager(wp_fractional_scale_manager_v1* aManager) { mFractionalScaleManager = aManager; } @@ -84,6 +90,7 @@ class nsWaylandDisplay { wp_viewporter* mViewporter = nullptr; zwp_linux_dmabuf_v1* mDmabuf = nullptr; xdg_activation_v1* mXdgActivation = nullptr; + xdg_dbus_annotation_manager_v1* mXdgDbusAnnotationManager = nullptr; wp_fractional_scale_manager_v1* mFractionalScaleManager = nullptr; bool mExplicitSync = false; bool mIsPrimarySelectionEnabled = false; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 8185c7bda9..e84044990c 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -100,6 +100,7 @@ #include "ScreenHelperGTK.h" #include "SystemTimeConverter.h" #include "WidgetUtilsGtk.h" +#include "NativeMenuGtk.h" #ifdef ACCESSIBILITY # include "mozilla/a11y/LocalAccessible.h" @@ -182,8 +183,6 @@ static nsWindow* get_window_for_gtk_widget(GtkWidget* widget); static nsWindow* get_window_for_gdk_window(GdkWindow* window); static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window); static GdkCursor* get_gtk_cursor(nsCursor aCursor); -static GdkWindow* get_inner_gdk_window(GdkWindow* aWindow, gint x, gint y, - gint* retx, gint* rety); /* callbacks from widgets */ static gboolean expose_event_cb(GtkWidget* widget, cairo_t* cr); @@ -404,6 +403,7 @@ nsWindow::nsWindow() mIsDragPopup(false), mCompositedScreen(gdk_screen_is_composited(gdk_screen_get_default())), mIsAccelerated(false), + mIsAlert(false), mWindowShouldStartDragging(false), mHasMappedToplevel(false), mRetryPointerGrab(false), @@ -572,21 +572,6 @@ bool nsWindow::AreBoundsSane() { return !mLastSizeRequest.IsEmpty(); } -// Walk the list of child windows and call destroy on them. -void nsWindow::DestroyChildWindows() { - LOG("nsWindow::DestroyChildWindows()"); - if (!mGdkWindow) { - return; - } - while (GList* children = gdk_window_peek_children(mGdkWindow)) { - GdkWindow* child = GDK_WINDOW(children->data); - nsWindow* kid = get_window_for_gdk_window(child); - if (kid) { - kid->Destroy(); - } - } -} - void nsWindow::Destroy() { MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); @@ -4141,6 +4126,16 @@ void nsWindow::OnUnmap() { 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) { @@ -5394,6 +5389,7 @@ void nsWindow::OnDPIChanged() { } mWidgetListener->UIResolutionChanged(); } + NotifyAPZOfDPIChange(); } void nsWindow::OnCheckResize() { mPendingConfigures++; } @@ -5427,6 +5423,8 @@ void nsWindow::OnScaleChanged(bool aNotify) { return; } + NotifyAPZOfDPIChange(); + LOG("OnScaleChanged %d, %f -> %d, %f\n", int(mCeiledScaleFactor), mFractionalScaleFactor, newCeiled, newFractional); @@ -5816,9 +5814,6 @@ void nsWindow::EnsureGdkWindow() { if (!mGdkWindow) { mGdkWindow = gtk_widget_get_window(GTK_WIDGET(mContainer)); g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", this); - if (mIMContext) { - mIMContext->SetGdkWindow(mGdkWindow); - } } } @@ -5871,13 +5866,13 @@ void nsWindow::ConfigureGdkWindow() { EnsureGdkWindow(); OnScaleChanged(/* aNotify = */ false); + if (mIsAlert) { + gdk_window_set_override_redirect(mGdkWindow, TRUE); + } + #ifdef MOZ_X11 if (GdkIsX11Display()) { - GdkVisual* gdkVisual = gdk_window_get_visual(mGdkWindow); - Visual* visual = gdk_x11_visual_get_xvisual(gdkVisual); - int depth = gdk_visual_get_depth(gdkVisual); - mSurfaceProvider.Initialize(GetX11Window(), visual, depth, - GetShapedState()); + mSurfaceProvider.Initialize(GetX11Window(), GetShapedState()); // Set window manager hint to keep fullscreen windows composited. // @@ -6019,6 +6014,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, // and can be changed so we use WaylandPopupIsPermanent() to get // recent popup config (Bug 1728952). mNoAutoHide = aInitData && aInitData->mNoAutoHide; + mIsAlert = aInitData && aInitData->mIsAlert; // Popups that are not noautohide are only temporary. The are used // for menus and the like and disappear when another window is used. @@ -6108,10 +6104,11 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, if (mIsPIPWindow) { LOG(" Is PIP window\n"); gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_UTILITY); - } else if (aInitData && aInitData->mIsAlert) { + } else if (mIsAlert) { LOG(" Is alert window\n"); gtk_window_set_type_hint(GTK_WINDOW(mShell), GDK_WINDOW_TYPE_HINT_NOTIFICATION); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(mShell), TRUE); } else if (mWindowType == WindowType::Dialog) { mGtkWindowRoleName = "Dialog"; @@ -6706,10 +6703,6 @@ void nsWindow::ResumeCompositorImpl() { LOG("nsWindow::ResumeCompositorImpl()\n"); MOZ_DIAGNOSTIC_ASSERT(mCompositorWidgetDelegate); - - // DisableRendering() clears stored X11Window so we're sure EnableRendering() - // really updates it. - mCompositorWidgetDelegate->DisableRendering(); mCompositorWidgetDelegate->EnableRendering(GetX11Window(), GetShapedState()); // As WaylandStartVsync needs mCompositorWidgetDelegate this is the right @@ -6988,6 +6981,13 @@ void nsWindow::UpdateWindowDraggingRegion( } } +#ifdef MOZ_ENABLE_DBUS +void nsWindow::SetDBusMenuBar( + RefPtr<mozilla::widget::DBusMenuBar> aDbusMenuBar) { + mDBusMenuBar = std::move(aDbusMenuBar); +} +#endif + LayoutDeviceIntCoord nsWindow::GetTitlebarRadius() { MOZ_RELEASE_ASSERT(NS_IsMainThread()); int32_t cssCoord = LookAndFeel::GetInt(LookAndFeel::IntID::TitlebarRadius); @@ -7839,14 +7839,246 @@ static GtkWidget* get_gtk_widget_for_gdk_window(GdkWindow* window) { return GTK_WIDGET(user_data); } -static GdkCursor* get_gtk_cursor(nsCursor aCursor) { +static GdkCursor* get_gtk_cursor_from_type(uint8_t aCursorType) { + GdkDisplay* defaultDisplay = gdk_display_get_default(); GdkCursor* gdkcursor = nullptr; - uint8_t newType = 0xff; - if ((gdkcursor = gCursorCache[aCursor])) { - return gdkcursor; + // GtkCursors are defined at nsGtkCursors.h + if (aCursorType > MOZ_CURSOR_NONE) { + return nullptr; } + // If by now we don't have a xcursor, this means we have to make a custom + // one. First, we try creating a named cursor based on the hash of our + // custom bitmap, as libXcursor has some magic to convert bitmapped cursors + // to themed cursors + if (GtkCursors[aCursorType].hash) { + gdkcursor = + gdk_cursor_new_from_name(defaultDisplay, GtkCursors[aCursorType].hash); + if (gdkcursor) { + return gdkcursor; + } + } + + LOGW("get_gtk_cursor_from_type(): Failed to get cursor type %d, try bitmap", + aCursorType); + + // If we still don't have a xcursor, we now really create a bitmap cursor + GdkPixbuf* cursor_pixbuf = + gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); + if (!cursor_pixbuf) { + return nullptr; + } + + guchar* data = gdk_pixbuf_get_pixels(cursor_pixbuf); + + // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and + // mask GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for + // each pixel) so it's 128 byte array (4 bytes for are one bitmap row and + // there are 32 rows here). + const unsigned char* bits = GtkCursors[aCursorType].bits; + const unsigned char* mask_bits = GtkCursors[aCursorType].mask_bits; + + for (int i = 0; i < 128; i++) { + char bit = (char)*bits++; + char mask = (char)*mask_bits++; + for (int j = 0; j < 8; j++) { + unsigned char pix = ~(((bit >> j) & 0x01) * 0xff); + *data++ = pix; + *data++ = pix; + *data++ = pix; + *data++ = (((mask >> j) & 0x01) * 0xff); + } + } + + gdkcursor = gdk_cursor_new_from_pixbuf( + gdk_display_get_default(), cursor_pixbuf, GtkCursors[aCursorType].hot_x, + GtkCursors[aCursorType].hot_y); + + g_object_unref(cursor_pixbuf); + return gdkcursor; +} + +static GdkCursor* get_gtk_cursor_legacy(nsCursor aCursor) { + GdkCursor* gdkcursor = nullptr; + Maybe<uint8_t> fallbackType; + + GdkDisplay* defaultDisplay = gdk_display_get_default(); + + // The strategy here is to use standard GDK cursors, and, if not available, + // load by standard name with gdk_cursor_new_from_name. + // Spec is here: http://www.freedesktop.org/wiki/Specifications/cursor-spec/ + switch (aCursor) { + case eCursor_standard: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR); + break; + case eCursor_wait: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_WATCH); + break; + case eCursor_select: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_XTERM); + break; + case eCursor_hyperlink: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_HAND2); + break; + case eCursor_n_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_SIDE); + break; + case eCursor_s_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_SIDE); + break; + case eCursor_w_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_SIDE); + break; + case eCursor_e_resize: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_RIGHT_SIDE); + break; + case eCursor_nw_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_LEFT_CORNER); + break; + case eCursor_se_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_RIGHT_CORNER); + break; + case eCursor_ne_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_TOP_RIGHT_CORNER); + break; + case eCursor_sw_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_BOTTOM_LEFT_CORNER); + break; + case eCursor_crosshair: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_CROSSHAIR); + break; + case eCursor_move: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR); + break; + case eCursor_help: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_QUESTION_ARROW); + break; + case eCursor_copy: // CSS3 + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_COPY); + break; + case eCursor_alias: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ALIAS); + break; + case eCursor_context_menu: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_CONTEXT_MENU); + break; + case eCursor_cell: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_PLUS); + break; + // Those two aren’t standardized. Trying both KDE’s and GNOME’s names + case eCursor_grab: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "openhand"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRAB); + break; + case eCursor_grabbing: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "closedhand"); + if (!gdkcursor) { + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing"); + } + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRABBING); + break; + case eCursor_spinning: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_SPINNING); + break; + case eCursor_zoom_in: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_IN); + break; + case eCursor_zoom_out: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_OUT); + break; + case eCursor_not_allowed: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed"); + if (!gdkcursor) { // nonstandard, yet common + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "crossed_circle"); + } + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); + break; + case eCursor_no_drop: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop"); + if (!gdkcursor) { // this nonstandard sequence makes it work on KDE and + // GNOME + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "forbidden"); + } + if (!gdkcursor) { + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle"); + } + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); + break; + case eCursor_vertical_text: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "vertical-text"); + if (!gdkcursor) { + fallbackType.emplace(MOZ_CURSOR_VERTICAL_TEXT); + } + break; + case eCursor_all_scroll: + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_FLEUR); + break; + case eCursor_nesw_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_bdiag"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NESW_RESIZE); + break; + case eCursor_nwse_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "size_fdiag"); + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NWSE_RESIZE); + break; + case eCursor_ns_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_V_DOUBLE_ARROW); + break; + case eCursor_ew_resize: + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_H_DOUBLE_ARROW); + break; + // Here, two better fitting cursors exist in some cursor themes. Try those + // first + case eCursor_row_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_v"); + if (!gdkcursor) { + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_V_DOUBLE_ARROW); + } + break; + case eCursor_col_resize: + gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "split_h"); + if (!gdkcursor) { + gdkcursor = + gdk_cursor_new_for_display(defaultDisplay, GDK_SB_H_DOUBLE_ARROW); + } + break; + case eCursor_none: + fallbackType.emplace(MOZ_CURSOR_NONE); + break; + default: + NS_ASSERTION(aCursor, "Invalid cursor type"); + gdkcursor = gdk_cursor_new_for_display(defaultDisplay, GDK_LEFT_PTR); + break; + } + + if (!gdkcursor && fallbackType.isSome()) { + LOGW("get_gtk_cursor_legacy(): Failed to get cursor %d, try fallback", + aCursor); + gdkcursor = get_gtk_cursor_from_type(*fallbackType); + } + + return gdkcursor; +} + +static GdkCursor* get_gtk_cursor_from_name(nsCursor aCursor) { + GdkCursor* gdkcursor = nullptr; + Maybe<uint8_t> fallbackType; + GdkDisplay* defaultDisplay = gdk_display_get_default(); switch (aCursor) { @@ -7897,42 +8129,42 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; case eCursor_copy: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "copy"); - if (!gdkcursor) newType = MOZ_CURSOR_COPY; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_COPY); break; case eCursor_alias: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "alias"); - if (!gdkcursor) newType = MOZ_CURSOR_ALIAS; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ALIAS); break; case eCursor_context_menu: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "context-menu"); - if (!gdkcursor) newType = MOZ_CURSOR_CONTEXT_MENU; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_CONTEXT_MENU); break; case eCursor_cell: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "cell"); break; case eCursor_grab: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grab"); - if (!gdkcursor) newType = MOZ_CURSOR_HAND_GRAB; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRAB); break; case eCursor_grabbing: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "grabbing"); - if (!gdkcursor) newType = MOZ_CURSOR_HAND_GRABBING; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_HAND_GRABBING); break; case eCursor_spinning: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "progress"); - if (!gdkcursor) newType = MOZ_CURSOR_SPINNING; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_SPINNING); break; case eCursor_zoom_in: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-in"); - if (!gdkcursor) newType = MOZ_CURSOR_ZOOM_IN; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_IN); break; case eCursor_zoom_out: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "zoom-out"); - if (!gdkcursor) newType = MOZ_CURSOR_ZOOM_OUT; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_ZOOM_OUT); break; case eCursor_not_allowed: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "not-allowed"); - if (!gdkcursor) newType = MOZ_CURSOR_NOT_ALLOWED; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); break; case eCursor_no_drop: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "no-drop"); @@ -7943,12 +8175,12 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { if (!gdkcursor) { gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "circle"); } - if (!gdkcursor) newType = MOZ_CURSOR_NOT_ALLOWED; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NOT_ALLOWED); break; case eCursor_vertical_text: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "vertical-text"); if (!gdkcursor) { - newType = MOZ_CURSOR_VERTICAL_TEXT; + fallbackType.emplace(MOZ_CURSOR_VERTICAL_TEXT); } break; case eCursor_all_scroll: @@ -7956,11 +8188,11 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; case eCursor_nesw_resize: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "nesw-resize"); - if (!gdkcursor) newType = MOZ_CURSOR_NESW_RESIZE; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NESW_RESIZE); break; case eCursor_nwse_resize: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "nwse-resize"); - if (!gdkcursor) newType = MOZ_CURSOR_NWSE_RESIZE; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NWSE_RESIZE); break; case eCursor_ns_resize: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "ns-resize"); @@ -7976,7 +8208,7 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; case eCursor_none: gdkcursor = gdk_cursor_new_from_name(defaultDisplay, "none"); - if (!gdkcursor) newType = MOZ_CURSOR_NONE; + if (!gdkcursor) fallbackType.emplace(MOZ_CURSOR_NONE); break; default: NS_ASSERTION(aCursor, "Invalid cursor type"); @@ -7984,51 +8216,26 @@ static GdkCursor* get_gtk_cursor(nsCursor aCursor) { break; } - // If by now we don't have a xcursor, this means we have to make a custom - // one. First, we try creating a named cursor based on the hash of our - // custom bitmap, as libXcursor has some magic to convert bitmapped cursors - // to themed cursors - if (newType != 0xFF && GtkCursors[newType].hash) { - gdkcursor = - gdk_cursor_new_from_name(defaultDisplay, GtkCursors[newType].hash); + if (!gdkcursor && fallbackType.isSome()) { + LOGW("get_gtk_cursor_from_name(): Failed to get cursor %d, try fallback", + aCursor); + gdkcursor = get_gtk_cursor_from_type(*fallbackType); } - // If we still don't have a xcursor, we now really create a bitmap cursor - if (newType != 0xff && !gdkcursor) { - GdkPixbuf* cursor_pixbuf = - gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 32, 32); - if (!cursor_pixbuf) { - return nullptr; - } - - guchar* data = gdk_pixbuf_get_pixels(cursor_pixbuf); - - // Read data from GtkCursors and compose RGBA surface from 1bit bitmap and - // mask GtkCursors bits and mask are 32x32 monochrome bitmaps (1 bit for - // each pixel) so it's 128 byte array (4 bytes for are one bitmap row and - // there are 32 rows here). - const unsigned char* bits = GtkCursors[newType].bits; - const unsigned char* mask_bits = GtkCursors[newType].mask_bits; - - for (int i = 0; i < 128; i++) { - char bit = (char)*bits++; - char mask = (char)*mask_bits++; - for (int j = 0; j < 8; j++) { - unsigned char pix = ~(((bit >> j) & 0x01) * 0xff); - *data++ = pix; - *data++ = pix; - *data++ = pix; - *data++ = (((mask >> j) & 0x01) * 0xff); - } - } + return gdkcursor; +} - gdkcursor = gdk_cursor_new_from_pixbuf( - gdk_display_get_default(), cursor_pixbuf, GtkCursors[newType].hot_x, - GtkCursors[newType].hot_y); +static GdkCursor* get_gtk_cursor(nsCursor aCursor) { + GdkCursor* gdkcursor = nullptr; - g_object_unref(cursor_pixbuf); + if ((gdkcursor = gCursorCache[aCursor])) { + return gdkcursor; } + gdkcursor = StaticPrefs::widget_gtk_legacy_cursors_enabled() + ? get_gtk_cursor_legacy(aCursor) + : get_gtk_cursor_from_name(aCursor); + gCursorCache[aCursor] = gdkcursor; return gdkcursor; @@ -8214,6 +8421,10 @@ static gboolean button_press_event_cb(GtkWidget* widget, GdkEventButton* event) { UpdateLastInputEventTime(event); + if (event->button == 2 && !StaticPrefs::widget_gtk_middle_click_enabled()) { + return FALSE; + } + RefPtr<nsWindow> window = GetFirstNSWindowForGDKWindow(event->window); if (!window) { return FALSE; @@ -8232,6 +8443,10 @@ static gboolean button_release_event_cb(GtkWidget* widget, GdkEventButton* event) { UpdateLastInputEventTime(event); + if (event->button == 2 && !StaticPrefs::widget_gtk_middle_click_enabled()) { + return FALSE; + } + RefPtr<nsWindow> window = GetFirstNSWindowForGDKWindow(event->window); if (!window) { return FALSE; @@ -8579,28 +8794,26 @@ gboolean WindowDragMotionHandler(GtkWidget* aWidget, GdkDragContext* aDragContext, gint aX, gint aY, guint aTime) { RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); - if (!window) { + if (!window || !window->GetGdkWindow()) { return FALSE; } - // figure out which internal widget this drag motion actually happened on - nscoord retx = 0; - nscoord rety = 0; - - GdkWindow* innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), - aX, aY, &retx, &rety); - RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow); - if (!innerMostWindow) { - innerMostWindow = window; + // We're getting aX,aY in mShell coordinates space. + // mContainer is shifted by CSD decorations so translate the coords + // to mContainer space where our content lives. + if (aWidget == window->GetGtkWidget()) { + int x, y; + gdk_window_get_geometry(window->GetGdkWindow(), &x, &y, nullptr, nullptr); + aX -= x; + aY -= y; } - LOGDRAG("WindowDragMotionHandler target nsWindow [%p]", - innerMostWindow.get()); + + LOGDRAG("WindowDragMotionHandler target nsWindow [%p]", window.get()); RefPtr<nsDragService> dragService = nsDragService::GetInstance(); nsDragService::AutoEventLoop loop(dragService); if (!dragService->ScheduleMotionEvent( - innerMostWindow, aDragContext, - GetWindowDropPosition(innerMostWindow, retx, rety), aTime)) { + window, aDragContext, GetWindowDropPosition(window, aX, aY), aTime)) { return FALSE; } return TRUE; @@ -8656,28 +8869,25 @@ static void drag_leave_event_cb(GtkWidget* aWidget, gboolean WindowDragDropHandler(GtkWidget* aWidget, GdkDragContext* aDragContext, gint aX, gint aY, guint aTime) { RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget); - if (!window) { + if (!window || !window->GetGdkWindow()) { return FALSE; } - // figure out which internal widget this drag motion actually happened on - nscoord retx = 0; - nscoord rety = 0; - - GdkWindow* innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), - aX, aY, &retx, &rety); - RefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow); - - if (!innerMostWindow) { - innerMostWindow = window; + // We're getting aX,aY in mShell coordinates space. + // mContainer is shifted by CSD decorations so translate the coords + // to mContainer space where our content lives. + if (aWidget == window->GetGtkWidget()) { + int x, y; + gdk_window_get_geometry(window->GetGdkWindow(), &x, &y, nullptr, nullptr); + aX -= x; + aY -= y; } - LOGDRAG("WindowDragDropHandler nsWindow [%p]", innerMostWindow.get()); + LOGDRAG("WindowDragDropHandler nsWindow [%p]", window.get()); RefPtr<nsDragService> dragService = nsDragService::GetInstance(); nsDragService::AutoEventLoop loop(dragService); return dragService->ScheduleDropEvent( - innerMostWindow, aDragContext, - GetWindowDropPosition(innerMostWindow, retx, rety), aTime); + window, aDragContext, GetWindowDropPosition(window, aX, aY), aTime); } static gboolean drag_drop_event_cb(GtkWidget* aWidget, @@ -8710,27 +8920,6 @@ static nsresult initialize_prefs(void) { return NS_OK; } -// TODO: Can we simplify it for mShell/mContainer only scenario? -static GdkWindow* get_inner_gdk_window(GdkWindow* aWindow, gint x, gint y, - gint* retx, gint* rety) { - gint cx, cy, cw, ch; - GList* children = gdk_window_peek_children(aWindow); - for (GList* child = g_list_last(children); child; - child = g_list_previous(child)) { - auto* childWindow = (GdkWindow*)child->data; - if (get_window_for_gdk_window(childWindow)) { - gdk_window_get_geometry(childWindow, &cx, &cy, &cw, &ch); - if ((cx < x) && (x < (cx + cw)) && (cy < y) && (y < (cy + ch)) && - gdk_window_is_visible(childWindow)) { - return get_inner_gdk_window(childWindow, x - cx, y - cy, retx, rety); - } - } - } - *retx = x; - *rety = y; - return aWindow; -} - #ifdef ACCESSIBILITY void nsWindow::CreateRootAccessible() { if (!mRootAccessible) { @@ -9548,8 +9737,19 @@ void nsWindow::GetCompositorWidgetInitData( LOG("nsWindow::GetCompositorWidgetInitData"); + Window window = GetX11Window(); +#ifdef MOZ_X11 + // We're bit hackish here. Old GLX backend needs XWindow when GLContext + // is created so get XWindow now before map signal. + // We may see crashes/errors when nsWindow is unmapped (XWindow is + // invalidated) but we can't do anything about it. + if (!window && !gfxVars::UseEGL()) { + window = + gdk_x11_window_get_xid(gtk_widget_get_window(GTK_WIDGET(mContainer))); + } +#endif *aInitData = mozilla::widget::GtkCompositorWidgetInitData( - GetX11Window(), displayName, GetShapedState(), GdkIsX11Display(), + window, displayName, GetShapedState(), GdkIsX11Display(), GetClientSize()); #ifdef MOZ_X11 @@ -9927,38 +10127,42 @@ void nsWindow::DisableRendering() { LOG("nsWindow::DisableRendering()"); if (mGdkWindow) { - if (mIMContext) { - mIMContext->SetGdkWindow(nullptr); - } g_object_set_data(G_OBJECT(mGdkWindow), "nsWindow", nullptr); mGdkWindow = nullptr; } + // 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(). - // 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()); + // 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(). + // 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()); + } } - } #endif + } } // Apply workaround for Mutter compositor bug (mzbz#1777269). diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index e235d12c08..f8fe344f09 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -113,6 +113,7 @@ class CurrentX11TimeGetter; #endif namespace widget { +class DBusMenuBar; class Screen; } // namespace widget } // namespace mozilla @@ -373,6 +374,10 @@ class nsWindow final : public nsBaseWidget { void UpdateWindowDraggingRegion( const LayoutDeviceIntRegion& aRegion) override; +#ifdef MOZ_ENABLE_DBUS + void SetDBusMenuBar(RefPtr<mozilla::widget::DBusMenuBar> aDbusMenuBar); +#endif + // HiDPI scale conversion gint GdkCeiledScaleFactor(); double FractionalScaleFactor(); @@ -654,6 +659,7 @@ class nsWindow final : public nsBaseWidget { bool mIsDragPopup : 1; bool mCompositedScreen : 1; bool mIsAccelerated : 1; + bool mIsAlert : 1; bool mWindowShouldStartDragging : 1; bool mHasMappedToplevel : 1; bool mRetryPointerGrab : 1; @@ -904,6 +910,10 @@ class nsWindow final : public nsBaseWidget { RefPtr<nsWindow> mWaylandPopupNext; RefPtr<nsWindow> mWaylandPopupPrev; +#ifdef MOZ_ENABLE_DBUS + RefPtr<mozilla::widget::DBusMenuBar> mDBusMenuBar; +#endif + // When popup is resized by Gtk by move-to-rect callback, // we store final popup size here. Then we use mMoveToRectPopupSize size // in following popup operations unless mLayoutPopupSizeCleared is set. diff --git a/widget/gtk/wayland/moz.build b/widget/gtk/wayland/moz.build index e033187a3b..b9ea6a4f70 100644 --- a/widget/gtk/wayland/moz.build +++ b/widget/gtk/wayland/moz.build @@ -15,6 +15,7 @@ SOURCES += [ "relative-pointer-unstable-v1-protocol.c", "viewporter-protocol.c", "xdg-activation-v1-protocol.c", + "xdg-dbus-annotation-v1-protocol.c", "xdg-output-unstable-v1-protocol.c", ] @@ -26,6 +27,7 @@ EXPORTS.mozilla.widget += [ "relative-pointer-unstable-v1-client-protocol.h", "viewporter-client-protocol.h", "xdg-activation-v1-client-protocol.h", + "xdg-dbus-annotation-v1-client-protocol.h", "xdg-output-unstable-v1-client-protocol.h", ] diff --git a/widget/gtk/wayland/xdg-dbus-annotation-v1-client-protocol.h b/widget/gtk/wayland/xdg-dbus-annotation-v1-client-protocol.h new file mode 100644 index 0000000000..fe5567cf8e --- /dev/null +++ b/widget/gtk/wayland/xdg-dbus-annotation-v1-client-protocol.h @@ -0,0 +1,284 @@ +/* Generated by wayland-scanner 1.19.0 */ + +#ifndef XDG_DBUS_ANNOTATION_V1_CLIENT_PROTOCOL_H +#define XDG_DBUS_ANNOTATION_V1_CLIENT_PROTOCOL_H + +#include <stdint.h> +#include <stddef.h> +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_xdg_dbus_annotation_v1 The xdg_dbus_annotation_v1 protocol + * Wayland protocol for associating DBus objects with toplevels + * + * @section page_desc_xdg_dbus_annotation_v1 Description + * + * This description provides a high-level overview of the interplay between + * the interfaces defined in this protocol. For details, see the protocol + * specification. + * + * The dbus_annotation_manager allows a client to request the creation of an + * annotation object associated with an wl_surface or itself. The annotation + * object allows a client to notify the compositor of a DBus object associated + * with itself. + * + * Clients should request the creation of an dbus_annotation object when they + * create a DBus object associated with an wl_surface or themselves, and should + * release the object when they destroy a DBus object associated with their + * wl_surface or themselves. + * + * Clients should only own at most one dbus_annotation object with a given name + * for each of their wl_surface objects or themselves. A protocol error will be + * raised if a client requests more than one dbus_annotation object for an + * wl_surface or themselves with a given name. + * + * @section page_ifaces_xdg_dbus_annotation_v1 Interfaces + * - @subpage page_iface_xdg_dbus_annotation_manager_v1 - controller object for + * registering dbus objects associated with wl_surfaces or clients + * - @subpage page_iface_xdg_dbus_annotation_v1 - controller object for + * associating dbus objects with an wl_surface + * @section page_copyright_xdg_dbus_annotation_v1 Copyright + * <pre> + * + * Copyright © 2017 David Edmundson + * Copyrihgt © 2023 Janet Blackquill + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * </pre> + */ +struct wl_surface; +struct xdg_dbus_annotation_manager_v1; +struct xdg_dbus_annotation_v1; + +#ifndef XDG_DBUS_ANNOTATION_MANAGER_V1_INTERFACE +# define XDG_DBUS_ANNOTATION_MANAGER_V1_INTERFACE +/** + * @page page_iface_xdg_dbus_annotation_manager_v1 + * xdg_dbus_annotation_manager_v1 + * @section page_iface_xdg_dbus_annotation_manager_v1_desc Description + * + * An object that provides access to the creation of dbus_annotation objects. + * @section page_iface_xdg_dbus_annotation_manager_v1_api API + * See @ref iface_xdg_dbus_annotation_manager_v1. + */ +/** + * @defgroup iface_xdg_dbus_annotation_manager_v1 The + * xdg_dbus_annotation_manager_v1 interface + * + * An object that provides access to the creation of dbus_annotation objects. + */ +extern const struct wl_interface xdg_dbus_annotation_manager_v1_interface; +#endif +#ifndef XDG_DBUS_ANNOTATION_V1_INTERFACE +# define XDG_DBUS_ANNOTATION_V1_INTERFACE +/** + * @page page_iface_xdg_dbus_annotation_v1 xdg_dbus_annotation_v1 + * @section page_iface_xdg_dbus_annotation_v1_desc Description + * + * An object that provides access to clients to notify the compositor of + * associated DBus objects for an wl_surface. + * + * If not applicable, clients should remove this object. + * @section page_iface_xdg_dbus_annotation_v1_api API + * See @ref iface_xdg_dbus_annotation_v1. + */ +/** + * @defgroup iface_xdg_dbus_annotation_v1 The xdg_dbus_annotation_v1 interface + * + * An object that provides access to clients to notify the compositor of + * associated DBus objects for an wl_surface. + * + * If not applicable, clients should remove this object. + */ +extern const struct wl_interface xdg_dbus_annotation_v1_interface; +#endif + +#ifndef XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM +# define XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM +enum xdg_dbus_annotation_manager_v1_error { + /** + * given wl_surface or client already has a dbus_annotation with the same + * interface + */ + XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ALREADY_ANNOTATED = 0, + /** + * given wl_surface is invalid + */ + XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_BAD_TARGET = 1, +}; +#endif /* XDG_DBUS_ANNOTATION_MANAGER_V1_ERROR_ENUM */ + +#define XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY 0 +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT 1 +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE 2 + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + */ +#define XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + */ +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + */ +#define XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE_SINCE_VERSION 1 + +/** @ingroup iface_xdg_dbus_annotation_manager_v1 */ +static inline void xdg_dbus_annotation_manager_v1_set_user_data( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1, + void* user_data) { + wl_proxy_set_user_data((struct wl_proxy*)xdg_dbus_annotation_manager_v1, + user_data); +} + +/** @ingroup iface_xdg_dbus_annotation_manager_v1 */ +static inline void* xdg_dbus_annotation_manager_v1_get_user_data( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) { + return wl_proxy_get_user_data( + (struct wl_proxy*)xdg_dbus_annotation_manager_v1); +} + +static inline uint32_t xdg_dbus_annotation_manager_v1_get_version( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) { + return wl_proxy_get_version((struct wl_proxy*)xdg_dbus_annotation_manager_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + * + * Destroy the xdg_dbus_annotation_manager object. xdg_dbus_annotation objects + * created from this object remain valid and should be destroyed separately. + */ +static inline void xdg_dbus_annotation_manager_v1_destroy( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1) { + wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_manager_v1, + XDG_DBUS_ANNOTATION_MANAGER_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy*)xdg_dbus_annotation_manager_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + * + * The interface other DBus clients can expect the object specified by the + * annotation to implement. + */ +static inline struct xdg_dbus_annotation_v1* +xdg_dbus_annotation_manager_v1_create_client( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1, + const char* interface) { + struct wl_proxy* id; + + id = wl_proxy_marshal_constructor( + (struct wl_proxy*)xdg_dbus_annotation_manager_v1, + XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_CLIENT, + &xdg_dbus_annotation_v1_interface, interface, NULL); + + return (struct xdg_dbus_annotation_v1*)id; +} + +/** + * @ingroup iface_xdg_dbus_annotation_manager_v1 + * + * The surface to associate the annotation with + */ +static inline struct xdg_dbus_annotation_v1* +xdg_dbus_annotation_manager_v1_create_surface( + struct xdg_dbus_annotation_manager_v1* xdg_dbus_annotation_manager_v1, + const char* interface, struct wl_surface* toplevel) { + struct wl_proxy* id; + + id = wl_proxy_marshal_constructor( + (struct wl_proxy*)xdg_dbus_annotation_manager_v1, + XDG_DBUS_ANNOTATION_MANAGER_V1_CREATE_SURFACE, + &xdg_dbus_annotation_v1_interface, interface, NULL, toplevel); + + return (struct xdg_dbus_annotation_v1*)id; +} + +#define XDG_DBUS_ANNOTATION_V1_DESTROY 0 +#define XDG_DBUS_ANNOTATION_V1_SET_ADDRESS 1 + +/** + * @ingroup iface_xdg_dbus_annotation_v1 + */ +#define XDG_DBUS_ANNOTATION_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_dbus_annotation_v1 + */ +#define XDG_DBUS_ANNOTATION_V1_SET_ADDRESS_SINCE_VERSION 1 + +/** @ingroup iface_xdg_dbus_annotation_v1 */ +static inline void xdg_dbus_annotation_v1_set_user_data( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1, void* user_data) { + wl_proxy_set_user_data((struct wl_proxy*)xdg_dbus_annotation_v1, user_data); +} + +/** @ingroup iface_xdg_dbus_annotation_v1 */ +static inline void* xdg_dbus_annotation_v1_get_user_data( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) { + return wl_proxy_get_user_data((struct wl_proxy*)xdg_dbus_annotation_v1); +} + +static inline uint32_t xdg_dbus_annotation_v1_get_version( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) { + return wl_proxy_get_version((struct wl_proxy*)xdg_dbus_annotation_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_v1 + */ +static inline void xdg_dbus_annotation_v1_destroy( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1) { + wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_v1, + XDG_DBUS_ANNOTATION_V1_DESTROY); + + wl_proxy_destroy((struct wl_proxy*)xdg_dbus_annotation_v1); +} + +/** + * @ingroup iface_xdg_dbus_annotation_v1 + * + * Set or update the service name and object path corresponding to the + * DBus object. The DBus object should be registered on the session bus + * before sending this request. + * + * Strings should be formatted in Latin-1 matching the relevant DBus + * specifications. + */ +static inline void xdg_dbus_annotation_v1_set_address( + struct xdg_dbus_annotation_v1* xdg_dbus_annotation_v1, + const char* service_name, const char* object_path) { + wl_proxy_marshal((struct wl_proxy*)xdg_dbus_annotation_v1, + XDG_DBUS_ANNOTATION_V1_SET_ADDRESS, service_name, + object_path); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/widget/gtk/wayland/xdg-dbus-annotation-v1-protocol.c b/widget/gtk/wayland/xdg-dbus-annotation-v1-protocol.c new file mode 100644 index 0000000000..af51b3b0e8 --- /dev/null +++ b/widget/gtk/wayland/xdg-dbus-annotation-v1-protocol.c @@ -0,0 +1,75 @@ +/* Generated by wayland-scanner 1.19.0 */ + +/* + * Copyright © 2017 David Edmundson + * Copyrihgt © 2023 Janet Blackquill + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <stdint.h> +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +# define WL_PRIVATE __attribute__((visibility("hidden"))) +#else +# define WL_PRIVATE +#endif + +#pragma GCC visibility push(default) +extern const struct wl_interface wl_surface_interface; +#pragma GCC visibility pop +extern const struct wl_interface xdg_dbus_annotation_v1_interface; + +static const struct wl_interface* xdg_dbus_annotation_v1_types[] = { + NULL, + NULL, + NULL, + &xdg_dbus_annotation_v1_interface, + NULL, + &xdg_dbus_annotation_v1_interface, + &wl_surface_interface, +}; + +static const struct wl_message xdg_dbus_annotation_manager_v1_requests[] = { + {"destroy", "", xdg_dbus_annotation_v1_types + 0}, + {"create_client", "sn", xdg_dbus_annotation_v1_types + 2}, + {"create_surface", "sno", xdg_dbus_annotation_v1_types + 4}, +}; + +WL_PRIVATE const struct wl_interface xdg_dbus_annotation_manager_v1_interface = + { + "xdg_dbus_annotation_manager_v1", 1, 3, + xdg_dbus_annotation_manager_v1_requests, 0, NULL, +}; + +static const struct wl_message xdg_dbus_annotation_v1_requests[] = { + {"destroy", "", xdg_dbus_annotation_v1_types + 0}, + {"set_address", "ss", xdg_dbus_annotation_v1_types + 0}, +}; + +WL_PRIVATE const struct wl_interface xdg_dbus_annotation_v1_interface = { + "xdg_dbus_annotation_v1", 1, 2, xdg_dbus_annotation_v1_requests, 0, NULL, +}; diff --git a/widget/headless/HeadlessLookAndFeelGTK.cpp b/widget/headless/HeadlessLookAndFeelGTK.cpp index f8f6270cd7..462a877f34 100644 --- a/widget/headless/HeadlessLookAndFeelGTK.cpp +++ b/widget/headless/HeadlessLookAndFeelGTK.cpp @@ -118,9 +118,6 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::ScrollbarButtonAutoRepeatBehavior: aResult = 0; break; - case IntID::TooltipDelay: - aResult = 500; - break; case IntID::SwipeAnimationEnabled: aResult = 0; break; diff --git a/widget/headless/tests/test_headless.js b/widget/headless/tests/test_headless.js index f9183245d2..4331cb3fe7 100644 --- a/widget/headless/tests/test_headless.js +++ b/widget/headless/tests/test_headless.js @@ -22,7 +22,7 @@ registerCleanupFunction(() => { const progressListeners = new Map(); function loadContentWindow(windowlessBrowser, uri) { - return new Promise((resolve, reject) => { + return new Promise(resolve => { let loadURIOptions = { triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }; @@ -46,7 +46,7 @@ function loadContentWindow(windowlessBrowser, uri) { progressListeners.delete(progressListener); contentWindow.addEventListener( "load", - event => { + () => { resolve(contentWindow); }, { once: true } diff --git a/widget/moz.build b/widget/moz.build index 54a231c67d..557db5e288 100644 --- a/widget/moz.build +++ b/widget/moz.build @@ -323,7 +323,7 @@ if toolkit in {"gtk", "cocoa", "windows", "android", "uikit"}: "nsBaseFilePicker.cpp", ] -if toolkit in ("gtk", "windows", "cocoa", "android"): +if toolkit in ("gtk", "windows", "cocoa", "uikit", "android"): UNIFIED_SOURCES += [ "nsNativeTheme.cpp", ] diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp index 40af1de388..333d154e70 100644 --- a/widget/nsBaseClipboard.cpp +++ b/widget/nsBaseClipboard.cpp @@ -812,56 +812,124 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetData( return NS_OK; } -void nsBaseClipboard::AsyncGetDataInternal( +already_AddRefed<nsIAsyncGetClipboardData> +nsBaseClipboard::MaybeCreateGetRequestFromClipboardCache( const nsTArray<nsCString>& aFlavorList, int32_t aClipboardType, - mozilla::dom::WindowContext* aRequestingWindowContext, - nsIAsyncClipboardGetCallback* aCallback) { - MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); + mozilla::dom::WindowContext* aRequestingWindowContext) { + MOZ_DIAGNOSTIC_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); - if (mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) { - // If we were the last ones to put something on the native clipboard, then - // just use the cached transferable. Otherwise clear it because it isn't - // relevant any more. - if (auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType)) { - nsITransferable* cachedTransferable = clipboardCache->GetTransferable(); - MOZ_ASSERT(cachedTransferable); - - nsTArray<nsCString> transferableFlavors; - if (NS_SUCCEEDED(cachedTransferable->FlavorsTransferableCanExport( - transferableFlavors))) { - nsTArray<nsCString> results; - for (const auto& transferableFlavor : transferableFlavors) { - for (const auto& flavor : aFlavorList) { - // XXX We need special check for image as we always put the - // image as "native" on the clipboard. - if (transferableFlavor.Equals(flavor) || - (transferableFlavor.Equals(kNativeImageMime) && - nsContentUtils::IsFlavorImage(flavor))) { - MOZ_CLIPBOARD_LOG(" has %s", flavor.get()); - results.AppendElement(flavor); - } - } - } + if (!mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()) { + return nullptr; + } + + // If we were the last ones to put something on the native clipboard, then + // just use the cached transferable. Otherwise clear it because it isn't + // relevant any more. + ClipboardCache* clipboardCache = GetClipboardCacheIfValid(aClipboardType); + if (!clipboardCache) { + return nullptr; + } + + nsITransferable* cachedTransferable = clipboardCache->GetTransferable(); + MOZ_ASSERT(cachedTransferable); + + nsTArray<nsCString> transferableFlavors; + if (NS_FAILED(cachedTransferable->FlavorsTransferableCanExport( + transferableFlavors))) { + return nullptr; + } - // XXX Do we need to check system clipboard for the flavors that cannot - // be found in cache? - auto asyncGetClipboardData = mozilla::MakeRefPtr<AsyncGetClipboardData>( - aClipboardType, clipboardCache->GetSequenceNumber(), - std::move(results), true, this, aRequestingWindowContext); - aCallback->OnSuccess(asyncGetClipboardData); - return; + nsTArray<nsCString> results; + for (const auto& flavor : aFlavorList) { + for (const auto& transferableFlavor : transferableFlavors) { + // XXX We need special check for image as we always put the + // image as "native" on the clipboard. + if (transferableFlavor.Equals(flavor) || + (transferableFlavor.Equals(kNativeImageMime) && + nsContentUtils::IsFlavorImage(flavor))) { + MOZ_CLIPBOARD_LOG(" has %s", flavor.get()); + results.AppendElement(flavor); } } + } - // At this point we can't satisfy the request from cache data so let's look - // for things other people put on the system clipboard. + // XXX Do we need to check system clipboard for the flavors that cannot + // be found in cache? + return mozilla::MakeAndAddRef<AsyncGetClipboardData>( + aClipboardType, clipboardCache->GetSequenceNumber(), std::move(results), + true /* aFromCache */, this, aRequestingWindowContext); +} + +void nsBaseClipboard::AsyncGetDataInternal( + const nsTArray<nsCString>& aFlavorList, int32_t aClipboardType, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncClipboardGetCallback* aCallback) { + MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); + + if (nsCOMPtr<nsIAsyncGetClipboardData> asyncGetClipboardData = + MaybeCreateGetRequestFromClipboardCache(aFlavorList, aClipboardType, + aRequestingWindowContext)) { + aCallback->OnSuccess(asyncGetClipboardData); + return; } + // At this point we can't satisfy the request from cache data so let's + // look for things other people put on the system clipboard. MaybeRetryGetAvailableFlavors(aFlavorList, aClipboardType, aCallback, kGetAvailableFlavorsRetryCount, aRequestingWindowContext); } +NS_IMETHODIMP nsBaseClipboard::GetDataSnapshotSync( + const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncGetClipboardData** _retval) { + MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); + + *_retval = nullptr; + + if (aFlavorList.IsEmpty()) { + return NS_ERROR_INVALID_ARG; + } + + if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) { + MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__, + aWhichClipboard); + return NS_ERROR_FAILURE; + } + + if (nsCOMPtr<nsIAsyncGetClipboardData> asyncGetClipboardData = + MaybeCreateGetRequestFromClipboardCache(aFlavorList, aWhichClipboard, + aRequestingWindowContext)) { + asyncGetClipboardData.forget(_retval); + return NS_OK; + } + + auto sequenceNumberOrError = + GetNativeClipboardSequenceNumber(aWhichClipboard); + if (sequenceNumberOrError.isErr()) { + MOZ_CLIPBOARD_LOG("%s: unable to get sequence number for clipboard %d.", + __FUNCTION__, aWhichClipboard); + return sequenceNumberOrError.unwrapErr(); + } + + nsTArray<nsCString> results; + for (const auto& flavor : aFlavorList) { + auto resultOrError = HasNativeClipboardDataMatchingFlavors( + AutoTArray<nsCString, 1>{flavor}, aWhichClipboard); + if (resultOrError.isOk() && resultOrError.unwrap()) { + results.AppendElement(flavor); + } + } + + *_retval = + mozilla::MakeAndAddRef<AsyncGetClipboardData>( + aWhichClipboard, sequenceNumberOrError.unwrap(), std::move(results), + false /* aFromCache */, this, aRequestingWindowContext) + .take(); + return NS_OK; +} + NS_IMETHODIMP nsBaseClipboard::EmptyClipboard(int32_t aWhichClipboard) { MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); diff --git a/widget/nsBaseClipboard.h b/widget/nsBaseClipboard.h index 8f90be725a..ffa68d6240 100644 --- a/widget/nsBaseClipboard.h +++ b/widget/nsBaseClipboard.h @@ -55,6 +55,10 @@ class nsBaseClipboard : public nsIClipboard { mozilla::dom::WindowContext* aRequestingWindowContext, nsIPrincipal* aRequestingPrincipal, nsIAsyncClipboardGetCallback* aCallback) override final; + NS_IMETHOD GetDataSnapshotSync( + const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncGetClipboardData** _retval) override final; NS_IMETHOD EmptyClipboard(int32_t aWhichClipboard) override final; NS_IMETHOD HasDataMatchingFlavors(const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard, @@ -206,6 +210,11 @@ class nsBaseClipboard : public nsIClipboard { nsIPrincipal* aRequestingPrincipal, nsIAsyncClipboardGetCallback* aCallback); + already_AddRefed<nsIAsyncGetClipboardData> + MaybeCreateGetRequestFromClipboardCache( + const nsTArray<nsCString>& aFlavorList, int32_t aClipboardType, + mozilla::dom::WindowContext* aRequestingWindowContext); + // Track the pending request for each clipboard type separately. And only need // to track the latest request for each clipboard type as the prior pending // request will be canceled when a new request is made. diff --git a/widget/nsBaseFilePicker.cpp b/widget/nsBaseFilePicker.cpp index 53be3d80b7..faa78f06f2 100644 --- a/widget/nsBaseFilePicker.cpp +++ b/widget/nsBaseFilePicker.cpp @@ -18,6 +18,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/Components.h" #include "mozilla/StaticPrefs_widget.h" #include "WidgetUtils.h" @@ -156,17 +157,16 @@ nsBaseFilePicker::nsBaseFilePicker() nsBaseFilePicker::~nsBaseFilePicker() = default; -NS_IMETHODIMP nsBaseFilePicker::Init( - mozIDOMWindowProxy* aParent, const nsAString& aTitle, - nsIFilePicker::Mode aMode, - mozilla::dom::BrowsingContext* aBrowsingContext) { - MOZ_ASSERT(aParent, - "Null parent passed to filepicker, no file " +NS_IMETHODIMP nsBaseFilePicker::Init(BrowsingContext* aBrowsingContext, + const nsAString& aTitle, + nsIFilePicker::Mode aMode) { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(aBrowsingContext, + "Null bc passed to filepicker, no file " "picker for you!"); - mParent = nsPIDOMWindowOuter::From(aParent); - - nsCOMPtr<nsIWidget> widget = WidgetUtils::DOMWindowToWidget(mParent); + nsCOMPtr<nsIWidget> widget = + aBrowsingContext->Canonical()->GetParentProcessWidgetContaining(); NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE); mBrowsingContext = aBrowsingContext; @@ -462,6 +462,8 @@ nsBaseFilePicker::GetOkButtonLabel(nsAString& aLabel) { NS_IMETHODIMP nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue) { + MOZ_ASSERT(XRE_IsParentProcess()); + NS_ENSURE_ARG_POINTER(mBrowsingContext); nsCOMPtr<nsIFile> localFile; nsresult rv = GetFile(getter_AddRefs(localFile)); NS_ENSURE_SUCCESS(rv, rv); @@ -471,7 +473,10 @@ nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue) { return NS_OK; } - auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr; + auto* innerParent = + mBrowsingContext->GetDOMWindow() + ? mBrowsingContext->GetDOMWindow()->GetCurrentInnerWindow() + : nullptr; if (!innerParent) { return NS_ERROR_FAILURE; @@ -485,11 +490,19 @@ NS_IMETHODIMP nsBaseFilePicker::GetDomFileOrDirectoryEnumerator( nsISimpleEnumerator** aValue) { nsCOMPtr<nsISimpleEnumerator> iter; + MOZ_ASSERT(XRE_IsParentProcess()); + NS_ENSURE_ARG_POINTER(mBrowsingContext); nsresult rv = GetFiles(getter_AddRefs(iter)); NS_ENSURE_SUCCESS(rv, rv); + auto* parent = mBrowsingContext->GetDOMWindow(); + + if (!parent) { + return NS_ERROR_FAILURE; + } + RefPtr<nsBaseFilePickerEnumerator> retIter = - new nsBaseFilePickerEnumerator(mParent, iter, mMode); + new nsBaseFilePickerEnumerator(parent, iter, mMode); retIter.forget(aValue); return NS_OK; diff --git a/widget/nsBaseFilePicker.h b/widget/nsBaseFilePicker.h index 3d26a1822e..52b8bc7798 100644 --- a/widget/nsBaseFilePicker.h +++ b/widget/nsBaseFilePicker.h @@ -28,9 +28,8 @@ class nsBaseFilePicker : public nsIFilePicker { nsBaseFilePicker(); virtual ~nsBaseFilePicker(); - NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - nsIFilePicker::Mode aMode, - mozilla::dom::BrowsingContext* aBrowsingContext) override; + NS_IMETHOD Init(mozilla::dom::BrowsingContext* aBrowsingContext, + const nsAString& aTitle, nsIFilePicker::Mode aMode) override; NS_IMETHOD IsModeSupported(nsIFilePicker::Mode aMode, JSContext* aCx, mozilla::dom::Promise** aPromise) override; #ifndef XP_WIN @@ -70,9 +69,6 @@ class nsBaseFilePicker : public nsIFilePicker { nsCOMPtr<nsIFile> mDisplayDirectory; nsString mDisplaySpecialDirectory; - nsCOMPtr<nsPIDOMWindowOuter> mParent; - // The BrowsingContext from which the file picker is being opened. - // Used for content analysis. RefPtr<mozilla::dom::BrowsingContext> mBrowsingContext; nsIFilePicker::Mode mMode; nsString mOkButtonLabel; diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 35c580c106..d35afb470a 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -547,6 +547,12 @@ nsIWidget* nsBaseWidget::GetSheetWindowParent(void) { return nullptr; } float nsBaseWidget::GetDPI() { return 96.0f; } +void nsBaseWidget::NotifyAPZOfDPIChange() { + if (mAPZC) { + mAPZC->SetDPI(GetDPI()); + } +} + CSSToLayoutDeviceScale nsIWidget::GetDefaultScale() { double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx(); diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 8b6e8ad0eb..756f2ab7eb 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -354,6 +354,8 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { // theme changes. void NotifyThemeChanged(mozilla::widget::ThemeChangeKind); + void NotifyAPZOfDPIChange(); + #ifdef ACCESSIBILITY // Get the accessible for the window. mozilla::a11y::LocalAccessible* GetRootAccessible(); @@ -365,13 +367,6 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { PopupLevel GetPopupLevel() { return mPopupLevel; } - // return true if this is a popup widget with a native titlebar - bool IsPopupWithTitleBar() const { - return (mWindowType == WindowType::Popup && - mBorderStyle != BorderStyle::Default && - mBorderStyle & BorderStyle::Title); - } - void ReparentNativeWidget(nsIWidget* aNewParent) override {} const SizeConstraints GetSizeConstraints() override; diff --git a/widget/nsClipboardProxy.cpp b/widget/nsClipboardProxy.cpp index 41f285461b..3b27d5954d 100644 --- a/widget/nsClipboardProxy.cpp +++ b/widget/nsClipboardProxy.cpp @@ -174,6 +174,28 @@ NS_IMETHODIMP AsyncGetClipboardDataProxy::GetData( return NS_OK; } +static Result<RefPtr<AsyncGetClipboardDataProxy>, nsresult> +CreateAsyncGetClipboardDataProxy( + ClipboardReadRequestOrError&& aClipboardReadRequestOrError) { + if (aClipboardReadRequestOrError.type() == + ClipboardReadRequestOrError::Tnsresult) { + MOZ_ASSERT(NS_FAILED(aClipboardReadRequestOrError.get_nsresult())); + return Err(aClipboardReadRequestOrError.get_nsresult()); + } + + ClipboardReadRequest& request = + aClipboardReadRequestOrError.get_ClipboardReadRequest(); + auto requestChild = MakeRefPtr<ClipboardReadRequestChild>( + std::move(request.availableTypes())); + if (NS_WARN_IF( + !ContentChild::GetSingleton()->BindPClipboardReadRequestEndpoint( + std::move(request.childEndpoint()), requestChild))) { + return Err(NS_ERROR_FAILURE); + } + + return MakeRefPtr<AsyncGetClipboardDataProxy>(requestChild); +} + } // namespace NS_IMETHODIMP nsClipboardProxy::AsyncGetData( @@ -198,23 +220,16 @@ NS_IMETHODIMP nsClipboardProxy::AsyncGetData( ->Then( GetMainThreadSerialEventTarget(), __func__, /* resolve */ - [callback = nsCOMPtr{aCallback}](const PClipboardReadRequestOrError& - aClipboardReadRequestOrError) { - if (aClipboardReadRequestOrError.type() == - PClipboardReadRequestOrError::Tnsresult) { - MOZ_ASSERT( - NS_FAILED(aClipboardReadRequestOrError.get_nsresult())); - callback->OnError(aClipboardReadRequestOrError.get_nsresult()); + [callback = nsCOMPtr{aCallback}]( + ClipboardReadRequestOrError&& aClipboardReadRequestOrError) { + auto result = CreateAsyncGetClipboardDataProxy( + std::move(aClipboardReadRequestOrError)); + if (result.isErr()) { + callback->OnError(result.unwrapErr()); return; } - auto asyncGetClipboardData = MakeRefPtr<AsyncGetClipboardDataProxy>( - static_cast<ClipboardReadRequestChild*>( - aClipboardReadRequestOrError.get_PClipboardReadRequest() - .AsChild() - .get())); - - callback->OnSuccess(asyncGetClipboardData); + callback->OnSuccess(result.inspect()); }, /* reject */ [callback = nsCOMPtr{aCallback}]( @@ -224,6 +239,35 @@ NS_IMETHODIMP nsClipboardProxy::AsyncGetData( return NS_OK; } +NS_IMETHODIMP nsClipboardProxy::GetDataSnapshotSync( + const nsTArray<nsCString>& aFlavorList, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, + nsIAsyncGetClipboardData** _retval) { + *_retval = nullptr; + + if (aFlavorList.IsEmpty()) { + return NS_ERROR_INVALID_ARG; + } + + if (!nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)) { + MOZ_CLIPBOARD_LOG("%s: clipboard %d is not supported.", __FUNCTION__, + aWhichClipboard); + return NS_ERROR_FAILURE; + } + + ContentChild* contentChild = ContentChild::GetSingleton(); + ClipboardReadRequestOrError requestOrError; + contentChild->SendGetClipboardDataSnapshotSync( + aFlavorList, aWhichClipboard, aRequestingWindowContext, &requestOrError); + auto result = CreateAsyncGetClipboardDataProxy(std::move(requestOrError)); + if (result.isErr()) { + return result.unwrapErr(); + } + + result.unwrap().forget(_retval); + return NS_OK; +} + NS_IMETHODIMP nsClipboardProxy::EmptyClipboard(int32_t aWhichClipboard) { ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard); diff --git a/widget/nsFilePickerProxy.cpp b/widget/nsFilePickerProxy.cpp index 8777f338cb..b71ad0920e 100644 --- a/widget/nsFilePickerProxy.cpp +++ b/widget/nsFilePickerProxy.cpp @@ -25,19 +25,19 @@ nsFilePickerProxy::nsFilePickerProxy() nsFilePickerProxy::~nsFilePickerProxy() = default; NS_IMETHODIMP -nsFilePickerProxy::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - nsIFilePicker::Mode aMode, - BrowsingContext* aBrowsingContext) { - BrowserChild* browserChild = BrowserChild::GetFrom(aParent); +nsFilePickerProxy::Init(BrowsingContext* aBrowsingContext, + const nsAString& aTitle, nsIFilePicker::Mode aMode) { + BrowserChild* browserChild = + BrowserChild::GetFrom(aBrowsingContext->GetDocShell()); if (!browserChild) { return NS_ERROR_FAILURE; } - mParent = nsPIDOMWindowOuter::From(aParent); - + mBrowsingContext = aBrowsingContext; mMode = aMode; - browserChild->SendPFilePickerConstructor(this, aTitle, aMode); + browserChild->SendPFilePickerConstructor(this, aTitle, aMode, + aBrowsingContext); mIPCActive = true; return NS_OK; @@ -155,8 +155,9 @@ nsFilePickerProxy::Close() { mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__( const MaybeInputData& aData, const nsIFilePicker::ResultCode& aResult) { - nsPIDOMWindowInner* inner = - mParent ? mParent->GetCurrentInnerWindow() : nullptr; + auto* inner = mBrowsingContext->GetDOMWindow() + ? mBrowsingContext->GetDOMWindow()->GetCurrentInnerWindow() + : nullptr; if (NS_WARN_IF(!inner)) { return IPC_OK(); diff --git a/widget/nsFilePickerProxy.h b/widget/nsFilePickerProxy.h index 1b6aef13ed..9f12ded3ab 100644 --- a/widget/nsFilePickerProxy.h +++ b/widget/nsFilePickerProxy.h @@ -34,9 +34,8 @@ class nsFilePickerProxy : public nsBaseFilePicker, NS_DECL_ISUPPORTS // nsIFilePicker (less what's in nsBaseFilePicker) - NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - nsIFilePicker::Mode aMode, - mozilla::dom::BrowsingContext* aBrowsingContext) override; + NS_IMETHOD Init(mozilla::dom::BrowsingContext* aBrowsingContext, + const nsAString& aTitle, nsIFilePicker::Mode aMode) override; NS_IMETHOD AppendFilter(const nsAString& aTitle, const nsAString& aFilter) override; NS_IMETHOD GetCapture(nsIFilePicker::CaptureTarget* aCapture) override; diff --git a/widget/nsIClipboard.idl b/widget/nsIClipboard.idl index 5ed2d22600..a34f0f9298 100644 --- a/widget/nsIClipboard.idl +++ b/widget/nsIClipboard.idl @@ -178,6 +178,31 @@ interface nsIClipboard : nsISupports in nsIPrincipal aRequestingPrincipal, in nsIAsyncClipboardGetCallback aCallback); + /** + * Requests getting data from the native clipboard. This does not actually + * retreive the data, but returns a nsIAsyncGetClipboardData contains + * current avaiable data formats. If the native clipboard is updated, either + * by us or other application, the existing nsIAsyncGetClipboardData becomes + * invalid. + * + * @param aFlavorList + * Specific data formats ('flavors') that can be retrieved from the + * clipboard. + * @param aWhichClipboard + * Specifies the clipboard to which this operation applies. + * @param aRequestingWindowContext [optional] + * The window context window that is requesting the clipboard, which is + * used for content analysis. Passing null means that the content is + * exempt from content analysis. (for example, scripted clipboard read by + * system code) This parameter should not be null when calling this from a + * content process. + * @return nsIAsyncSetClipboardData if successful. + * @throws if the request can not be made. + */ + nsIAsyncGetClipboardData getDataSnapshotSync(in Array<ACString> aFlavorList, + in long aWhichClipboard, + [optional] in WindowContext aRequestingWindowContext); + /** * This empties the clipboard and notifies the clipboard owner. * This empties the "logical" clipboard. It does not clear the native clipboard. diff --git a/widget/nsIFilePicker.idl b/widget/nsIFilePicker.idl index e9bdedfd42..db5ac9f7bd 100644 --- a/widget/nsIFilePicker.idl +++ b/widget/nsIFilePicker.idl @@ -66,19 +66,13 @@ interface nsIFilePicker : nsISupports * Initialize the file picker widget. The file picker is not valid until this * method is called. * - * @param parent mozIDOMWindow parent. This dialog will be dependent - * on this parent. parent must be non-null. - * @param title The title for the file widget - * @param mode load, save, or get folder - * @param browsingContext [optional] - * The context in which the file picker is being shown. This is - * used for content analysis and can be omitted if chrome is - * showing the file picker. - */ - void init(in mozIDOMWindowProxy parent, - in AString title, - in nsIFilePicker_Mode mode, - [optional] in BrowsingContext browsingContext); + * @param browsingContext The context in which the file picker is being + * shown, must be non-null. + * @param title The title for the file widget + * @param mode load, save, or get folder + * + */ + void init(in BrowsingContext browsingContext, in AString title, in nsIFilePicker_Mode mode); /** * Returns a Promise that resolves to true if the passed nsIFilePicker mode diff --git a/widget/nsIGfxInfo.idl b/widget/nsIGfxInfo.idl index 4f4f0b7e1e..0df8696da7 100644 --- a/widget/nsIGfxInfo.idl +++ b/widget/nsIGfxInfo.idl @@ -19,6 +19,8 @@ interface nsIGfxInfo : nsISupports readonly attribute AString AzureCanvasBackend; readonly attribute AString AzureContentBackend; readonly attribute boolean usingGPUProcess; + readonly attribute boolean usingRemoteCanvas; + readonly attribute boolean usingAcceleratedCanvas; readonly attribute boolean hasBattery; readonly attribute AString DWriteVersion; readonly attribute AString cleartypeParameters; @@ -215,8 +217,12 @@ interface nsIGfxInfo : nsISupports const long FEATURE_VIDEO_SOFTWARE_OVERLAY = 46; /* Whether WebGL is allowed to use hardware rendering, otherwise software fallbacks. */ const long FEATURE_WEBGL_USE_HARDWARE = 47; + /* Whether overlay is allowed to VideoProcessor-HDR on SDR content */ + const long FEATURE_OVERLAY_VP_AUTO_HDR = 48; + /* Whether overlay is allowed to VideoProcessor Super Resolution */ + const long FEATURE_OVERLAY_VP_SUPER_RESOLUTION = 49; /* the maximum feature value. */ - const long FEATURE_MAX_VALUE = FEATURE_WEBGL_USE_HARDWARE; + const long FEATURE_MAX_VALUE = FEATURE_OVERLAY_VP_SUPER_RESOLUTION; /* * A set of return values from GetFeatureStatus diff --git a/widget/nsIPrintDialogService.idl b/widget/nsIPrintDialogService.idl index 1c79ddd76b..b1e318b948 100644 --- a/widget/nsIPrintDialogService.idl +++ b/widget/nsIPrintDialogService.idl @@ -56,11 +56,3 @@ interface nsIPrintDialogService : nsISupports in nsIPrintSettings aPrintSettings); }; - -%{C++ -#define NS_PRINTDIALOGSERVICE_IID \ - {0x88af6712, 0xa9fd, 0x4393, { 0x9a, 0xf3, 0x3f, 0xfb, 0xb1, 0xf2, 0xca, 0xaf}} - -#define NS_PRINTDIALOGSERVICE_CONTRACTID \ - ("@mozilla.org/widget/printdialog-service;1") -%} diff --git a/widget/nsIPrintSettingsService.idl b/widget/nsIPrintSettingsService.idl index 3b33724c34..7e57c6766e 100644 --- a/widget/nsIPrintSettingsService.idl +++ b/widget/nsIPrintSettingsService.idl @@ -143,9 +143,3 @@ interface nsIPrintSettingsService : nsISupports in nsIPrintSettings aPrintSettings); }; - -%{C++ -// {841387C8-72E6-484b-9296-BF6EEA80D58A} -#define NS_PRINTSETTINGSSERVICE_IID \ - {0x841387c8, 0x72e6, 0x484b, { 0x92, 0x96, 0xbf, 0x6e, 0xea, 0x80, 0xd5, 0x8a}} -%} diff --git a/widget/nsISound.idl b/widget/nsISound.idl index 8b4a2f29c0..c3aca6674d 100644 --- a/widget/nsISound.idl +++ b/widget/nsISound.idl @@ -23,10 +23,6 @@ interface nsISound : nsISupports /** * In some situations, playEventSound will be called. Then, each * implementations will play a system sound for the event if it's necessary. - * - * NOTE: Don't change these values because they are used in - * nsPIPromptService.idl. So, if they are changed, that makes big impact for - * the embedders. */ const unsigned long EVENT_NEW_MAIL_RECEIVED = 0; const unsigned long EVENT_ALERT_DIALOG_OPEN = 1; diff --git a/widget/nsNativeTheme.cpp b/widget/nsNativeTheme.cpp index 2484f802cd..6900414ff3 100644 --- a/widget/nsNativeTheme.cpp +++ b/widget/nsNativeTheme.cpp @@ -57,6 +57,12 @@ NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed) aAppearance == StyleAppearance::ButtonArrowPrevious || aAppearance == StyleAppearance::ButtonArrowNext || aAppearance == StyleAppearance::ButtonArrowUp || +#ifdef MOZ_WIDGET_GTK + aAppearance == StyleAppearance::MozWindowButtonClose || + aAppearance == StyleAppearance::MozWindowButtonMinimize || + aAppearance == StyleAppearance::MozWindowButtonRestore || + aAppearance == StyleAppearance::MozWindowButtonMaximize || +#endif aAppearance == StyleAppearance::ButtonArrowDown) { aFrame = aFrame->GetParent(); frameContent = aFrame->GetContent(); diff --git a/widget/nsPrinterBase.cpp b/widget/nsPrinterBase.cpp index 7e01b9e12f..0c8f71d574 100644 --- a/widget/nsPrinterBase.cpp +++ b/widget/nsPrinterBase.cpp @@ -10,6 +10,7 @@ #include "nsIPrintSettings.h" #include "nsPrintSettingsService.h" #include "PrintBackgroundTask.h" +#include "mozilla/EnumeratedArrayCycleCollection.h" #include "mozilla/dom/Promise.h" using namespace mozilla; @@ -83,25 +84,6 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPrinterInfo) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPrinterInfo) -template <typename Index, Index Size, typename Value> -inline void ImplCycleCollectionTraverse( - nsCycleCollectionTraversalCallback& aCallback, - EnumeratedArray<Index, Size, Value>& aArray, const char* aName, - uint32_t aFlags = 0) { - aFlags |= CycleCollectionEdgeNameArrayFlag; - for (Value& element : aArray) { - ImplCycleCollectionTraverse(aCallback, element, aName, aFlags); - } -} - -template <typename Index, Index Size, typename Value> -inline void ImplCycleCollectionUnlink( - EnumeratedArray<Index, Size, Value>& aArray) { - for (Value& element : aArray) { - ImplCycleCollectionUnlink(element); - } -} - namespace mozilla { template <> @@ -143,8 +125,8 @@ nsresult nsPrinterBase::AsyncPromiseAttributeGetter( BackgroundTask<T, Args...> aBackgroundTask, Args... aArgs) { MOZ_ASSERT(NS_IsMainThread()); - static constexpr EnumeratedArray<AsyncAttribute, AsyncAttribute::Last, - nsLiteralCString> + static constexpr EnumeratedArray<AsyncAttribute, nsLiteralCString, + size_t(AsyncAttribute::Last)> attributeKeys{"SupportsDuplex"_ns, "SupportsColor"_ns, "SupportsMonochrome"_ns, "SupportsCollation"_ns, "PrinterInfo"_ns}; diff --git a/widget/nsPrinterBase.h b/widget/nsPrinterBase.h index abd4f82705..8ce1f8a90d 100644 --- a/widget/nsPrinterBase.h +++ b/widget/nsPrinterBase.h @@ -104,8 +104,8 @@ class nsPrinterBase : public nsIPrinter { const mozilla::gfx::SizeDouble& aSize) const; private: - mozilla::EnumeratedArray<AsyncAttribute, AsyncAttribute::Last, - RefPtr<Promise>> + mozilla::EnumeratedArray<AsyncAttribute, RefPtr<Promise>, + size_t(AsyncAttribute::Last)> mAsyncAttributePromises; // List of built-in, commonly used paper sizes. const RefPtr<const mozilla::CommonPaperInfoArray> mCommonPaperInfo; diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index 05b8952b04..9eabbd84b6 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -57,7 +57,7 @@ using FontID = mozilla::LookAndFeel::FontID; template <typename Index, typename Value, Index kEnd> class EnumeratedCache { - mozilla::EnumeratedArray<Index, kEnd, Value> mEntries; + mozilla::EnumeratedArray<Index, Value, size_t(kEnd)> mEntries; std::bitset<size_t(kEnd)> mValidity; public: @@ -161,7 +161,6 @@ static const char sIntPrefs[][45] = { "ui.SpellCheckerUnderlineStyle", "ui.menuBarDrag", "ui.scrollbarButtonAutoRepeatBehavior", - "ui.tooltipDelay", "ui.swipeAnimationEnabled", "ui.scrollbarDisplayOnMouseMove", "ui.scrollbarFadeBeginDelay", @@ -185,6 +184,7 @@ static const char sIntPrefs[][45] = { "ui.systemScrollbarSize", "ui.touchDeviceSupportPresent", "ui.titlebarRadius", + "ui.titlebarButtonSpacing", "ui.dynamicRange", "ui.videoDynamicRange", "ui.panelAnimations", diff --git a/widget/tests/browser/browser_test_clipboard_contextmenu.js b/widget/tests/browser/browser_test_clipboard_contextmenu.js index 4d268d5be4..80e8c33282 100644 --- a/widget/tests/browser/browser_test_clipboard_contextmenu.js +++ b/widget/tests/browser/browser_test_clipboard_contextmenu.js @@ -63,7 +63,7 @@ async function clipboardAsyncGetData(aBrowser, aClipboardType) { "nsIAsyncClipboardGetCallback", ]), // nsIAsyncClipboardGetCallback - onSuccess: aAsyncGetClipboardData => { + onSuccess: () => { resolve(); }, onError: aResult => { diff --git a/widget/tests/browser/browser_test_clipboardcache.js b/widget/tests/browser/browser_test_clipboardcache.js index 8cb6adb8b5..64deb99608 100644 --- a/widget/tests/browser/browser_test_clipboardcache.js +++ b/widget/tests/browser/browser_test_clipboardcache.js @@ -103,6 +103,10 @@ async function testCopyPaste(isPrivate) { document.execCommand("paste"); return pastePromise; }); + + // Don't use Assert.strictEqual here because the test starts timing out, + // because the logging creates lots of copies of this very huge string. + // eslint-disable-next-line mozilla/no-comparison-or-assignment-inside-ok ok(readStr === Ipsum, "Read what we pasted"); if (isPrivate) { diff --git a/widget/tests/browser/browser_test_swipe_gesture.js b/widget/tests/browser/browser_test_swipe_gesture.js index 0ac85d80c8..0f45672206 100644 --- a/widget/tests/browser/browser_test_swipe_gesture.js +++ b/widget/tests/browser/browser_test_swipe_gesture.js @@ -123,9 +123,9 @@ add_task(async () => { computedOpacity = window .getComputedStyle(gHistorySwipeAnimation._prevBox) .getPropertyValue("opacity"); - ok(computedOpacity == 1, "computed opacity of prevbox is 1"); + Assert.equal(computedOpacity, 1, "computed opacity of prevbox is 1"); opacity = gHistorySwipeAnimation._prevBox.style.opacity; - ok(opacity == 0, "element.style opacity of prevbox 0"); + Assert.equal(opacity, 0, "element.style opacity of prevbox 0"); if (isTranslatingIcon) { // We don't have a transition for translate property so that we still have @@ -232,9 +232,9 @@ add_task(async () => { computedOpacity = window .getComputedStyle(gHistorySwipeAnimation._prevBox) .getPropertyValue("opacity"); - ok(computedOpacity == 1, "computed opacity of prevbox is 1"); + Assert.equal(computedOpacity, 1, "computed opacity of prevbox is 1"); opacity = gHistorySwipeAnimation._prevBox.style.opacity; - ok(opacity == 0, "element.style opacity of prevbox 0"); + Assert.equal(opacity, 0, "element.style opacity of prevbox 0"); // Make sure the gesture triggered going back to the previous page. await Promise.all([startLoadingPromise, stoppedLoadingPromise]); @@ -317,9 +317,9 @@ add_task(async () => { let computedOpacity = window .getComputedStyle(gHistorySwipeAnimation._prevBox) .getPropertyValue("opacity"); - ok(computedOpacity == 1, "computed opacity of prevbox is 1"); + Assert.equal(computedOpacity, 1, "computed opacity of prevbox is 1"); let opacity = gHistorySwipeAnimation._prevBox.style.opacity; - ok(opacity == 0, "element.style opacity of prevbox 0"); + Assert.equal(opacity, 0, "element.style opacity of prevbox 0"); // Make sure the gesture triggered going back to the previous page. await Promise.all([startLoadingPromise, stoppedLoadingPromise]); @@ -343,7 +343,7 @@ add_task(async () => { } numTries--; } - ok(numTries > 0, "never ran the test"); + Assert.greater(numTries, 0, "never ran the test"); await SpecialPowers.popPrefEnv(); }); @@ -576,7 +576,7 @@ add_task(async () => { await panLeftToRightBegin(tab.linkedBrowser, 100, 100, 100); - ok(gHistorySwipeAnimation._prevBox != null, "should have prevbox"); + Assert.notEqual(gHistorySwipeAnimation._prevBox, null, "should have prevbox"); let transitionCancelPromise = new Promise(resolve => { gHistorySwipeAnimation._prevBox.addEventListener( "transitioncancel", @@ -635,7 +635,7 @@ add_task(async () => { await panRightToLeftBegin(tab.linkedBrowser, 100, 100, 100); - ok(gHistorySwipeAnimation._nextBox != null, "should have nextbox"); + Assert.notEqual(gHistorySwipeAnimation._nextBox, null, "should have nextbox"); transitionCancelPromise = new Promise(resolve => { gHistorySwipeAnimation._nextBox.addEventListener( "transitioncancel", @@ -1131,7 +1131,7 @@ add_task(async () => { // Set up an APZ aware event listener and... await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { - content.document.documentElement.addEventListener("wheel", e => {}, { + content.document.documentElement.addEventListener("wheel", () => {}, { passive: false, }); await content.wrappedJSObject.promiseApzFlushedRepaints(); diff --git a/widget/tests/chrome.toml b/widget/tests/chrome.toml index 08d02d3c36..8706a67185 100644 --- a/widget/tests/chrome.toml +++ b/widget/tests/chrome.toml @@ -9,9 +9,6 @@ support-files = [ # Privacy relevant -["test_bug1123480.xhtml"] -skip-if = ["win11_2009 && bits == 32"] - ["test_bug343416.xhtml"] skip-if = ["debug"] @@ -66,8 +63,8 @@ run-if = ["os == 'mac'"] # Cocoa widget test ["test_bug760802.xhtml"] -["test_clipboard_chrome.html"] -support-files = "file_test_clipboard.js" +["test_bug1123480.xhtml"] +skip-if = ["win11_2009 && bits == 32"] ["test_clipboard_asyncGetData_chrome.html"] support-files = "file_test_clipboard_asyncGetData.js" @@ -77,11 +74,20 @@ support-files = "file_test_clipboard_asyncSetData.js" ["test_clipboard_cache_chrome.html"] +["test_clipboard_chrome.html"] +support-files = "file_test_clipboard.js" + +["test_clipboard_getDataSnapshotSync_chrome.html"] +support-files = "file_test_clipboard_getDataSnapshotSync.js" + ["test_clipboard_owner_chrome.html"] ["test_composition_text_querycontent.xhtml"] support-files = ["window_composition_text_querycontent.xhtml"] +["test_ime_focus_with_multiple_contenteditable.html"] +support-files = ["file_ime_state_test_helper.js"] + ["test_ime_state_in_contenteditable_on_readonly_change_in_parent.html"] support-files = [ "file_ime_state_test_helper.js", diff --git a/widget/tests/clipboard_helper.js b/widget/tests/clipboard_helper.js index 96787468fb..76ed2aeb37 100644 --- a/widget/tests/clipboard_helper.js +++ b/widget/tests/clipboard_helper.js @@ -157,11 +157,21 @@ function getClipboardData(aFlavor, aClipboardType) { } } -function asyncGetClipboardData(aClipboardType) { +function getClipboardDataSnapshotSync(aClipboardType) { + return clipboard.getDataSnapshotSync( + ["text/plain", "text/html", "image/png"], + aClipboardType + ); +} + +function asyncGetClipboardData( + aClipboardType, + aFormats = ["text/plain", "text/html", "image/png"] +) { return new Promise((resolve, reject) => { try { clipboard.asyncGetData( - ["text/plain", "text/html", "image/png"], + aFormats, aClipboardType, null, SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(), diff --git a/widget/tests/file_test_clipboard_asyncGetData.js b/widget/tests/file_test_clipboard_asyncGetData.js index e7f2e5f0f1..3ff5700e1a 100644 --- a/widget/tests/file_test_clipboard_asyncGetData.js +++ b/widget/tests/file_test_clipboard_asyncGetData.js @@ -94,7 +94,7 @@ clipboardTypes.forEach(function (type) { () => { ok(false, "asyncClipboardRequestGetData should not success"); }, - e => { + () => { ok(true, "asyncClipboardRequestGetData should reject"); } ); @@ -133,7 +133,7 @@ clipboardTypes.forEach(function (type) { () => { ok(false, "asyncClipboardRequestGetData should not success"); }, - e => { + () => { ok(true, "asyncClipboardRequestGetData should reject"); } ); diff --git a/widget/tests/file_test_clipboard_getDataSnapshotSync.js b/widget/tests/file_test_clipboard_getDataSnapshotSync.js new file mode 100644 index 0000000000..92d7475e9c --- /dev/null +++ b/widget/tests/file_test_clipboard_getDataSnapshotSync.js @@ -0,0 +1,153 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/* import-globals-from clipboard_helper.js */ + +"use strict"; + +clipboardTypes.forEach(function (type) { + if (!clipboard.isClipboardTypeSupported(type)) { + add_task(async function test_clipboard_requestGetData_not_support() { + info(`Test getDataSnapshotSync request throwing on ${type}`); + SimpleTest.doesThrow( + () => clipboard.getDataSnapshotSync(["text/plain"], type), + "Passing unsupported clipboard type should throw" + ); + }); + return; + } + + add_task(async function test_clipboard_getDataSnapshotSync_throw() { + info(`Test getDataSnapshotSync request throwing on ${type}`); + SimpleTest.doesThrow( + () => clipboard.getDataSnapshotSync([], type), + "Passing empty flavor list should throw" + ); + }); + + add_task( + async function test_clipboard_getDataSnapshotSync_no_matched_flavor() { + info(`Test getDataSnapshotSync have no matched flavor on ${type}`); + cleanupAllClipboard(); + is( + getClipboardData("text/plain", type), + null, + "ensure clipboard is empty" + ); + + writeRandomStringToClipboard("text/plain", type); + let request = clipboard.getDataSnapshotSync(["text/html"], type); + isDeeply(request.flavorList, [], "Check flavorList"); + } + ); + + add_task(async function test_empty_data() { + info(`Test getDataSnapshotSync request with empty data on ${type}`); + cleanupAllClipboard(); + is(getClipboardData("text/plain", type), null, "ensure clipboard is empty"); + + let request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, [], "Check flavorList"); + await asyncClipboardRequestGetData(request, "text/plain", true).catch( + () => {} + ); + }); + + add_task(async function test_clipboard_getDataSnapshotSync_after_write() { + info(`Test getDataSnapshotSync request after write on ${type}`); + + let str = writeRandomStringToClipboard("text/plain", type); + let request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, ["text/plain"], "Check flavorList"); + is( + await asyncClipboardRequestGetData(request, "text/plain"), + str, + "Check data" + ); + ok(request.valid, "request should still be valid"); + // Requesting a flavor that is not in the list should throw error. + await asyncClipboardRequestGetData(request, "text/html", true).catch( + () => {} + ); + ok(request.valid, "request should still be valid"); + + // Writing a new data should invalid existing get request. + str = writeRandomStringToClipboard("text/plain", type); + await asyncClipboardRequestGetData(request, "text/plain").then( + () => { + ok(false, "asyncClipboardRequestGetData should not success"); + }, + () => { + ok(true, "asyncClipboardRequestGetData should reject"); + } + ); + ok(!request.valid, "request should no longer be valid"); + + info(`check clipboard data again`); + request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, ["text/plain"], "Check flavorList"); + is( + await asyncClipboardRequestGetData(request, "text/plain"), + str, + "Check data" + ); + + cleanupAllClipboard(); + }); + + add_task(async function test_clipboard_getDataSnapshotSync_after_empty() { + info(`Test getDataSnapshotSync request after empty on ${type}`); + + let str = writeRandomStringToClipboard("text/plain", type); + let request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, ["text/plain"], "Check flavorList"); + is( + await asyncClipboardRequestGetData(request, "text/plain"), + str, + "Check data" + ); + ok(request.valid, "request should still be valid"); + + // Empty clipboard data + emptyClipboardData(type); + is(getClipboardData("text/plain", type), null, "ensure clipboard is empty"); + + await asyncClipboardRequestGetData(request, "text/plain").then( + () => { + ok(false, "asyncClipboardRequestGetData should not success"); + }, + () => { + ok(true, "asyncClipboardRequestGetData should reject"); + } + ); + ok(!request.valid, "request should no longer be valid"); + + info(`check clipboard data again`); + request = getClipboardDataSnapshotSync(type); + isDeeply(request.flavorList, [], "Check flavorList"); + + cleanupAllClipboard(); + }); +}); + +add_task(async function test_clipboard_getDataSnapshotSync_html_data() { + info(`Test getDataSnapshotSync request with html data`); + + const html_str = `<img src="https://example.com/oops">`; + writeStringToClipboard(html_str, "text/html", clipboard.kGlobalClipboard); + + let request = getClipboardDataSnapshotSync(clipboard.kGlobalClipboard); + isDeeply(request.flavorList, ["text/html"], "Check flavorList"); + is( + await asyncClipboardRequestGetData(request, "text/html"), + // On Windows, widget adds extra data into HTML clipboard. + navigator.platform.includes("Win") + ? `<html><body>\n<!--StartFragment-->${html_str}<!--EndFragment-->\n</body>\n</html>` + : html_str, + "Check data" + ); + // Requesting a flavor that is not in the list should throw error. + await asyncClipboardRequestGetData(request, "text/plain", true).catch( + () => {} + ); +}); diff --git a/widget/tests/mochitest.toml b/widget/tests/mochitest.toml index 32dcf5c795..4a4a9d2729 100644 --- a/widget/tests/mochitest.toml +++ b/widget/tests/mochitest.toml @@ -25,12 +25,16 @@ skip-if = [ support-files = ["file_test_clipboard.js"] ["test_clipboard_asyncGetData.html"] -skip-if = ["display == 'wayland'"] # Bug 1864211 +skip-if = ["display == 'wayland'"] # Bug 1879835 support-files = ["file_test_clipboard_asyncGetData.js"] ["test_clipboard_asyncSetData.html"] support-files = ["file_test_clipboard_asyncSetData.js"] +["test_clipboard_getDataSnapshotSync.html"] +skip-if = ["display == 'wayland'"] # Bug 1879835 +support-files = "file_test_clipboard_getDataSnapshotSync.js" + ["test_contextmenu_by_mouse_on_unix.html"] run-if = ["os == 'linux'"] skip-if = ["headless"] # headless widget doesn't dispatch contextmenu event by mouse event. diff --git a/widget/tests/standalone_native_menu_window.xhtml b/widget/tests/standalone_native_menu_window.xhtml index 4155126ad5..36c65b0e2c 100644 --- a/widget/tests/standalone_native_menu_window.xhtml +++ b/widget/tests/standalone_native_menu_window.xhtml @@ -141,13 +141,13 @@ let popupShowingFired = false; let itemActivated = false; - menupopup.addEventListener("popupshowing", function (e) { + menupopup.addEventListener("popupshowing", function () { popupShowingFired = true; let menuitem = document.createElementNS(XUL_NS, "menuitem"); menuitem.setAttribute("label", "detached menu item"); /* eslint-disable-next-line no-shadow */ - menuitem.addEventListener("command", function (e) { + menuitem.addEventListener("command", function () { itemActivated = true; }) menupopup.appendChild(menuitem); diff --git a/widget/tests/test_bug343416.xhtml b/widget/tests/test_bug343416.xhtml index bf4dbbb9b4..32e717d6e5 100644 --- a/widget/tests/test_bug343416.xhtml +++ b/widget/tests/test_bug343416.xhtml @@ -30,7 +30,7 @@ SimpleTest.waitForExplicitFinish(); var idleObserver = { QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), - observe: function _observe(subject, topic, data) + observe: function _observe(subject, topic) { if (topic != "idle") return; diff --git a/widget/tests/test_clipboard_cache_chrome.html b/widget/tests/test_clipboard_cache_chrome.html index 55b6d41589..81d7a652b6 100644 --- a/widget/tests/test_clipboard_cache_chrome.html +++ b/widget/tests/test_clipboard_cache_chrome.html @@ -37,7 +37,12 @@ function testClipboardCache(aClipboardType, aAsync, aIsSupportGetFromCachedTrans addStringToTransferable("text/foo", string, trans); // XXX macOS caches the transferable to implement kSelectionCache type, too, // so it behaves differently than other types. - if (aClipboardType == clipboard.kSelectionCache && !aIsSupportGetFromCachedTransferable) { + if ( + navigator.platform.includes("Mac") && + aClipboardType == clipboard.kSelectionCache && + !aIsSupportGetFromCachedTransferable && + !SpecialPowers.isHeadless + ) { todo_is(getClipboardData("text/foo", aClipboardType), aIsSupportGetFromCachedTransferable ? string : null, `Check text/foo data on clipboard ${aClipboardType}`); @@ -66,7 +71,12 @@ function testClipboardCache(aClipboardType, aAsync, aIsSupportGetFromCachedTrans `Check text/plain data on clipboard ${aClipboardType} again`); // XXX macOS caches the transferable to implement kSelectionCache type, too, // so it behaves differently than other types. - if (aClipboardType == clipboard.kSelectionCache && !aIsSupportGetFromCachedTransferable) { + if ( + navigator.platform.includes("Mac") && + aClipboardType == clipboard.kSelectionCache && + !aIsSupportGetFromCachedTransferable && + !SpecialPowers.isHeadless + ) { todo_is(getClipboardData("text/foo", aClipboardType), aIsSupportGetFromCachedTransferable ? string : null, `Check text/foo data on clipboard ${aClipboardType} again`); @@ -118,7 +128,12 @@ function runClipboardCacheTests(aIsSupportGetFromCachedTransferable) { `Check if there is text/plain flavor on clipboard ${type}`); // XXX macOS caches the transferable to implement kSelectionCache type, too, // so it behaves differently than other types. - if (type == clipboard.kSelectionCache && !aIsSupportGetFromCachedTransferable) { + if ( + navigator.platform.includes("Mac") && + type == clipboard.kSelectionCache && + !aIsSupportGetFromCachedTransferable && + !SpecialPowers.isHeadless + ) { todo_is(clipboard.hasDataMatchingFlavors(["text/foo"], type), aIsSupportGetFromCachedTransferable, `Check if there is text/foo flavor on clipboard ${type}`); @@ -147,7 +162,12 @@ function runClipboardCacheTests(aIsSupportGetFromCachedTransferable) { `Check if there is text/plain flavor on clipboard ${type}`); // XXX macOS caches the transferable to implement kSelectionCache type, too, // so it behaves differently than other types. - if (type == clipboard.kSelectionCache && !aIsSupportGetFromCachedTransferable) { + if ( + navigator.platform.includes("Mac") && + type == clipboard.kSelectionCache && + !aIsSupportGetFromCachedTransferable && + !SpecialPowers.isHeadless + ) { todo_is(clipboard.hasDataMatchingFlavors(["text/foo"], type), aIsSupportGetFromCachedTransferable, `Check if there is text/foo flavor on clipboard ${type}`); @@ -212,6 +232,33 @@ function runClipboardCacheTests(aIsSupportGetFromCachedTransferable) { await testClipboardData(await asyncGetClipboardData(type), clipboardData); }); + add_task(async function test_flavorList_order() { + info(`test_flavorList_order with pref ` + + `${aIsSupportGetFromCachedTransferable ? "enabled" : "disabled"}`); + + const trans = generateNewTransferable("text/plain", generateRandomString()); + addStringToTransferable("text/html", `<div>${generateRandomString()}</div>`, trans); + + info(`Writedata to clipboard ${type}`); + clipboard.setData(trans, null, type); + + // Read with reverse order. + let flavors = trans.flavorsTransferableCanExport().reverse(); + let request = await asyncGetClipboardData(type, flavors); + // XXX Not all clipboard type supports html format, e.g. kFindClipboard + // on macOS only support writing text/plain. + if ( + navigator.platform.includes("Mac") && + type == clipboard.kFindClipboard && + !aIsSupportGetFromCachedTransferable && + !SpecialPowers.isHeadless + ) { + isDeeply(request.flavorList, ["text/plain"], "check flavor orders"); + } else { + isDeeply(request.flavorList, flavors, "check flavor orders"); + } + }); + // Test sync set clipboard data. testClipboardCache(type, false, aIsSupportGetFromCachedTransferable); diff --git a/widget/tests/test_clipboard_getDataSnapshotSync.html b/widget/tests/test_clipboard_getDataSnapshotSync.html new file mode 100644 index 0000000000..e800f6a1b0 --- /dev/null +++ b/widget/tests/test_clipboard_getDataSnapshotSync.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1858627 +--> +<head> +<title>Test for Bug 1858627</title> +<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="clipboard_helper.js"></script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<!-- Tests are in file_test_clipboard_getDataSnapshotSync.js --> +<script src="file_test_clipboard_getDataSnapshotSync.js"></script> +</body> +</html> diff --git a/widget/tests/test_clipboard_getDataSnapshotSync_chrome.html b/widget/tests/test_clipboard_getDataSnapshotSync_chrome.html new file mode 100644 index 0000000000..fbc283ccb3 --- /dev/null +++ b/widget/tests/test_clipboard_getDataSnapshotSync_chrome.html @@ -0,0 +1,19 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1858627 +--> +<head> +<title>Test for Bug 1858627</title> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> +<script src="clipboard_helper.js"></script> +</head> +<body> +<p id="display"></p> +<div id="content" style="display: none"></div> +<pre id="test"></pre> +<!-- Tests are in file_test_clipboard_getDataSnapshotSync.js --> +<script src="file_test_clipboard_getDataSnapshotSync.js"></script> +</body> +</html> diff --git a/widget/tests/test_clipboard_owner_chrome.html b/widget/tests/test_clipboard_owner_chrome.html index 4759b2b14c..2991646616 100644 --- a/widget/tests/test_clipboard_owner_chrome.html +++ b/widget/tests/test_clipboard_owner_chrome.html @@ -20,7 +20,7 @@ function testClipboardOwner(aClipboardType, aAsync) { const clipboardOwner = { QueryInterface: ChromeUtils.generateQI(["nsIClipboardOwner"]), // nsIClipboardOwner - LosingOwnership(aTransferable) { + LosingOwnership() { losingOwnership = true; }, }; diff --git a/widget/tests/test_contextmenu_by_mouse_on_unix.html b/widget/tests/test_contextmenu_by_mouse_on_unix.html index 2b1f643dfc..04bca8116c 100644 --- a/widget/tests/test_contextmenu_by_mouse_on_unix.html +++ b/widget/tests/test_contextmenu_by_mouse_on_unix.html @@ -60,7 +60,7 @@ async function process_contextmenu_event({ isMousedown, preventEvent }) { is(count++, 1, "The second event is contextmenu"); e.preventDefault(); }, { once: true }); - target.addEventListener("mouseup", e => { + target.addEventListener("mouseup", () => { is(count++, 2, "The third event is mouseup"); resolve(); }, { once: true} ); diff --git a/widget/tests/test_ime_focus_with_multiple_contenteditable.html b/widget/tests/test_ime_focus_with_multiple_contenteditable.html new file mode 100644 index 0000000000..6b435a1a2e --- /dev/null +++ b/widget/tests/test_ime_focus_with_multiple_contenteditable.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1872863 +--> +<head> +<title>Test IME state and focus with multiple contenteditable elements</title> + <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + <script src="file_ime_state_test_helper.js"></script> +<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> + +<style> +#div0 { + width: 200px; + height: 50px; +} + +#div3 { + background-color: blue; + width: 200px; + height: 100px; +} + +#div4 { + width: 200px; + height: 50px; +} +</style> +<script> +add_task(async function test_modify_contenteditable_on_focused_element() { + await SimpleTest.promiseFocus(); + + const tipWrapper = new TIPWrapper(window); + ok(tipWrapper.isAvailable(), "TextInputProcessor should've been initialized"); + + const div1 = document.getElementById("div1"); + const div2 = document.getElementById("div2"); + const div3 = document.getElementById("div3"); + + div1.addEventListener("mousedown", () => { + div2.contentEditable = true; + }); + div3.addEventListener("mousedown", e => { + div2.contentEditable = false; + e.preventDefault(); + }); + + // Set focus by mouse then contenteditable becomes true by script. + + const promiseFocus = new Promise(resolve => { + div2.addEventListener("focus", resolve, { once: true }); + }); + synthesizeMouseAtCenter(div1, {}); + await promiseFocus; + + is( + SpecialPowers.DOMWindowUtils.IMEStatus, + SpecialPowers.DOMWindowUtils.IME_STATUS_ENABLED, + "IMEStatus is enabled on contenteditable=true" + ); + ok(tipWrapper.IMEHasFocus, "IME has focus"); + + // Move focus by mouse then contenteditable becomes false by script. + + const promiseMouseUp = new Promise(resolve => { + div3.addEventListener("mouseup", resolve, { once: true }); + }); + synthesizeMouseAtCenter(div3, {}); + await promiseMouseUp; + + is( + SpecialPowers.DOMWindowUtils.IMEStatus, + SpecialPowers.DOMWindowUtils.IME_STATUS_DISABLED, + "IMEStatus is disabled on contenteditable=false" + ); + ok(!tipWrapper.IMEHasFocus, "IME losts focus after contenteditable=false"); + + // contenteditable changes to true on focused element. + + const promiseMouseUp2 = new Promise(resolve => { + div1.addEventListener("mouseup", resolve, { once: true }); + }); + synthesizeMouseAtCenter(div1, {}); + await promiseMouseUp2; + + is( + SpecialPowers.DOMWindowUtils.IMEStatus, + SpecialPowers.DOMWindowUtils.IME_STATUS_ENABLED, + "IMEStatus is enabled on contenteditable=true" + ); + + ok(tipWrapper.IMEHasFocus, "IME has focus after contenteditable=true again"); +}); +</script> +</head> +<body> +<div id="div0"><div id="div1"> +<div id="div2" contenteditable="false"><div>foo</div></div> +</div></div> +<div id="div3"></div> +<div id="div4" contenteditable></div> +</body> +</html> diff --git a/widget/tests/test_ime_state_others_in_parent.html b/widget/tests/test_ime_state_others_in_parent.html index e6ae0ab272..e71ef90556 100644 --- a/widget/tests/test_ime_state_others_in_parent.html +++ b/widget/tests/test_ime_state_others_in_parent.html @@ -96,7 +96,7 @@ function runTestPasswordFieldOnDialog() { WindowObserver.prototype = { QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), - observe(subject, topic, data) { + observe(subject, topic) { if (topic === "domwindowopened") { ok(true, "dialog window is created"); dialog = subject; diff --git a/widget/tests/test_key_event_counts.xhtml b/widget/tests/test_key_event_counts.xhtml index 6eda6a52fb..e50164ddfd 100644 --- a/widget/tests/test_key_event_counts.xhtml +++ b/widget/tests/test_key_event_counts.xhtml @@ -24,7 +24,7 @@ var gKeyPressEventCount = 0; var gKeyDownEventCount = 0; - function onKeyDown(e) + function onKeyDown() { gKeyDownEventCount++; } diff --git a/widget/tests/test_keycodes.xhtml b/widget/tests/test_keycodes.xhtml index aea6aaae98..5adb07b4d8 100644 --- a/widget/tests/test_keycodes.xhtml +++ b/widget/tests/test_keycodes.xhtml @@ -519,14 +519,14 @@ function* runKeyEventTests() document.addEventListener("keypress", onKeyEvent); document.addEventListener("keyup", onKeyEvent); // Prevent almost all shortcut key handlers. - SpecialPowers.addSystemEventListener(document, "keypress", consumer, true); + SpecialPowers.wrap(document).addEventListener("keypress", consumer, { capture: true, mozSystemGroup: true }); function cleanup() { document.removeEventListener("keydown", onKeyEvent); document.removeEventListener("keypress", onKeyEvent); document.removeEventListener("keyup", onKeyEvent); - SpecialPowers.removeSystemEventListener(document, "keypress", consumer, true); + SpecialPowers.wrap(document).removeEventListener("keypress", consumer, { capture: true, mozSystemGroup: true }); } function* testKeysOnMac() @@ -4802,7 +4802,7 @@ function* runAccessKeyTests() var button = document.getElementById("button"); var activationCount; - function onClick(e) + function onClick() { ++activationCount; } @@ -4968,10 +4968,10 @@ function* runXULKeyTests() buttonParent.addEventListener("keypress", onKeyInDefaultEventGroup, true); buttonParent.addEventListener("keydown", onKeyInDefaultEventGroup); buttonParent.addEventListener("keypress", onKeyInDefaultEventGroup); - SpecialPowers.addSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, true); - SpecialPowers.addSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, true); - SpecialPowers.addSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, false); - SpecialPowers.addSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, false); + SpecialPowers.wrap(buttonParent).addEventListener("keydown", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(buttonParent).addEventListener("keypress", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(buttonParent).addEventListener("keydown", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); + SpecialPowers.wrap(buttonParent).addEventListener("keypress", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); function finializeKeyElementTest() { @@ -4979,10 +4979,10 @@ function* runXULKeyTests() buttonParent.removeEventListener("keypress", onKeyInDefaultEventGroup, true); buttonParent.removeEventListener("keydown", onKeyInDefaultEventGroup); buttonParent.removeEventListener("keypress", onKeyInDefaultEventGroup); - SpecialPowers.removeSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, true); - SpecialPowers.removeSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, true); - SpecialPowers.removeSystemEventListener(buttonParent, "keydown", onKeyInSystemEventGroup, false); - SpecialPowers.removeSystemEventListener(buttonParent, "keypress", onKeyInSystemEventGroup, false); + SpecialPowers.wrap(buttonParent).removeEventListener("keydown", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(buttonParent).removeEventListener("keypress", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(buttonParent).removeEventListener("keydown", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); + SpecialPowers.wrap(buttonParent).removeEventListener("keypress", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); } // If aKeyElement is empty string, this function tests if the event kicks @@ -5218,10 +5218,10 @@ function* runReservedKeyTests() contents[i].addEventListener("keypress", onKeyInDefaultEventGroup, true); contents[i].addEventListener("keydown", onKeyInDefaultEventGroup); contents[i].addEventListener("keypress", onKeyInDefaultEventGroup); - SpecialPowers.addSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, true); - SpecialPowers.addSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, true); - SpecialPowers.addSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, false); - SpecialPowers.addSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, false); + SpecialPowers.wrap(contents[i]).addEventListener("keydown", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(contents[i]).addEventListener("keypress", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(contents[i]).addEventListener("keydown", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); + SpecialPowers.wrap(contents[i]).addEventListener("keypress", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); } var keyEvents = []; @@ -5256,10 +5256,10 @@ function* runReservedKeyTests() contents[i].removeEventListener("keypress", onKeyInDefaultEventGroup, true); contents[i].removeEventListener("keydown", onKeyInDefaultEventGroup); contents[i].removeEventListener("keypress", onKeyInDefaultEventGroup); - SpecialPowers.removeSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, true); - SpecialPowers.removeSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, true); - SpecialPowers.removeSystemEventListener(contents[i], "keydown", onKeyInSystemEventGroup, false); - SpecialPowers.removeSystemEventListener(contents[i], "keypress", onKeyInSystemEventGroup, false); + SpecialPowers.wrap(contents[i]).removeEventListener("keydown", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(contents[i]).removeEventListener("keypress", onKeyInSystemEventGroup, { capture: true, mozSystemGroup: true }); + SpecialPowers.wrap(contents[i]).removeEventListener("keydown", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); + SpecialPowers.wrap(contents[i]).removeEventListener("keypress", onKeyInSystemEventGroup, { capture: false, mozSystemGroup: true }); } } diff --git a/widget/tests/test_native_key_bindings_mac.html b/widget/tests/test_native_key_bindings_mac.html index 8767a5a77d..81a13aea0e 100644 --- a/widget/tests/test_native_key_bindings_mac.html +++ b/widget/tests/test_native_key_bindings_mac.html @@ -292,7 +292,7 @@ aElement.id + ": Incorrect selection end"); } - function* testRun(aElement, aSelectionCheck, aCallback) { + function* testRun(aElement, aSelectionCheck) { if (document.activeElement) { document.activeElement.blur(); } diff --git a/widget/tests/test_sizemode_events.xhtml b/widget/tests/test_sizemode_events.xhtml index bd1e3a38d1..c1224fbf70 100644 --- a/widget/tests/test_sizemode_events.xhtml +++ b/widget/tests/test_sizemode_events.xhtml @@ -31,7 +31,7 @@ const kAsyncChanges = kIsLinux || kIsMacOS; let gSizeModeDidChange = false; let gSizeModeDidChangeTo = 0; -function sizemodeChanged(e) { +function sizemodeChanged() { gSizeModeDidChange = true; gSizeModeDidChangeTo = gWindow.windowState; } diff --git a/widget/tests/window_bug478536.xhtml b/widget/tests/window_bug478536.xhtml index 7318eb0bff..206226ec74 100644 --- a/widget/tests/window_bug478536.xhtml +++ b/widget/tests/window_bug478536.xhtml @@ -177,7 +177,7 @@ function fireWheelScrollEvent(aForward) }, window); } -function onScrollView(aEvent) +function onScrollView() { if (gIgnoreScrollEvent) return; @@ -187,7 +187,7 @@ function onScrollView(aEvent) gTimer = setTimeout(runNextTest, 0); } -function onMouseScrollFailed(aEvent) +function onMouseScrollFailed() { clearTimer(); gIgnoreScrollEvent = true; @@ -197,7 +197,7 @@ function onMouseScrollFailed(aEvent) runNextTest(); } -function onTransactionTimeout(aEvent) +function onTransactionTimeout() { if (!gTimer) return; diff --git a/widget/tests/window_composition_text_querycontent.xhtml b/widget/tests/window_composition_text_querycontent.xhtml index db3b10ea30..4806d0d187 100644 --- a/widget/tests/window_composition_text_querycontent.xhtml +++ b/widget/tests/window_composition_text_querycontent.xhtml @@ -6270,7 +6270,7 @@ function runQueryContentEventRelativeToInsertionPoint() synthesizeComposition({ type: "compositioncommitasis" }); // Move start of composition at first compositionupdate event. - function onCompositionUpdate(aEvent) + function onCompositionUpdate() { startOffset = textarea.selectionStart = textarea.selectionEnd = textarea.selectionStart - 1; textarea.removeEventListener("compositionupdate", onCompositionUpdate); diff --git a/widget/tests/window_imestate_iframes.html b/widget/tests/window_imestate_iframes.html index c8b182977f..3c2d0908ae 100644 --- a/widget/tests/window_imestate_iframes.html +++ b/widget/tests/window_imestate_iframes.html @@ -49,6 +49,8 @@ function onUnload() { var gFocusObservingElement = null; var gBlurObservingElement = null; +var canTabMoveFocusToRootElement = + !SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element"); function onFocus(aEvent) { if (aEvent.target != gFocusObservingElement) { @@ -201,18 +203,30 @@ function runTests() { root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_not_editable"); - testTabKey(true, root, false, prev, true, - false, "input#prev[readonly] -> html"); - testTabKey(true, editor, true, root, false, - true, "html -> input in the subdoc"); + if (canTabMoveFocusToRootElement) { + testTabKey(true, root, false, prev, true, + false, "input#prev[readonly] -> html"); + testTabKey(true, editor, true, root, false, + true, "html -> input in the subdoc"); + } else { + testTabKey(true, editor, true, prev, true, + true, "input#prev[readonly] -> input in the subdoc"); + } testTabKey(true, next, true, editor, true, false, "input in the subdoc -> input#next[readonly]"); testTabKey(false, editor, true, next, true, true, "input#next[readonly] -> input in the subdoc"); - testTabKey(false, root, false, editor, true, - false, "input in the subdoc -> html"); - testTabKey(false, prev, true, root, false, - false, "html -> input#next[readonly]"); + if (canTabMoveFocusToRootElement) { + testTabKey(false, root, false, editor, true, + false, "input in the subdoc -> html"); + testTabKey(false, prev, true, root, false, + false, "html -> input#next[readonly]"); + } else { + testTabKey(false, prev, true, editor, true, + false, "input in the subdoc -> input#prev[readonly]"); + testTabKey(false, next, true, prev, true, + false, "input#prev[readonly] -> input#next[readonly]"); + } iframe.style.display = "none"; @@ -236,8 +250,13 @@ function runTests() { resetFocusToParentHTML("testing iframe_html"); testTabKey(true, editor, true, html, false, true, "html of parent -> html[contentediable=true]"); - testTabKey(false, html, false, editor, true, - false, "html[contenteditable=true] -> html of parent"); + if (canTabMoveFocusToRootElement) { + testTabKey(false, html, false, editor, true, + false, "html[contenteditable=true] -> html of parent"); + } else { + testTabKey(false, next, true, editor, true, + false, "html[contenteditable=true] -> input#next[readonly]"); + } prev.style.display = "inline"; resetFocusToInput("after parent html <-> html[contenteditable=true]"); @@ -270,8 +289,13 @@ function runTests() { resetFocusToParentHTML("testing iframe_designMode"); testTabKey(true, root, false, html, false, true, "html of parent -> html in designMode"); - testTabKey(false, html, false, root, false, - false, "html in designMode -> html of parent"); + if (canTabMoveFocusToRootElement) { + testTabKey(false, html, false, root, false, + false, "html[contenteditable=true] -> html of parent"); + } else { + testTabKey(false, next, true, root, false, + false, "html in designMode -> html of parent"); + } prev.style.display = "inline"; resetFocusToInput("after parent html <-> html in designMode"); @@ -302,8 +326,13 @@ function runTests() { resetFocusToParentHTML("testing iframe_body"); testTabKey(true, editor, true, html, false, true, "html of parent -> body[contentediable=true]"); - testTabKey(false, html, false, editor, true, - false, "body[contenteditable=true] -> html of parent"); + if (canTabMoveFocusToRootElement) { + testTabKey(false, html, false, editor, true, + false, "body[contenteditable=true] -> html of parent"); + } else { + testTabKey(false, next, true, editor, true, + false, "body[contenteditable=true] -> input#next[readonly]"); + } prev.style.display = "inline"; resetFocusToInput("after parent html <-> body[contenteditable=true]"); @@ -321,25 +350,44 @@ function runTests() { root = iframe.contentDocument.documentElement; resetFocusToInput("initializing for iframe_p"); - testTabKey(true, root, false, prev, true, - false, "input#prev[readonly] -> html (has p[contenteditable=true])"); - testTabKey(true, editor, true, root, false, - true, "html (has p[contenteditable=true]) -> p[contentediable=true]"); + if (canTabMoveFocusToRootElement) { + testTabKey(true, root, false, prev, true, + false, "input#prev[readonly] -> html (has p[contenteditable=true])"); + testTabKey(true, editor, true, root, false, + true, "html (has p[contenteditable=true]) -> p[contentediable=true]"); + } else { + testTabKey(true, editor, true, prev, true, + true, "input#prev[readonly] -> p[contenteditable=true]"); + } testTabKey(true, next, true, editor, true, false, "p[contentediable=true] -> input#next[readonly]"); testTabKey(false, editor, true, next, true, true, "input#next[readonly] -> p[contentediable=true]"); - testTabKey(false, root, false, editor, true, - false, "p[contenteditable=true] -> html (has p[contenteditable=true])"); - testTabKey(false, prev, true, root, false, - false, "html (has p[contenteditable=true]) -> input#prev[readonly]"); + if (canTabMoveFocusToRootElement) { + testTabKey(false, root, false, editor, true, + false, "p[contenteditable=true] -> html (has p[contenteditable=true])"); + testTabKey(false, prev, true, root, false, + false, "html (has p[contenteditable=true]) -> input#prev[readonly]"); + } else { + testTabKey(false, prev, true, editor, true, + false, "p[contenteditable=true] -> html (has p[contenteditable=true])"); + testTabKey(false, next, true, prev, true, + false, "html (has p[contenteditable=true]) -> input#next[readonly]"); + } prev.style.display = "none"; resetFocusToParentHTML("testing iframe_p"); - testTabKey(true, root, false, html, false, - false, "html of parent -> html (has p[contentediable=true])"); - testTabKey(false, html, false, root, false, - false, "html (has p[contentediable=true]) -> html of parent"); + if (canTabMoveFocusToRootElement) { + testTabKey(true, root, false, html, false, + false, "html of parent -> html (has p[contentediable=true])"); + testTabKey(false, html, false, root, false, + false, "html (has p[contentediable=true]) -> html of parent"); + } else { + testTabKey(true, editor, true, html, false, + true, "html of parent -> p[contentediable=true]"); + testTabKey(false, next, true, editor, true, + false, "p[contentediable=true] -> input#next[readonly]"); + } prev.style.display = "inline"; resetFocusToInput("after parent html <-> html (has p[contentediable=true])"); diff --git a/widget/tests/window_mouse_scroll_win.html b/widget/tests/window_mouse_scroll_win.html index bf90abb1b5..48ccc1fed4 100644 --- a/widget/tests/window_mouse_scroll_win.html +++ b/widget/tests/window_mouse_scroll_win.html @@ -214,7 +214,7 @@ var gPreparingSteps = [ target: gP1, x: 10, y: 10, window, modifiers: 0, additionalFlags: 0, - onLineScrollEvent(aEvent) { + onLineScrollEvent() { return true; }, onPixelScrollEvent(aEvent) { @@ -234,7 +234,7 @@ var gPreparingSteps = [ target: gP1, x: 10, y: 10, window, modifiers: 0, additionalFlags: 0, - onLineScrollEvent(aEvent) { + onLineScrollEvent() { return true; }, onPixelScrollEvent(aEvent) { @@ -250,7 +250,7 @@ var gPreparingSteps = [ target: gP1, x: 10, y: 10, window, modifiers: 0, additionalFlags: 0, - onLineScrollEvent(aEvent) { + onLineScrollEvent() { return true; }, onPixelScrollEvent(aEvent) { @@ -270,7 +270,7 @@ var gPreparingSteps = [ target: gP1, x: 10, y: 10, window, modifiers: 0, additionalFlags: 0, - onLineScrollEvent(aEvent) { + onLineScrollEvent() { return true; }, onPixelScrollEvent(aEvent) { @@ -1100,7 +1100,7 @@ var gDeactiveWindowTests = [ SpecialPowers.setIntPref(kVAmountPref, 3); SpecialPowers.setIntPref(kHAmountPref, 3); }, - onLineScrollEvent(aEvent) { + onLineScrollEvent() { var fm = Services.focus; is(fm.activeWindow, gOtherWindow, "The other window isn't activated"); }, diff --git a/widget/uikit/GfxInfo.cpp b/widget/uikit/GfxInfo.cpp index d797e4f4a0..cb378915aa 100644 --- a/widget/uikit/GfxInfo.cpp +++ b/widget/uikit/GfxInfo.cpp @@ -17,9 +17,19 @@ GfxInfo::GfxInfo() {} GfxInfo::~GfxInfo() {} -nsresult GfxInfo::GetD2DEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; } +NS_IMETHODIMP +GfxInfo::GetD2DEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; } + +NS_IMETHODIMP +GfxInfo::GetDWriteEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; } -nsresult GfxInfo::GetDWriteEnabled(bool* aEnabled) { return NS_ERROR_FAILURE; } +NS_IMETHODIMP +GfxInfo::GetEmbeddedInFirefoxReality(bool* aEmbeddedInFirefoxReality) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +GfxInfo::GetHasBattery(bool* aHasBattery) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP GfxInfo::GetDWriteVersion(nsAString& aDwriteVersion) { @@ -32,6 +42,14 @@ GfxInfo::GetCleartypeParameters(nsAString& aCleartypeParams) { } NS_IMETHODIMP +GfxInfo::GetWindowProtocol(nsAString& aWindowProtocol) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +GfxInfo::GetTestType(nsAString& aTestType) { return NS_ERROR_NOT_IMPLEMENTED; } + +NS_IMETHODIMP GfxInfo::GetAdapterDescription(nsAString& aAdapterDescription) { return NS_ERROR_FAILURE; } @@ -48,6 +66,16 @@ NS_IMETHODIMP GfxInfo::GetAdapterRAM2(uint32_t* aAdapterRAM) { return NS_ERROR_FAILURE; } NS_IMETHODIMP +GfxInfo::GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor2) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP GfxInfo::GetAdapterDriver(nsAString& aAdapterDriver) { return NS_ERROR_FAILURE; } @@ -90,7 +118,6 @@ GfxInfo::GetAdapterVendorID2(nsAString& aAdapterVendorID) { NS_IMETHODIMP GfxInfo::GetAdapterDeviceID(nsAString& aAdapterDeviceID) { return NS_ERROR_FAILURE; - return NS_OK; } NS_IMETHODIMP @@ -111,12 +138,18 @@ GfxInfo::GetAdapterSubsysID2(nsAString& aAdapterSubsysID) { NS_IMETHODIMP GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) { return NS_ERROR_FAILURE; } +NS_IMETHODIMP +GfxInfo::GetDrmRenderDevice(nsACString& aDrmRenderDevice) { + return NS_ERROR_NOT_IMPLEMENTED; +} + const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { if (sDriverInfo->IsEmpty()) { APPEND_TO_DRIVER_BLOCKLIST2( OperatingSystem::Ios, DeviceFamily::All, nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_STATUS_OK, - DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions); + DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions, + "FEATURE_OK_FORCE_OPENGL"); } return *sDriverInfo; @@ -124,7 +157,7 @@ const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { nsresult GfxInfo::GetFeatureStatusImpl( int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedDriverVersion, - const nsTArray<GfxDriverInfo>& aDriverInfo, + const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId, OperatingSystem* aOS /* = nullptr */) { NS_ENSURE_ARG_POINTER(aStatus); aSuggestedDriverVersion.SetIsVoid(true); @@ -145,7 +178,7 @@ nsresult GfxInfo::GetFeatureStatusImpl( } return GfxInfoBase::GetFeatureStatusImpl( - aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aOS); + aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aFailureId, aOS); } #ifdef DEBUG diff --git a/widget/uikit/GfxInfo.h b/widget/uikit/GfxInfo.h index fe75e28d23..55deb58544 100644 --- a/widget/uikit/GfxInfo.h +++ b/widget/uikit/GfxInfo.h @@ -29,18 +29,26 @@ class GfxInfo : public GfxInfoBase { public: GfxInfo(); + OperatingSystem GetOperatingSystem() override { return OperatingSystem::Ios; } + // We only declare the subset of nsIGfxInfo that we actually implement. The // rest is brought forward from GfxInfoBase. NS_IMETHOD GetD2DEnabled(bool* aD2DEnabled) override; NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override; + NS_IMETHOD GetEmbeddedInFirefoxReality( + bool* aEmbeddedInFirefoxReality) override; + NS_IMETHOD GetHasBattery(bool* aHasBattery) override; NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override; NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override; + NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override; + NS_IMETHOD GetTestType(nsAString& aTestType) override; NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override; NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override; NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override; NS_IMETHOD GetAdapterDeviceID(nsAString& aAdapterDeviceID) override; NS_IMETHOD GetAdapterSubsysID(nsAString& aAdapterSubsysID) override; NS_IMETHOD GetAdapterRAM(uint32_t* aAdapterRAM) override; + NS_IMETHOD GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) override; NS_IMETHOD GetAdapterDriverVersion(nsAString& aAdapterDriverVersion) override; NS_IMETHOD GetAdapterDriverDate(nsAString& aAdapterDriverDate) override; NS_IMETHOD GetAdapterDescription2(nsAString& aAdapterDescription) override; @@ -49,10 +57,12 @@ class GfxInfo : public GfxInfoBase { NS_IMETHOD GetAdapterDeviceID2(nsAString& aAdapterDeviceID) override; NS_IMETHOD GetAdapterSubsysID2(nsAString& aAdapterSubsysID) override; NS_IMETHOD GetAdapterRAM2(uint32_t* aAdapterRAM) override; + NS_IMETHOD GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) override; NS_IMETHOD GetAdapterDriverVersion2( nsAString& aAdapterDriverVersion) override; NS_IMETHOD GetAdapterDriverDate2(nsAString& aAdapterDriverDate) override; NS_IMETHOD GetIsGPU2Active(bool* aIsGPU2Active) override; + NS_IMETHOD GetDrmRenderDevice(nsACString& aDrmRenderDevice) override; using GfxInfoBase::GetFeatureStatus; using GfxInfoBase::GetFeatureSuggestedDriverVersion; @@ -62,11 +72,12 @@ class GfxInfo : public GfxInfoBase { #endif protected: - virtual nsresult GetFeatureStatusImpl( - int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedDriverVersion, - const nsTArray<GfxDriverInfo>& aDriverInfo, - OperatingSystem* aOS = nullptr); - virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo(); + nsresult GetFeatureStatusImpl(int32_t aFeature, int32_t* aStatus, + nsAString& aSuggestedDriverVersion, + const nsTArray<GfxDriverInfo>& aDriverInfo, + nsACString& aFailureId, + OperatingSystem* aOS = nullptr) override; + const nsTArray<GfxDriverInfo>& GetGfxDriverInfo() override; }; } // namespace widget diff --git a/widget/uikit/MediaKeysEventSourceFactory.cpp b/widget/uikit/MediaKeysEventSourceFactory.cpp new file mode 100644 index 0000000000..b52919d4cc --- /dev/null +++ b/widget/uikit/MediaKeysEventSourceFactory.cpp @@ -0,0 +1,17 @@ +/* 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 "MediaKeysEventSourceFactory.h" + +namespace mozilla { +namespace widget { + +mozilla::dom::MediaControlKeySource* CreateMediaControlKeySource() { + // GeckoView uses MediaController.webidl for media session events and control, + // see bug 1623715. + return nullptr; +} + +} // namespace widget +} // namespace mozilla diff --git a/widget/uikit/TextInputHandler.h b/widget/uikit/TextInputHandler.h new file mode 100644 index 0000000000..1f27bf11f0 --- /dev/null +++ b/widget/uikit/TextInputHandler.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TextInputHandler_h_ +#define TextInputHandler_h_ + +#import <UIKit/UITextInput.h> + +#include "mozilla/EventForwards.h" +#include "mozilla/TextEventDispatcherListener.h" +#include "mozilla/widget/IMEData.h" +#include "nsCOMPtr.h" + +class nsWindow; + +namespace mozilla::widget { +class TextEventDispatcher; + +// This is the temporary input class. When implementing UITextInpt protocol, we +// should share this class with Cocoa's version. +class TextInputHandler final : public TextEventDispatcherListener { + public: + explicit TextInputHandler(nsWindow* aWidget); + TextInputHandler() = delete; + + NS_DECL_ISUPPORTS + + NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, + const IMENotification& aNotification) override; + NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override; + NS_IMETHOD_(void) + OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override; + NS_IMETHOD_(void) + WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher, + WidgetKeyboardEvent& aKeyboardEvent, + uint32_t aIndexOfKeypress, void* aData) override; + + // UIKeyInput delegation + bool InsertText(NSString* aText); + bool HandleCommand(Command aCommand); + + void OnDestroyed(); + + private: + virtual ~TextInputHandler() = default; + + bool DispatchKeyDownEvent(uint32_t aKeyCode, KeyNameIndex aKeyNameIndex, + char16_t aCharCode, nsEventStatus& aStatus); + bool DispatchKeyUpEvent(uint32_t aKeyCode, KeyNameIndex aKeyNameIndex, + char16_t aCharCode, nsEventStatus& aStatus); + bool DispatchKeyPressEvent(uint32_t aKeyCode, KeyNameIndex aKeyNameIndex, + char16_t aCharCode, nsEventStatus& aStatus); + + bool EmulateKeyboardEvent(uint32_t aKeyCode, KeyNameIndex aKeyNameIndex, + char16_t charCode); + + bool Destroyed() { return !mWidget; } + + nsWindow* mWidget; // weak ref + RefPtr<TextEventDispatcher> mDispatcher; +}; + +} // namespace mozilla::widget +#endif diff --git a/widget/uikit/TextInputHandler.mm b/widget/uikit/TextInputHandler.mm new file mode 100644 index 0000000000..9a4a6ae226 --- /dev/null +++ b/widget/uikit/TextInputHandler.mm @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "TextInputHandler.h" + +#import <UIKit/UIKit.h> + +#include "mozilla/EventForwards.h" +#include "mozilla/Logging.h" +#include "mozilla/MiscEvents.h" +#include "mozilla/TextEventDispatcher.h" +#include "mozilla/TextEvents.h" +#include "mozilla/WidgetUtils.h" +#include "nsIWidget.h" +#include "nsObjCExceptions.h" +#include "nsString.h" +#include "nsWindow.h" + +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) + +TextInputHandler::TextInputHandler(nsWindow* aWidget) + : mWidget(aWidget), mDispatcher(aWidget->GetTextEventDispatcher()) {} + +nsresult TextInputHandler::NotifyIME(TextEventDispatcher* aTextEventDispatcher, + const IMENotification& aNotification) { + return NS_OK; +} + +IMENotificationRequests TextInputHandler::GetIMENotificationRequests() { + return IMENotificationRequests(); +} + +void TextInputHandler::OnRemovedFrom( + TextEventDispatcher* aTextEventDispatcher) {} + +void TextInputHandler::WillDispatchKeyboardEvent( + TextEventDispatcher* aTextEventDispatcher, + WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress, + void* aData) {} + +bool TextInputHandler::InsertText(NSString* aText) { + nsString str; + GetStringForNSString(aText, str); + + MOZ_LOG(gIMELog, LogLevel::Info, + ("%p TextInputHandler::InsertText(aText=%s)", this, + NS_ConvertUTF16toUTF8(str).get())); + + if (Destroyed()) { + return false; + } + + if (str.Length() == 1) { + char16_t charCode = str[0]; + if (charCode == 0x0a) { + return EmulateKeyboardEvent(NS_VK_RETURN, KEY_NAME_INDEX_Enter, charCode); + } + if (charCode == 0x08) { + return EmulateKeyboardEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, + charCode); + } + if (uint32_t keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode)) { + return EmulateKeyboardEvent(keyCode, KEY_NAME_INDEX_USE_STRING, charCode); + } + } + + nsEventStatus status = nsEventStatus_eIgnore; + RefPtr<nsWindow> widget(mWidget); + if (!DispatchKeyDownEvent(NS_VK_PROCESSKEY, KEY_NAME_INDEX_Process, 0, + status)) { + return false; + } + if (Destroyed()) { + return false; + } + + mDispatcher->CommitComposition(status, &str, nullptr); + if (widget->Destroyed()) { + return false; + } + + DispatchKeyUpEvent(NS_VK_PROCESSKEY, KEY_NAME_INDEX_Process, 0, status); + + return true; +} + +bool TextInputHandler::HandleCommand(Command aCommand) { + MOZ_LOG(gIMELog, LogLevel::Info, + ("%p TextInputHandler::HandleCommand, aCommand=%s", this, + ToChar(aCommand))); + + if (Destroyed()) { + return false; + } + + if (aCommand != Command::DeleteCharBackward) { + return false; + } + + nsEventStatus status = nsEventStatus_eIgnore; + if (!DispatchKeyDownEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, 0, status)) { + return true; + } + if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) { + return true; + } + + // TODO: Focus check + + if (!DispatchKeyPressEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, 0, status)) { + return true; + } + if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) { + return true; + } + + // TODO: Focus check + + DispatchKeyUpEvent(NS_VK_BACK, KEY_NAME_INDEX_Backspace, 0, status); + + return true; +} + +static uint32_t ComputeKeyModifiers(uint32_t aCharCode) { + if (aCharCode >= 'A' && aCharCode <= 'Z') { + return MODIFIER_SHIFT; + } + return 0; +} + +static void InitKeyEvent(WidgetKeyboardEvent& aEvent, uint32_t aKeyCode, + KeyNameIndex aKeyNameIndex, char16_t aCharCode) { + aEvent.mKeyCode = aKeyCode; + aEvent.mIsRepeat = false; + aEvent.mKeyNameIndex = aKeyNameIndex; + // TODO(m_kato): + // How to get native key? Then, implement NativeKeyToDOM*.h for iOS + aEvent.mCodeNameIndex = CODE_NAME_INDEX_UNKNOWN; + if (aEvent.mKeyNameIndex == KEY_NAME_INDEX_USE_STRING) { + aEvent.mKeyValue = aCharCode; + } + aEvent.mModifiers = ComputeKeyModifiers(aCharCode); + aEvent.mLocation = eKeyLocationStandard; + aEvent.mTimeStamp = TimeStamp::Now(); +} + +bool TextInputHandler::DispatchKeyDownEvent(uint32_t aKeyCode, + KeyNameIndex aKeyNameIndex, + char16_t aCharCode, + nsEventStatus& aStatus) { + MOZ_ASSERT(aKeyCode); + MOZ_ASSERT(mWidget); + + WidgetKeyboardEvent keydownEvent(true, eKeyDown, mWidget); + InitKeyEvent(keydownEvent, aKeyCode, aKeyNameIndex, aCharCode); + nsresult rv = mDispatcher->BeginNativeInputTransaction(); + if (NS_FAILED(rv)) { + NS_WARNING("BeginNativeInputTransaction is failed"); + return false; + } + return mDispatcher->DispatchKeyboardEvent(eKeyDown, keydownEvent, aStatus); +} + +bool TextInputHandler::DispatchKeyUpEvent(uint32_t aKeyCode, + KeyNameIndex aKeyNameIndex, + char16_t aCharCode, + nsEventStatus& aStatus) { + MOZ_ASSERT(aKeyCode); + MOZ_ASSERT(mWidget); + + WidgetKeyboardEvent keyupEvent(true, eKeyUp, mWidget); + InitKeyEvent(keyupEvent, aKeyCode, aKeyNameIndex, aCharCode); + nsresult rv = mDispatcher->BeginNativeInputTransaction(); + if (NS_FAILED(rv)) { + NS_WARNING("BeginNativeInputTransaction is failed"); + return false; + } + return mDispatcher->DispatchKeyboardEvent(eKeyUp, keyupEvent, aStatus); +} + +bool TextInputHandler::DispatchKeyPressEvent(uint32_t aKeyCode, + KeyNameIndex aKeyNameIndex, + char16_t aCharCode, + nsEventStatus& aStatus) { + MOZ_ASSERT(aKeyCode); + MOZ_ASSERT(mWidget); + + WidgetKeyboardEvent keypressEvent(true, eKeyPress, mWidget); + InitKeyEvent(keypressEvent, aKeyCode, aKeyNameIndex, aCharCode); + nsresult rv = mDispatcher->BeginNativeInputTransaction(); + if (NS_FAILED(rv)) { + NS_WARNING("BeginNativeInputTransaction is failed"); + return false; + } + return mDispatcher->MaybeDispatchKeypressEvents(keypressEvent, aStatus); +} + +bool TextInputHandler::EmulateKeyboardEvent(uint32_t aKeyCode, + KeyNameIndex aKeyNameIndex, + char16_t aCharCode) { + MOZ_ASSERT(aCharCode); + + MOZ_LOG(gIMELog, LogLevel::Info, + ("%p TextInputHandler::EmulateKeyboardEvent(aKeyCode=%x, " + "aKeyNameIndex=%x, aCharCode=%x)", + this, aKeyCode, aKeyNameIndex, aCharCode)); + + nsEventStatus status = nsEventStatus_eIgnore; + if (!DispatchKeyDownEvent(aKeyCode, aKeyNameIndex, aCharCode, status)) { + return true; + } + if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) { + return true; + } + // TODO: Focus check + + if (!DispatchKeyPressEvent(aKeyCode, aKeyNameIndex, aCharCode, status)) { + return true; + } + if (Destroyed() || status == nsEventStatus_eConsumeNoDefault) { + return true; + } + // TODO: Focus check + + DispatchKeyUpEvent(aKeyCode, aKeyNameIndex, aCharCode, status); + return true; +} + +void TextInputHandler::OnDestroyed() { mWidget = nullptr; } + +} // namespace mozilla::widget diff --git a/widget/uikit/UIKitUtils.h b/widget/uikit/UIKitUtils.h new file mode 100644 index 0000000000..2e34b126fd --- /dev/null +++ b/widget/uikit/UIKitUtils.h @@ -0,0 +1,23 @@ +/* -*- 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/. */ + +#import <UIKit/UIKit.h> + +#include "IMEData.h" + +namespace mozilla::widget { + +class UIKitUtils final { + public: + UIKitUtils() = delete; + ~UIKitUtils() = delete; + + static UIKeyboardType GetUIKeyboardType(const InputContext& aContext); + static UIReturnKeyType GetUIReturnKeyType(const InputContext& aContext); + static UITextAutocapitalizationType GetUITextAutocapitalizationType( + const InputContext& aContext); +}; + +} // namespace mozilla::widget diff --git a/widget/uikit/UIKitUtils.mm b/widget/uikit/UIKitUtils.mm new file mode 100644 index 0000000000..ddcd2312b0 --- /dev/null +++ b/widget/uikit/UIKitUtils.mm @@ -0,0 +1,89 @@ +/* -*- 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 "UIKitUtils.h" + +namespace mozilla::widget { + +// static +UIKeyboardType UIKitUtils::GetUIKeyboardType(const InputContext& aContext) { + if (aContext.mHTMLInputMode.EqualsLiteral("email")) { + return UIKeyboardTypeEmailAddress; + } + if (aContext.mHTMLInputMode.EqualsLiteral("deciaml")) { + return UIKeyboardTypeDecimalPad; + } + if (aContext.mHTMLInputMode.EqualsLiteral("numeric")) { + return UIKeyboardTypeNumberPad; + } + if (aContext.mHTMLInputMode.EqualsLiteral("search")) { + return UIKeyboardTypeWebSearch; + } + if (aContext.mHTMLInputMode.EqualsLiteral("tel")) { + return UIKeyboardTypePhonePad; + } + if (aContext.mHTMLInputMode.EqualsLiteral("url")) { + return UIKeyboardTypeURL; + } + + if (aContext.mHTMLInputType.EqualsLiteral("email")) { + return UIKeyboardTypeEmailAddress; + } + if (aContext.mHTMLInputType.EqualsLiteral("number")) { + return UIKeyboardTypeNumberPad; + } + if (aContext.mHTMLInputType.EqualsLiteral("tel")) { + return UIKeyboardTypePhonePad; + } + if (aContext.mHTMLInputType.EqualsLiteral("url")) { + return UIKeyboardTypeURL; + } + + return UIKeyboardTypeDefault; +} + +// static +UIReturnKeyType UIKitUtils::GetUIReturnKeyType(const InputContext& aContext) { + if (aContext.mActionHint.EqualsLiteral("done")) { + return UIReturnKeyDone; + } + if (aContext.mActionHint.EqualsLiteral("go")) { + return UIReturnKeyGo; + } + if (aContext.mActionHint.EqualsLiteral("next") || + aContext.mActionHint.EqualsLiteral("maybenext")) { + return UIReturnKeyNext; + } + if (aContext.mActionHint.EqualsLiteral("search")) { + return UIReturnKeySearch; + } + if (aContext.mActionHint.EqualsLiteral("send")) { + return UIReturnKeySend; + } + + return UIReturnKeyDefault; +} + +// static +UITextAutocapitalizationType UIKitUtils::GetUITextAutocapitalizationType( + const InputContext& aContext) { + if (aContext.mAutocapitalize.EqualsLiteral("characters")) { + return UITextAutocapitalizationTypeAllCharacters; + } + if (aContext.mAutocapitalize.EqualsLiteral("none")) { + return UITextAutocapitalizationTypeNone; + } + if (aContext.mAutocapitalize.EqualsLiteral("sentences")) { + return UITextAutocapitalizationTypeSentences; + } + if (aContext.mAutocapitalize.EqualsLiteral("words")) { + return UITextAutocapitalizationTypeWords; + } + // TODO(m_kato): + // Infer autocapitalization type by input type like GeckoView. + return UITextAutocapitalizationTypeNone; +} + +} // namespace mozilla::widget diff --git a/widget/uikit/components.conf b/widget/uikit/components.conf new file mode 100644 index 0000000000..0a4480ee4a --- /dev/null +++ b/widget/uikit/components.conf @@ -0,0 +1,28 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +Headers = '/widget/uikit/nsWidgetFactory.h', + +InitFunc = 'nsWidgetUIKitModuleCtor' +UnloadFunc = 'nsWidgetUIKitModuleDtor' + +Classes = [ + { + 'name': 'GfxInfo', + 'cid': '{d755a760-9f27-11df-0800-200c9a664242}', + 'contract_ids': ['@mozilla.org/gfx/info;1'], + 'type': 'mozilla::widget::GfxInfo', + 'headers': ['/widget/uikit/GfxInfo.h'], + 'init_method': 'Init', + }, + { + 'cid': '{2d96b3df-c051-11d1-a827-0040959a28c9}', + 'contract_ids': ['@mozilla.org/widget/appshell/uikit;1'], + 'legacy_constructor': 'nsAppShellConstructor', + 'headers': ['/widget/uikit/nsWidgetFactory.h'], + 'processes': ProcessSelector.ALLOW_IN_GPU_RDD_VR_SOCKET_AND_UTILITY_PROCESS, + }, +] diff --git a/widget/uikit/moz.build b/widget/uikit/moz.build index 6a3c2db994..88780c9690 100644 --- a/widget/uikit/moz.build +++ b/widget/uikit/moz.build @@ -7,13 +7,20 @@ with Files("**"): BUG_COMPONENT = ("Core", "Widget") +with Files("*TextInput*"): + BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling") + SOURCES += [ "GfxInfo.cpp", + "MediaKeysEventSourceFactory.cpp", "nsAppShell.mm", + "nsBidiKeyboard.mm", "nsLookAndFeel.mm", - "nsScreenManager.mm", + "nsNativeThemeUIKit.mm", "nsWidgetFactory.mm", "nsWindow.mm", + "TextInputHandler.mm", + "UIKitUtils.mm", ] include("/ipc/chromium/chromium-config.mozbuild") @@ -22,3 +29,11 @@ FINAL_LIBRARY = "xul" LOCAL_INCLUDES += [ "/widget", ] + +XPCOM_MANIFESTS += [ + "components.conf", +] + +OS_LIBS += [ + "-framework UIKit", +] diff --git a/widget/uikit/nsAppShell.h b/widget/uikit/nsAppShell.h index fd46a51025..c8ae7ce9ef 100644 --- a/widget/uikit/nsAppShell.h +++ b/widget/uikit/nsAppShell.h @@ -41,8 +41,8 @@ class nsAppShell : public nsBaseAppShell { virtual ~nsAppShell(); static void ProcessGeckoEvents(void* aInfo); - virtual void ScheduleNativeEventCallback(); - virtual bool ProcessNextNativeEvent(bool aMayWait); + void ScheduleNativeEventCallback() override; + bool ProcessNextNativeEvent(bool aMayWait) override; NSAutoreleasePool* mAutoreleasePool; AppShellDelegate* mDelegate; diff --git a/widget/uikit/nsBidiKeyboard.h b/widget/uikit/nsBidiKeyboard.h new file mode 100644 index 0000000000..3a9a6fe2fb --- /dev/null +++ b/widget/uikit/nsBidiKeyboard.h @@ -0,0 +1,23 @@ +/* -*- 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/. */ + +#ifndef nsBidiKeyboard_h_ +#define nsBidiKeyboard_h_ + +#include "nsIBidiKeyboard.h" + +class nsBidiKeyboard : public nsIBidiKeyboard { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIBIDIKEYBOARD + + nsBidiKeyboard(); + + protected: + virtual ~nsBidiKeyboard(); +}; + +#endif // nsBidiKeyboard_h_ diff --git a/widget/uikit/nsBidiKeyboard.mm b/widget/uikit/nsBidiKeyboard.mm new file mode 100644 index 0000000000..bbe1eff92f --- /dev/null +++ b/widget/uikit/nsBidiKeyboard.mm @@ -0,0 +1,35 @@ +/* -*- 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 "nsBidiKeyboard.h" +#include "nsIWidget.h" + +// This must be the last include: +#include "nsObjCExceptions.h" + +using namespace mozilla::widget; + +NS_IMPL_ISUPPORTS(nsBidiKeyboard, nsIBidiKeyboard) + +nsBidiKeyboard::nsBidiKeyboard() : nsIBidiKeyboard() { Reset(); } + +nsBidiKeyboard::~nsBidiKeyboard() {} + +NS_IMETHODIMP nsBidiKeyboard::Reset() { return NS_OK; } + +NS_IMETHODIMP nsBidiKeyboard::IsLangRTL(bool* aIsRTL) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsBidiKeyboard::GetHaveBidiKeyboards(bool* aResult) { + // not implemented yet + return NS_ERROR_NOT_IMPLEMENTED; +} + +// static +already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboardInner() { + return do_AddRef(new nsBidiKeyboard()); +} diff --git a/widget/uikit/nsLookAndFeel.h b/widget/uikit/nsLookAndFeel.h index 7de7e0712b..c696502ec1 100644 --- a/widget/uikit/nsLookAndFeel.h +++ b/widget/uikit/nsLookAndFeel.h @@ -14,13 +14,13 @@ class nsLookAndFeel final : public nsXPLookAndFeel { virtual ~nsLookAndFeel(); void NativeInit() final; - virtual void RefreshImpl(); - nsresult NativeGetImpl(IntID aID, int32_t& aResult) override; + void RefreshImpl() override; + nsresult NativeGetInt(IntID aID, int32_t& aResult) override; nsresult NativeGetFloat(FloatID aID, float& aResult) override; nsresult NativeGetColor(ColorID, ColorScheme, nscolor& aResult) override; bool NativeGetFont(FontID aID, nsString& aFontName, gfxFontStyle& aFontStyle) override; - virtual char16_t GetPasswordCharacterImpl() { + char16_t GetPasswordCharacterImpl() override { // unicode value for the bullet character, used for password textfields. return 0x2022; } @@ -28,7 +28,6 @@ class nsLookAndFeel final : public nsXPLookAndFeel { static bool UseOverlayScrollbars() { return true; } private: - nscolor mColorTextSelectForeground; nscolor mColorDarkText; bool mInitialized; diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm index 29eb52a234..b420cc9a1d 100644 --- a/widget/uikit/nsLookAndFeel.mm +++ b/widget/uikit/nsLookAndFeel.mm @@ -13,6 +13,8 @@ #include "gfxFont.h" #include "gfxFontConstants.h" +using namespace mozilla; + nsLookAndFeel::nsLookAndFeel() : mInitialized(false) {} nsLookAndFeel::~nsLookAndFeel() {} @@ -41,7 +43,8 @@ void nsLookAndFeel::RefreshImpl() { mInitialized = false; } -nsresult nsLookAndFeel::NativeGetColor(ColorID, ColorScheme, nscolor& aResult) { +nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme, + nscolor& aResult) { EnsureInit(); nsresult res = NS_OK; @@ -55,7 +58,7 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID, ColorScheme, nscolor& aResult) { break; case ColorID::Highlighttext: case ColorID::MozMenuhovertext: - aResult = mColorTextSelectForeground; + aResult = NS_SAME_AS_FOREGROUND_COLOR; break; case ColorID::IMESelectedRawTextBackground: case ColorID::IMESelectedConvertedTextBackground: @@ -154,11 +157,11 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID, ColorScheme, nscolor& aResult) { aResult = NS_RGB(0xaa, 0xaa, 0xaa); break; case ColorID::Window: - case ColorID::MozField: + case ColorID::Field: case ColorID::MozCombobox: aResult = NS_RGB(0xff, 0xff, 0xff); break; - case ColorID::MozFieldtext: + case ColorID::Fieldtext: case ColorID::MozComboboxtext: aResult = mColorDarkText; break; @@ -175,12 +178,6 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID, ColorScheme, nscolor& aResult) { case ColorID::MozMacFocusring: aResult = NS_RGB(0x3F, 0x98, 0xDD); break; - case ColorID::MozMacMenutextdisable: - aResult = NS_RGB(0x88, 0x88, 0x88); - break; - case ColorID::MozMacMenutextselect: - aResult = NS_RGB(0xaa, 0xaa, 0xaa); - break; case ColorID::MozMacDisabledtoolbartext: aResult = NS_RGB(0x3F, 0x3F, 0x3F); break; @@ -260,6 +257,19 @@ nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::ScrollArrowStyle: aResult = eScrollArrow_None; break; + case IntID::UseOverlayScrollbars: + case IntID::AllowOverlayScrollbarsOverlap: + aResult = 1; + break; + case IntID::ScrollbarDisplayOnMouseMove: + aResult = 0; + break; + case IntID::ScrollbarFadeBeginDelay: + aResult = 450; + break; + case IntID::ScrollbarFadeDuration: + aResult = 200; + break; case IntID::TreeOpenDelay: aResult = 1000; break; @@ -326,10 +336,10 @@ nsLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) { bool nsLookAndFeel::NativeGetFont(FontID aID, nsString& aFontName, gfxFontStyle& aFontStyle) { // hack for now - if (aID == FontID::Window || aID == FontID::Document) { - aFontStyle.style = FontSlantStyle::Normal(); - aFontStyle.weight = FontWeight::Normal(); - aFontStyle.stretch = FontStretch::Normal(); + if (aID == FontID::Caption || aID == FontID::Menu) { + aFontStyle.style = FontSlantStyle::NORMAL; + aFontStyle.weight = FontWeight::NORMAL; + aFontStyle.stretch = FontStretch::NORMAL; aFontStyle.size = 14; aFontStyle.systemFont = true; @@ -347,14 +357,6 @@ void nsLookAndFeel::EnsureInit() { } mInitialized = true; - nscolor color; - GetColor(ColorID::TextSelectBackground, color); - if (color == 0x000000) { - mColorTextSelectForeground = NS_RGB(0xff, 0xff, 0xff); - } else { - mColorTextSelectForeground = NS_SAME_AS_FOREGROUND_COLOR; - } - mColorDarkText = GetColorFromUIColor([UIColor darkTextColor]); RecordTelemetry(); diff --git a/widget/uikit/nsNativeThemeUIKit.h b/widget/uikit/nsNativeThemeUIKit.h new file mode 100644 index 0000000000..87c631a8b8 --- /dev/null +++ b/widget/uikit/nsNativeThemeUIKit.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsNativeThemeUIKit_h +#define nsNativeThemeUIKit_h + +#include "nsITheme.h" +#include "Theme.h" + +class nsNativeThemeUIKit final : public mozilla::widget::Theme { + public: + nsNativeThemeUIKit(); + + protected: + virtual ~nsNativeThemeUIKit() = default; +}; + +#endif // nsNativeThemeUIKit_h diff --git a/widget/uikit/nsNativeThemeUIKit.mm b/widget/uikit/nsNativeThemeUIKit.mm new file mode 100644 index 0000000000..19cde52426 --- /dev/null +++ b/widget/uikit/nsNativeThemeUIKit.mm @@ -0,0 +1,19 @@ +/* -*- 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 "nsNativeThemeUIKit.h" + +using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::widget; + +nsNativeThemeUIKit::nsNativeThemeUIKit() : Theme(ScrollbarStyle()) {} + +already_AddRefed<Theme> do_CreateNativeThemeDoNotUseDirectly() { + if (gfxPlatform::IsHeadless()) { + return do_AddRef(new Theme(Theme::ScrollbarStyle())); + } + return do_AddRef(new nsNativeThemeUIKit()); +} diff --git a/widget/uikit/nsScreenManager.h b/widget/uikit/nsScreenManager.h deleted file mode 100644 index 0d19728264..0000000000 --- a/widget/uikit/nsScreenManager.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef nsScreenManager_h_ -#define nsScreenManager_h_ - -#include "nsBaseScreen.h" -#include "nsIScreenManager.h" -#include "nsCOMPtr.h" -#include "nsRect.h" - -@class UIScreen; - -class UIKitScreen : public nsBaseScreen { - public: - explicit UIKitScreen(UIScreen* screen); - ~UIKitScreen() {} - - NS_IMETHOD GetId(uint32_t* outId) override { - *outId = 0; - return NS_OK; - } - - NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, - int32_t* aHeight) override; - NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, - int32_t* aHeight) override; - NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, - int32_t* aHeight) override; - NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, - int32_t* aWidth, int32_t* aHeight) override; - NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth) override; - NS_IMETHOD GetColorDepth(int32_t* aColorDepth) override; - NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor) override; - NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) override { - return GetContentsScaleFactor(aScaleFactor); - } - - private: - UIScreen* mScreen; -}; - -class UIKitScreenManager : public nsIScreenManager { - public: - UIKitScreenManager(); - - NS_DECL_ISUPPORTS - - NS_DECL_NSISCREENMANAGER - - static LayoutDeviceIntRect GetBounds(); - - private: - virtual ~UIKitScreenManager() {} - // TODO: support >1 screen, iPad supports external displays - nsCOMPtr<nsIScreen> mScreen; -}; - -#endif // nsScreenManager_h_ diff --git a/widget/uikit/nsScreenManager.mm b/widget/uikit/nsScreenManager.mm deleted file mode 100644 index da37a4199d..0000000000 --- a/widget/uikit/nsScreenManager.mm +++ /dev/null @@ -1,104 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#import <UIKit/UIScreen.h> - -#include "gfxPoint.h" -#include "nsScreenManager.h" -#include "nsAppShell.h" - -static LayoutDeviceIntRect gScreenBounds; -static bool gScreenBoundsSet = false; - -UIKitScreen::UIKitScreen(UIScreen* aScreen) { mScreen = [aScreen retain]; } - -NS_IMETHODIMP -UIKitScreen::GetRect(int32_t* outX, int32_t* outY, int32_t* outWidth, - int32_t* outHeight) { - return GetRectDisplayPix(outX, outY, outWidth, outHeight); -} - -NS_IMETHODIMP -UIKitScreen::GetAvailRect(int32_t* outX, int32_t* outY, int32_t* outWidth, - int32_t* outHeight) { - return GetAvailRectDisplayPix(outX, outY, outWidth, outHeight); -} - -NS_IMETHODIMP -UIKitScreen::GetRectDisplayPix(int32_t* outX, int32_t* outY, int32_t* outWidth, - int32_t* outHeight) { - nsIntRect rect = UIKitScreenManager::GetBounds(); - *outX = rect.x; - *outY = rect.y; - *outWidth = rect.width; - *outHeight = rect.height; - - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreen::GetAvailRectDisplayPix(int32_t* outX, int32_t* outY, - int32_t* outWidth, int32_t* outHeight) { - CGRect rect = [mScreen applicationFrame]; - CGFloat scale = [mScreen scale]; - - *outX = rect.origin.x * scale; - *outY = rect.origin.y * scale; - *outWidth = rect.size.width * scale; - *outHeight = rect.size.height * scale; - - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreen::GetPixelDepth(int32_t* aPixelDepth) { - // Close enough. - *aPixelDepth = 24; - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreen::GetColorDepth(int32_t* aColorDepth) { - return GetPixelDepth(aColorDepth); -} - -NS_IMETHODIMP -UIKitScreen::GetContentsScaleFactor(double* aContentsScaleFactor) { - *aContentsScaleFactor = [mScreen scale]; - return NS_OK; -} - -NS_IMPL_ISUPPORTS(UIKitScreenManager, nsIScreenManager) - -UIKitScreenManager::UIKitScreenManager() - : mScreen(new UIKitScreen([UIScreen mainScreen])) {} - -LayoutDeviceIntRect UIKitScreenManager::GetBounds() { - if (!gScreenBoundsSet) { - CGRect rect = [[UIScreen mainScreen] bounds]; - CGFloat scale = [[UIScreen mainScreen] scale]; - gScreenBounds.x = rect.origin.x * scale; - gScreenBounds.y = rect.origin.y * scale; - gScreenBounds.width = rect.size.width * scale; - gScreenBounds.height = rect.size.height * scale; - gScreenBoundsSet = true; - } - printf("UIKitScreenManager::GetBounds: %d %d %d %d\n", gScreenBounds.x, - gScreenBounds.y, gScreenBounds.width, gScreenBounds.height); - return gScreenBounds; -} - -NS_IMETHODIMP -UIKitScreenManager::GetPrimaryScreen(nsIScreen** outScreen) { - NS_IF_ADDREF(*outScreen = mScreen.get()); - return NS_OK; -} - -NS_IMETHODIMP -UIKitScreenManager::ScreenForRect(int32_t inLeft, int32_t inTop, - int32_t inWidth, int32_t inHeight, - nsIScreen** outScreen) { - return GetPrimaryScreen(outScreen); -} diff --git a/widget/uikit/nsWidgetFactory.h b/widget/uikit/nsWidgetFactory.h new file mode 100644 index 0000000000..570639e495 --- /dev/null +++ b/widget/uikit/nsWidgetFactory.h @@ -0,0 +1,21 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef widget_uikit_nsWidgetFactory_h +#define widget_uikit_nsWidgetFactory_h + +#include "nscore.h" +#include "nsID.h" + +class nsISupports; + +nsresult nsAppShellConstructor(const nsIID& iid, void** result); + +void nsWidgetUIKitModuleCtor(); +void nsWidgetUIKitModuleDtor(); + +#endif // defined widget_uikit_nsWidgetFactory_h diff --git a/widget/uikit/nsWidgetFactory.mm b/widget/uikit/nsWidgetFactory.mm index 356e5b8cdf..e5b24d200f 100644 --- a/widget/uikit/nsWidgetFactory.mm +++ b/widget/uikit/nsWidgetFactory.mm @@ -3,51 +3,18 @@ * 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.h" -#include "mozilla/ModuleUtils.h" - -#include "nsWidgetsCID.h" - #include "nsAppShell.h" #include "nsAppShellSingleton.h" #include "nsLookAndFeel.h" -#include "nsScreenManager.h" - -NS_GENERIC_FACTORY_CONSTRUCTOR(UIKitScreenManager) +#include "nsWidgetFactory.h" +#include "mozilla/WidgetUtils.h" -#include "GfxInfo.h" -namespace mozilla { -namespace widget { -// This constructor should really be shared with all platforms. -NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init) -} // namespace widget -} // namespace mozilla +using namespace mozilla::widget; -NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); -NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID); -NS_DEFINE_NAMED_CID(NS_GFXINFO_CID); +void nsWidgetUIKitModuleCtor() { nsAppShellInit(); } -static const mozilla::Module::CIDEntry kWidgetCIDs[] = { - {&kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor}, - {&kNS_SCREENMANAGER_CID, false, nullptr, UIKitScreenManagerConstructor}, - {&kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor}, - {nullptr}}; - -static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { - {"@mozilla.org/widget/appshell/uikit;1", &kNS_APPSHELL_CID}, - {"@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID}, - {"@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID}, - {nullptr}}; - -static void nsWidgetUIKitModuleDtor() { +void nsWidgetUIKitModuleDtor() { + WidgetUtils::Shutdown(); nsLookAndFeel::Shutdown(); nsAppShellShutdown(); } - -extern const mozilla::Module kWidgetModule = {mozilla::Module::kVersion, - kWidgetCIDs, - kWidgetContracts, - nullptr, - nullptr, - nsAppShellInit, - nsWidgetUIKitModuleDtor}; diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h index 5dad452930..cb5d676d7c 100644 --- a/widget/uikit/nsWindow.h +++ b/widget/uikit/nsWindow.h @@ -15,6 +15,10 @@ @class UIView; @class ChildView; +namespace mozilla::widget { +class TextInputHandler; +} + class nsWindow final : public nsBaseWidget { typedef nsBaseWidget Inherited; @@ -27,49 +31,47 @@ class nsWindow final : public nsBaseWidget { // nsIWidget // - [[nodiscard]] virtual nsresult Create( + [[nodiscard]] nsresult Create( nsIWidget* aParent, nsNativeWidget aNativeParent, const LayoutDeviceIntRect& aRect, - widget::InitData* aInitData = nullptr) override; - virtual void Destroy() override; - virtual void Show(bool aState) override; - virtual void Enable(bool aState) override {} - virtual bool IsEnabled() const override { return true; } - virtual bool IsVisible() const override { return mVisible; } - virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; - virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; - - virtual void SetBackgroundColor(const nscolor& aColor) override; - virtual void* GetNativeData(uint32_t aDataType) override; - - virtual void Move(double aX, double aY) override; - virtual nsSizeMode SizeMode() override { return mSizeMode; } - virtual void SetSizeMode(nsSizeMode aMode) override; + mozilla::widget::InitData* aInitData = nullptr) override; + void Destroy() override; + void Show(bool aState) override; + void Enable(bool aState) override {} + bool IsEnabled() const override { return true; } + bool IsVisible() const override { return mVisible; } + void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override; + LayoutDeviceIntPoint WidgetToScreenOffset() override; + + void SetBackgroundColor(const nscolor& aColor) override; + void* GetNativeData(uint32_t aDataType) override; + + void Move(double aX, double aY) override; + nsSizeMode SizeMode() override { return mSizeMode; } + void SetSizeMode(nsSizeMode aMode) override; void EnteredFullScreen(bool aFullScreen); - virtual void Resize(double aWidth, double aHeight, bool aRepaint) override; - virtual void Resize(double aX, double aY, double aWidth, double aHeight, - bool aRepaint) override; - virtual LayoutDeviceIntRect GetScreenBounds() override; + void Resize(double aWidth, double aHeight, bool aRepaint) override; + void Resize(double aX, double aY, double aWidth, double aHeight, + bool aRepaint) override; + LayoutDeviceIntRect GetScreenBounds() override; void ReportMoveEvent(); void ReportSizeEvent(); void ReportSizeModeEvent(nsSizeMode aMode); CGFloat BackingScaleFactor(); void BackingScaleFactorChanged(); - virtual float GetDPI() override { + float GetDPI() override { // XXX: terrible return 326.0f; } - virtual double GetDefaultScaleInternal() override { - return BackingScaleFactor(); - } - virtual int32_t RoundsWidgetCoordinatesTo() override; + double GetDefaultScaleInternal() override { return BackingScaleFactor(); } + int32_t RoundsWidgetCoordinatesTo() override; - virtual nsresult SetTitle(const nsAString& aTitle) override { return NS_OK; } + nsresult SetTitle(const nsAString& aTitle) override { return NS_OK; } - virtual void Invalidate(const LayoutDeviceIntRect& aRect) override; - virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, - nsEventStatus& aStatus) override; + void Invalidate(const LayoutDeviceIntRect& aRect) override; + nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, + nsEventStatus& aStatus) override; void WillPaintWindow(); bool PaintWindow(LayoutDeviceIntRegion aRegion); @@ -78,9 +80,16 @@ class nsWindow final : public nsBaseWidget { // virtual nsresult // NotifyIME(const IMENotification& aIMENotification) override; - virtual void SetInputContext(const InputContext& aContext, - const InputContextAction& aAction); - virtual InputContext GetInputContext(); + void SetInputContext(const InputContext& aContext, + const InputContextAction& aAction) override; + InputContext GetInputContext() override; + TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override; + + mozilla::widget::TextInputHandler* GetTextInputHandler() const { + return mTextInputHandler; + } + bool IsVirtualKeyboardDisabled() const; + /* virtual bool ExecuteNativeKeyBinding( NativeKeyBindingsType aType, @@ -104,7 +113,9 @@ class nsWindow final : public nsBaseWidget { nsSizeMode mSizeMode; nsTArray<nsWindow*> mChildren; nsWindow* mParent; - InputContext mInputContext; + + mozilla::widget::InputContext mInputContext; + RefPtr<mozilla::widget::TextInputHandler> mTextInputHandler; void OnSizeChanged(const mozilla::gfx::IntSize& aSize); diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm index 0c1a38c27c..51b317ee61 100644 --- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #import <UIKit/UIEvent.h> +#import <UIKit/UIKit.h> #import <UIKit/UIGraphics.h> #import <UIKit/UIInterface.h> #import <UIKit/UIScreen.h> @@ -17,29 +18,41 @@ #include <algorithm> #include "nsWindow.h" -#include "nsScreenManager.h" #include "nsAppShell.h" +#ifdef ACCESSIBILITY +# include "nsAccessibilityService.h" +# include "mozilla/a11y/LocalAccessible.h" +#endif #include "nsWidgetsCID.h" #include "nsGfxCIID.h" +#include "gfxPlatform.h" #include "gfxQuartzSurface.h" #include "gfxUtils.h" #include "gfxImageSurface.h" #include "gfxContext.h" +#include "nsObjCExceptions.h" #include "nsRegion.h" -#include "Layers.h" #include "nsTArray.h" +#include "TextInputHandler.h" +#include "UIKitUtils.h" #include "mozilla/BasicEvents.h" #include "mozilla/ProfilerLabels.h" #include "mozilla/TouchEvents.h" #include "mozilla/Unused.h" #include "mozilla/dom/MouseEventBinding.h" +#include "mozilla/gfx/Logging.h" +#ifdef ACCESSIBILITY +# include "mozilla/a11y/MUIRootAccessibleProtocol.h" +#endif using namespace mozilla; -using namespace mozilla::dom; +using namespace mozilla::gfx; using namespace mozilla::layers; +using mozilla::dom::Touch; +using mozilla::widget::UIKitUtils; #define ALOG(args...) \ fprintf(stderr, args); \ @@ -62,18 +75,22 @@ static CGRect DevPixelsToUIKitPoints(const LayoutDeviceIntRect& aRect, // Used to retain a Cocoa object for the remainder of a method's execution. class nsAutoRetainUIKitObject { public: - nsAutoRetainUIKitObject(id anObject) { mObject = [anObject retain]; } + explicit nsAutoRetainUIKitObject(id anObject) { mObject = [anObject retain]; } ~nsAutoRetainUIKitObject() { [mObject release]; } private: id mObject; // [STRONG] }; -@interface ChildView : UIView { +#ifdef ACCESSIBILITY +@interface ChildView : UIView <UIKeyInput, MUIRootAccessibleProtocol> { +#else +@interface ChildView : UIView <UIKeyInput> { +#endif @public nsWindow* mGeckoChild; // weak ref BOOL mWaitingForPaint; - CFMutableDictionaryRef mTouches; + NSMapTable<UITouch*, NSNumber*>* mTouches; int mNextTouchID; } // sets up our view, attaching it to its owning gecko view @@ -93,10 +110,32 @@ class nsAutoRetainUIKitObject { touches:(NSSet*)aTouches widget:(nsWindow*)aWindow; // Event handling (UIResponder) -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event; -- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event; -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event; -- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event; +- (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event; +- (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event; +- (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event; + +- (void)activateWindow:(NSNotification*)notification; +- (void)deactivateWindow:(NSNotification*)notification; + +#ifdef ACCESSIBILITY +// MUIRootAccessible +- (BOOL)hasRepresentedView; +- (id)representedView; + +// MUIAccessible +- (BOOL)isAccessibilityElement; +- (NSString*)accessibilityLabel; +- (CGRect)accessibilityFrame; +- (NSString*)accessibilityValue; +- (uint64_t)accessibilityTraits; +- (NSInteger)accessibilityElementCount; +- (nullable id)accessibilityElementAtIndex:(NSInteger)index; +- (NSInteger)indexOfAccessibilityElement:(id)element; +- (NSArray* _Nullable)accessibilityElements; +- (UIAccessibilityContainerType)accessibilityContainerType; +#endif + @end @implementation ChildView @@ -120,15 +159,29 @@ class nsAutoRetainUIKitObject { tapRecognizer.numberOfTapsRequired = 1; [self addGestureRecognizer:tapRecognizer]; - mTouches = - CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr); + mTouches = [[NSMapTable alloc] init]; mNextTouchID = 0; + + // This is managed with weak references by the notification center so that we + // do not need to call removeObserver. + // https://developer.apple.com/documentation/foundation/nsnotificationcenter/1415360-addobserver#discussion + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(activateWindow:) + name:UIWindowDidBecomeKeyNotification + object:nil]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(deactivateWindow:) + name:UIWindowDidResignKeyNotification + object:nil]; + return self; } - (void)widgetDestroyed { mGeckoChild = nullptr; - CFRelease(mTouches); + [mTouches release]; } - (void)delayedTearDown { @@ -136,6 +189,30 @@ class nsAutoRetainUIKitObject { [self release]; } +- (void)activateWindow:(NSNotification*)notification { + ALOG("[[ChildView[%p] activateWindow]", (void*)self); + + if (!mGeckoChild) { + return; + } + + if (nsIWidgetListener* listener = mGeckoChild->GetWidgetListener()) { + listener->WindowActivated(); + } +} + +- (void)deactivateWindow:(NSNotification*)notification { + ALOG("[[ChildView[%p] deactivateWindow]", (void*)self); + + if (!mGeckoChild) { + return; + } + + if (nsIWidgetListener* listener = mGeckoChild->GetWidgetListener()) { + listener->WindowDeactivated(); + } +} + - (void)sendMouseEvent:(EventMessage)aType point:(LayoutDeviceIntPoint)aPoint widget:(nsWindow*)aWindow { @@ -144,9 +221,8 @@ class nsAutoRetainUIKitObject { event.mRefPoint = aPoint; event.mClickCount = 1; - event.button = MouseButton::ePrimary; - event.mTime = PR_IntervalNow(); - event.inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; + event.mButton = MouseButton::ePrimary; + event.mInputSource = mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; nsEventStatus status; aWindow->DispatchEvent(&event, status); @@ -169,20 +245,20 @@ class nsAutoRetainUIKitObject { WidgetTouchEvent event(true, aType, aWindow); // XXX: I think nativeEvent.timestamp * 1000 is probably usable here but // I don't care that much right now. - event.mTime = PR_IntervalNow(); event.mTouches.SetCapacity(aTouches.count); for (UITouch* touch in aTouches) { LayoutDeviceIntPoint loc = UIKitPointsToDevPixels( [touch locationInView:self], [self contentScaleFactor]); - LayoutDeviceIntPoint radius = - UIKitPointsToDevPixels([touch majorRadius], [touch majorRadius]); - void* value; - if (!CFDictionaryGetValueIfPresent(mTouches, touch, (const void**)&value)) { + LayoutDeviceIntPoint radius = UIKitPointsToDevPixels( + CGPointMake([touch majorRadius], [touch majorRadius]), + [self contentScaleFactor]); + NSNumber* value = [mTouches objectForKey:touch]; + if (value == nil) { // This shouldn't happen. NS_ASSERTION(false, "Got a touch that we didn't know about"); continue; } - int id = reinterpret_cast<int>(value); + int id = [value intValue]; RefPtr<Touch> t = new Touch(id, loc, radius, 0.0f, 1.0f); event.mRefPoint = loc; event.mTouches.AppendElement(t); @@ -190,12 +266,12 @@ class nsAutoRetainUIKitObject { aWindow->DispatchInputEvent(&event); } -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { +- (void)touchesBegan:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event { ALOG("[ChildView[%p] touchesBegan", self); if (!mGeckoChild) return; for (UITouch* touch : touches) { - CFDictionaryAddValue(mTouches, touch, (void*)mNextTouchID); + [mTouches setObject:[NSNumber numberWithInt:mNextTouchID] forKey:touch]; mNextTouchID++; } [self sendTouchEvent:eTouchStart @@ -203,31 +279,31 @@ class nsAutoRetainUIKitObject { widget:mGeckoChild]; } -- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { +- (void)touchesCancelled:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event { ALOG("[ChildView[%p] touchesCancelled", self); [self sendTouchEvent:eTouchCancel touches:touches widget:mGeckoChild]; for (UITouch* touch : touches) { - CFDictionaryRemoveValue(mTouches, touch); + [mTouches removeObjectForKey:touch]; } - if (CFDictionaryGetCount(mTouches) == 0) { + if (mTouches.count == 0) { mNextTouchID = 0; } } -- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { +- (void)touchesEnded:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event { ALOG("[ChildView[%p] touchesEnded", self); if (!mGeckoChild) return; [self sendTouchEvent:eTouchEnd touches:touches widget:mGeckoChild]; for (UITouch* touch : touches) { - CFDictionaryRemoveValue(mTouches, touch); + [mTouches removeObjectForKey:touch]; } - if (CFDictionaryGetCount(mTouches) == 0) { + if (mTouches.count == 0) { mNextTouchID = 0; } } -- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { +- (void)touchesMoved:(NSSet<UITouch*>*)touches withEvent:(UIEvent*)event { ALOG("[ChildView[%p] touchesMoved", self); if (!mGeckoChild) return; @@ -236,6 +312,17 @@ class nsAutoRetainUIKitObject { widget:mGeckoChild]; } +- (BOOL)canBecomeFirstResponder { + if (!mGeckoChild) { + return NO; + } + + if (mGeckoChild->IsVirtualKeyboardDisabled()) { + return NO; + } + return YES; +} + - (void)setNeedsDisplayInRect:(CGRect)aRect { if ([self isUsingMainThreadOpenGL]) { // Draw without calling drawRect. This prevent us from @@ -326,7 +413,8 @@ class nsAutoRetainUIKitObject { CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale); CGSize viewSize = [self bounds].size; - gfx::IntSize backingSize(viewSize.width * scale, viewSize.height * scale); + gfx::IntSize backingSize(NSToIntRound(viewSize.width * scale), + NSToIntRound(viewSize.height * scale)); CGContextSaveGState(aContext); @@ -339,13 +427,12 @@ class nsAutoRetainUIKitObject { // Create Cairo objects. RefPtr<gfxQuartzSurface> targetSurface; - UniquePtrPtr<gfxContext> targetContext; + UniquePtr<gfxContext> targetContext; if (gfxPlatform::GetPlatform()->SupportsAzureContentForType( gfx::BackendType::CAIRO)) { // This is dead code unless you mess with prefs, but keep it around for // debugging. targetSurface = new gfxQuartzSurface(aContext, backingSize); - targetSurface->SetAllowUseAsSource(false); RefPtr<gfx::DrawTarget> dt = gfxPlatform::CreateDrawTargetForSurface(targetSurface, backingSize); if (!dt || !dt->IsValid()) { @@ -399,6 +486,183 @@ class nsAutoRetainUIKitObject { CGContextStrokeRect(aContext, aRect); #endif } + +// UIKeyInput + +- (void)insertText:(NSString*)text { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return; + } + widget::TextInputHandler* textInputHandler = + mGeckoChild->GetTextInputHandler(); + if (!textInputHandler) { + return; + } + textInputHandler->InsertText(text); +} + +- (void)deleteBackward { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return; + } + widget::TextInputHandler* textInputHandler = + mGeckoChild->GetTextInputHandler(); + if (!textInputHandler) { + return; + } + textInputHandler->HandleCommand(Command::DeleteCharBackward); +} + +- (BOOL)hasText { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return NO; + } + widget::InputContext context = mGeckoChild->GetInputContext(); + if (context.mIMEState.mEnabled == mozilla::widget::IMEEnabled::Disabled) { + return NO; + } + return YES; +} + +// UITextInputTraits + +- (UIKeyboardType)keyboardType { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return UIKeyboardTypeDefault; + } + return UIKitUtils::GetUIKeyboardType(mGeckoChild->GetInputContext()); +} + +- (UIReturnKeyType)returnKeyType { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return UIReturnKeyDefault; + } + return UIKitUtils::GetUIReturnKeyType(mGeckoChild->GetInputContext()); +} + +- (UITextAutocapitalizationType)autocapitalizationType { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return UITextAutocapitalizationTypeNone; + } + return UIKitUtils::GetUITextAutocapitalizationType( + mGeckoChild->GetInputContext()); +} + +- (BOOL)isSecureTextEntry { + if (!mGeckoChild || mGeckoChild->Destroyed()) { + return NO; + } + if (mGeckoChild->GetInputContext().IsPasswordEditor()) { + return YES; + } + return NO; +} + +#ifdef ACCESSIBILITY +// MUIRootAccessible + +- (id<MUIRootAccessibleProtocol>)accessible { + if (!mGeckoChild) return nil; + + id<MUIRootAccessibleProtocol> nativeAccessible = nil; + + // nsAutoRetainCocoaObject kungFuDeathGrip(self); + RefPtr<nsWindow> geckoChild(mGeckoChild); + RefPtr<a11y::LocalAccessible> accessible = geckoChild->GetRootAccessible(); + if (!accessible) return nil; + + accessible->GetNativeInterface((void**)&nativeAccessible); + + return nativeAccessible; +} + +- (BOOL)hasRepresentedView { + return YES; +} + +- (id)representedView { + return self; +} + +- (BOOL)isAccessibilityElement { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super isAccessibilityElement]; + } + + return [[self accessible] isAccessibilityElement]; +} + +- (NSString*)accessibilityLabel { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityLabel]; + } + + return [[self accessible] accessibilityLabel]; +} + +- (CGRect)accessibilityFrame { + // Use the UIView implementation here. We rely on the position of this + // frame to place gecko bounds in the right offset. + return [super accessibilityFrame]; +} + +- (NSString*)accessibilityValue { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityValue]; + } + + return [[self accessible] accessibilityValue]; +} + +- (uint64_t)accessibilityTraits { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityTraits]; + } + + return [[self accessible] accessibilityTraits]; +} + +- (NSInteger)accessibilityElementCount { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityElementCount]; + } + + return [[self accessible] accessibilityElementCount]; +} + +- (nullable id)accessibilityElementAtIndex:(NSInteger)index { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityElementAtIndex:index]; + } + + return [[self accessible] accessibilityElementAtIndex:index]; +} + +- (NSInteger)indexOfAccessibilityElement:(id)element { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super indexOfAccessibilityElement:element]; + } + + return [[self accessible] indexOfAccessibilityElement:element]; +} + +- (NSArray* _Nullable)accessibilityElements { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityElements]; + } + + return [[self accessible] accessibilityElements]; +} + +- (UIAccessibilityContainerType)accessibilityContainerType { + if (!mozilla::a11y::ShouldA11yBeEnabled()) { + return [super accessibilityContainerType]; + } + + return [[self accessible] accessibilityContainerType]; +} +#endif + @end nsWindow::nsWindow() @@ -442,20 +706,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, if (parent == nullptr && nativeParent) parent = nativeParent->mGeckoChild; if (parent && nativeParent == nullptr) nativeParent = parent->mNativeView; - // for toplevel windows, bounds are fixed to full screen size - if (parent == nullptr) { - if (nsAppShell::gWindow == nil) { - mBounds = UIKitScreenManager::GetBounds(); - } else { - CGRect cgRect = [nsAppShell::gWindow bounds]; - mBounds.x = cgRect.origin.x; - mBounds.y = cgRect.origin.y; - mBounds.width = cgRect.size.width; - mBounds.height = cgRect.size.height; - } - } else { - mBounds = aRect; - } + mBounds = aRect; ALOG("nsWindow[%p]::Create bounds: %d %d %d %d", (void*)this, mBounds.x, mBounds.y, mBounds.width, mBounds.height); @@ -487,6 +738,8 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, [nsAppShell::gTopLevelViews addObject:mNativeView]; } + mTextInputHandler = new widget::TextInputHandler(this); + return NS_OK; } @@ -498,6 +751,11 @@ void nsWindow::Destroy() { if (mParent) mParent->mChildren.RemoveElement(this); + if (mTextInputHandler) { + mTextInputHandler->OnDestroyed(); + } + mTextInputHandler = nullptr; + [mNativeView widgetDestroyed]; nsBaseWidget::Destroy(); @@ -507,8 +765,6 @@ void nsWindow::Destroy() { TearDownView(); nsBaseWidget::OnDestroy(); - - return NS_OK; } void nsWindow::Show(bool aState) { @@ -595,16 +851,12 @@ void nsWindow::SetSizeMode(nsSizeMode aMode) { void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) { if (!mNativeView || !mVisible) return; - MOZ_RELEASE_ASSERT( - GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_WR, - "Shouldn't need to invalidate with accelerated OMTC layers!"); - [mNativeView setNeedsLayout]; [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints( mBounds, BackingScaleFactor())]; } -void nsWindow::SetFocus(Raise) { +void nsWindow::SetFocus(Raise, mozilla::dom::CallerType) { [[mNativeView window] makeKeyWindow]; [mNativeView becomeFirstResponder]; } @@ -649,10 +901,16 @@ void nsWindow::ReportSizeModeEvent(nsSizeMode aMode) { } void nsWindow::ReportSizeEvent() { + LayoutDeviceIntRect innerBounds = GetClientBounds(); + if (mWidgetListener) { - LayoutDeviceIntRect innerBounds = GetClientBounds(); mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height); } + + if (mAttachedWidgetListener) { + mAttachedWidgetListener->WindowResized(this, innerBounds.width, + innerBounds.height); + } } LayoutDeviceIntRect nsWindow::GetScreenBounds() { @@ -672,8 +930,8 @@ LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() { temp = [nsAppShell::gWindow convertPoint:temp toWindow:nil]; } - offset.x += temp.x; - offset.y += temp.y; + offset.x += static_cast<int32_t>(temp.x); + offset.y += static_cast<int32_t>(temp.y); return offset; } @@ -682,23 +940,63 @@ nsresult nsWindow::DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) { aStatus = nsEventStatus_eIgnore; nsCOMPtr<nsIWidget> kungFuDeathGrip(aEvent->mWidget); + mozilla::Unused << kungFuDeathGrip; // Not used within this function - if (mWidgetListener) + if (mAttachedWidgetListener) { + aStatus = mAttachedWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); + } else if (mWidgetListener) { aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); + } return NS_OK; } void nsWindow::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { - // TODO: actually show VKB + NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; + + const bool changingEnabledState = + aContext.IsInputAttributeChanged(mInputContext); + mInputContext = aContext; + + if (IsVirtualKeyboardDisabled()) { + [mNativeView resignFirstResponder]; + return; + } + + [mNativeView becomeFirstResponder]; + + if (aAction.UserMightRequestOpenVKB() || changingEnabledState) { + // TODO(m_kato): + // It is unnecessary to call reloadInputViews with changingEnabledState if + // virtual keyboard is disappeared. + [mNativeView reloadInputViews]; + } + + NS_OBJC_END_TRY_IGNORE_BLOCK; } -mozilla::widget::InputContext nsWindow::GetInputContext() { +widget::InputContext nsWindow::GetInputContext() { + if (!mTextInputHandler) { + InputContext context; + context.mIMEState.mEnabled = IMEEnabled::Disabled; + context.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; + return context; + } return mInputContext; } +widget::TextEventDispatcherListener* +nsWindow::GetNativeTextEventDispatcherListener() { + return mTextInputHandler; +} + +bool nsWindow::IsVirtualKeyboardDisabled() const { + return mInputContext.mIMEState.mEnabled == IMEEnabled::Disabled || + mInputContext.mHTMLInputMode.EqualsLiteral("none"); +} + void nsWindow::SetBackgroundColor(const nscolor& aColor) { mNativeView.backgroundColor = [UIColor colorWithRed:NS_GET_R(aColor) green:NS_GET_G(aColor) diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 6f033785f5..0e1b4002d3 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1092,25 +1092,20 @@ void GfxInfo::AddCrashReportAnnotations() { } nsString deviceID, vendorID, driverVersion, subsysID; - nsCString narrowDeviceID, narrowVendorID, narrowDriverVersion, narrowSubsysID; GetAdapterDeviceID(deviceID); - CopyUTF16toUTF8(deviceID, narrowDeviceID); GetAdapterVendorID(vendorID); - CopyUTF16toUTF8(vendorID, narrowVendorID); GetAdapterDriverVersion(driverVersion); - CopyUTF16toUTF8(driverVersion, narrowDriverVersion); GetAdapterSubsysID(subsysID); - CopyUTF16toUTF8(subsysID, narrowSubsysID); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterVendorID, - narrowVendorID); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterDeviceID, - narrowDeviceID); - CrashReporter::AnnotateCrashReport( - CrashReporter::Annotation::AdapterDriverVersion, narrowDriverVersion); - CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::AdapterSubsysID, - narrowSubsysID); + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterVendorID, vendorID); + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterDeviceID, deviceID); + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterDriverVersion, driverVersion); + CrashReporter::RecordAnnotationNSString( + CrashReporter::Annotation::AdapterSubsysID, subsysID); /* Add an App Note, this contains extra information. */ nsAutoCString note; @@ -1741,24 +1736,6 @@ const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { "FEATURE_UNQUALIFIED_P010_NVIDIA"); //////////////////////////////////// - // FEATURE_VIDEO_OVERLAY - ALLOWLIST -#ifdef EARLY_BETA_OR_EARLIER - APPEND_TO_DRIVER_BLOCKLIST2( - OperatingSystem::Windows, DeviceFamily::All, - nsIGfxInfo::FEATURE_VIDEO_OVERLAY, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, - DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_ROLLOUT_ALL"); -#else - APPEND_TO_DRIVER_BLOCKLIST2( - OperatingSystem::Windows, DeviceFamily::IntelAll, - nsIGfxInfo::FEATURE_VIDEO_OVERLAY, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, - DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_ROLLOUT_INTEL"); - APPEND_TO_DRIVER_BLOCKLIST2( - OperatingSystem::Windows, DeviceFamily::NvidiaAll, - nsIGfxInfo::FEATURE_VIDEO_OVERLAY, nsIGfxInfo::FEATURE_ALLOW_ALWAYS, - DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_ROLLOUT_NVIDIA"); -#endif - - //////////////////////////////////// // FEATURE_HW_DECODED_VIDEO_ZERO_COPY APPEND_TO_DRIVER_BLOCKLIST_RANGE( @@ -1801,9 +1778,13 @@ const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { //////////////////////////////////// // FEATURE_HW_DECODED_VIDEO_ZERO_COPY - ALLOWLIST - - // XXX ZeroCopyNV12Texture is disabled with non-intel GPUs for now. - // See Bug 1798242 +#ifdef EARLY_BETA_OR_EARLIER + APPEND_TO_DRIVER_BLOCKLIST2( + OperatingSystem::Windows, DeviceFamily::NvidiaAll, + nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY, + nsIGfxInfo::FEATURE_ALLOW_ALWAYS, DRIVER_COMPARISON_IGNORED, + V(0, 0, 0, 0), "FEATURE_ROLLOUT_ALL"); +#endif APPEND_TO_DRIVER_BLOCKLIST2( OperatingSystem::Windows, DeviceFamily::IntelAll, nsIGfxInfo::FEATURE_HW_DECODED_VIDEO_ZERO_COPY, @@ -1828,6 +1809,26 @@ const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { "Intel driver 10.18.15.*"); //////////////////////////////////// + // FEATURE_OVERLAY_VP_AUTO_HDR + + APPEND_TO_DRIVER_BLOCKLIST( + OperatingSystem::Windows, DeviceFamily::NvidiaAll, + nsIGfxInfo::FEATURE_OVERLAY_VP_AUTO_HDR, + nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN_OR_EQUAL, + V(31, 0, 15, 5050), "FEATURE_FAILURE_VP_AUTO_HDR", + "nVidia driver > 550.50"); + + //////////////////////////////////// + // FEATURE_OVERLAY_VP_SUPER_RESOLUTION + + APPEND_TO_DRIVER_BLOCKLIST( + OperatingSystem::Windows, DeviceFamily::NvidiaAll, + nsIGfxInfo::FEATURE_OVERLAY_VP_SUPER_RESOLUTION, + nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION, DRIVER_LESS_THAN_OR_EQUAL, + V(31, 0, 15, 3000), "FEATURE_FAILURE_VP_AUTO_HDR", + "nVidia driver > 530.00"); + + //////////////////////////////////// // FEATURE_WEBRENDER // Block 8.56.1.15/16 APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Windows, DeviceFamily::AtiAll, diff --git a/widget/windows/JumpListBuilder.cpp b/widget/windows/JumpListBuilder.cpp index 80b1c29aa7..e25c8c038f 100644 --- a/widget/windows/JumpListBuilder.cpp +++ b/widget/windows/JumpListBuilder.cpp @@ -176,8 +176,9 @@ JumpListBuilder::JumpListBuilder(const nsAString& aAppUserModelId, // the app, as it is set in the package manifest instead. if (!mozilla::widget::WinUtils::HasPackageIdentity()) { mIOThread->Dispatch( - NewRunnableMethod<nsString>( - "SetAppID", this, &JumpListBuilder::DoSetAppID, aAppUserModelId), + NewRunnableMethod<nsString>("SetAppID", this, + &JumpListBuilder::DoSetAppIDIfAvailable, + aAppUserModelId), NS_DISPATCH_NORMAL); } } @@ -203,10 +204,13 @@ void JumpListBuilder::DoShutdownBackend() { mJumpListBackend = nullptr; } -void JumpListBuilder::DoSetAppID(nsString aAppUserModelID) { +void JumpListBuilder::DoSetAppIDIfAvailable(nsString aAppUserModelID) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(mJumpListBackend); - mJumpListBackend->SetAppID(aAppUserModelID.get()); + + if (mJumpListBackend->IsAvailable()) { + mJumpListBackend->SetAppID(aAppUserModelID.get()); + } } NS_IMETHODIMP diff --git a/widget/windows/JumpListBuilder.h b/widget/windows/JumpListBuilder.h index e228669f11..b8d5253153 100644 --- a/widget/windows/JumpListBuilder.h +++ b/widget/windows/JumpListBuilder.h @@ -64,7 +64,7 @@ class JumpListBuilder : public nsIJumpListBuilder, public nsIObserver { void DoSetupBackend(); void DoSetupTestingBackend(RefPtr<JumpListBackend> aTestingBackend); void DoShutdownBackend(); - void DoSetAppID(nsString aAppUserModelID); + void DoSetAppIDIfAvailable(nsString aAppUserModelID); void DoIsAvailable(const nsMainThreadPtrHandle<dom::Promise>& aPromiseHolder); void DoCheckForRemovals( const nsMainThreadPtrHandle<dom::Promise>& aPromiseHolder); diff --git a/widget/windows/WinTaskbar.cpp b/widget/windows/WinTaskbar.cpp index 56608503da..66854f3ee2 100644 --- a/widget/windows/WinTaskbar.cpp +++ b/widget/windows/WinTaskbar.cpp @@ -223,6 +223,11 @@ bool WinTaskbar::GenerateAppUserModelID(nsAString& aAppUserModelId, id.AppendInt(HashString(path)); if (!id.IsEmpty()) { aAppUserModelId.Assign(id); + + if (aPrivateBrowsing) { + aAppUserModelId.AppendLiteral(";PrivateBrowsingAUMID"); + } + return true; } } diff --git a/widget/windows/WindowsSMTCProvider.cpp b/widget/windows/WindowsSMTCProvider.cpp index 04d833a8e7..69915cafd8 100644 --- a/widget/windows/WindowsSMTCProvider.cpp +++ b/widget/windows/WindowsSMTCProvider.cpp @@ -255,6 +255,25 @@ void WindowsSMTCProvider::UnregisterEvents() { if (mControls && mButtonPressedToken.value != 0) { mControls->remove_ButtonPressed(mButtonPressedToken); } + + if (mControls && mSeekRegistrationToken.value != 0) { + ComPtr<ISystemMediaTransportControls2> smtc2; + HRESULT hr = mControls.As(&smtc2); + if (FAILED(hr)) { + LOG("Failed to cast controls to ISystemMediaTransportControls2 (hr=%lx)", + hr); + return; + } + MOZ_ASSERT(smtc2); + + hr = smtc2->remove_PlaybackPositionChangeRequested(mSeekRegistrationToken); + if (FAILED(hr)) { + LOG("SystemMediaTransportControls: Failed unregister position change " + "event (hr=%lx)", + hr); + return; + } + } } bool WindowsSMTCProvider::RegisterEvents() { @@ -289,6 +308,48 @@ bool WindowsSMTCProvider::RegisterEvents() { return false; } + ComPtr<ISystemMediaTransportControls2> smtc2; + HRESULT hr = mControls.As(&smtc2); + if (FAILED(hr)) { + LOG("Failed to cast controls to ISystemMediaTransportControls2 (hr=%lx)", + hr); + return false; + } + MOZ_ASSERT(smtc2); + + auto positionChangeRequested = + Callback<ITypedEventHandler<SystemMediaTransportControls*, + PlaybackPositionChangeRequestedEventArgs*>>( + [this, self]( + ISystemMediaTransportControls*, + IPlaybackPositionChangeRequestedEventArgs* pArgs) -> HRESULT { + MOZ_ASSERT(pArgs); + + TimeSpan value; + HRESULT hr = pArgs->get_RequestedPlaybackPosition(&value); + if (FAILED(hr)) { + LOG("SystemMediaTransportControls: Playback Position Change - " + "failed to get requested position (hr=%lx)", + hr); + return S_OK; // Propagating the error probably wouldn't help. + } + + double position = + static_cast<double>(value.Duration) / (1e6 * 10.0); + this->OnPositionChangeRequested(position); + + return S_OK; + }); + + hr = smtc2->add_PlaybackPositionChangeRequested(positionChangeRequested.Get(), + &mSeekRegistrationToken); + if (FAILED(hr)) { + LOG("SystemMediaTransportControls: Failed to register position change " + "event (hr=%lx)", + hr); + return false; + } + return true; } @@ -309,12 +370,14 @@ bool WindowsSMTCProvider::EnableControl(bool aEnabled) const { return SUCCEEDED(mControls->put_IsEnabled(aEnabled)); } -bool WindowsSMTCProvider::UpdateButtons() const { +bool WindowsSMTCProvider::UpdateButtons() { static const mozilla::dom::MediaControlKey kKeys[] = { - mozilla::dom::MediaControlKey::Play, mozilla::dom::MediaControlKey::Pause, + mozilla::dom::MediaControlKey::Play, + mozilla::dom::MediaControlKey::Pause, mozilla::dom::MediaControlKey::Previoustrack, mozilla::dom::MediaControlKey::Nexttrack, - mozilla::dom::MediaControlKey::Stop}; + mozilla::dom::MediaControlKey::Stop, + mozilla::dom::MediaControlKey::Seekto}; bool success = true; for (const mozilla::dom::MediaControlKey& key : kKeys) { @@ -347,12 +410,28 @@ bool WindowsSMTCProvider::EnableKey(mozilla::dom::MediaControlKey aKey, return SUCCEEDED(mControls->put_IsNextEnabled(aEnable)); case mozilla::dom::MediaControlKey::Stop: return SUCCEEDED(mControls->put_IsStopEnabled(aEnable)); + case mozilla::dom::MediaControlKey::Seekto: + // The callback for the event checks if the key is supported + return mSeekRegistrationToken.value != 0; default: LOG("No button for %s", ToMediaControlKeyStr(aKey)); return false; } } +void WindowsSMTCProvider::OnPositionChangeRequested(double aPosition) const { + if (!IsKeySupported(mozilla::dom::MediaControlKey::Seekto)) { + LOG("Seekto is not supported"); + return; + } + + for (const auto& listener : mListeners) { + listener->OnActionPerformed( + mozilla::dom::MediaControlAction(mozilla::dom::MediaControlKey::Seekto, + mozilla::dom::SeekDetails(aPosition))); + } +} + bool WindowsSMTCProvider::InitDisplayAndControls() { // As Open() might be called multiple times, "cache" the results of the COM // API @@ -422,6 +501,100 @@ bool WindowsSMTCProvider::SetMusicMetadata(const nsString& aArtist, return true; } +void WindowsSMTCProvider::SetPositionState( + const mozilla::Maybe<mozilla::dom::PositionState>& aState) { + ComPtr<ISystemMediaTransportControls2> smtc2; + HRESULT hr = mControls.As(&smtc2); + if (FAILED(hr)) { + LOG("Failed to cast controls to ISystemMediaTransportControls2 (hr=%lx)", + hr); + return; + } + MOZ_ASSERT(smtc2); + + ComPtr<ISystemMediaTransportControlsTimelineProperties> properties; + hr = RoActivateInstance( + HStringReference( + RuntimeClass_Windows_Media_SystemMediaTransportControlsTimelineProperties) + .Get(), + &properties); + if (FAILED(hr)) { + LOG("Failed to create timeline properties (hr=%lx)", hr); + return; + } + MOZ_ASSERT(properties); + + // Converts a value in seconds to a TimeSpan + // The TimeSpan's Duration is a value in 100ns ticks + // https://learn.microsoft.com/en-us/windows/win32/api/windows.foundation/ns-windows-foundation-timespan#members + auto toTimeSpan = [this](double seconds) { + constexpr double kMaxMicroseconds = + static_cast<double>(std::numeric_limits<LONG64>::max() / 10); + + double microseconds = seconds * 1e6; + + if (microseconds > kMaxMicroseconds) { + LOG("Failed to convert %f microseconds to TimeSpan (overflow)", + microseconds); + return TimeSpan{0}; + } + + return TimeSpan{static_cast<LONG64>(microseconds * 10.0)}; + }; + + TimeSpan endTime{0}; + TimeSpan position{0}; + double playbackRate = 1.0; + + if (aState) { + endTime = toTimeSpan(aState->mDuration); + position = toTimeSpan(aState->CurrentPlaybackPosition()); + playbackRate = aState->mPlaybackRate; + } + + hr = properties->put_StartTime({0}); + if (FAILED(hr)) { + LOG("Failed to set the start time (hr=%lx)", hr); + return; + } + + hr = properties->put_MinSeekTime({0}); + if (FAILED(hr)) { + LOG("Failed to set the min seek time (hr=%lx)", hr); + return; + } + + hr = properties->put_EndTime(endTime); + if (FAILED(hr)) { + LOG("Failed to set the end time (hr=%lx)", hr); + return; + } + + hr = properties->put_MaxSeekTime(endTime); + if (FAILED(hr)) { + LOG("Failed to set the max seek time (hr=%lx)", hr); + return; + } + + hr = properties->put_Position(position); + if (FAILED(hr)) { + LOG("Failed to set the playback position (hr=%lx)", hr); + return; + } + + hr = smtc2->UpdateTimelineProperties(properties.Get()); + if (FAILED(hr)) { + LOG("Failed to update timeline properties (hr=%lx)", hr); + return; + } + + hr = smtc2->put_PlaybackRate(playbackRate); + if (FAILED(hr)) { + LOG("Failed to set the playback rate (hr=%lx)", hr); + return; + } +} + void WindowsSMTCProvider::LoadThumbnail( const nsTArray<mozilla::dom::MediaImage>& aArtwork) { MOZ_ASSERT(NS_IsMainThread()); diff --git a/widget/windows/WindowsSMTCProvider.h b/widget/windows/WindowsSMTCProvider.h index 3926618d1f..2f0d1f8344 100644 --- a/widget/windows/WindowsSMTCProvider.h +++ b/widget/windows/WindowsSMTCProvider.h @@ -46,6 +46,9 @@ class WindowsSMTCProvider final : public mozilla::dom::MediaControlKeySource { void SetSupportedMediaKeys(const MediaKeysArray& aSupportedKeys) override; + void SetPositionState( + const mozilla::Maybe<mozilla::dom::PositionState>& aState) override; + private: ~WindowsSMTCProvider(); void UnregisterEvents(); @@ -54,12 +57,14 @@ class WindowsSMTCProvider final : public mozilla::dom::MediaControlKeySource { void OnButtonPressed(mozilla::dom::MediaControlKey aKey) const; // Enable the SMTC interface bool EnableControl(bool aEnabled) const; - // Sets the play, pause, next, previous buttons on the SMTC interface by - // mSupportedKeys - bool UpdateButtons() const; + // Sets the play, pause, next, previous, seekto buttons on the SMTC interface + // by mSupportedKeys + bool UpdateButtons(); bool IsKeySupported(mozilla::dom::MediaControlKey aKey) const; bool EnableKey(mozilla::dom::MediaControlKey aKey, bool aEnable) const; + void OnPositionChangeRequested(double aPosition) const; + bool InitDisplayAndControls(); // Sets the Metadata for the currently playing media and sets the playback @@ -122,6 +127,7 @@ class WindowsSMTCProvider final : public mozilla::dom::MediaControlKeySource { // EventRegistrationTokens are used to have a handle on a callback (to remove // it again) EventRegistrationToken mButtonPressedToken; + EventRegistrationToken mSeekRegistrationToken; }; #endif // __MINGW32__ diff --git a/widget/windows/filedialog/WinFileDialogChild.cpp b/widget/windows/filedialog/WinFileDialogChild.cpp index 1a2903f8ec..a41018ff0e 100644 --- a/widget/windows/filedialog/WinFileDialogChild.cpp +++ b/widget/windows/filedialog/WinFileDialogChild.cpp @@ -40,11 +40,8 @@ WinFileDialogChild::~WinFileDialogChild() { template <size_t N> WinFileDialogChild::IPCResult WinFileDialogChild::MakeIpcFailure( HRESULT hr, const char (&what)[N]) { - // The crash-report annotator stringifies integer values anyway. We do so - // eagerly here to avoid questions about C int/long conversion semantics. - nsPrintfCString data("%lu", hr); - CrashReporter::AnnotateCrashReport( - CrashReporter::Annotation::WindowsFileDialogErrorCode, data); + CrashReporter::RecordAnnotationU32( + CrashReporter::Annotation::WindowsFileDialogErrorCode, hr); return IPC_FAIL(this, what); } diff --git a/widget/windows/filedialog/WinFileDialogCommands.cpp b/widget/windows/filedialog/WinFileDialogCommands.cpp index f0503ab8f0..838856893d 100644 --- a/widget/windows/filedialog/WinFileDialogCommands.cpp +++ b/widget/windows/filedialog/WinFileDialogCommands.cpp @@ -294,7 +294,7 @@ void LogProcessingError(LogModule* aModule, ipc::IProtocol* aCaller, ipc::SandboxingKind::WINDOWS_FILE_DIALOG); } else { // ... which (presumably) is us - CrashReporter::AnnotateCrashReport( + CrashReporter::AutoRecordAnnotation( CrashReporter::Annotation::ipc_channel_error, reason); MOZ_CRASH("IPC error"); diff --git a/widget/windows/nsFilePicker.cpp b/widget/windows/nsFilePicker.cpp index 310c54bb40..fb4c6e80b5 100644 --- a/widget/windows/nsFilePicker.cpp +++ b/widget/windows/nsFilePicker.cpp @@ -19,6 +19,7 @@ #include "mozilla/BackgroundHangMonitor.h" #include "mozilla/Components.h" #include "mozilla/dom/BrowsingContext.h" +#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/Directory.h" #include "mozilla/Logging.h" #include "mozilla/ipc/UtilityProcessManager.h" @@ -94,19 +95,14 @@ nsFilePicker::nsFilePicker() : mSelectedType(1) {} NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker) NS_IMETHODIMP nsFilePicker::Init( - mozIDOMWindowProxy* aParent, const nsAString& aTitle, - nsIFilePicker::Mode aMode, - mozilla::dom::BrowsingContext* aBrowsingContext) { + mozilla::dom::BrowsingContext* aBrowsingContext, const nsAString& aTitle, + nsIFilePicker::Mode aMode) { // Don't attempt to open a real file-picker in headless mode. if (gfxPlatform::IsHeadless()) { return nsresult::NS_ERROR_NOT_AVAILABLE; } - nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aParent); - nsIDocShell* docShell = window ? window->GetDocShell() : nullptr; - mLoadContext = do_QueryInterface(docShell); - - return nsBaseFilePicker::Init(aParent, aTitle, aMode, aBrowsingContext); + return nsBaseFilePicker::Init(aBrowsingContext, aTitle, aMode); } namespace mozilla::detail { @@ -1053,7 +1049,7 @@ void nsFilePicker::RememberLastUsedDirectory() { } bool nsFilePicker::IsPrivacyModeEnabled() { - return mLoadContext && mLoadContext->UsePrivateBrowsing(); + return mBrowsingContext && mBrowsingContext->UsePrivateBrowsing(); } bool nsFilePicker::IsDefaultPathLink() { diff --git a/widget/windows/nsFilePicker.h b/widget/windows/nsFilePicker.h index 1938b8bcb6..59108bc0dd 100644 --- a/widget/windows/nsFilePicker.h +++ b/widget/windows/nsFilePicker.h @@ -66,9 +66,8 @@ class nsFilePicker final : public nsBaseWinFilePicker { public: nsFilePicker(); - NS_IMETHOD Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle, - nsIFilePicker::Mode aMode, - mozilla::dom::BrowsingContext* aBrowsingContext) override; + NS_IMETHOD Init(mozilla::dom::BrowsingContext* aBrowsingContext, + const nsAString& aTitle, nsIFilePicker::Mode aMode) override; NS_DECL_ISUPPORTS @@ -117,7 +116,6 @@ class nsFilePicker final : public nsBaseWinFilePicker { bool IsDefaultPathLink(); bool IsDefaultPathHtml(); - nsCOMPtr<nsILoadContext> mLoadContext; nsCOMPtr<nsIWidget> mParentWidget; nsString mTitle; nsCString mFile; diff --git a/widget/windows/nsLookAndFeel.cpp b/widget/windows/nsLookAndFeel.cpp index 01b126cd42..6122000c9e 100644 --- a/widget/windows/nsLookAndFeel.cpp +++ b/widget/windows/nsLookAndFeel.cpp @@ -409,9 +409,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { // (400ms) on error. aResult = GetSystemParam(SPI_GETMENUSHOWDELAY, 400); break; - case IntID::TooltipDelay: - aResult = 500; - break; case IntID::MenusCanOverlapOSBar: // we want XUL popups to be able to overlap the task bar. aResult = 1; diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 1a8646d620..b304d2efac 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -175,12 +175,11 @@ #include <zmouse.h> #include <richedit.h> -#if defined(ACCESSIBILITY) - +#ifdef ACCESSIBILITY # ifdef DEBUG # include "mozilla/a11y/Logging.h" # endif - +# include "mozilla/a11y/Compatibility.h" # include "oleidl.h" # include <winuser.h> # include "nsAccessibilityService.h" @@ -190,7 +189,7 @@ # if !defined(WINABLEAPI) # include <winable.h> # endif // !defined(WINABLEAPI) -#endif // defined(ACCESSIBILITY) +#endif #include "WindowsUIUtils.h" @@ -1221,18 +1220,19 @@ static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512); /* static */ const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType) { - switch (aWindowType) { - case WindowType::Invisible: - return RegisterWindowClass(kClassNameHidden, 0, gStockApplicationIcon); - case WindowType::Dialog: - return RegisterWindowClass(kClassNameDialog, 0, nullptr); - case WindowType::Popup: - return RegisterWindowClass(kClassNameDropShadow, 0, - gStockApplicationIcon); - default: - return RegisterWindowClass(GetMainWindowClass(), 0, - gStockApplicationIcon); - } + const wchar_t* className = [aWindowType] { + switch (aWindowType) { + case WindowType::Invisible: + return kClassNameHidden; + case WindowType::Dialog: + return kClassNameDialog; + case WindowType::Popup: + return kClassNameDropShadow; + default: + return GetMainWindowClass(); + } + }(); + return RegisterWindowClass(className, 0, gStockApplicationIcon); } /************************************************************** @@ -1310,13 +1310,6 @@ DWORD nsWindow::WindowStyle() { if (mBorderStyle == BorderStyle::None || !(mBorderStyle & BorderStyle::Maximize)) style &= ~WS_MAXIMIZEBOX; - - if (IsPopupWithTitleBar()) { - style |= WS_CAPTION; - if (mBorderStyle & BorderStyle::Close) { - style |= WS_SYSMENU; - } - } } if (mIsChildWindow) { @@ -1332,7 +1325,6 @@ DWORD nsWindow::WindowStyle() { // Return nsWindow extended styles DWORD nsWindow::WindowExStyle() { - MOZ_ASSERT_IF(mIsAlert, mWindowType == WindowType::Dialog); switch (mWindowType) { case WindowType::Child: return 0; @@ -1343,18 +1335,18 @@ DWORD nsWindow::WindowExStyle() { } return extendedStyle; } - case WindowType::Dialog: { - if (mIsAlert) { - return WS_EX_TOOLWINDOW | WS_EX_TOPMOST; - } - return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME; - } case WindowType::Sheet: MOZ_FALLTHROUGH_ASSERT("Sheets are macOS specific"); + case WindowType::Dialog: case WindowType::TopLevel: case WindowType::Invisible: break; } + if (mIsAlert) { + MOZ_ASSERT(mWindowType == WindowType::Dialog, + "Expect alert windows to have type=dialog"); + return WS_EX_TOOLWINDOW | WS_EX_TOPMOST; + } return WS_EX_WINDOWEDGE; } @@ -3058,9 +3050,7 @@ void nsWindow::SetTransparencyMode(TransparencyMode aMode) { void nsWindow::UpdateWindowDraggingRegion( const LayoutDeviceIntRegion& aRegion) { - if (mDraggableRegion != aRegion) { - mDraggableRegion = aRegion; - } + mDraggableRegion = aRegion; } /************************************************************** @@ -3674,7 +3664,7 @@ LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() { } LayoutDeviceIntMargin nsWindow::ClientToWindowMargin() { - if (mWindowType == WindowType::Popup && !IsPopupWithTitleBar()) { + if (mWindowType == WindowType::Popup) { return {}; } @@ -4420,6 +4410,17 @@ HWND nsWindow::GetTopLevelForFocus(HWND aCurWnd) { } void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate) { + if (aIsActivate && mPickerDisplayCount) { + // We disable the root window when a picker opens. See PickerOpen. When the + // picker closes (but before PickerClosed is called), our window will get + // focus, but it will still be disabled. This confuses the focus system. + // Therefore, we ignore this focus and explicitly call this function once + // we re-enable the window. Rarely, the picker seems to re-enable our root + // window before we do, but for simplicity, we always ignore focus before + // the final call to PickerClosed. See bug 1883568 for further details. + return; + } + if (aIsActivate) { sJustGotActivate = false; } @@ -5039,6 +5040,44 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, break; } + case WM_GETTITLEBARINFOEX: { + if (!mCustomNonClient) { + break; + } + auto* info = reinterpret_cast<TITLEBARINFOEX*>(lParam); + const LayoutDeviceIntPoint origin = WidgetToScreenOffset(); + auto GeckoClientToWinScreenRect = + [&origin](LayoutDeviceIntRect aRect) -> RECT { + aRect.MoveBy(origin); + return { + .left = aRect.x, + .top = aRect.y, + .right = aRect.XMost(), + .bottom = aRect.YMost(), + }; + }; + auto SetButton = [&](size_t aIndex, WindowButtonType aType) { + info->rgrect[aIndex] = + GeckoClientToWinScreenRect(mWindowBtnRect[aType]); + DWORD& state = info->rgstate[aIndex]; + if (mWindowBtnRect[aType].IsEmpty()) { + state = STATE_SYSTEM_INVISIBLE; + } else { + state = STATE_SYSTEM_FOCUSABLE; + } + }; + info->rgrect[0] = info->rcTitleBar = + GeckoClientToWinScreenRect(mDraggableRegion.GetBounds()); + info->rgstate[0] = 0; + SetButton(2, WindowButtonType::Minimize); + SetButton(3, WindowButtonType::Maximize); + SetButton(5, WindowButtonType::Close); + // We don't have a help button. + info->rgstate[4] = STATE_SYSTEM_INVISIBLE; + info->rgrect[4] = {0, 0, 0, 0}; + result = true; + } break; + case WM_NCHITTEST: { if (mInputRegion.mFullyTransparent) { // Treat this window as transparent. @@ -6169,6 +6208,9 @@ int32_t nsWindow::ClientMarginHitTestPoint(int32_t aX, int32_t aY) { if (mWindowBtnRect[WindowButtonType::Minimize].Contains(pt)) { testResult = HTMINBUTTON; } else if (mWindowBtnRect[WindowButtonType::Maximize].Contains(pt)) { +#ifdef ACCESSIBILITY + a11y::Compatibility::SuppressA11yForSnapLayouts(); +#endif testResult = HTMAXBUTTON; } else if (mWindowBtnRect[WindowButtonType::Close].Contains(pt)) { testResult = HTCLOSE; @@ -8168,8 +8210,23 @@ WPARAM nsWindow::wParamFromGlobalMouseState() { return result; } +// WORKAROUND FOR UNDOCUMENTED BEHAVIOR: `IFileDialog::Show` disables the +// top-level ancestor of its provided owner-window. If the modal window's +// container process crashes, it will never get a chance to undo that. +// +// For simplicity's sake we simply unconditionally perform both the disabling +// and reenabling here, synchronously, on the main thread, rather than leaving +// it to happen in our asynchronously-operated IFileDialog. + void nsWindow::PickerOpen() { AssertIsOnMainThread(); + + // Disable the root-level window synchronously before any file-dialogs get a + // chance to fight over doing it asynchronously. + if (!mPickerDisplayCount) { + ::EnableWindow(::GetAncestor(GetWindowHandle(), GA_ROOT), FALSE); + } + mPickerDisplayCount++; } @@ -8179,16 +8236,10 @@ void nsWindow::PickerClosed() { if (!mPickerDisplayCount) return; mPickerDisplayCount--; - // WORKAROUND FOR UNDOCUMENTED BEHAVIOR: `IFileDialog::Show` disables the - // top-level ancestor of its provided owner-window. If the modal window's - // container process crashes, it will never get a chance to undo that, so we - // do it manually here. - // - // Note that this may cause problems in the embedded case if you reparent a - // subtree of the native window hierarchy containing a Gecko window while that - // Gecko window has a file-dialog open. + // Once all the file-dialogs are gone, reenable the root-level window. if (!mPickerDisplayCount) { ::EnableWindow(::GetAncestor(GetWindowHandle(), GA_ROOT), TRUE); + DispatchFocusToTopLevelWindow(true); } if (!mPickerDisplayCount && mDestroyCalled) { @@ -8528,6 +8579,7 @@ void nsWindow::ChangedDPI() { presShell->BackingScaleFactorChanged(); } } + NotifyAPZOfDPIChange(); } static Result<POINTER_FLAGS, nsresult> PointerStateToFlag( diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index c75c9d174d..3a521fb978 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -887,8 +887,8 @@ class nsWindow final : public nsBaseWidget { mozilla::UniquePtr<mozilla::widget::DirectManipulationOwner> mDmOwner; // Client rect for minimize, maximize and close buttons. - mozilla::EnumeratedArray<WindowButtonType, WindowButtonType::Count, - LayoutDeviceIntRect> + mozilla::EnumeratedArray<WindowButtonType, LayoutDeviceIntRect, + size_t(WindowButtonType::Count)> mWindowBtnRect; mozilla::DataMutex<Desktop> mDesktopId; |