diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:43:14 +0000 |
commit | 8dd16259287f58f9273002717ec4d27e97127719 (patch) | |
tree | 3863e62a53829a84037444beab3abd4ed9dfc7d0 /widget | |
parent | Releasing progress-linux version 126.0.1-1~progress7.99u1. (diff) | |
download | firefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz firefox-8dd16259287f58f9273002717ec4d27e97127719.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
97 files changed, 1941 insertions, 2069 deletions
diff --git a/widget/ClipboardWriteRequestParent.cpp b/widget/ClipboardWriteRequestParent.cpp index 751befbe05..8a05c6a194 100644 --- a/widget/ClipboardWriteRequestParent.cpp +++ b/widget/ClipboardWriteRequestParent.cpp @@ -27,7 +27,9 @@ ClipboardWriteRequestParent::ClipboardWriteRequestParent( ClipboardWriteRequestParent::~ClipboardWriteRequestParent() = default; -nsresult ClipboardWriteRequestParent::Init(const int32_t& aClipboardType) { +nsresult ClipboardWriteRequestParent::Init( + const int32_t& aClipboardType, + mozilla::dom::WindowContext* aSettingWindowContext) { nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID)); if (!clipboard) { Unused << PClipboardWriteRequestParent::Send__delete__(this, @@ -35,8 +37,9 @@ nsresult ClipboardWriteRequestParent::Init(const int32_t& aClipboardType) { return NS_ERROR_FAILURE; } - nsresult rv = clipboard->AsyncSetData(aClipboardType, this, - getter_AddRefs(mAsyncSetClipboardData)); + nsresult rv = + clipboard->AsyncSetData(aClipboardType, aSettingWindowContext, this, + getter_AddRefs(mAsyncSetClipboardData)); if (NS_FAILED(rv)) { Unused << PClipboardWriteRequestParent::Send__delete__(this, rv); return rv; @@ -57,12 +60,12 @@ NS_IMETHODIMP ClipboardWriteRequestParent::OnComplete(nsresult aResult) { IPCResult ClipboardWriteRequestParent::RecvSetData( const IPCTransferable& aTransferable) { if (!mManager->ValidatePrincipal( - aTransferable.requestingPrincipal(), + aTransferable.dataPrincipal(), {ContentParent::ValidatePrincipalOptions::AllowNullPtr, ContentParent::ValidatePrincipalOptions::AllowExpanded, ContentParent::ValidatePrincipalOptions::AllowSystem})) { ContentParent::LogAndAssertFailedPrincipalValidationInfo( - aTransferable.requestingPrincipal(), __func__); + aTransferable.dataPrincipal(), __func__); } if (!mAsyncSetClipboardData) { diff --git a/widget/ClipboardWriteRequestParent.h b/widget/ClipboardWriteRequestParent.h index 6027661bdb..73dcc98192 100644 --- a/widget/ClipboardWriteRequestParent.h +++ b/widget/ClipboardWriteRequestParent.h @@ -27,7 +27,8 @@ class ClipboardWriteRequestParent final explicit ClipboardWriteRequestParent(ContentParent* aManager); - nsresult Init(const int32_t& aClipboardType); + nsresult Init(const int32_t& aClipboardType, + mozilla::dom::WindowContext* aSettingWindowContext); IPCResult RecvSetData(const IPCTransferable& aTransferable); IPCResult Recv__delete__(nsresult aReason); diff --git a/widget/GfxDriverInfo.cpp b/widget/GfxDriverInfo.cpp index d60b023ddc..4deafa5ad0 100644 --- a/widget/GfxDriverInfo.cpp +++ b/widget/GfxDriverInfo.cpp @@ -358,6 +358,77 @@ const GfxDeviceFamily* GfxDriverInfo::GetDeviceFamily(DeviceFamily id) { case DeviceFamily::IntelMobileHDGraphics: APPEND_DEVICE(0x0046); /* IntelMobileHDGraphics */ break; + case DeviceFamily::IntelGen12: + // Rocket Lake + APPEND_DEVICE(0x4C8C); // rkl_gt05 + APPEND_DEVICE(0x4C8A); // rkl_gt1 + APPEND_DEVICE(0x4C8B); // rkl_gt1 + APPEND_DEVICE(0x4C90); // rkl_gt1 + APPEND_DEVICE(0x4C9A); // rkl_gt1 + // Alder Lake + APPEND_DEVICE(0x4683); // adl_gt05 + APPEND_DEVICE(0x4693); // adl_gt05 + APPEND_DEVICE(0x4680); // adl_gt1 + APPEND_DEVICE(0x4681); // adl_gt1 + APPEND_DEVICE(0x4682); // adl_gt1 + APPEND_DEVICE(0x4688); // adl_gt1 + APPEND_DEVICE(0x4689); // adl_gt1 + APPEND_DEVICE(0x4690); // adl_gt1 + APPEND_DEVICE(0x4691); // adl_gt1 + APPEND_DEVICE(0x4692); // adl_gt1 + APPEND_DEVICE(0x4626); // adl_gt2 + APPEND_DEVICE(0x4628); // adl_gt2 + APPEND_DEVICE(0x462A); // adl_gt2 + APPEND_DEVICE(0x46A0); // adl_gt2 + APPEND_DEVICE(0x46A1); // adl_gt2 + APPEND_DEVICE(0x46A2); // adl_gt2 + APPEND_DEVICE(0x46A3); // adl_gt2 + APPEND_DEVICE(0x46A6); // adl_gt2 + APPEND_DEVICE(0x46A8); // adl_gt2 + APPEND_DEVICE(0x46AA); // adl_gt2 + APPEND_DEVICE(0x46B0); // adl_gt2 + APPEND_DEVICE(0x46B1); // adl_gt2 + APPEND_DEVICE(0x46B2); // adl_gt2 + APPEND_DEVICE(0x46B3); // adl_gt2 + APPEND_DEVICE(0x46C0); // adl_gt2 + APPEND_DEVICE(0x46C1); // adl_gt2 + APPEND_DEVICE(0x46C2); // adl_gt2 + APPEND_DEVICE(0x46C3); // adl_gt2 + APPEND_DEVICE(0x46D0); // adl_n + APPEND_DEVICE(0x46D1); // adl_n + APPEND_DEVICE(0x46D2); // adl_n + // Tiger Lake + APPEND_DEVICE(0x9A60); // tgl_gt1 + APPEND_DEVICE(0x9A68); // tgl_gt1 + APPEND_DEVICE(0x9A70); // tgl_gt1 + APPEND_DEVICE(0x9A40); // tgl_gt2 + APPEND_DEVICE(0x9A49); // tgl_gt2 + APPEND_DEVICE(0x9A59); // tgl_gt2 + APPEND_DEVICE(0x9A78); // tgl_gt2 + APPEND_DEVICE(0x9AC0); // tgl_gt2 + APPEND_DEVICE(0x9AC9); // tgl_gt2 + APPEND_DEVICE(0x9AD9); // tgl_gt2 + APPEND_DEVICE(0x9AF8); // tgl_gt2 + // Raptop Lake + APPEND_DEVICE(0xA780); // rpl + APPEND_DEVICE(0xA781); // rpl + APPEND_DEVICE(0xA782); // rpl + APPEND_DEVICE(0xA783); // rpl + APPEND_DEVICE(0xA788); // rpl + APPEND_DEVICE(0xA789); // rpl + APPEND_DEVICE(0xA720); // rpl_p + APPEND_DEVICE(0xA721); // rpl_p + APPEND_DEVICE(0xA7A0); // rpl_p + APPEND_DEVICE(0xA7A1); // rpl_p + APPEND_DEVICE(0xA7A8); // rpl_p + APPEND_DEVICE(0xA7A9); // rpl_p + // DG1 + APPEND_DEVICE(0x4905); + APPEND_DEVICE(0x4906); + APPEND_DEVICE(0x4907); + APPEND_DEVICE(0x4908); + APPEND_DEVICE(0x4909); + break; case DeviceFamily::NvidiaBlockD3D9Layers: // Glitches whilst scrolling (see bugs 612007, 644787, 645872) APPEND_DEVICE(0x00f3); /* NV43 [GeForce 6200 (TM)] */ @@ -651,6 +722,7 @@ const nsAString& GfxDriverInfo::GetDeviceVendor(DeviceFamily id) { case DeviceFamily::IntelKabyLake: case DeviceFamily::IntelHD520: case DeviceFamily::IntelMobileHDGraphics: + case DeviceFamily::IntelGen12: case DeviceFamily::IntelWebRenderBlocked: case DeviceFamily::Bug1116812: case DeviceFamily::Bug1155608: diff --git a/widget/GfxDriverInfo.h b/widget/GfxDriverInfo.h index a584c7ac3f..0e1ba5c02d 100644 --- a/widget/GfxDriverInfo.h +++ b/widget/GfxDriverInfo.h @@ -181,6 +181,7 @@ enum class DeviceFamily : uint8_t { IntelKabyLake, IntelHD520, IntelMobileHDGraphics, + IntelGen12, NvidiaBlockD3D9Layers, RadeonX1000, RadeonCaicos, diff --git a/widget/LookAndFeel.h b/widget/LookAndFeel.h index 13219ff54b..2300f51388 100644 --- a/widget/LookAndFeel.h +++ b/widget/LookAndFeel.h @@ -92,8 +92,6 @@ class LookAndFeel { TreeScrollDelay, // the maximum number of lines to be scrolled at ones TreeScrollLinesMax, - // What type of tab-order to use - TabFocusModel, // Should menu items blink when they're chosen? ChosenMenuItemsShouldBlink, diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 07b22d9a2b..7da6de3eaa 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -900,17 +900,14 @@ void PuppetWidget::SetCursor(const Cursor& aCursor) { return; } - bool hasCustomCursor = false; - Maybe<mozilla::ipc::BigBuffer> customCursorData; - size_t length = 0; - IntSize customCursorSize; - int32_t stride = 0; - auto format = SurfaceFormat::B8G8R8A8; ImageResolution resolution = aCursor.mResolution; + Maybe<IPCImage> customCursor; if (aCursor.IsCustom()) { - int32_t width = 0, height = 0; + int32_t width = 0; + int32_t height = 0; aCursor.mContainer->GetWidth(&width); aCursor.mContainer->GetHeight(&height); + const int32_t flags = imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY; RefPtr<SourceSurface> surface; @@ -928,22 +925,17 @@ void PuppetWidget::SetCursor(const Cursor& aCursor) { surface = aCursor.mContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags); } + if (surface) { if (RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface()) { - hasCustomCursor = true; - customCursorData = - nsContentUtils::GetSurfaceData(*dataSurface, &length, &stride); - customCursorSize = dataSurface->GetSize(); - format = dataSurface->GetFormat(); + customCursor = nsContentUtils::SurfaceToIPCImage(*dataSurface); } } } if (!mBrowserChild->SendSetCursor( - aCursor.mDefaultCursor, hasCustomCursor, std::move(customCursorData), - customCursorSize.width, customCursorSize.height, resolution.mX, - resolution.mY, stride, format, aCursor.mHotspotX, aCursor.mHotspotY, - force)) { + aCursor.mDefaultCursor, std::move(customCursor), resolution.mX, + resolution.mY, aCursor.mHotspotX, aCursor.mHotspotY, force)) { return; } mCursor = aCursor; diff --git a/widget/ScrollbarDrawingGTK.cpp b/widget/ScrollbarDrawingGTK.cpp index 3e0cad9179..9262667dcb 100644 --- a/widget/ScrollbarDrawingGTK.cpp +++ b/widget/ScrollbarDrawingGTK.cpp @@ -116,10 +116,7 @@ bool ScrollbarDrawingGTK::PaintScrollbarThumb( } bool ScrollbarDrawingGTK::ShouldDrawScrollbarButtons() { - if (StaticPrefs::widget_non_native_theme_enabled()) { - return StaticPrefs::widget_non_native_theme_gtk_scrollbar_allow_buttons(); - } - return true; + return StaticPrefs::widget_non_native_theme_gtk_scrollbar_allow_buttons(); } void ScrollbarDrawingGTK::RecomputeScrollbarParams() { diff --git a/widget/Theme.cpp b/widget/Theme.cpp index 15bf68ec01..fcfea14175 100644 --- a/widget/Theme.cpp +++ b/widget/Theme.cpp @@ -767,11 +767,11 @@ void Theme::PaintMenulistArrow(nsIFrame* aFrame, DrawTarget& aDrawTarget, const auto direction = [&] { const auto wm = aFrame->GetWritingMode(); switch (wm.GetBlockDir()) { - case WritingMode::BlockDir::eBlockLR: + case WritingMode::BlockDir::LR: return PhysicalArrowDirection::Right; - case WritingMode::BlockDir::eBlockRL: + case WritingMode::BlockDir::RL: return PhysicalArrowDirection::Left; - case WritingMode::BlockDir::eBlockTB: + case WritingMode::BlockDir::TB: return PhysicalArrowDirection::Bottom; } MOZ_ASSERT_UNREACHABLE("Unknown direction?"); @@ -1099,9 +1099,6 @@ bool Theme::CreateWebRenderCommandsForWidget( const mozilla::layers::StackingContextHelper& aSc, mozilla::layers::RenderRootStateManager* aManager, nsIFrame* aFrame, StyleAppearance aAppearance, const nsRect& aRect) { - if (!StaticPrefs::widget_non_native_theme_webrender()) { - return false; - } WebRenderBackendData data{aBuilder, aResources, aSc, aManager}; return DoDrawWidgetBackground(data, aFrame, aAppearance, aRect, DrawOverflow::Yes); @@ -1208,6 +1205,7 @@ bool Theme::DoDrawWidgetBackground(PaintBackendData& aPaintData, case StyleAppearance::Textarea: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: PaintTextField(aPaintData, devPxRect, elementState, colors, dpiRatio); break; case StyleAppearance::Listbox: @@ -1435,6 +1433,7 @@ LayoutDeviceIntMargin Theme::GetWidgetBorder(nsDeviceContext* aContext, case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Listbox: case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: @@ -1503,6 +1502,7 @@ bool Theme::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame, case StyleAppearance::Textarea: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Listbox: case StyleAppearance::MenulistButton: case StyleAppearance::Menulist: @@ -1678,6 +1678,7 @@ bool Theme::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::MozMenulistArrowButton: case StyleAppearance::SpinnerUpbutton: case StyleAppearance::SpinnerDownbutton: diff --git a/widget/ThemeColors.h b/widget/ThemeColors.h index 739a7ca0f0..0e0fd044bb 100644 --- a/widget/ThemeColors.h +++ b/widget/ThemeColors.h @@ -62,7 +62,8 @@ class ThemeColors { explicit ThemeColors(const nsIFrame* aFrame, StyleAppearance aAppearance) : mDoc(*aFrame->PresContext()->Document()), mHighContrastInfo(ShouldBeHighContrast(*aFrame->PresContext())), - mColorScheme(ColorSchemeForWidget(aFrame, aAppearance, mHighContrastInfo)), + mColorScheme( + ColorSchemeForWidget(aFrame, aAppearance, mHighContrastInfo)), mAccentColor(*aFrame->Style(), mColorScheme) {} virtual ~ThemeColors() = default; diff --git a/widget/android/GeckoTelemetryDelegate.h b/widget/android/GeckoTelemetryDelegate.h deleted file mode 100644 index 43b20d4ad6..0000000000 --- a/widget/android/GeckoTelemetryDelegate.h +++ /dev/null @@ -1,100 +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 GeckoTelemetryDelegate_h__ -#define GeckoTelemetryDelegate_h__ - -#include "geckoview/streaming/GeckoViewStreamingTelemetry.h" - -#include <jni.h> - -#include "mozilla/java/RuntimeTelemetryNatives.h" -#include "mozilla/jni/Natives.h" - -namespace mozilla { -namespace widget { - -class GeckoTelemetryDelegate final - : public GeckoViewStreamingTelemetry::StreamingTelemetryDelegate, - public mozilla::java::RuntimeTelemetry::Proxy::Natives< - GeckoTelemetryDelegate> { - public: - // Implement Proxy native. - static void RegisterDelegateProxy( - mozilla::java::RuntimeTelemetry::Proxy::Param aProxy) { - MOZ_ASSERT(aProxy); - - GeckoViewStreamingTelemetry::RegisterDelegate( - new GeckoTelemetryDelegate(aProxy)); - } - - explicit GeckoTelemetryDelegate( - mozilla::java::RuntimeTelemetry::Proxy::Param aProxy) - : mProxy(aProxy) {} - - private: - void DispatchHistogram(bool aIsCategorical, const nsCString& aName, - const nsTArray<uint32_t>& aSamples) { - if (!mozilla::jni::IsAvailable() || !mProxy || aSamples.Length() < 1) { - return; - } - - // Convert aSamples to an array of int64_t. We know |samples| required - // capacity needs to match |aSamples.Length()|. - nsTArray<int64_t> samples(aSamples.Length()); - for (size_t i = 0, l = aSamples.Length(); i < l; ++i) { - samples.AppendElement(static_cast<int64_t>(aSamples[i])); - } - - // LongArray::From *copies* the elements - mProxy->DispatchHistogram(aIsCategorical, aName, - mozilla::jni::LongArray::From(samples)); - } - - // Implement StreamingTelemetryDelegate. - void ReceiveHistogramSamples(const nsCString& aName, - const nsTArray<uint32_t>& aSamples) override { - DispatchHistogram(/* isCategorical */ false, aName, aSamples); - } - - void ReceiveCategoricalHistogramSamples( - const nsCString& aName, const nsTArray<uint32_t>& aSamples) override { - DispatchHistogram(/* isCategorical */ true, aName, aSamples); - } - - void ReceiveBoolScalarValue(const nsCString& aName, bool aValue) override { - if (!mozilla::jni::IsAvailable() || !mProxy) { - return; - } - - mProxy->DispatchBooleanScalar(aName, aValue); - } - - void ReceiveStringScalarValue(const nsCString& aName, - const nsCString& aValue) override { - if (!mozilla::jni::IsAvailable() || !mProxy) { - return; - } - - mProxy->DispatchStringScalar(aName, aValue); - } - - void ReceiveUintScalarValue(const nsCString& aName, - uint32_t aValue) override { - if (!mozilla::jni::IsAvailable() || !mProxy) { - return; - } - - mProxy->DispatchLongScalar(aName, static_cast<int64_t>(aValue)); - } - - mozilla::java::RuntimeTelemetry::Proxy::GlobalRef mProxy; -}; - -} // namespace widget -} // namespace mozilla - -#endif // GeckoTelemetryDelegate_h__ diff --git a/widget/android/jni/Utils.cpp b/widget/android/jni/Utils.cpp index 8d3800fd03..78e1ab647a 100644 --- a/widget/android/jni/Utils.cpp +++ b/widget/android/jni/Utils.cpp @@ -9,6 +9,7 @@ #include <android/log.h> #include <pthread.h> +#include <sys/prctl.h> #include "mozilla/Assertions.h" #include "mozilla/java/GeckoAppShellWrappers.h" @@ -164,10 +165,20 @@ JNIEnv* GetEnvForThread() { return env; } + // By default the VM has a nasty habit of overwriting our lovely + // thread names with "Thread-<n>" making them hard to identify in a debugger, + // so we pass the name in AttachArgs below to prevent that from happening. + // PR_GET_NAME requires a 16 byte buffer: https://linux.die.net/man/2/prctl. + // JNI_VERSION_1_4 is required for NewDirectByteBuffer. + char threadName[16] = {'\0'}; + prctl(PR_GET_NAME, threadName); + JavaVMAttachArgs attachArgs{ + .version = JNI_VERSION_1_4, .name = threadName, .group = nullptr}; + // We don't have a saved JNIEnv, so try to get one. // AttachCurrentThread() does the same thing as GetEnv() when a thread is // already attached, so we don't have to call GetEnv() at all. - if (!sJavaVM->AttachCurrentThread(&env, nullptr)) { + if (!sJavaVM->AttachCurrentThread(&env, &attachArgs)) { MOZ_ASSERT(env); MOZ_ALWAYS_TRUE(!pthread_setspecific(sThreadEnvKey, env)); return env; diff --git a/widget/android/moz.build b/widget/android/moz.build index e4525bb747..2623442020 100644 --- a/widget/android/moz.build +++ b/widget/android/moz.build @@ -69,7 +69,6 @@ classes_with_WrapForJNI = [ "ImageDecoder", "MediaDrmProxy", "PanZoomController", - "RuntimeTelemetry", "Sample", "SampleBuffer", "ScreenManagerHelper", @@ -193,7 +192,6 @@ LOCAL_INCLUDES += [ "/layout/forms", "/layout/painting", "/netwerk/base", - "/toolkit/components/telemetry", "/widget", "/widget/headless", "/xpcom/threads", diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index 4d396945c4..c42f025b2c 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -69,7 +69,6 @@ #include "GeckoNetworkManager.h" #include "GeckoProcessManager.h" #include "GeckoSystemStateListener.h" -#include "GeckoTelemetryDelegate.h" #include "GeckoVRManager.h" #include "ImageDecoderSupport.h" #include "JavaBuiltins.h" @@ -412,7 +411,6 @@ nsAppShell::nsAppShell() GeckoAppShellSupport::Init(); XPCOMEventTargetWrapper::Init(); mozilla::widget::Telemetry::Init(); - mozilla::widget::GeckoTelemetryDelegate::Init(); if (XRE_IsGPUProcess()) { mozilla::gl::AndroidSurfaceTexture::Init(); @@ -443,7 +441,6 @@ nsAppShell::nsAppShell() mozilla::widget::Base64UtilsSupport::Init(); nsWindow::InitNatives(); mozilla::gl::AndroidSurfaceTexture::Init(); - mozilla::widget::GeckoTelemetryDelegate::Init(); java::GeckoThread::SetState(java::GeckoThread::State::JNI_READY()); diff --git a/widget/android/nsClipboard.cpp b/widget/android/nsClipboard.cpp index c2a03dd540..3c94b89c01 100644 --- a/widget/android/nsClipboard.cpp +++ b/widget/android/nsClipboard.cpp @@ -164,7 +164,7 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, } } - return NS_ERROR_FAILURE; + return NS_OK; } nsresult nsClipboard::EmptyNativeClipboardData(int32_t aWhichClipboard) { diff --git a/widget/cocoa/AppearanceOverride.h b/widget/cocoa/AppearanceOverride.h deleted file mode 100644 index c4ae629320..0000000000 --- a/widget/cocoa/AppearanceOverride.h +++ /dev/null @@ -1,19 +0,0 @@ -/* -*- 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 AppearanceOverride_h -#define AppearanceOverride_h - -#import <Cocoa/Cocoa.h> - -// Implements support for the browser.theme.toolbar-theme pref. -// Use MOZGlobalAppearance.sharedInstance.effectiveAppearance -// in all places where you would like the global override to be respected. The -// effectiveAppearance property can be key-value observed. -@interface MOZGlobalAppearance : NSObject <NSAppearanceCustomization> -@property(class, readonly) MOZGlobalAppearance* sharedInstance; -@end - -#endif diff --git a/widget/cocoa/AppearanceOverride.mm b/widget/cocoa/AppearanceOverride.mm deleted file mode 100644 index c3938d6b93..0000000000 --- a/widget/cocoa/AppearanceOverride.mm +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- 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/. */ - -#import <Cocoa/Cocoa.h> - -#include "AppearanceOverride.h" - -#include "mozilla/Preferences.h" -#include "mozilla/StaticPrefs_browser.h" -#include "mozilla/StaticPrefs_widget.h" - -#include "nsXULAppAPI.h" - -static void ToolbarThemePrefChanged(const char* aPref, void* aUserInfo); - -@interface MOZGlobalAppearance () -@property NSInteger toolbarTheme; -@end - -@implementation MOZGlobalAppearance - -+ (MOZGlobalAppearance*)sharedInstance { - static MOZGlobalAppearance* sInstance = nil; - if (!sInstance) { - sInstance = [[MOZGlobalAppearance alloc] init]; - if (XRE_IsParentProcess()) { - mozilla::Preferences::RegisterCallbackAndCall( - &ToolbarThemePrefChanged, - nsDependentCString( - mozilla::StaticPrefs::GetPrefName_browser_theme_toolbar_theme())); - } - } - return sInstance; -} - -+ (NSSet*)keyPathsForValuesAffectingAppearance { - return [NSSet setWithObjects:@"toolbarTheme", nil]; -} - -- (NSAppearance*)appearance { - switch (self.toolbarTheme) { // Value for browser.theme.toolbar-theme pref - case 0: // Dark - return [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; - case 1: // Light - return [NSAppearance appearanceNamed:NSAppearanceNameAqua]; - case 2: // System - default: - return nil; // nil means "no override". - } -} - -- (void)setAppearance:(NSAppearance*)aAppearance { - // ignored -} - -- (NSApplication*)_app { - return NSApp; -} - -+ (NSSet*)keyPathsForValuesAffectingEffectiveAppearance { - // Automatically notify any key-value observers of our effectiveAppearance - // property whenever the pref or the NSApp's effectiveAppearance change. - return - [NSSet setWithObjects:@"toolbarTheme", @"_app.effectiveAppearance", nil]; -} - -- (NSAppearance*)effectiveAppearance { - switch (self.toolbarTheme) { // Value for browser.theme.toolbar-theme pref - case 0: // Dark - return [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; - case 1: // Light - return [NSAppearance appearanceNamed:NSAppearanceNameAqua]; - case 2: // System - default: - // Use the NSApp effectiveAppearance. This is the system appearance. - return NSApp.effectiveAppearance; - } -} - -@end - -static void ToolbarThemePrefChanged(const char* aPref, void* aUserInfo) { - MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); - MOZ_RELEASE_ASSERT(NS_IsMainThread()); - - MOZGlobalAppearance.sharedInstance.toolbarTheme = - mozilla::StaticPrefs::browser_theme_toolbar_theme(); -} diff --git a/widget/cocoa/VibrancyManager.h b/widget/cocoa/VibrancyManager.h index c70ec60a3b..43adace794 100644 --- a/widget/cocoa/VibrancyManager.h +++ b/widget/cocoa/VibrancyManager.h @@ -7,24 +7,24 @@ #ifndef VibrancyManager_h #define VibrancyManager_h -#include "mozilla/Assertions.h" -#include "nsClassHashtable.h" -#include "nsRegion.h" -#include "nsTArray.h" -#include "ViewRegion.h" +#include "mozilla/EnumeratedArray.h" +#include "Units.h" -#import <Foundation/NSGeometry.h> - -@class NSColor; @class NSView; class nsChildView; namespace mozilla { +class ViewRegion; + enum class VibrancyType { - TOOLTIP, - MENU, - TITLEBAR, + // Add new values here, or update MaxEnumValue below if you add them after. + Titlebar, +}; + +template <> +struct MaxContiguousEnumValue<VibrancyType> { + static constexpr auto value = VibrancyType::Titlebar; }; /** @@ -51,9 +51,9 @@ class VibrancyManager { * NSVisualEffectViews which will be created for vibrant regions. */ VibrancyManager(const nsChildView& aCoordinateConverter, - NSView* aContainerView) - : mCoordinateConverter(aCoordinateConverter), - mContainerView(aContainerView) {} + NSView* aContainerView); + + ~VibrancyManager(); /** * Update the placement of the NSVisualEffectViews inside the container @@ -66,26 +66,10 @@ class VibrancyManager { bool UpdateVibrantRegion(VibrancyType aType, const LayoutDeviceIntRegion& aRegion); - bool HasVibrantRegions() { return !mVibrantRegions.IsEmpty(); } - - LayoutDeviceIntRegion GetUnionOfVibrantRegions() const; - - /** - * Create an NSVisualEffectView for the specified vibrancy type. The return - * value is not autoreleased. We return an object of type NSView* because we - * compile with an SDK that does not contain a definition for - * NSVisualEffectView. - * @param aIsContainer Whether this NSView will have child views. This value - * affects hit testing: Container views will pass through - * hit testing requests to their children, and leaf views - * will be transparent to hit testing. - */ - static NSView* CreateEffectView(VibrancyType aType, BOOL aIsContainer = NO); - protected: const nsChildView& mCoordinateConverter; NSView* mContainerView; - nsClassHashtable<nsUint32HashKey, ViewRegion> mVibrantRegions; + EnumeratedArray<VibrancyType, UniquePtr<ViewRegion>> mVibrantRegions; }; } // namespace mozilla diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm index 6062acb931..a2bef29a19 100644 --- a/widget/cocoa/VibrancyManager.mm +++ b/widget/cocoa/VibrancyManager.mm @@ -5,6 +5,9 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "VibrancyManager.h" +#include "ViewRegion.h" +#include "nsRegion.h" +#include "ViewRegion.h" #import <objc/message.h> @@ -20,18 +23,10 @@ using namespace mozilla; vibrancyType:(VibrancyType)aVibrancyType; @end -@interface MOZVibrantLeafView : MOZVibrantView -@end - static NSVisualEffectState VisualEffectStateForVibrancyType( VibrancyType aType) { switch (aType) { - case VibrancyType::TOOLTIP: - case VibrancyType::MENU: - // Tooltip and menu windows are never "key", so we need to tell the - // vibrancy effect to look active regardless of window state. - return NSVisualEffectStateActive; - case VibrancyType::TITLEBAR: + case VibrancyType::Titlebar: break; } return NSVisualEffectStateFollowsWindowActiveState; @@ -40,11 +35,7 @@ static NSVisualEffectState VisualEffectStateForVibrancyType( static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType( VibrancyType aType) { switch (aType) { - case VibrancyType::TOOLTIP: - return (NSVisualEffectMaterial)NSVisualEffectMaterialToolTip; - case VibrancyType::MENU: - return NSVisualEffectMaterialMenu; - case VibrancyType::TITLEBAR: + case VibrancyType::Titlebar: return NSVisualEffectMaterialTitlebar; } } @@ -52,10 +43,7 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType( static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType( VibrancyType aType) { switch (aType) { - case VibrancyType::TOOLTIP: - case VibrancyType::MENU: - return NSVisualEffectBlendingModeBehindWindow; - case VibrancyType::TITLEBAR: + case VibrancyType::Titlebar: return StaticPrefs::widget_macos_titlebar_blend_mode_behind_window() ? NSVisualEffectBlendingModeBehindWindow : NSVisualEffectBlendingModeWithinWindow; @@ -63,7 +51,6 @@ static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType( } @implementation MOZVibrantView - - (instancetype)initWithFrame:(NSRect)aRect vibrancyType:(VibrancyType)aType { self = [super initWithFrame:aRect]; mType = aType; @@ -76,50 +63,31 @@ static NSVisualEffectBlendingMode VisualEffectBlendingModeForVibrancyType( return self; } -// Don't override allowsVibrancy here, because this view may have subviews, and -// returning YES from allowsVibrancy forces on foreground vibrancy for all -// descendant views, which can have unintended effects. - -@end - -@implementation MOZVibrantLeafView - - (NSView*)hitTest:(NSPoint)aPoint { // This view must be transparent to mouse events. return nil; } +@end -// MOZVibrantLeafView does not have subviews, so we can return YES here without -// having unintended effects on other contents of the window. -- (BOOL)allowsVibrancy { - return NO; -} +VibrancyManager::VibrancyManager(const nsChildView& aCoordinateConverter, + NSView* aContainerView) + : mCoordinateConverter(aCoordinateConverter), + mContainerView(aContainerView) {} -@end +VibrancyManager::~VibrancyManager() = default; bool VibrancyManager::UpdateVibrantRegion( VibrancyType aType, const LayoutDeviceIntRegion& aRegion) { + auto& slot = mVibrantRegions[aType]; if (aRegion.IsEmpty()) { - return mVibrantRegions.Remove(uint32_t(aType)); + bool hadRegion = !!slot; + slot = nullptr; + return hadRegion; } - auto& vr = *mVibrantRegions.GetOrInsertNew(uint32_t(aType)); - return vr.UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() { - return CreateEffectView(aType); - }); -} - -LayoutDeviceIntRegion VibrancyManager::GetUnionOfVibrantRegions() const { - LayoutDeviceIntRegion result; - for (const auto& region : mVibrantRegions.Values()) { - result.OrWith(region->Region()); + if (!slot) { + slot = MakeUnique<ViewRegion>(); } - return result; -} - -/* static */ NSView* VibrancyManager::CreateEffectView(VibrancyType aType, - BOOL aIsContainer) { - return aIsContainer ? [[MOZVibrantView alloc] initWithFrame:NSZeroRect - vibrancyType:aType] - : [[MOZVibrantLeafView alloc] initWithFrame:NSZeroRect - vibrancyType:aType]; + return slot->UpdateRegion(aRegion, mCoordinateConverter, mContainerView, ^() { + return [[MOZVibrantView alloc] initWithFrame:NSZeroRect vibrancyType:aType]; + }); } diff --git a/widget/cocoa/ViewRegion.h b/widget/cocoa/ViewRegion.h index b2ed0c8835..d44bde5f71 100644 --- a/widget/cocoa/ViewRegion.h +++ b/widget/cocoa/ViewRegion.h @@ -8,6 +8,7 @@ #define ViewRegion_h #include "Units.h" +#include "nsRegion.h" #include "nsTArray.h" class nsChildView; diff --git a/widget/cocoa/moz.build b/widget/cocoa/moz.build index d0939d34c7..125a7f2fc0 100644 --- a/widget/cocoa/moz.build +++ b/widget/cocoa/moz.build @@ -23,7 +23,6 @@ EXPORTS += [ ] UNIFIED_SOURCES += [ - "AppearanceOverride.mm", "GfxInfo.mm", "MOZIconHelper.mm", "MOZMenuOpeningCoordinator.mm", diff --git a/widget/cocoa/nsChildView.mm b/widget/cocoa/nsChildView.mm index d3241a983f..b57df15825 100644 --- a/widget/cocoa/nsChildView.mm +++ b/widget/cocoa/nsChildView.mm @@ -1726,32 +1726,52 @@ static Maybe<VibrancyType> ThemeGeometryTypeToVibrancyType( nsITheme::ThemeGeometryType aThemeGeometryType) { switch (aThemeGeometryType) { case eThemeGeometryTypeTitlebar: - return Some(VibrancyType::TITLEBAR); + return Some(VibrancyType::Titlebar); default: return Nothing(); } } -static LayoutDeviceIntRegion GatherVibrantRegion( - const nsTArray<nsIWidget::ThemeGeometry>& aThemeGeometries, - VibrancyType aVibrancyType) { - LayoutDeviceIntRegion region; +static EnumeratedArray<VibrancyType, LayoutDeviceIntRegion> +GatherVibrantRegions(Span<const nsIWidget::ThemeGeometry> aThemeGeometries) { + EnumeratedArray<VibrancyType, LayoutDeviceIntRegion> regions; for (const auto& geometry : aThemeGeometries) { - if (ThemeGeometryTypeToVibrancyType(geometry.mType) == - Some(aVibrancyType)) { - region.OrWith(geometry.mRect); + auto vibrancyType = ThemeGeometryTypeToVibrancyType(geometry.mType); + if (!vibrancyType) { + continue; } + regions[*vibrancyType].OrWith(geometry.mRect); + } + return regions; +} + +// Subtracts parts from regions in such a way that they don't have any overlap. +// Each region in the argument list will have the union of all the regions +// *following* it subtracted from itself. In other words, the arguments are +// treated as low priority to high priority. +static void MakeRegionsNonOverlapping(Span<LayoutDeviceIntRegion> aRegions) { + LayoutDeviceIntRegion unionOfAll; + for (auto& region : aRegions) { + region.SubOut(unionOfAll); + unionOfAll.OrWith(region); } - return region; } void nsChildView::UpdateVibrancy( const nsTArray<ThemeGeometry>& aThemeGeometries) { - LayoutDeviceIntRegion titlebarRegion = - GatherVibrantRegion(aThemeGeometries, VibrancyType::TITLEBAR); + auto regions = GatherVibrantRegions(aThemeGeometries); + MakeRegionsNonOverlapping(regions); auto& vm = EnsureVibrancyManager(); - bool changed = vm.UpdateVibrantRegion(VibrancyType::TITLEBAR, titlebarRegion); + bool changed = false; + + // EnumeratedArray doesn't have an iterator that also yields the enum type, + // but we rely on VibrancyType being contiguous and starting at 0, so we can + // do that manually. + size_t i = 0; + for (const auto& region : regions) { + changed |= vm.UpdateVibrantRegion(VibrancyType(i++), region); + } if (changed) { SuspendAsyncCATransactions(); diff --git a/widget/cocoa/nsClipboard.mm b/widget/cocoa/nsClipboard.mm index ba747e5b27..1c4788460c 100644 --- a/widget/cocoa/nsClipboard.mm +++ b/widget/cocoa/nsClipboard.mm @@ -499,13 +499,12 @@ nsClipboard::HasNativeClipboardDataMatchingFlavors( // XXX we only check the first pasteboard item as we only get data from // first item in TransferableFromPasteboard for now. if (NSPasteboardItem* item = [items objectAtIndex:0]) { - if (NSString *availableType = [item - availableTypeFromArray: - [NSArray - arrayWithObjects:[UTIHelper - stringFromPboardType: - (NSString*)kUTTypeFileURL], - nil]]) { + if ([item availableTypeFromArray: + [NSArray + arrayWithObjects:[UTIHelper + stringFromPboardType: + (NSString*)kUTTypeFileURL], + nil]]) { MOZ_CLIPBOARD_LOG(" has %s\n", mimeType.get()); return true; } diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 8e54d9e7fd..5032f35fa1 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -6,7 +6,6 @@ #include "nsCocoaWindow.h" -#include "AppearanceOverride.h" #include "NativeKeyBindings.h" #include "ScreenHelperCocoa.h" #include "TextInputHandler.h" @@ -158,6 +157,13 @@ void nsCocoaWindow::DestroyNativeWindow() { // we clear our delegate. EndOurNativeTransition(); + // We are about to destroy mWindow. Before we do that, make sure that we + // hide the window using the Show() method, because it has several side + // effects that our parent and listeners might be expecting. If we don't + // do this now, then these side effects will never execute, though the + // window will definitely no longer be shown. + Show(false); + [mWindow releaseJSObjects]; // We want to unhook the delegate here because we don't want events // sent to it after this object has been destroyed. @@ -525,10 +531,6 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect& aRect, [WindowDataMap.sharedWindowDataMap ensureDataForWindow:mWindow]; mWindowMadeHere = true; - // Make the window respect the global appearance, which follows the - // browser.theme.toolbar-theme pref. - mWindow.appearanceSource = MOZGlobalAppearance.sharedInstance; - return NS_OK; NS_OBJC_END_TRY_BLOCK_RETURN(NS_ERROR_FAILURE); @@ -1969,7 +1971,6 @@ void nsCocoaWindow::BackingScaleFactorChanged() { if (PresShell* presShell = mWidgetListener->GetPresShell()) { presShell->BackingScaleFactorChanged(); } - mWidgetListener->UIResolutionChanged(); } int32_t nsCocoaWindow::RoundsWidgetCoordinatesTo() { @@ -2327,9 +2328,11 @@ void nsCocoaWindow::SetColorScheme(const Maybe<ColorScheme>& aScheme) { if (!mWindow) { return; } - - mWindow.appearance = aScheme ? NSAppearanceForColorScheme(*aScheme) : nil; - + NSAppearance* appearance = + aScheme ? NSAppearanceForColorScheme(*aScheme) : nil; + if (mWindow.appearance != appearance) { + mWindow.appearance = appearance; + } NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -3071,10 +3074,6 @@ static NSMutableSet* gSwizzledFrameViewClasses = nil; - (void)_setNeedsDisplayInRect:(NSRect)aRect; @end -@interface NSView (NSVisualEffectViewSetMaskImage) -- (void)setMaskImage:(NSImage*)image; -@end - @interface BaseWindow (Private) - (void)removeTrackingArea; - (void)cursorUpdated:(NSEvent*)aEvent; @@ -3174,35 +3173,38 @@ static NSImage* GetMenuMaskImage() { return maskImage; } -- (void)swapOutChildViewWrapper:(NSView*)aNewWrapper { - aNewWrapper.frame = self.contentView.frame; +// Add an effect view wrapper if needed so that the OS draws the appropriate +// vibrancy effect and window border. +- (void)setEffectViewWrapperForStyle:(WindowShadow)aStyle { + NSView* wrapper = [&]() -> NSView* { + if (aStyle == WindowShadow::Menu || aStyle == WindowShadow::Tooltip) { + const bool isMenu = aStyle == WindowShadow::Menu; + auto* effectView = + [[NSVisualEffectView alloc] initWithFrame:self.contentView.frame]; + effectView.material = + isMenu ? NSVisualEffectMaterialMenu : NSVisualEffectMaterialToolTip; + // Tooltip and menu windows are never "key", so we need to tell the + // vibrancy effect to look active regardless of window state. + effectView.state = NSVisualEffectStateActive; + effectView.blendingMode = NSVisualEffectBlendingModeBehindWindow; + if (isMenu) { + // Turn on rounded corner masking. + effectView.maskImage = GetMenuMaskImage(); + } + return effectView; + } + return [[NSView alloc] initWithFrame:self.contentView.frame]; + }(); + + wrapper.wantsLayer = YES; + // Swap out our content view by the new view. Setting .contentView releases + // the old view. NSView* childView = [self.mainChildView retain]; [childView removeFromSuperview]; - [aNewWrapper addSubview:childView]; + [wrapper addSubview:childView]; [childView release]; - [super setContentView:aNewWrapper]; -} - -- (void)setEffectViewWrapperForStyle:(WindowShadow)aStyle { - if (aStyle == WindowShadow::Menu || aStyle == WindowShadow::Tooltip) { - // Add an effect view wrapper so that the OS draws the appropriate - // vibrancy effect and window border. - BOOL isMenu = aStyle == WindowShadow::Menu; - NSView* effectView = VibrancyManager::CreateEffectView( - isMenu ? VibrancyType::MENU : VibrancyType::TOOLTIP, YES); - if (isMenu) { - // Turn on rounded corner masking. - [effectView setMaskImage:GetMenuMaskImage()]; - } - [self swapOutChildViewWrapper:effectView]; - [effectView release]; - } else { - // Remove the existing wrapper. - NSView* wrapper = [[NSView alloc] initWithFrame:NSZeroRect]; - [wrapper setWantsLayer:YES]; - [self swapOutChildViewWrapper:wrapper]; - [wrapper release]; - } + super.contentView = wrapper; + [wrapper release]; } - (NSTouchBar*)makeTouchBar { diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index 779984be0c..69bd348da8 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -3,7 +3,6 @@ * 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 "AppearanceOverride.h" #include "mozilla/widget/ThemeChangeKind.h" #include "nsLookAndFeel.h" #include "nsCocoaFeatures.h" @@ -239,9 +238,6 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::MozButtonhoverface: case ColorID::MozButtonactiveface: case ColorID::MozButtondisabledface: - case ColorID::MozColheader: - case ColorID::MozColheaderhover: - case ColorID::MozColheaderactive: color = GetColorFromNSColor(NSColor.controlColor); if (!NS_GET_A(color)) { color = GetColorFromNSColor(NSColor.controlBackgroundColor); @@ -301,9 +297,6 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, case ColorID::Menutext: case ColorID::Infotext: case ColorID::MozCellhighlighttext: - case ColorID::MozColheadertext: - case ColorID::MozColheaderhovertext: - case ColorID::MozColheaderactivetext: case ColorID::MozSidebartext: color = GetColorFromNSColor(NSColor.controlTextColor); break; @@ -319,6 +312,17 @@ nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme, // For inactive list selection color = GetColorFromNSColor(NSColor.secondarySelectedControlColor); break; + case ColorID::MozColheadertext: + case ColorID::MozColheaderhovertext: + case ColorID::MozColheaderactivetext: + color = GetColorFromNSColor(NSColor.headerTextColor); + break; + case ColorID::MozColheaderactive: + color = GetColorFromNSColor( + NSColor.unemphasizedSelectedContentBackgroundColor); + break; + case ColorID::MozColheader: + case ColorID::MozColheaderhover: case ColorID::MozEventreerow: // Background color of even list rows. color = @@ -460,11 +464,6 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::AlertNotificationOrigin: aResult = NS_ALERT_TOP; break; - case IntID::TabFocusModel: - aResult = [NSApp isFullKeyboardAccessEnabled] - ? nsIContent::eTabFocus_any - : nsIContent::eTabFocus_textControlsMask; - break; case IntID::ScrollToClick: { aResult = [[NSUserDefaults standardUserDefaults] boolForKey:@"AppleScrollerPagingBehavior"]; @@ -630,10 +629,6 @@ void nsLookAndFeel::RecordAccessibilityTelemetry() { object:nil suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately]; - [MOZGlobalAppearance.sharedInstance addObserver:self - forKeyPath:@"effectiveAppearance" - options:0 - context:nil]; [NSApp addObserver:self forKeyPath:@"effectiveAppearance" options:0 diff --git a/widget/cocoa/nsNativeThemeCocoa.h b/widget/cocoa/nsNativeThemeCocoa.h index ebbf782264..66e5aec1df 100644 --- a/widget/cocoa/nsNativeThemeCocoa.h +++ b/widget/cocoa/nsNativeThemeCocoa.h @@ -40,8 +40,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { eSquareBezelPushButton, eArrowButton, eHelpButton, - eTreeTwistyPointingRight, - eTreeTwistyPointingDown, eDisclosureButtonClosed, eDisclosureButtonOpen }; @@ -131,12 +129,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { bool rtl = false; }; - struct TreeHeaderCellParams { - ControlParams controlParams; - TreeSortDirection sortDirection = eTreeSortDirection_Natural; - bool lastTreeHeaderCell = false; - }; - struct ScaleParams { int32_t value = 0; int32_t min = 0; @@ -165,7 +157,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { eSearchField, // TextFieldParams eProgressBar, // ProgressParams eMeter, // MeterParams - eTreeHeaderCell, // TreeHeaderCellParams eScale, // ScaleParams eMultilineTextField, // bool eListBox, @@ -221,9 +212,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { static WidgetInfo Meter(const MeterParams& aParams) { return WidgetInfo(Widget::eMeter, aParams); } - static WidgetInfo TreeHeaderCell(const TreeHeaderCellParams& aParams) { - return WidgetInfo(Widget::eTreeHeaderCell, aParams); - } static WidgetInfo Scale(const ScaleParams& aParams) { return WidgetInfo(Widget::eScale, aParams); } @@ -251,7 +239,7 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { mozilla::Variant<mozilla::gfx::sRGBColor, CheckboxOrRadioParams, ButtonParams, DropdownParams, SpinButtonParams, SegmentParams, TextFieldParams, ProgressParams, - MeterParams, TreeHeaderCellParams, ScaleParams, bool> + MeterParams, ScaleParams, bool> mVariant; enum Widget mWidget; @@ -262,47 +250,42 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { NS_DECL_ISUPPORTS_INHERITED // The nsITheme interface. - NS_IMETHOD DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame, - StyleAppearance aAppearance, - const nsRect& aRect, const nsRect& aDirtyRect, + NS_IMETHOD DrawWidgetBackground(gfxContext* aContext, nsIFrame*, + StyleAppearance, const nsRect& aRect, + const nsRect& aDirtyRect, DrawOverflow) override; bool CreateWebRenderCommandsForWidget( mozilla::wr::DisplayListBuilder& aBuilder, mozilla::wr::IpcResourceUpdateQueue& aResources, const mozilla::layers::StackingContextHelper& aSc, - mozilla::layers::RenderRootStateManager* aManager, nsIFrame* aFrame, - StyleAppearance aAppearance, const nsRect& aRect) override; - [[nodiscard]] LayoutDeviceIntMargin GetWidgetBorder( - nsDeviceContext* aContext, nsIFrame* aFrame, - StyleAppearance aAppearance) override; - - bool GetWidgetPadding(nsDeviceContext* aContext, nsIFrame* aFrame, - StyleAppearance aAppearance, + mozilla::layers::RenderRootStateManager* aManager, nsIFrame*, + StyleAppearance, const nsRect& aRect) override; + [[nodiscard]] LayoutDeviceIntMargin GetWidgetBorder(nsDeviceContext* aContext, + nsIFrame*, + StyleAppearance) override; + + bool GetWidgetPadding(nsDeviceContext* aContext, nsIFrame*, StyleAppearance, LayoutDeviceIntMargin* aResult) override; - virtual bool GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame, - StyleAppearance aAppearance, - nsRect* aOverflowRect) override; + bool GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame*, StyleAppearance, + nsRect* aOverflowRect) override; LayoutDeviceIntSize GetMinimumWidgetSize(nsPresContext*, nsIFrame*, StyleAppearance) override; - NS_IMETHOD WidgetStateChanged(nsIFrame* aFrame, StyleAppearance aAppearance, - nsAtom* aAttribute, bool* aShouldRepaint, + NS_IMETHOD WidgetStateChanged(nsIFrame*, StyleAppearance, nsAtom* aAttribute, + bool* aShouldRepaint, const nsAttrValue* aOldValue) override; NS_IMETHOD ThemeChanged() override; - bool ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame, - StyleAppearance aAppearance) override; - bool WidgetIsContainer(StyleAppearance aAppearance) override; + bool ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame*, + StyleAppearance) override; + bool WidgetIsContainer(StyleAppearance) override; bool ThemeDrawsFocusForWidget(nsIFrame*, StyleAppearance) override; bool ThemeNeedsComboboxDropmarker() override; - virtual bool WidgetAppearanceDependsOnWindowFocus( - StyleAppearance aAppearance) override; - virtual ThemeGeometryType ThemeGeometryTypeForWidget( - nsIFrame* aFrame, StyleAppearance aAppearance) override; - virtual Transparency GetWidgetTransparency( - nsIFrame* aFrame, StyleAppearance aAppearance) override; - mozilla::Maybe<WidgetInfo> ComputeWidgetInfo(nsIFrame* aFrame, - StyleAppearance aAppearance, + bool WidgetAppearanceDependsOnWindowFocus(StyleAppearance) override; + ThemeGeometryType ThemeGeometryTypeForWidget(nsIFrame*, + StyleAppearance) override; + Transparency GetWidgetTransparency(nsIFrame*, StyleAppearance) override; + mozilla::Maybe<WidgetInfo> ComputeWidgetInfo(nsIFrame*, StyleAppearance, const nsRect& aRect); void DrawProgress(CGContextRef context, const HIRect& inBoxRect, const ProgressParams& aParams); @@ -310,24 +293,18 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { protected: virtual ~nsNativeThemeCocoa(); - LayoutDeviceIntMargin DirectionAwareMargin( - const LayoutDeviceIntMargin& aMargin, nsIFrame* aFrame); + LayoutDeviceIntMargin DirectionAwareMargin(const LayoutDeviceIntMargin&, + nsIFrame*); nsIFrame* SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter); - ControlParams ComputeControlParams(nsIFrame* aFrame, - mozilla::dom::ElementState aEventState); - SegmentParams ComputeSegmentParams(nsIFrame* aFrame, - mozilla::dom::ElementState aEventState, - SegmentType aSegmentType); - TextFieldParams ComputeTextFieldParams( - nsIFrame* aFrame, mozilla::dom::ElementState aEventState); - ProgressParams ComputeProgressParams(nsIFrame* aFrame, - mozilla::dom::ElementState aEventState, + ControlParams ComputeControlParams(nsIFrame*, mozilla::dom::ElementState); + SegmentParams ComputeSegmentParams(nsIFrame*, mozilla::dom::ElementState, + SegmentType); + TextFieldParams ComputeTextFieldParams(nsIFrame*, mozilla::dom::ElementState); + ProgressParams ComputeProgressParams(nsIFrame*, mozilla::dom::ElementState, bool aIsHorizontal); - MeterParams ComputeMeterParams(nsIFrame* aFrame); - TreeHeaderCellParams ComputeTreeHeaderCellParams( - nsIFrame* aFrame, mozilla::dom::ElementState aEventState); + MeterParams ComputeMeterParams(nsIFrame*); mozilla::Maybe<ScaleParams> ComputeHTMLScaleParams( - nsIFrame* aFrame, mozilla::dom::ElementState aEventState); + nsIFrame*, mozilla::dom::ElementState); // HITheme drawing routines void DrawMeter(CGContextRef context, const HIRect& inBoxRect, @@ -363,8 +340,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { const ControlParams& aParams); void DrawButton(CGContextRef context, const HIRect& inBoxRect, const ButtonParams& aParams); - void DrawTreeHeaderCell(CGContextRef context, const HIRect& inBoxRect, - const TreeHeaderCellParams& aParams); void DrawDropdown(CGContextRef context, const HIRect& inBoxRect, const DropdownParams& aParams); HIThemeButtonDrawInfo SpinButtonDrawInfo(ThemeButtonKind aKind, @@ -396,7 +371,6 @@ class nsNativeThemeCocoa : public mozilla::widget::ThemeCocoa { NSComboBoxCell* mComboBoxCell; NSProgressBarCell* mProgressBarCell; NSLevelIndicatorCell* mMeterBarCell; - NSTableHeaderCell* mTreeHeaderCell; MOZCellDrawWindow* mCellDrawWindow = nil; MOZCellDrawView* mCellDrawView; }; diff --git a/widget/cocoa/nsNativeThemeCocoa.mm b/widget/cocoa/nsNativeThemeCocoa.mm index 18913facea..ad16fac04a 100644 --- a/widget/cocoa/nsNativeThemeCocoa.mm +++ b/widget/cocoa/nsNativeThemeCocoa.mm @@ -458,8 +458,6 @@ nsNativeThemeCocoa::nsNativeThemeCocoa() : ThemeCocoa(ScrollbarStyle()) { mMeterBarCell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSLevelIndicatorStyleContinuousCapacity]; - mTreeHeaderCell = [[NSTableHeaderCell alloc] init]; - mCellDrawView = [[MOZCellDrawView alloc] init]; if (XRE_IsParentProcess()) { @@ -498,7 +496,6 @@ nsNativeThemeCocoa::~nsNativeThemeCocoa() { [mSearchFieldCell release]; [mDropdownCell release]; [mComboBoxCell release]; - [mTreeHeaderCell release]; [mCellDrawWindow release]; [mCellDrawView release]; @@ -1094,6 +1091,7 @@ static bool ShouldUnconditionallyDrawFocusRingIfFocused(nsIFrame* aFrame) { // Mac always draws focus rings for textboxes and lists. switch (aFrame->StyleDisplay()->EffectiveAppearance()) { case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Searchfield: @@ -1316,17 +1314,6 @@ static void RenderButton(CGContextRef cgContext, const HIRect& aRenderRect, NULL); } -static ThemeDrawState ToThemeDrawState( - const nsNativeThemeCocoa::ControlParams& aParams) { - if (aParams.disabled) { - return kThemeStateUnavailable; - } - if (aParams.pressed) { - return kThemeStatePressed; - } - return kThemeStateActive; -} - void nsNativeThemeCocoa::DrawHIThemeButton( CGContextRef cgContext, const HIRect& aRect, ThemeButtonKind aKind, ThemeButtonValue aValue, ThemeDrawState aState, @@ -1376,16 +1363,6 @@ void nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, case ButtonType::eHelpButton: DrawHelpButton(cgContext, inBoxRect, controlParams); return; - case ButtonType::eTreeTwistyPointingRight: - DrawHIThemeButton(cgContext, inBoxRect, kThemeDisclosureButton, - kThemeDisclosureRight, ToThemeDrawState(controlParams), - kThemeAdornmentNone, controlParams); - return; - case ButtonType::eTreeTwistyPointingDown: - DrawHIThemeButton(cgContext, inBoxRect, kThemeDisclosureButton, - kThemeDisclosureDown, ToThemeDrawState(controlParams), - kThemeAdornmentNone, controlParams); - return; case ButtonType::eDisclosureButtonClosed: DrawDisclosureButton(cgContext, inBoxRect, controlParams, NSControlStateValueOff); @@ -1397,87 +1374,6 @@ void nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, } } -nsNativeThemeCocoa::TreeHeaderCellParams -nsNativeThemeCocoa::ComputeTreeHeaderCellParams(nsIFrame* aFrame, - ElementState aEventState) { - TreeHeaderCellParams params; - params.controlParams = ComputeControlParams(aFrame, aEventState); - params.sortDirection = GetTreeSortDirection(aFrame); - params.lastTreeHeaderCell = IsLastTreeHeaderCell(aFrame); - return params; -} - -@interface NSTableHeaderCell (NSTableHeaderCell_setSortable) -// This method has been present in the same form since at least macOS 10.4. -- (void)_setSortable:(BOOL)arg1 - showSortIndicator:(BOOL)arg2 - ascending:(BOOL)arg3 - priority:(NSInteger)arg4 - highlightForSort:(BOOL)arg5; -@end - -void nsNativeThemeCocoa::DrawTreeHeaderCell( - CGContextRef cgContext, const HIRect& inBoxRect, - const TreeHeaderCellParams& aParams) { - NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; - - // Without clearing the cell's title, it takes on a default value of "Field", - // which is displayed underneath the title set in the front-end. - NSCell* cell = (NSCell*)mTreeHeaderCell; - cell.title = @""; - - if ([mTreeHeaderCell - respondsToSelector:@selector - (_setSortable: - showSortIndicator:ascending:priority:highlightForSort:)]) { - switch (aParams.sortDirection) { - case eTreeSortDirection_Ascending: - [mTreeHeaderCell _setSortable:YES - showSortIndicator:YES - ascending:YES - priority:0 - highlightForSort:YES]; - break; - case eTreeSortDirection_Descending: - [mTreeHeaderCell _setSortable:YES - showSortIndicator:YES - ascending:NO - priority:0 - highlightForSort:YES]; - break; - default: - // eTreeSortDirection_Natural - [mTreeHeaderCell _setSortable:YES - showSortIndicator:NO - ascending:YES - priority:0 - highlightForSort:NO]; - break; - } - } - - mTreeHeaderCell.enabled = !aParams.controlParams.disabled; - mTreeHeaderCell.state = - (mTreeHeaderCell.enabled && aParams.controlParams.pressed) - ? NSControlStateValueOn - : NSControlStateValueOff; - - mCellDrawView._drawingEndSeparator = !aParams.lastTreeHeaderCell; - - NSGraphicsContext* savedContext = NSGraphicsContext.currentContext; - NSGraphicsContext.currentContext = - [NSGraphicsContext graphicsContextWithCGContext:cgContext flipped:YES]; - DrawCellIncludingFocusRing(mTreeHeaderCell, inBoxRect, mCellDrawView); - NSGraphicsContext.currentContext = savedContext; - -#if DRAW_IN_FRAME_DEBUG - CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25); - CGContextFillRect(cgContext, inBoxRect); -#endif - - NS_OBJC_END_TRY_IGNORE_BLOCK; -} - static const CellRenderSettings dropdownSettings = { { NSMakeSize(0, 16), // mini @@ -2159,8 +2055,6 @@ Maybe<nsNativeThemeCocoa::WidgetInfo> nsNativeThemeCocoa::ComputeWidgetInfo( switch (aAppearance) { case StyleAppearance::Menupopup: - return Nothing(); - case StyleAppearance::Tooltip: return Nothing(); @@ -2323,6 +2217,7 @@ Maybe<nsNativeThemeCocoa::WidgetInfo> nsNativeThemeCocoa::ComputeWidgetInfo( case StyleAppearance::Textfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: return Some( WidgetInfo::TextField(ComputeTextFieldParams(aFrame, elementState))); @@ -2348,30 +2243,6 @@ Maybe<nsNativeThemeCocoa::WidgetInfo> nsNativeThemeCocoa::ComputeWidgetInfo( // Do nothing: progress and meter bars cases will draw chunks. break; - case StyleAppearance::Treetwisty: - return Some(WidgetInfo::Button( - ButtonParams{ComputeControlParams(aFrame, elementState), - ButtonType::eTreeTwistyPointingRight})); - - case StyleAppearance::Treetwistyopen: - return Some(WidgetInfo::Button( - ButtonParams{ComputeControlParams(aFrame, elementState), - ButtonType::eTreeTwistyPointingDown})); - - case StyleAppearance::Treeheadercell: - return Some(WidgetInfo::TreeHeaderCell( - ComputeTreeHeaderCellParams(aFrame, elementState))); - - case StyleAppearance::Treeitem: - case StyleAppearance::Treeview: - return Some(WidgetInfo::ColorFill(sRGBColor(1.0, 1.0, 1.0, 1.0))); - - case StyleAppearance::Treeheader: - // do nothing, taken care of by individual header cells - case StyleAppearance::Treeline: - // do nothing, these lines don't exist on macos - break; - case StyleAppearance::Range: { Maybe<ScaleParams> params = ComputeHTMLScaleParams(aFrame, elementState); if (params) { @@ -2581,12 +2452,6 @@ void nsNativeThemeCocoa::RenderWidget(const WidgetInfo& aWidgetInfo, DrawMeter(cgContext, macRect, params); break; } - case Widget::eTreeHeaderCell: { - TreeHeaderCellParams params = - aWidgetInfo.Params<TreeHeaderCellParams>(); - DrawTreeHeaderCell(cgContext, macRect, params); - break; - } case Widget::eScale: { ScaleParams params = aWidgetInfo.Params<ScaleParams>(); DrawScale(cgContext, macRect, params); @@ -2668,14 +2533,10 @@ bool nsNativeThemeCocoa::CreateWebRenderCommandsForWidget( case StyleAppearance::MozMenulistArrowButton: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Searchfield: case StyleAppearance::ProgressBar: case StyleAppearance::Meter: - case StyleAppearance::Treeheadercell: - case StyleAppearance::Treetwisty: - case StyleAppearance::Treetwistyopen: - case StyleAppearance::Treeitem: - case StyleAppearance::Treeview: case StyleAppearance::Range: return false; @@ -2747,6 +2608,7 @@ LayoutDeviceIntMargin nsNativeThemeCocoa::GetWidgetBorder( break; case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: { SInt32 frameOutset = 0; ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset); @@ -2845,6 +2707,7 @@ bool nsNativeThemeCocoa::GetWidgetOverflow(nsDeviceContext* aContext, case StyleAppearance::MozMacHelpButton: case StyleAppearance::Toolbarbutton: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Searchfield: @@ -2957,6 +2820,7 @@ LayoutDeviceIntSize nsNativeThemeCocoa::GetMinimumWidgetSize( } case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Searchfield: { @@ -2985,23 +2849,6 @@ LayoutDeviceIntSize nsNativeThemeCocoa::GetMinimumWidgetSize( break; } - case StyleAppearance::Treetwisty: - case StyleAppearance::Treetwistyopen: { - SInt32 twistyHeight = 0, twistyWidth = 0; - ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth); - ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight); - result.SizeTo(twistyWidth, twistyHeight); - break; - } - - case StyleAppearance::Treeheader: - case StyleAppearance::Treeheadercell: { - SInt32 headerHeight = 0; - ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight); - result.SizeTo(0, headerHeight); - break; - } - case StyleAppearance::Tab: { result.SizeTo(0, tabHeights[miniControlSize]); break; @@ -3128,6 +2975,7 @@ bool nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::SpinnerDownbutton: case StyleAppearance::Statusbar: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Searchfield: @@ -3140,14 +2988,6 @@ bool nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::Tabpanels: case StyleAppearance::Tab: - case StyleAppearance::Treetwisty: - case StyleAppearance::Treetwistyopen: - case StyleAppearance::Treeview: - case StyleAppearance::Treeheader: - case StyleAppearance::Treeheadercell: - case StyleAppearance::Treeitem: - case StyleAppearance::Treeline: - case StyleAppearance::Range: return !IsWidgetStyled(aPresContext, aFrame, aAppearance); @@ -3184,6 +3024,7 @@ bool nsNativeThemeCocoa::ThemeDrawsFocusForWidget(nsIFrame*, case StyleAppearance::Textfield: case StyleAppearance::Searchfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: case StyleAppearance::Button: @@ -3212,9 +3053,8 @@ bool nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus( case StyleAppearance::SpinnerDownbutton: case StyleAppearance::Separator: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: - case StyleAppearance::Treeview: - case StyleAppearance::Treeline: case StyleAppearance::Textarea: case StyleAppearance::Listbox: return false; diff --git a/widget/gtk/AsyncGtkClipboardRequest.cpp b/widget/gtk/AsyncGtkClipboardRequest.cpp index 75801c698d..872b005bfd 100644 --- a/widget/gtk/AsyncGtkClipboardRequest.cpp +++ b/widget/gtk/AsyncGtkClipboardRequest.cpp @@ -17,17 +17,17 @@ AsyncGtkClipboardRequest::AsyncGtkClipboardRequest(ClipboardDataType aDataType, switch (aDataType) { case ClipboardDataType::Data: - LOGCLIP(" getting DATA MIME %s\n", aMimeType); + MOZ_CLIPBOARD_LOG(" getting DATA MIME %s\n", aMimeType); gtk_clipboard_request_contents(clipboard, gdk_atom_intern(aMimeType, FALSE), OnDataReceived, mRequest.get()); break; case ClipboardDataType::Text: - LOGCLIP(" getting TEXT\n"); + MOZ_CLIPBOARD_LOG(" getting TEXT\n"); gtk_clipboard_request_text(clipboard, OnTextReceived, mRequest.get()); break; case ClipboardDataType::Targets: - LOGCLIP(" getting TARGETS\n"); + MOZ_CLIPBOARD_LOG(" getting TARGETS\n"); gtk_clipboard_request_contents(clipboard, gdk_atom_intern("TARGETS", FALSE), OnDataReceived, mRequest.get()); @@ -39,9 +39,10 @@ void AsyncGtkClipboardRequest::OnDataReceived(GtkClipboard* clipboard, GtkSelectionData* selection_data, gpointer data) { int whichClipboard = GetGeckoClipboardType(clipboard); - LOGCLIP("OnDataReceived(%s) callback\n", - whichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("OnDataReceived(%s) callback\n", + whichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); static_cast<Request*>(data)->Complete(selection_data); } @@ -49,14 +50,16 @@ void AsyncGtkClipboardRequest::OnTextReceived(GtkClipboard* clipboard, const gchar* text, gpointer data) { int whichClipboard = GetGeckoClipboardType(clipboard); - LOGCLIP("OnTextReceived(%s) callback\n", - whichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("OnTextReceived(%s) callback\n", + whichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); static_cast<Request*>(data)->Complete(text); } void AsyncGtkClipboardRequest::Request::Complete(const void* aData) { - LOGCLIP("Request::Complete(), aData = %p, timedOut = %d\n", aData, mTimedOut); + MOZ_CLIPBOARD_LOG("Request::Complete(), aData = %p, timedOut = %d\n", aData, + mTimedOut); if (mTimedOut) { delete this; @@ -75,13 +78,14 @@ void AsyncGtkClipboardRequest::Request::Complete(const void* aData) { // Negative size means no data or data error. if (dataLength <= 0) { - LOGCLIP(" zero dataLength, quit.\n"); + MOZ_CLIPBOARD_LOG(" zero dataLength, quit.\n"); return; } switch (mDataType) { case ClipboardDataType::Targets: { - LOGCLIP(" getting %d bytes of clipboard targets.\n", dataLength); + MOZ_CLIPBOARD_LOG(" getting %d bytes of clipboard targets.\n", + dataLength); gint n_targets = 0; GdkAtom* targets = nullptr; if (!gtk_selection_data_get_targets((GtkSelectionData*)aData, &targets, @@ -95,16 +99,18 @@ void AsyncGtkClipboardRequest::Request::Complete(const void* aData) { break; } case ClipboardDataType::Text: { - LOGCLIP(" getting %d bytes of text.\n", dataLength); + MOZ_CLIPBOARD_LOG(" getting %d bytes of text.\n", dataLength); mData->SetText(Span(static_cast<const char*>(aData), dataLength)); - LOGCLIP(" done, mClipboardData = %p\n", mData->AsSpan().data()); + MOZ_CLIPBOARD_LOG(" done, mClipboardData = %p\n", + mData->AsSpan().data()); break; } case ClipboardDataType::Data: { - LOGCLIP(" getting %d bytes of data.\n", dataLength); + MOZ_CLIPBOARD_LOG(" getting %d bytes of data.\n", dataLength); mData->SetData(Span(gtk_selection_data_get_data((GtkSelectionData*)aData), dataLength)); - LOGCLIP(" done, mClipboardData = %p\n", mData->AsSpan().data()); + MOZ_CLIPBOARD_LOG(" done, mClipboardData = %p\n", + mData->AsSpan().data()); break; } } diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp index 457b2c72ce..9c4faa26bb 100644 --- a/widget/gtk/GfxInfo.cpp +++ b/widget/gtk/GfxInfo.cpp @@ -1042,30 +1042,14 @@ const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { //////////////////////////////////// // FEATURE_DMABUF_SURFACE_EXPORT - // Disabled due to: + // Disabled on all Mesa drivers due to various issue, among them: // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6666 // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6796 - APPEND_TO_DRIVER_BLOCKLIST_EXT( - OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, - WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::AtiAll, - nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT, - nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, - V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", ""); - - // Disabled due to: // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6688 - APPEND_TO_DRIVER_BLOCKLIST_EXT( - OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, - WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::IntelAll, - nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT, - nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, - V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", ""); - - // Disabled due to: // https://gitlab.freedesktop.org/mesa/mesa/-/issues/6988 APPEND_TO_DRIVER_BLOCKLIST_EXT( OperatingSystem::Linux, ScreenSizeStatus::All, BatteryStatus::All, - WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::QualcommAll, + WindowProtocol::All, DriverVendor::MesaAll, DeviceFamily::All, nsIGfxInfo::FEATURE_DMABUF_SURFACE_EXPORT, nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, V(0, 0, 0, 0), "FEATURE_FAILURE_BROKEN_DRIVER", ""); diff --git a/widget/gtk/GtkCompositorWidget.cpp b/widget/gtk/GtkCompositorWidget.cpp index 073ad5248f..50eb90a0c8 100644 --- a/widget/gtk/GtkCompositorWidget.cpp +++ b/widget/gtk/GtkCompositorWidget.cpp @@ -141,15 +141,12 @@ bool GtkCompositorWidget::SetEGLNativeWindowSize( } LayoutDeviceIntRegion GtkCompositorWidget::GetTransparentRegion() { - // We need to clear target buffer alpha values of popup windows as - // SW-WR paints with alpha blending (see Bug 1674473). - if (!mWidget || mWidget->IsPopup()) { - return LayoutDeviceIntRect(LayoutDeviceIntPoint(0, 0), GetClientSize()); + LayoutDeviceIntRegion fullRegion( + LayoutDeviceIntRect(LayoutDeviceIntPoint(), GetClientSize())); + if (mWidget) { + fullRegion.SubOut(mWidget->GetOpaqueRegion()); } - - // Clear background of titlebar area to render titlebar - // transparent corners correctly. - return mWidget->GetTitlebarRect(); + return fullRegion; } #ifdef MOZ_WAYLAND diff --git a/widget/gtk/MozContainerWayland.cpp b/widget/gtk/MozContainerWayland.cpp index 39e8a48390..3a4660f4e0 100644 --- a/widget/gtk/MozContainerWayland.cpp +++ b/widget/gtk/MozContainerWayland.cpp @@ -84,7 +84,8 @@ using namespace mozilla::widget; static bool moz_container_wayland_surface_create_locked( const MutexAutoLock& aProofOfLock, MozContainer* container); static void moz_container_wayland_set_opaque_region_locked( - const MutexAutoLock& aProofOfLock, MozContainer* container); + const MutexAutoLock& aProofOfLock, MozContainer* container, + const LayoutDeviceIntRegion&); // Lock mozcontainer and get wayland surface of it. You need to pair with // moz_container_wayland_surface_unlock() even @@ -413,7 +414,10 @@ gboolean moz_container_wayland_map_event(GtkWidget* widget, nsWindow* window = moz_container_get_nsWindow(MOZ_CONTAINER(widget)); moz_container_wayland_set_scale_factor_locked(lock, MOZ_CONTAINER(widget), window->GdkCeiledScaleFactor()); - moz_container_wayland_set_opaque_region_locked(lock, MOZ_CONTAINER(widget)); + if (container->data.wl_container.opaque_region_needs_updates) { + moz_container_wayland_set_opaque_region_locked(lock, container, + window->GetOpaqueRegion()); + } moz_container_clear_input_region(MOZ_CONTAINER(widget)); moz_container_wayland_invalidate(MOZ_CONTAINER(widget)); return FALSE; @@ -456,7 +460,10 @@ void moz_container_wayland_size_allocate(GtkWidget* widget, nsWindow* window = moz_container_get_nsWindow(container); moz_container_wayland_set_scale_factor_locked( lock, container, window->GdkCeiledScaleFactor()); - moz_container_wayland_set_opaque_region_locked(lock, container); + if (container->data.wl_container.opaque_region_needs_updates) { + moz_container_wayland_set_opaque_region_locked(lock, container, + window->GetOpaqueRegion()); + } moz_container_wayland_move_locked(lock, container, allocation->x, allocation->y); moz_container_clear_input_region(container); @@ -465,54 +472,28 @@ void moz_container_wayland_size_allocate(GtkWidget* widget, } } -static wl_region* moz_container_wayland_create_opaque_region( - int aX, int aY, int aWidth, int aHeight, int aCornerRadius) { - struct wl_compositor* compositor = WaylandDisplayGet()->GetCompositor(); - wl_region* region = wl_compositor_create_region(compositor); - wl_region_add(region, aX, aY, aWidth, aHeight); - if (aCornerRadius) { - wl_region_subtract(region, aX, aY, aCornerRadius, aCornerRadius); - wl_region_subtract(region, aX + aWidth - aCornerRadius, aY, aCornerRadius, - aCornerRadius); - wl_region_subtract(region, aX, aY + aHeight - aCornerRadius, aCornerRadius, - aCornerRadius); - wl_region_subtract(region, aX + aWidth - aCornerRadius, - aY + aHeight - aCornerRadius, aCornerRadius, - aCornerRadius); - } - return region; -} - static void moz_container_wayland_set_opaque_region_locked( - const MutexAutoLock& aProofOfLock, MozContainer* container) { + const MutexAutoLock& aProofOfLock, MozContainer* container, + const LayoutDeviceIntRegion& aRegion) { MozContainerWayland* wl_container = &container->data.wl_container; - - if (!wl_container->opaque_region_needs_updates) { + MOZ_ASSERT(wl_container->opaque_region_needs_updates); + if (!wl_container->surface) { return; } + wl_container->opaque_region_needs_updates = false; if (!wl_container->opaque_region_used) { - wl_container->opaque_region_needs_updates = false; return; } - GtkAllocation allocation; - gtk_widget_get_allocation(GTK_WIDGET(container), &allocation); - - wl_region* region = moz_container_wayland_create_opaque_region( - 0, 0, allocation.width, allocation.height, - wl_container->opaque_region_corner_radius); + wl_region* region = + wl_compositor_create_region(WaylandDisplayGet()->GetCompositor()); + for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) { + const auto& rect = iter.Get(); + wl_region_add(region, rect.x, rect.y, rect.Width(), rect.Height()); + } wl_surface_set_opaque_region(wl_container->surface, region); wl_region_destroy(region); - wl_container->opaque_region_needs_updates = false; -} - -static void moz_container_wayland_set_opaque_region(MozContainer* container) { - MozContainerWayland* wl_container = &container->data.wl_container; - MutexAutoLock lock(wl_container->container_lock); - if (wl_container->surface) { - moz_container_wayland_set_opaque_region_locked(lock, container); - } } static void moz_container_wayland_surface_set_scale_locked( @@ -757,17 +738,18 @@ gboolean moz_container_wayland_has_egl_window(MozContainer* container) { return !!container->data.wl_container.eglwindow; } -void moz_container_wayland_update_opaque_region(MozContainer* container, - int corner_radius) { +void moz_container_wayland_update_opaque_region(MozContainer* container) { MozContainerWayland* wl_container = &container->data.wl_container; + MutexAutoLock lock(wl_container->container_lock); wl_container->opaque_region_needs_updates = true; - wl_container->opaque_region_corner_radius = corner_radius; // When GL compositor / WebRender is used, // moz_container_wayland_get_egl_window() is called only once when window // is created or resized so update opaque region now. if (moz_container_wayland_has_egl_window(container)) { - moz_container_wayland_set_opaque_region(container); + nsWindow* window = moz_container_get_nsWindow(container); + moz_container_wayland_set_opaque_region_locked(lock, container, + window->GetOpaqueRegion()); } } diff --git a/widget/gtk/MozContainerWayland.h b/widget/gtk/MozContainerWayland.h index 068c674256..6a33df2642 100644 --- a/widget/gtk/MozContainerWayland.h +++ b/widget/gtk/MozContainerWayland.h @@ -42,7 +42,6 @@ struct MozContainerWayland { struct wp_viewport* viewport = nullptr; struct wp_fractional_scale_v1* fractional_scale = nullptr; gboolean opaque_region_needs_updates = false; - int opaque_region_corner_radius = 0; gboolean opaque_region_used = false; gboolean ready_to_draw = false; gboolean commit_to_parent = false; @@ -97,8 +96,7 @@ void moz_container_wayland_add_or_fire_initial_draw_callback( void moz_container_wayland_clear_initial_draw_callback(MozContainer* container); wl_surface* moz_gtk_widget_get_wl_surface(GtkWidget* aWidget); -void moz_container_wayland_update_opaque_region(MozContainer* container, - int corner_radius); +void moz_container_wayland_update_opaque_region(MozContainer* container); gboolean moz_container_wayland_can_draw(MozContainer* container); double moz_container_wayland_get_scale(MozContainer* container); double moz_container_wayland_get_fractional_scale(MozContainer* container); diff --git a/widget/gtk/WidgetStyleCache.cpp b/widget/gtk/WidgetStyleCache.cpp index 13b194a64e..b3d9bfcbbd 100644 --- a/widget/gtk/WidgetStyleCache.cpp +++ b/widget/gtk/WidgetStyleCache.cpp @@ -1079,11 +1079,6 @@ static GtkStyleContext* GetCssNodeStyleInternal(WidgetNodeType aNodeType) { // TODO - create from CSS node style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW, GTK_STYLE_CLASS_VIEW); break; - case MOZ_GTK_TREEVIEW_EXPANDER: - // TODO - create from CSS node - style = - CreateSubStyleWithClass(MOZ_GTK_TREEVIEW, GTK_STYLE_CLASS_EXPANDER); - break; case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL: style = CreateChildCSSNode("separator", MOZ_GTK_SPLITTER_HORIZONTAL); break; @@ -1218,10 +1213,6 @@ static GtkStyleContext* GetWidgetStyleInternal(WidgetNodeType aNodeType) { case MOZ_GTK_TREEVIEW_VIEW: style = CreateSubStyleWithClass(MOZ_GTK_TREEVIEW, GTK_STYLE_CLASS_VIEW); break; - case MOZ_GTK_TREEVIEW_EXPANDER: - style = - CreateSubStyleWithClass(MOZ_GTK_TREEVIEW, GTK_STYLE_CLASS_EXPANDER); - break; case MOZ_GTK_SPLITTER_SEPARATOR_HORIZONTAL: style = CreateSubStyleWithClass(MOZ_GTK_SPLITTER_HORIZONTAL, GTK_STYLE_CLASS_PANE_SEPARATOR); diff --git a/widget/gtk/WindowSurfaceProvider.cpp b/widget/gtk/WindowSurfaceProvider.cpp index c8b2c5a7d6..204718bd49 100644 --- a/widget/gtk/WindowSurfaceProvider.cpp +++ b/widget/gtk/WindowSurfaceProvider.cpp @@ -12,6 +12,7 @@ #include "mozilla/layers/LayersTypes.h" #include "nsWindow.h" #include "mozilla/ScopeExit.h" +#include "WidgetUtilsGtk.h" #ifdef MOZ_WAYLAND # include "mozilla/StaticPrefs_widget.h" diff --git a/widget/gtk/gtk3drawing.cpp b/widget/gtk/gtk3drawing.cpp index 122b43d688..c67716b90e 100644 --- a/widget/gtk/gtk3drawing.cpp +++ b/widget/gtk/gtk3drawing.cpp @@ -260,7 +260,8 @@ gint moz_gtk_splitter_get_metrics(gint orientation, gint* size) { } static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance, - ToolbarButtonGTKMetrics* aMetrics) { + ToolbarButtonGTKMetrics* aMetrics, + gint* aMaxInlineMargin) { 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!"); @@ -292,6 +293,11 @@ static void CalculateToolbarButtonMetrics(WidgetNodeType aAppearance, aMetrics->iconXPosition = (width - iconWidth) / 2; aMetrics->iconYPosition = (height - iconHeight) / 2; aMetrics->minSizeWithBorder = {width, height}; + + GtkBorder margin = {0}; + gtk_style_context_get_margin(style, gtk_style_context_get_state(style), + &margin); + *aMaxInlineMargin = std::max(*aMaxInlineMargin, margin.left + margin.right); } size_t GetGtkHeaderBarButtonLayout(Span<ButtonLayout> aButtonLayout, @@ -353,8 +359,7 @@ static void EnsureToolbarMetrics() { if (sToolbarMetrics.initialized) { return; } - // Make sure we have clean cache after theme reset, etc. - memset(&sToolbarMetrics, 0, sizeof(sToolbarMetrics)); + sToolbarMetrics = {}; // Calculate titlebar button visibility and positions. ButtonLayout buttonLayout[TOOLBAR_BUTTONS]; @@ -364,9 +369,15 @@ static void EnsureToolbarMetrics() { 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); + CalculateToolbarButtonMetrics(layout.mType, metrics, + &sToolbarMetrics.inlineSpacing); } + // Account for the spacing property in the header bar. + // Default to 6 pixels (gtk/gtkheaderbar.c) + gint spacing = 6; + g_object_get(GetWidget(MOZ_GTK_HEADER_BAR), "spacing", &spacing, nullptr); + sToolbarMetrics.inlineSpacing += spacing; sToolbarMetrics.initialized = true; } @@ -380,6 +391,11 @@ const ToolbarButtonGTKMetrics* GetToolbarButtonMetrics( return sToolbarMetrics.button + buttonIndex; } +gint moz_gtk_get_titlebar_button_spacing() { + EnsureToolbarMetrics(); + return sToolbarMetrics.inlineSpacing; +} + static gint moz_gtk_window_decoration_paint(cairo_t* cr, const GdkRectangle* rect, GtkWidgetState* state, @@ -937,54 +953,7 @@ static gint moz_gtk_treeview_paint(cairo_t* cr, GdkRectangle* rect, return MOZ_GTK_SUCCESS; } -static gint moz_gtk_tree_header_cell_paint(cairo_t* cr, - const GdkRectangle* aRect, - GtkWidgetState* state, - gboolean isSorted, - GtkTextDirection direction) { - moz_gtk_button_paint(cr, aRect, state, GTK_RELIEF_NORMAL, - GetWidget(MOZ_GTK_TREE_HEADER_CELL), direction); - return MOZ_GTK_SUCCESS; -} - -/* See gtk_expander_paint() for reference. - */ -static gint moz_gtk_treeview_expander_paint(cairo_t* cr, GdkRectangle* rect, - GtkWidgetState* state, - GtkExpanderStyle expander_state, - GtkTextDirection direction) { - /* Because the frame we get is of the entire treeview, we can't get the - * precise event state of one expander, thus rendering hover and active - * feedback useless. */ - GtkStateFlags state_flags = - state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; - - if (state->inHover) - state_flags = - static_cast<GtkStateFlags>(state_flags | GTK_STATE_FLAG_PRELIGHT); - if (state->selected) - state_flags = - static_cast<GtkStateFlags>(state_flags | GTK_STATE_FLAG_SELECTED); - - /* GTK_STATE_FLAG_ACTIVE controls expanded/colapsed state rendering - * in gtk_render_expander() - */ - if (expander_state == GTK_EXPANDER_EXPANDED) - state_flags = - static_cast<GtkStateFlags>(state_flags | checkbox_check_state); - else - state_flags = - static_cast<GtkStateFlags>(state_flags & ~(checkbox_check_state)); - - GtkStyleContext* style = GetStyleContext( - MOZ_GTK_TREEVIEW_EXPANDER, state->image_scale, direction, state_flags); - gtk_render_expander(style, cr, rect->x, rect->y, rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -/* See gtk_separator_draw() for reference. - */ +/* See gtk_separator_draw() for reference. */ static gint moz_gtk_combo_box_paint(cairo_t* cr, const GdkRectangle* aRect, GtkWidgetState* state, GtkTextDirection direction) { @@ -1572,18 +1541,6 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, moz_gtk_add_style_border(style, left, top, right, bottom); return MOZ_GTK_SUCCESS; } - case MOZ_GTK_TREE_HEADER_CELL: { - /* A Tree Header in GTK is just a different styled button - * It must be placed in a TreeView for getting the correct style - * assigned. - * That is why the following code is the same as for MOZ_GTK_BUTTON. - * */ - *left = *top = *right = *bottom = gtk_container_get_border_width( - GTK_CONTAINER(GetWidget(MOZ_GTK_TREE_HEADER_CELL))); - style = GetStyleContext(MOZ_GTK_TREE_HEADER_CELL); - moz_gtk_add_border_padding(style, left, top, right, bottom); - return MOZ_GTK_SUCCESS; - } case MOZ_GTK_DROPDOWN: { /* We need to account for the arrow on the dropdown, so text * doesn't come too close to the arrow, or in some cases spill @@ -1669,7 +1626,6 @@ gint moz_gtk_get_widget_border(WidgetNodeType widget, gint* left, gint* top, case MOZ_GTK_PROGRESS_CHUNK: case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE: case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE: - case MOZ_GTK_TREEVIEW_EXPANDER: case MOZ_GTK_HEADER_BAR: case MOZ_GTK_HEADER_BAR_MAXIMIZED: case MOZ_GTK_HEADER_BAR_BUTTON_CLOSE: @@ -1777,18 +1733,6 @@ void moz_gtk_get_arrow_size(WidgetNodeType widgetType, gint* width, } } -gint moz_gtk_get_expander_size(gint* size) { - GtkStyleContext* style = GetStyleContext(MOZ_GTK_EXPANDER); - gtk_style_context_get_style(style, "expander-size", size, NULL); - return MOZ_GTK_SUCCESS; -} - -gint moz_gtk_get_treeview_expander_size(gint* size) { - GtkStyleContext* style = GetStyleContext(MOZ_GTK_TREEVIEW); - gtk_style_context_get_style(style, "expander-size", size, NULL); - return MOZ_GTK_SUCCESS; -} - void moz_gtk_get_entry_min_height(gint* min_content_height, gint* border_padding_height) { GtkStyleContext* style = GetStyleContext(MOZ_GTK_ENTRY); @@ -2057,11 +2001,6 @@ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t* cr, } case MOZ_GTK_TREEVIEW: return moz_gtk_treeview_paint(cr, rect, state, direction); - case MOZ_GTK_TREE_HEADER_CELL: - return moz_gtk_tree_header_cell_paint(cr, rect, state, flags, direction); - case MOZ_GTK_TREEVIEW_EXPANDER: - return moz_gtk_treeview_expander_paint( - cr, rect, state, (GtkExpanderStyle)flags, direction); case MOZ_GTK_ENTRY: case MOZ_GTK_DROPDOWN_ENTRY: { GtkStyleContext* style = diff --git a/widget/gtk/gtkdrawing.h b/widget/gtk/gtkdrawing.h index 4ca226d9c7..227a33d9a9 100644 --- a/widget/gtk/gtkdrawing.h +++ b/widget/gtk/gtkdrawing.h @@ -69,38 +69,39 @@ struct MozGtkSize { } }; -typedef struct { +struct ToggleGTKMetrics { bool initialized; MozGtkSize minSizeWithBorder; GtkBorder borderAndPadding; -} ToggleGTKMetrics; +}; -typedef struct { - MozGtkSize minSizeWithBorder; - gint iconXPosition; - gint iconYPosition; -} ToolbarButtonGTKMetrics; +struct ToolbarButtonGTKMetrics { + MozGtkSize minSizeWithBorder{}; + gint iconXPosition = 0; + gint iconYPosition = 0; +}; #define TOOLBAR_BUTTONS 3 -typedef struct { - bool initialized; +struct ToolbarGTKMetrics { + bool initialized = false; + gint inlineSpacing = 0; ToolbarButtonGTKMetrics button[TOOLBAR_BUTTONS]; -} ToolbarGTKMetrics; +}; -typedef struct { +struct CSDWindowDecorationSize { bool initialized; GtkBorder decorationSize; -} CSDWindowDecorationSize; +}; /** flags for tab state **/ -typedef enum { +enum GtkTabFlags { /* first eight bits are used to pass a margin */ MOZ_GTK_TAB_MARGIN_MASK = 0xFF, /* the first tab in the group */ MOZ_GTK_TAB_FIRST = 1 << 9, /* the selected tab */ MOZ_GTK_TAB_SELECTED = 1 << 10 -} GtkTabFlags; +}; /*** result/error codes ***/ #define MOZ_GTK_SUCCESS 0 @@ -211,8 +212,6 @@ enum WidgetNodeType : int { MOZ_GTK_TREEVIEW_VIEW, /* Paints treeheader cells */ MOZ_GTK_TREE_HEADER_CELL, - /* Paints an expander for a GtkTreeView */ - MOZ_GTK_TREEVIEW_EXPANDER, /* Paints the background of menus, context menus. */ MOZ_GTK_MENUPOPUP, /* Menubar for -moz-headerbar colors */ @@ -458,22 +457,6 @@ void moz_gtk_get_entry_min_height(gint* min_content_height, gint moz_gtk_get_toolbar_separator_width(gint* size); /** - * Get the size of a regular GTK expander that shows/hides content - * size: [OUT] the size of the GTK expander, size = width = height. - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_expander_size(gint* size); - -/** - * Get the size of a treeview's expander (we call them twisties) - * size: [OUT] the size of the GTK expander, size = width = height. - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_treeview_expander_size(gint* size); - -/** * Get the desired size of a splitter * orientation: [IN] GTK_ORIENTATION_HORIZONTAL or GTK_ORIENTATION_VERTICAL * size: [OUT] width or height of the splitter handle @@ -493,6 +476,8 @@ gint moz_gtk_get_tab_thickness(WidgetNodeType aNodeType); const ToolbarButtonGTKMetrics* GetToolbarButtonMetrics( WidgetNodeType aAppearance); +gint moz_gtk_get_titlebar_button_spacing(); + /** * Get toolbar button layout. * aButtonLayout: [OUT] An array which will be filled by ButtonLayout diff --git a/widget/gtk/nsAppShell.cpp b/widget/gtk/nsAppShell.cpp index d26b43737d..652801fe69 100644 --- a/widget/gtk/nsAppShell.cpp +++ b/widget/gtk/nsAppShell.cpp @@ -53,7 +53,6 @@ LazyLogModule gWidgetWaylandLog("WidgetWayland"); LazyLogModule gWidgetPopupLog("WidgetPopup"); LazyLogModule gWidgetVsync("WidgetVsync"); LazyLogModule gDmabufLog("Dmabuf"); -LazyLogModule gClipboardLog("WidgetClipboard"); static GPollFunc sPollFunc; diff --git a/widget/gtk/nsClipboard.cpp b/widget/gtk/nsClipboard.cpp index 571b43f1cc..206c8ddeb3 100644 --- a/widget/gtk/nsClipboard.cpp +++ b/widget/gtk/nsClipboard.cpp @@ -88,7 +88,8 @@ static void SetTransferableData(nsITransferable* aTransferable, const nsACString& aFlavor, const char* aClipboardData, uint32_t aClipboardDataLength) { - LOGCLIP("SetTransferableData MIME %s\n", PromiseFlatCString(aFlavor).get()); + MOZ_CLIPBOARD_LOG("SetTransferableData MIME %s\n", + PromiseFlatCString(aFlavor).get()); nsCOMPtr<nsISupports> wrapper; nsPrimitiveHelpers::CreatePrimitiveForData( aFlavor, aClipboardData, aClipboardDataLength, getter_AddRefs(wrapper)); @@ -161,29 +162,30 @@ int GetGeckoClipboardType(GtkClipboard* aGtkClipboard) { void nsRetrievalContext::ClearCachedTargetsClipboard(GtkClipboard* aClipboard, GdkEvent* aEvent, gpointer data) { - LOGCLIP("nsRetrievalContext::ClearCachedTargetsClipboard()"); + MOZ_CLIPBOARD_LOG("nsRetrievalContext::ClearCachedTargetsClipboard()"); sClipboardTargets.Clear(); } void nsRetrievalContext::ClearCachedTargetsPrimary(GtkClipboard* aClipboard, GdkEvent* aEvent, gpointer data) { - LOGCLIP("nsRetrievalContext::ClearCachedTargetsPrimary()"); + MOZ_CLIPBOARD_LOG("nsRetrievalContext::ClearCachedTargetsPrimary()"); sPrimaryTargets.Clear(); } ClipboardTargets nsRetrievalContext::GetTargets(int32_t aWhichClipboard) { - LOGCLIP("nsRetrievalContext::GetTargets(%s)\n", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("nsRetrievalContext::GetTargets(%s)\n", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); ClipboardTargets& storedTargets = (aWhichClipboard == nsClipboard::kSelectionClipboard) ? sPrimaryTargets : sClipboardTargets; if (!storedTargets) { - LOGCLIP(" getting targets from system"); + MOZ_CLIPBOARD_LOG(" getting targets from system"); storedTargets.Set(GetTargetsImpl(aWhichClipboard)); } else { - LOGCLIP(" using cached targets"); + MOZ_CLIPBOARD_LOG(" using cached targets"); } return storedTargets.Clone(); } @@ -249,7 +251,7 @@ nsClipboard::Observe(nsISupports* aSubject, const char* aTopic, // runnable. return SchedulerGroup::Dispatch( NS_NewRunnableFunction("gtk_clipboard_store()", []() { - LOGCLIP("nsClipboard storing clipboard content\n"); + MOZ_CLIPBOARD_LOG("nsClipboard storing clipboard content\n"); gtk_clipboard_store(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); })); } @@ -269,8 +271,9 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, return NS_OK; } - LOGCLIP("nsClipboard::SetNativeClipboardData (%s)\n", - aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::SetNativeClipboardData (%s)\n", + aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); // List of suported targets GtkTargetList* list = gtk_target_list_new(nullptr, 0); @@ -279,7 +282,7 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, nsTArray<nsCString> flavors; nsresult rv = aTransferable->FlavorsTransferableCanExport(flavors); if (NS_FAILED(rv)) { - LOGCLIP(" FlavorsTransferableCanExport failed!\n"); + MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanExport failed!\n"); // Fall through. |gtkTargets| will be null below. } @@ -287,11 +290,11 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, bool imagesAdded = false; for (uint32_t i = 0; i < flavors.Length(); i++) { nsCString& flavorStr = flavors[i]; - LOGCLIP(" processing target %s\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" processing target %s\n", flavorStr.get()); // Special case text/plain since we can handle all of the string types. if (flavorStr.EqualsLiteral(kTextMime)) { - LOGCLIP(" adding TEXT targets\n"); + MOZ_CLIPBOARD_LOG(" adding TEXT targets\n"); gtk_target_list_add_text_targets(list, 0); continue; } @@ -300,7 +303,7 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, // Don't bother adding image targets twice if (!imagesAdded) { // accept any writable image type - LOGCLIP(" adding IMAGE targets\n"); + MOZ_CLIPBOARD_LOG(" adding IMAGE targets\n"); gtk_target_list_add_image_targets(list, 0, TRUE); imagesAdded = true; } @@ -308,14 +311,14 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, } if (flavorStr.EqualsLiteral(kFileMime)) { - LOGCLIP(" adding text/uri-list target\n"); + MOZ_CLIPBOARD_LOG(" adding text/uri-list target\n"); GdkAtom atom = gdk_atom_intern(kURIListMime, FALSE); gtk_target_list_add(list, atom, 0, 0); continue; } // Add this to our list of valid targets - LOGCLIP(" adding OTHER target %s\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" adding OTHER target %s\n", flavorStr.get()); GdkAtom atom = gdk_atom_intern(flavorStr.get(), FALSE); gtk_target_list_add(list, atom, 0, 0); } @@ -328,7 +331,7 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, GtkTargetEntry* gtkTargets = gtk_target_table_new_from_list(list, &numTargets); if (!gtkTargets || numTargets == 0) { - LOGCLIP( + MOZ_CLIPBOARD_LOG( " gtk_target_table_new_from_list() failed or empty list of " "targets!\n"); // Clear references to the any old data and let GTK know that it is no @@ -356,7 +359,7 @@ nsClipboard::SetNativeClipboardData(nsITransferable* aTransferable, rv = NS_OK; } else { - LOGCLIP(" gtk_clipboard_set_with_data() failed!\n"); + MOZ_CLIPBOARD_LOG(" gtk_clipboard_set_with_data() failed!\n"); EmptyNativeClipboardData(aWhichClipboard); rv = NS_ERROR_FAILURE; } @@ -390,11 +393,11 @@ static bool IsMIMEAtFlavourList(const nsTArray<nsCString>& aFlavourList, // So if clipboard contains images only remove text MIME offer. bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard, nsTArray<nsCString>& aFlavors) { - LOGCLIP("nsClipboard::FilterImportedFlavors"); + MOZ_CLIPBOARD_LOG("nsClipboard::FilterImportedFlavors"); auto targets = mContext->GetTargets(aWhichClipboard); if (!targets) { - LOGCLIP(" X11: no targes at clipboard (null), quit.\n"); + MOZ_CLIPBOARD_LOG(" X11: no targes at clipboard (null), quit.\n"); return true; } @@ -419,7 +422,8 @@ bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard, } // We have some other MIME type on clipboard which can be hopefully // converted to text without any problem. - LOGCLIP(" X11: text types in clipboard, no need to filter them.\n"); + MOZ_CLIPBOARD_LOG( + " X11: text types in clipboard, no need to filter them.\n"); return true; } @@ -436,9 +440,9 @@ bool nsClipboard::FilterImportedFlavors(int32_t aWhichClipboard, } aFlavors.SwapElements(clipboardFlavors); #ifdef MOZ_LOGGING - LOGCLIP(" X11: Flavors which match clipboard content:\n"); + MOZ_CLIPBOARD_LOG(" X11: Flavors which match clipboard content:\n"); for (uint32_t i = 0; i < aFlavors.Length(); i++) { - LOGCLIP(" %s\n", aFlavors[i].get()); + MOZ_CLIPBOARD_LOG(" %s\n", aFlavors[i].get()); } #endif return true; @@ -452,13 +456,13 @@ static nsresult GetTransferableFlavors(nsITransferable* aTransferable, // Get a list of flavors this transferable can import nsresult rv = aTransferable->FlavorsTransferableCanImport(aFlavors); if (NS_FAILED(rv)) { - LOGCLIP(" FlavorsTransferableCanImport falied!\n"); + MOZ_CLIPBOARD_LOG(" FlavorsTransferableCanImport falied!\n"); return rv; } #ifdef MOZ_LOGGING - LOGCLIP(" Flavors which can be imported:"); + MOZ_CLIPBOARD_LOG(" Flavors which can be imported:"); for (const auto& flavor : aFlavors) { - LOGCLIP(" %s", flavor.get()); + MOZ_CLIPBOARD_LOG(" %s", flavor.get()); } #endif return NS_OK; @@ -476,7 +480,7 @@ static bool TransferableSetFile(nsITransferable* aTransferable, rv = fileURL->GetFile(getter_AddRefs(file)); if (NS_SUCCEEDED(rv)) { aTransferable->SetTransferData(kFileMime, file); - LOGCLIP(" successfully set file to clipboard\n"); + MOZ_CLIPBOARD_LOG(" successfully set file to clipboard\n"); return true; } } @@ -492,17 +496,20 @@ static bool TransferableSetHTML(nsITransferable* aTransferable, nsAutoCString charset; if (!GetHTMLCharset(aData, charset)) { // Fall back to utf-8 in case html/data is missing kHTMLMarkupPrefix. - LOGCLIP("Failed to get html/text encoding, fall back to utf-8.\n"); + MOZ_CLIPBOARD_LOG( + "Failed to get html/text encoding, fall back to utf-8.\n"); charset.AssignLiteral("utf-8"); } - LOGCLIP("TransferableSetHTML: HTML detected charset %s", charset.get()); + MOZ_CLIPBOARD_LOG("TransferableSetHTML: HTML detected charset %s", + charset.get()); // app which use "text/html" to copy&paste // get the decoder auto encoding = Encoding::ForLabelNoReplacement(charset); if (!encoding) { - LOGCLIP("TransferableSetHTML: get unicode decoder error (charset: %s)", - charset.get()); + MOZ_CLIPBOARD_LOG( + "TransferableSetHTML: get unicode decoder error (charset: %s)", + charset.get()); return false; } @@ -523,16 +530,15 @@ static bool TransferableSetHTML(nsITransferable* aTransferable, nsAutoString unicodeData; auto [rv, enc] = encoding->Decode(AsBytes(aData), unicodeData); #if MOZ_LOGGING - if (enc != UTF_8_ENCODING && - MOZ_LOG_TEST(gClipboardLog, mozilla::LogLevel::Debug)) { + if (enc != UTF_8_ENCODING && MOZ_CLIPBOARD_LOG_ENABLED()) { nsCString decoderName; enc->Name(decoderName); - LOGCLIP("TransferableSetHTML: expected UTF-8 decoder but got %s", - decoderName.get()); + MOZ_CLIPBOARD_LOG("TransferableSetHTML: expected UTF-8 decoder but got %s", + decoderName.get()); } #endif if (NS_FAILED(rv)) { - LOGCLIP("TransferableSetHTML: failed to decode HTML"); + MOZ_CLIPBOARD_LOG("TransferableSetHTML: failed to decode HTML"); return false; } SetTransferableData(aTransferable, mimeType, @@ -548,8 +554,9 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, MOZ_DIAGNOSTIC_ASSERT( nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)); - LOGCLIP("nsClipboard::GetNativeClipboardData (%s)\n", - aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::GetNativeClipboardData (%s)\n", + aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); // TODO: Ensure we don't re-enter here. if (!mContext) { @@ -564,7 +571,7 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, // see Bug 1611407 if (widget::GdkIsX11Display() && !FilterImportedFlavors(aWhichClipboard, flavors)) { - LOGCLIP(" Missing suitable clipboard data, quit."); + MOZ_CLIPBOARD_LOG(" Missing suitable clipboard data, quit."); return NS_OK; } @@ -580,12 +587,13 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, flavorStr.Assign(kJPEGImageMime); } - LOGCLIP(" Getting image %s MIME clipboard data\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data\n", + flavorStr.get()); auto clipboardData = mContext->GetClipboardData(flavorStr.get(), aWhichClipboard); if (!clipboardData) { - LOGCLIP(" %s type is missing\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr.get()); continue; } @@ -593,18 +601,19 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, NS_NewByteInputStream(getter_AddRefs(byteStream), clipboardData.AsSpan(), NS_ASSIGNMENT_COPY); aTransferable->SetTransferData(flavorStr.get(), byteStream); - LOGCLIP(" got %s MIME data\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" got %s MIME data\n", flavorStr.get()); return NS_OK; } // Special case text/plain since we can convert any // string into text/plain if (flavorStr.EqualsLiteral(kTextMime)) { - LOGCLIP(" Getting text %s MIME clipboard data\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" Getting text %s MIME clipboard data\n", + flavorStr.get()); auto clipboardData = mContext->GetClipboardText(aWhichClipboard); if (!clipboardData) { - LOGCLIP(" failed to get text data\n"); + MOZ_CLIPBOARD_LOG(" failed to get text data\n"); // If the type was text/plain and we couldn't get // text off the clipboard, run the next loop // iteration. @@ -617,17 +626,18 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, (const char*)ucs2string.BeginReading(), ucs2string.Length() * 2); - LOGCLIP(" got text data, length %zd\n", ucs2string.Length()); + MOZ_CLIPBOARD_LOG(" got text data, length %zd\n", ucs2string.Length()); return NS_OK; } if (flavorStr.EqualsLiteral(kFileMime)) { - LOGCLIP(" Getting %s file clipboard data\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" Getting %s file clipboard data\n", + flavorStr.get()); auto clipboardData = mContext->GetClipboardData(kURIListMime, aWhichClipboard); if (!clipboardData) { - LOGCLIP(" text/uri-list type is missing\n"); + MOZ_CLIPBOARD_LOG(" text/uri-list type is missing\n"); continue; } @@ -638,19 +648,19 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, return NS_OK; } - LOGCLIP(" Getting %s MIME clipboard data\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" Getting %s MIME clipboard data\n", flavorStr.get()); auto clipboardData = mContext->GetClipboardData(flavorStr.get(), aWhichClipboard); #ifdef MOZ_LOGGING if (!clipboardData) { - LOGCLIP(" %s type is missing\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" %s type is missing\n", flavorStr.get()); } #endif if (clipboardData) { - LOGCLIP(" got %s mime type data.\n", flavorStr.get()); + MOZ_CLIPBOARD_LOG(" got %s mime type data.\n", flavorStr.get()); // Special case text/html since we can convert into UCS2 if (flavorStr.EqualsLiteral(kHTMLMime)) { @@ -666,7 +676,7 @@ nsClipboard::GetNativeClipboardData(nsITransferable* aTransferable, } } - LOGCLIP(" failed to get clipboard content.\n"); + MOZ_CLIPBOARD_LOG(" failed to get clipboard content.\n"); return NS_OK; } @@ -692,11 +702,11 @@ struct DataCallbackHandler { mMimeType(aMimeType), mDataType(aDataType) { MOZ_COUNT_CTOR(DataCallbackHandler); - LOGCLIP("DataCallbackHandler created [%p] MIME %s type %d", this, - mMimeType.get(), mDataType); + MOZ_CLIPBOARD_LOG("DataCallbackHandler created [%p] MIME %s type %d", this, + mMimeType.get(), mDataType); } ~DataCallbackHandler() { - LOGCLIP("DataCallbackHandler deleted [%p]", this); + MOZ_CLIPBOARD_LOG("DataCallbackHandler deleted [%p]", this); MOZ_COUNT_DTOR(DataCallbackHandler); } }; @@ -704,20 +714,21 @@ struct DataCallbackHandler { static void AsyncGetTextImpl(nsITransferable* aTransferable, int32_t aWhichClipboard, nsBaseClipboard::GetDataCallback&& aCallback) { - LOGCLIP("AsyncGetText() type '%s'", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("AsyncGetText() type '%s'", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); gtk_clipboard_request_text( gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)), [](GtkClipboard* aClipboard, const gchar* aText, gpointer aData) -> void { UniquePtr<DataCallbackHandler> ref( static_cast<DataCallbackHandler*>(aData)); - LOGCLIP("AsyncGetText async handler of [%p]", aData); + MOZ_CLIPBOARD_LOG("AsyncGetText async handler of [%p]", aData); size_t dataLength = aText ? strlen(aText) : 0; if (dataLength <= 0) { - LOGCLIP(" quit, text is not available"); + MOZ_CLIPBOARD_LOG(" quit, text is not available"); ref->mDataCallback(NS_OK); return; } @@ -728,7 +739,7 @@ static void AsyncGetTextImpl(nsITransferable* aTransferable, SetTransferableData(ref->mTransferable, flavor, (const char*)utf16string.BeginReading(), utf16string.Length() * 2); - LOGCLIP(" text is set, length = %d", (int)dataLength); + MOZ_CLIPBOARD_LOG(" text is set, length = %d", (int)dataLength); ref->mDataCallback(NS_OK); }, new DataCallbackHandler(aTransferable, std::move(aCallback), kTextMime)); @@ -738,9 +749,10 @@ static void AsyncGetDataImpl(nsITransferable* aTransferable, int32_t aWhichClipboard, const char* aMimeType, DataType aDataType, nsBaseClipboard::GetDataCallback&& aCallback) { - LOGCLIP("AsyncGetData() type '%s'", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("AsyncGetData() type '%s'", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); const char* gtkMIMEType = nullptr; switch (aDataType) { @@ -762,8 +774,8 @@ static void AsyncGetDataImpl(nsITransferable* aTransferable, gpointer aData) -> void { UniquePtr<DataCallbackHandler> ref( static_cast<DataCallbackHandler*>(aData)); - LOGCLIP("AsyncGetData async handler [%p] MIME %s type %d", aData, - ref->mMimeType.get(), ref->mDataType); + MOZ_CLIPBOARD_LOG("AsyncGetData async handler [%p] MIME %s type %d", + aData, ref->mMimeType.get(), ref->mDataType); int dataLength = gtk_selection_data_get_length(aSelection); if (dataLength <= 0) { @@ -777,7 +789,7 @@ static void AsyncGetDataImpl(nsITransferable* aTransferable, } switch (ref->mDataType) { case DATATYPE_IMAGE: { - LOGCLIP(" set image clipboard data"); + MOZ_CLIPBOARD_LOG(" set image clipboard data"); nsCOMPtr<nsIInputStream> byteStream; NS_NewByteInputStream(getter_AddRefs(byteStream), Span(data, dataLength), NS_ASSIGNMENT_COPY); @@ -786,19 +798,19 @@ static void AsyncGetDataImpl(nsITransferable* aTransferable, break; } case DATATYPE_FILE: { - LOGCLIP(" set file clipboard data"); + MOZ_CLIPBOARD_LOG(" set file clipboard data"); nsDependentCSubstring file(data, dataLength); TransferableSetFile(ref->mTransferable, file); break; } case DATATYPE_HTML: { - LOGCLIP(" html clipboard data"); + MOZ_CLIPBOARD_LOG(" html clipboard data"); Span dataSpan(data, dataLength); TransferableSetHTML(ref->mTransferable, dataSpan); break; } case DATATYPE_RAW: { - LOGCLIP(" raw clipboard data %s", ref->mMimeType.get()); + MOZ_CLIPBOARD_LOG(" raw clipboard data %s", ref->mMimeType.get()); SetTransferableData(ref->mTransferable, ref->mMimeType, data, dataLength); break; @@ -821,7 +833,8 @@ static void AsyncGetDataFlavor(nsITransferable* aTransferable, if (aFlavorStr.EqualsLiteral(kJPGImageMime)) { aFlavorStr.Assign(kJPEGImageMime); } - LOGCLIP(" Getting image %s MIME clipboard data", aFlavorStr.get()); + MOZ_CLIPBOARD_LOG(" Getting image %s MIME clipboard data", + aFlavorStr.get()); AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(), DATATYPE_IMAGE, std::move(aCallback)); return; @@ -829,23 +842,23 @@ static void AsyncGetDataFlavor(nsITransferable* aTransferable, // Special case text/plain since we can convert any // string into text/plain if (aFlavorStr.EqualsLiteral(kTextMime)) { - LOGCLIP(" Getting unicode clipboard data"); + MOZ_CLIPBOARD_LOG(" Getting unicode clipboard data"); AsyncGetTextImpl(aTransferable, aWhichClipboard, std::move(aCallback)); return; } if (aFlavorStr.EqualsLiteral(kFileMime)) { - LOGCLIP(" Getting file clipboard data\n"); + MOZ_CLIPBOARD_LOG(" Getting file clipboard data\n"); AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(), DATATYPE_FILE, std::move(aCallback)); return; } if (aFlavorStr.EqualsLiteral(kHTMLMime)) { - LOGCLIP(" Getting HTML clipboard data"); + MOZ_CLIPBOARD_LOG(" Getting HTML clipboard data"); AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(), DATATYPE_HTML, std::move(aCallback)); return; } - LOGCLIP(" Getting raw %s MIME clipboard data\n", aFlavorStr.get()); + MOZ_CLIPBOARD_LOG(" Getting raw %s MIME clipboard data\n", aFlavorStr.get()); AsyncGetDataImpl(aTransferable, aWhichClipboard, aFlavorStr.get(), DATATYPE_RAW, std::move(aCallback)); } @@ -857,9 +870,10 @@ void nsClipboard::AsyncGetNativeClipboardData(nsITransferable* aTransferable, MOZ_DIAGNOSTIC_ASSERT( nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)); - LOGCLIP("nsClipboard::AsyncGetNativeClipboardData (%s)", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("nsClipboard::AsyncGetNativeClipboardData (%s)", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); nsTArray<nsCString> importedFlavors; nsresult rv = GetTransferableFlavors(aTransferable, importedFlavors); if (NS_FAILED(rv)) { @@ -874,8 +888,9 @@ void nsClipboard::AsyncGetNativeClipboardData(nsITransferable* aTransferable, } #ifdef MOZ_LOGGING if (flavorsNum > 1) { - LOGCLIP(" Only first MIME type (%s) will be imported from clipboard!", - importedFlavors[0].get()); + MOZ_CLIPBOARD_LOG( + " Only first MIME type (%s) will be imported from clipboard!", + importedFlavors[0].get()); } #endif @@ -894,7 +909,7 @@ void nsClipboard::AsyncGetNativeClipboardData(nsITransferable* aTransferable, nsTArray<nsCString> clipboardFlavors = std::move(aResultOrError.unwrap()); if (!clipboardFlavors.Length()) { - LOGCLIP(" no flavors in clipboard, quit."); + MOZ_CLIPBOARD_LOG(" no flavors in clipboard, quit."); callback(NS_OK); return; } @@ -914,8 +929,9 @@ nsresult nsClipboard::EmptyNativeClipboardData(int32_t aWhichClipboard) { MOZ_DIAGNOSTIC_ASSERT( nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)); - LOGCLIP("nsClipboard::EmptyNativeClipboardData (%s)\n", - aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::EmptyNativeClipboardData (%s)\n", + aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); if (aWhichClipboard == kSelectionClipboard) { if (mSelectionTransferable) { gtk_clipboard_clear(gtk_clipboard_get(GDK_SELECTION_PRIMARY)); @@ -947,20 +963,21 @@ static bool FlavorMatchesTarget(const nsACString& aFlavor, GdkAtom aTarget) { return false; } if (aFlavor.Equals(atom_name.get())) { - LOGCLIP(" has %s\n", atom_name.get()); + MOZ_CLIPBOARD_LOG(" has %s\n", atom_name.get()); return true; } // X clipboard supports image/jpeg, but we want to emulate support // for image/jpg as well if (aFlavor.EqualsLiteral(kJPGImageMime) && !strcmp(atom_name.get(), kJPEGImageMime)) { - LOGCLIP(" has image/jpg\n"); + MOZ_CLIPBOARD_LOG(" has image/jpg\n"); return true; } // application/x-moz-file should be treated like text/uri-list if (aFlavor.EqualsLiteral(kFileMime) && !strcmp(atom_name.get(), kURIListMime)) { - LOGCLIP(" has text/uri-list treating as application/x-moz-file"); + MOZ_CLIPBOARD_LOG( + " has text/uri-list treating as application/x-moz-file"); return true; } return false; @@ -972,8 +989,9 @@ nsClipboard::HasNativeClipboardDataMatchingFlavors( MOZ_DIAGNOSTIC_ASSERT( nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)); - LOGCLIP("nsClipboard::HasNativeClipboardDataMatchingFlavors (%s)\n", - aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::HasNativeClipboardDataMatchingFlavors (%s)\n", + aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); if (!mContext) { return Err(NS_ERROR_FAILURE); @@ -981,25 +999,25 @@ nsClipboard::HasNativeClipboardDataMatchingFlavors( auto targets = mContext->GetTargets(aWhichClipboard); if (!targets) { - LOGCLIP(" no targes at clipboard (null)\n"); + MOZ_CLIPBOARD_LOG(" no targes at clipboard (null)\n"); return false; } #ifdef MOZ_LOGGING - if (LOGCLIP_ENABLED()) { - LOGCLIP(" Asking for content:\n"); + if (MOZ_CLIPBOARD_LOG_ENABLED()) { + MOZ_CLIPBOARD_LOG(" Asking for content:\n"); for (auto& flavor : aFlavorList) { - LOGCLIP(" MIME %s\n", flavor.get()); + MOZ_CLIPBOARD_LOG(" MIME %s\n", flavor.get()); } - LOGCLIP(" Clipboard content (target nums %zu):\n", - targets.AsSpan().Length()); + MOZ_CLIPBOARD_LOG(" Clipboard content (target nums %zu):\n", + targets.AsSpan().Length()); for (const auto& target : targets.AsSpan()) { GUniquePtr<gchar> atom_name(gdk_atom_name(target)); if (!atom_name) { - LOGCLIP(" failed to get MIME\n"); + MOZ_CLIPBOARD_LOG(" failed to get MIME\n"); continue; } - LOGCLIP(" MIME %s\n", atom_name.get()); + MOZ_CLIPBOARD_LOG(" MIME %s\n", atom_name.get()); } } #endif @@ -1011,7 +1029,7 @@ nsClipboard::HasNativeClipboardDataMatchingFlavors( if (flavor.EqualsLiteral(kTextMime) && gtk_targets_include_text(targets.AsSpan().data(), targets.AsSpan().Length())) { - LOGCLIP(" has kTextMime\n"); + MOZ_CLIPBOARD_LOG(" has kTextMime\n"); return true; } for (const auto& target : targets.AsSpan()) { @@ -1021,7 +1039,7 @@ nsClipboard::HasNativeClipboardDataMatchingFlavors( } } - LOGCLIP(" no targes at clipboard (bad match)\n"); + MOZ_CLIPBOARD_LOG(" no targes at clipboard (bad match)\n"); return false; } @@ -1030,10 +1048,10 @@ struct TragetCallbackHandler { nsBaseClipboard::HasMatchingFlavorsCallback&& aCallback) : mAcceptedFlavorList(aAcceptedFlavorList.Clone()), mCallback(std::move(aCallback)) { - LOGCLIP("TragetCallbackHandler(%p) created", this); + MOZ_CLIPBOARD_LOG("TragetCallbackHandler(%p) created", this); } ~TragetCallbackHandler() { - LOGCLIP("TragetCallbackHandler(%p) deleted", this); + MOZ_CLIPBOARD_LOG("TragetCallbackHandler(%p) deleted", this); } nsTArray<nsCString> mAcceptedFlavorList; nsBaseClipboard::HasMatchingFlavorsCallback mCallback; @@ -1045,15 +1063,17 @@ void nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors( MOZ_DIAGNOSTIC_ASSERT( nsIClipboard::IsClipboardTypeSupported(aWhichClipboard)); - LOGCLIP("nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors (%s)", - aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors (%s)", + aWhichClipboard == kSelectionClipboard ? "primary" : "clipboard"); gtk_clipboard_request_contents( gtk_clipboard_get(GetSelectionAtom(aWhichClipboard)), gdk_atom_intern("TARGETS", FALSE), [](GtkClipboard* aClipboard, GtkSelectionData* aSelection, gpointer aData) -> void { - LOGCLIP("gtk_clipboard_request_contents async handler (%p)", aData); + MOZ_CLIPBOARD_LOG("gtk_clipboard_request_contents async handler (%p)", + aData); UniquePtr<TragetCallbackHandler> handler( static_cast<TragetCallbackHandler*>(aData)); @@ -1061,15 +1081,30 @@ void nsClipboard::AsyncHasNativeClipboardDataMatchingFlavors( gint targetsNum = 0; if (gtk_selection_data_get_length(aSelection) > 0) { gtk_selection_data_get_targets(aSelection, &targets, &targetsNum); + +#ifdef MOZ_LOGGING + if (MOZ_CLIPBOARD_LOG_ENABLED()) { + MOZ_CLIPBOARD_LOG(" Clipboard content (target nums %d):\n", + targetsNum); + for (int i = 0; i < targetsNum; i++) { + GUniquePtr<gchar> atom_name(gdk_atom_name(targets[i])); + if (!atom_name) { + MOZ_CLIPBOARD_LOG(" failed to get MIME\n"); + continue; + } + MOZ_CLIPBOARD_LOG(" MIME %s\n", atom_name.get()); + } + } +#endif } nsTArray<nsCString> results; if (targetsNum) { for (auto& flavor : handler->mAcceptedFlavorList) { - LOGCLIP(" looking for %s", flavor.get()); + MOZ_CLIPBOARD_LOG(" looking for %s", flavor.get()); if (flavor.EqualsLiteral(kTextMime) && gtk_targets_include_text(targets, targetsNum)) { results.AppendElement(flavor); - LOGCLIP(" has kTextMime\n"); + MOZ_CLIPBOARD_LOG(" has kTextMime\n"); continue; } for (int i = 0; i < targetsNum; i++) { @@ -1113,14 +1148,16 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, else return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF - LOGCLIP("nsClipboard::SelectionGetEvent (%s)\n", - whichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::SelectionGetEvent (%s)\n", + whichClipboard == kSelectionClipboard ? "primary" : "clipboard"); nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard); if (!trans) { // We have nothing to serve - LOGCLIP("nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n", - whichClipboard == kSelectionClipboard ? "Primary" : "Clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::SelectionGetEvent() - %s clipboard is empty!\n", + whichClipboard == kSelectionClipboard ? "Primary" : "Clipboard"); return; } @@ -1128,18 +1165,18 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, nsCOMPtr<nsISupports> item; GdkAtom selectionTarget = gtk_selection_data_get_target(aSelectionData); - LOGCLIP(" selection target %s\n", - GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); + MOZ_CLIPBOARD_LOG(" selection target %s\n", + GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); // Check to see if the selection data is some text type. if (gtk_targets_include_text(&selectionTarget, 1)) { - LOGCLIP(" providing text/plain data\n"); + MOZ_CLIPBOARD_LOG(" providing text/plain data\n"); // Try to convert our internal type into a text string. Get // the transferable for this clipboard and try to get the // text/plain type for it. rv = trans->GetTransferData("text/plain", getter_AddRefs(item)); if (NS_FAILED(rv) || !item) { - LOGCLIP(" GetTransferData() failed to get text/plain!\n"); + MOZ_CLIPBOARD_LOG(" GetTransferData() failed to get text/plain!\n"); return; } @@ -1151,9 +1188,9 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, wideString->GetData(ucs2string); NS_ConvertUTF16toUTF8 utf8string(ucs2string); - LOGCLIP(" sent %zd bytes of utf-8 data\n", utf8string.Length()); + MOZ_CLIPBOARD_LOG(" sent %zd bytes of utf-8 data\n", utf8string.Length()); if (selectionTarget == gdk_atom_intern("text/plain;charset=utf-8", FALSE)) { - LOGCLIP( + MOZ_CLIPBOARD_LOG( " using gtk_selection_data_set for 'text/plain;charset=utf-8'\n"); // Bypass gtk_selection_data_set_text, which will convert \n to \r\n // in some versions of GTK. @@ -1169,7 +1206,7 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, // Check to see if the selection data is an image type if (gtk_targets_include_image(&selectionTarget, 1, TRUE)) { - LOGCLIP(" providing image data\n"); + MOZ_CLIPBOARD_LOG(" providing image data\n"); // Look through our transfer data for the image static const char* const imageMimeTypes[] = {kNativeImageMime, kPNGImageMime, kJPEGImageMime, @@ -1179,47 +1216,50 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, for (uint32_t i = 0; i < ArrayLength(imageMimeTypes); i++) { rv = trans->GetTransferData(imageMimeTypes[i], getter_AddRefs(imageItem)); if (NS_FAILED(rv)) { - LOGCLIP(" %s is missing at GetTransferData()\n", imageMimeTypes[i]); + MOZ_CLIPBOARD_LOG(" %s is missing at GetTransferData()\n", + imageMimeTypes[i]); continue; } image = do_QueryInterface(imageItem); if (image) { - LOGCLIP(" %s is available at GetTransferData()\n", - imageMimeTypes[i]); + MOZ_CLIPBOARD_LOG(" %s is available at GetTransferData()\n", + imageMimeTypes[i]); break; } } if (!image) { // Not getting an image for an image mime type!? - LOGCLIP(" Failed to get any image mime from GetTransferData()!\n"); + MOZ_CLIPBOARD_LOG( + " Failed to get any image mime from GetTransferData()!\n"); return; } RefPtr<GdkPixbuf> pixbuf = nsImageToPixbuf::ImageToPixbuf(image); if (!pixbuf) { - LOGCLIP(" nsImageToPixbuf::ImageToPixbuf() failed!\n"); + MOZ_CLIPBOARD_LOG(" nsImageToPixbuf::ImageToPixbuf() failed!\n"); return; } - LOGCLIP(" Setting pixbuf image data as %s\n", - GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); + MOZ_CLIPBOARD_LOG(" Setting pixbuf image data as %s\n", + GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); gtk_selection_data_set_pixbuf(aSelectionData, pixbuf); return; } if (selectionTarget == gdk_atom_intern(kHTMLMime, FALSE)) { - LOGCLIP(" providing %s data\n", kHTMLMime); + MOZ_CLIPBOARD_LOG(" providing %s data\n", kHTMLMime); rv = trans->GetTransferData(kHTMLMime, getter_AddRefs(item)); if (NS_FAILED(rv) || !item) { - LOGCLIP(" failed to get %s data by GetTransferData()!\n", kHTMLMime); + MOZ_CLIPBOARD_LOG(" failed to get %s data by GetTransferData()!\n", + kHTMLMime); return; } nsCOMPtr<nsISupportsString> wideString; wideString = do_QueryInterface(item); if (!wideString) { - LOGCLIP(" failed to get wideString interface!"); + MOZ_CLIPBOARD_LOG(" failed to get wideString interface!"); return; } @@ -1231,8 +1271,8 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, html.AppendLiteral(kHTMLMarkupPrefix); AppendUTF16toUTF8(ucs2string, html); - LOGCLIP(" Setting %zd bytes of %s data\n", html.Length(), - GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); + MOZ_CLIPBOARD_LOG(" Setting %zd bytes of %s data\n", html.Length(), + GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); gtk_selection_data_set(aSelectionData, selectionTarget, 8, (const guchar*)html.get(), html.Length()); return; @@ -1240,53 +1280,54 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, // We put kFileMime onto the clipboard as kURIListMime. if (selectionTarget == gdk_atom_intern(kURIListMime, FALSE)) { - LOGCLIP(" providing %s data\n", kURIListMime); + MOZ_CLIPBOARD_LOG(" providing %s data\n", kURIListMime); rv = trans->GetTransferData(kFileMime, getter_AddRefs(item)); if (NS_FAILED(rv) || !item) { - LOGCLIP(" failed to get %s data by GetTransferData()!\n", kFileMime); + MOZ_CLIPBOARD_LOG(" failed to get %s data by GetTransferData()!\n", + kFileMime); return; } nsCOMPtr<nsIFile> file = do_QueryInterface(item); if (!file) { - LOGCLIP(" failed to get nsIFile interface!"); + MOZ_CLIPBOARD_LOG(" failed to get nsIFile interface!"); return; } nsCOMPtr<nsIURI> fileURI; rv = NS_NewFileURI(getter_AddRefs(fileURI), file); if (NS_FAILED(rv)) { - LOGCLIP(" failed to get fileURI\n"); + MOZ_CLIPBOARD_LOG(" failed to get fileURI\n"); return; } nsAutoCString uri; if (NS_FAILED(fileURI->GetSpec(uri))) { - LOGCLIP(" failed to get fileURI spec\n"); + MOZ_CLIPBOARD_LOG(" failed to get fileURI spec\n"); return; } - LOGCLIP(" Setting %zd bytes of data\n", uri.Length()); + MOZ_CLIPBOARD_LOG(" Setting %zd bytes of data\n", uri.Length()); gtk_selection_data_set(aSelectionData, selectionTarget, 8, (const guchar*)uri.get(), uri.Length()); return; } - LOGCLIP(" Try if we have anything at GetTransferData() for %s\n", - GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); + MOZ_CLIPBOARD_LOG(" Try if we have anything at GetTransferData() for %s\n", + GUniquePtr<gchar>(gdk_atom_name(selectionTarget)).get()); // Try to match up the selection data target to something our // transferable provides. GUniquePtr<gchar> target_name(gdk_atom_name(selectionTarget)); if (!target_name) { - LOGCLIP(" Failed to get target name!\n"); + MOZ_CLIPBOARD_LOG(" Failed to get target name!\n"); return; } rv = trans->GetTransferData(target_name.get(), getter_AddRefs(item)); // nothing found? if (NS_FAILED(rv) || !item) { - LOGCLIP(" Failed to get anything from GetTransferData()!\n"); + MOZ_CLIPBOARD_LOG(" Failed to get anything from GetTransferData()!\n"); return; } @@ -1295,12 +1336,12 @@ void nsClipboard::SelectionGetEvent(GtkClipboard* aClipboard, nsPrimitiveHelpers::CreateDataFromPrimitive( nsDependentCString(target_name.get()), item, &primitive_data, &dataLen); if (!primitive_data) { - LOGCLIP(" Failed to get primitive data!\n"); + MOZ_CLIPBOARD_LOG(" Failed to get primitive data!\n"); return; } - LOGCLIP(" Setting %s as a primitive data type, %d bytes\n", - target_name.get(), dataLen); + MOZ_CLIPBOARD_LOG(" Setting %s as a primitive data type, %d bytes\n", + target_name.get(), dataLen); gtk_selection_data_set(aSelectionData, selectionTarget, 8, /* 8 bits in a unit */ (const guchar*)primitive_data, dataLen); @@ -1320,8 +1361,9 @@ void nsClipboard::SelectionClearEvent(GtkClipboard* aGtkClipboard) { if (whichClipboard < 0) { return; } - LOGCLIP("nsClipboard::SelectionClearEvent (%s)\n", - whichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::SelectionClearEvent (%s)\n", + whichClipboard == kSelectionClipboard ? "primary" : "clipboard"); ClearCachedTargets(whichClipboard); ClearTransferable(whichClipboard); ClearClipboardCache(whichClipboard); @@ -1333,8 +1375,9 @@ void nsClipboard::OwnerChangedEvent(GtkClipboard* aGtkClipboard, if (whichClipboard < 0) { return; } - LOGCLIP("nsClipboard::OwnerChangedEvent (%s)\n", - whichClipboard == kSelectionClipboard ? "primary" : "clipboard"); + MOZ_CLIPBOARD_LOG( + "nsClipboard::OwnerChangedEvent (%s)\n", + whichClipboard == kSelectionClipboard ? "primary" : "clipboard"); GtkWidget* gtkWidget = [aEvent]() -> GtkWidget* { if (!aEvent->owner) { return nullptr; @@ -1360,13 +1403,13 @@ void nsClipboard::OwnerChangedEvent(GtkClipboard* aGtkClipboard, void clipboard_get_cb(GtkClipboard* aGtkClipboard, GtkSelectionData* aSelectionData, guint info, gpointer user_data) { - LOGCLIP("clipboard_get_cb() callback\n"); + MOZ_CLIPBOARD_LOG("clipboard_get_cb() callback\n"); nsClipboard* clipboard = static_cast<nsClipboard*>(user_data); clipboard->SelectionGetEvent(aGtkClipboard, aSelectionData); } void clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data) { - LOGCLIP("clipboard_clear_cb() callback\n"); + MOZ_CLIPBOARD_LOG("clipboard_clear_cb() callback\n"); nsClipboard* clipboard = static_cast<nsClipboard*>(user_data); clipboard->SelectionClearEvent(aGtkClipboard); } @@ -1374,7 +1417,7 @@ void clipboard_clear_cb(GtkClipboard* aGtkClipboard, gpointer user_data) { void clipboard_owner_change_cb(GtkClipboard* aGtkClipboard, GdkEventOwnerChange* aEvent, gpointer aUserData) { - LOGCLIP("clipboard_owner_change_cb() callback\n"); + MOZ_CLIPBOARD_LOG("clipboard_owner_change_cb() callback\n"); nsClipboard* clipboard = static_cast<nsClipboard*>(aUserData); clipboard->OwnerChangedEvent(aGtkClipboard, aEvent); } diff --git a/widget/gtk/nsClipboard.h b/widget/gtk/nsClipboard.h index 0ddbb59827..358136ae4e 100644 --- a/widget/gtk/nsClipboard.h +++ b/widget/gtk/nsClipboard.h @@ -17,20 +17,6 @@ #include "GUniquePtr.h" #include <gtk/gtk.h> -#ifdef MOZ_LOGGING -# include "mozilla/Logging.h" -# include "nsTArray.h" -# include "Units.h" -extern mozilla::LazyLogModule gClipboardLog; -# define LOGCLIP(...) \ - MOZ_LOG(gClipboardLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) -# define LOGCLIP_ENABLED() \ - MOZ_LOG_TEST(gClipboardLog, mozilla::LogLevel::Debug) -#else -# define LOGCLIP(...) -# define LOGCLIP_ENABLED() false -#endif /* MOZ_LOGGING */ - class ClipboardTargets { friend class ClipboardData; diff --git a/widget/gtk/nsClipboardWayland.cpp b/widget/gtk/nsClipboardWayland.cpp index d179f242dd..316fcf90b6 100644 --- a/widget/gtk/nsClipboardWayland.cpp +++ b/widget/gtk/nsClipboardWayland.cpp @@ -22,7 +22,7 @@ nsRetrievalContextWayland::nsRetrievalContextWayland() = default; ClipboardTargets nsRetrievalContextWayland::GetTargetsImpl( int32_t aWhichClipboard) { - LOGCLIP("nsRetrievalContextWayland::GetTargetsImpl()\n"); + MOZ_CLIPBOARD_LOG("nsRetrievalContextWayland::GetTargetsImpl()\n"); return WaitForClipboardData(ClipboardDataType::Targets, aWhichClipboard) .ExtractTargets(); @@ -30,7 +30,8 @@ ClipboardTargets nsRetrievalContextWayland::GetTargetsImpl( ClipboardData nsRetrievalContextWayland::GetClipboardData( const char* aMimeType, int32_t aWhichClipboard) { - LOGCLIP("nsRetrievalContextWayland::GetClipboardData() mime %s\n", aMimeType); + MOZ_CLIPBOARD_LOG("nsRetrievalContextWayland::GetClipboardData() mime %s\n", + aMimeType); return WaitForClipboardData(ClipboardDataType::Data, aWhichClipboard, aMimeType); @@ -40,8 +41,9 @@ GUniquePtr<char> nsRetrievalContextWayland::GetClipboardText( int32_t aWhichClipboard) { GdkAtom selection = GetSelectionAtom(aWhichClipboard); - LOGCLIP("nsRetrievalContextWayland::GetClipboardText(), clipboard %s\n", - (selection == GDK_SELECTION_PRIMARY) ? "Primary" : "Selection"); + MOZ_CLIPBOARD_LOG( + "nsRetrievalContextWayland::GetClipboardText(), clipboard %s\n", + (selection == GDK_SELECTION_PRIMARY) ? "Primary" : "Selection"); return WaitForClipboardData(ClipboardDataType::Text, aWhichClipboard) .ExtractText(); @@ -50,8 +52,8 @@ GUniquePtr<char> nsRetrievalContextWayland::GetClipboardText( ClipboardData nsRetrievalContextWayland::WaitForClipboardData( ClipboardDataType aDataType, int32_t aWhichClipboard, const char* aMimeType) { - LOGCLIP("nsRetrievalContextWayland::WaitForClipboardData, MIME %s\n", - aMimeType); + MOZ_CLIPBOARD_LOG( + "nsRetrievalContextWayland::WaitForClipboardData, MIME %s\n", aMimeType); AsyncGtkClipboardRequest request(aDataType, aWhichClipboard, aMimeType); int iteration = 1; @@ -62,12 +64,13 @@ ClipboardData nsRetrievalContextWayland::WaitForClipboardData( /* sleep for 10 ms/iteration */ PR_Sleep(PR_MillisecondsToInterval(10)); if (PR_Now() - entryTime > kClipboardTimeout) { - LOGCLIP(" failed to get async clipboard data in time limit\n"); + MOZ_CLIPBOARD_LOG( + " failed to get async clipboard data in time limit\n"); break; } } - LOGCLIP("doing iteration %d msec %ld ...\n", (iteration - 1), - (long)((PR_Now() - entryTime) / 1000)); + MOZ_CLIPBOARD_LOG("doing iteration %d msec %ld ...\n", (iteration - 1), + (long)((PR_Now() - entryTime) / 1000)); gtk_main_iteration(); } diff --git a/widget/gtk/nsClipboardX11.cpp b/widget/gtk/nsClipboardX11.cpp index db12d1e69c..2693622922 100644 --- a/widget/gtk/nsClipboardX11.cpp +++ b/widget/gtk/nsClipboardX11.cpp @@ -138,25 +138,27 @@ ClipboardData nsRetrievalContextX11::WaitForClipboardData( (poll_result == -1 && errno == EINTR)); } - LOGCLIP("exceeded clipboard timeout"); + MOZ_CLIPBOARD_LOG("exceeded clipboard timeout"); return {}; } ClipboardTargets nsRetrievalContextX11::GetTargetsImpl( int32_t aWhichClipboard) { - LOGCLIP("nsRetrievalContextX11::GetTargetsImpl(%s)\n", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetTargetsImpl(%s)\n", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); return WaitForClipboardData(ClipboardDataType::Targets, aWhichClipboard) .ExtractTargets(); } ClipboardData nsRetrievalContextX11::GetClipboardData(const char* aMimeType, int32_t aWhichClipboard) { - LOGCLIP("nsRetrievalContextX11::GetClipboardData(%s) MIME %s\n", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard", - aMimeType); + MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetClipboardData(%s) MIME %s\n", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard", + aMimeType); return WaitForClipboardData(ClipboardDataType::Data, aWhichClipboard, aMimeType); @@ -164,9 +166,10 @@ ClipboardData nsRetrievalContextX11::GetClipboardData(const char* aMimeType, GUniquePtr<char> nsRetrievalContextX11::GetClipboardText( int32_t aWhichClipboard) { - LOGCLIP("nsRetrievalContextX11::GetClipboardText(%s)\n", - aWhichClipboard == nsClipboard::kSelectionClipboard ? "primary" - : "clipboard"); + MOZ_CLIPBOARD_LOG("nsRetrievalContextX11::GetClipboardText(%s)\n", + aWhichClipboard == nsClipboard::kSelectionClipboard + ? "primary" + : "clipboard"); return WaitForClipboardData(ClipboardDataType::Text, aWhichClipboard) .ExtractText(); diff --git a/widget/gtk/nsDragService.cpp b/widget/gtk/nsDragService.cpp index 6cabfbbe2e..c3a3136013 100644 --- a/widget/gtk/nsDragService.cpp +++ b/widget/gtk/nsDragService.cpp @@ -1750,7 +1750,7 @@ nsresult nsDragService::CreateTempFile(nsITransferable* aItem, } // create and open channel for source uri - nsCOMPtr<nsIPrincipal> principal = aItem->GetRequestingPrincipal(); + nsCOMPtr<nsIPrincipal> principal = aItem->GetDataPrincipal(); nsContentPolicyType contentPolicyType = aItem->GetContentPolicyType(); nsCOMPtr<nsICookieJarSettings> cookieJarSettings = aItem->GetCookieJarSettings(); diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp index 8702c154d6..bfd3f1b6f4 100644 --- a/widget/gtk/nsLookAndFeel.cpp +++ b/widget/gtk/nsLookAndFeel.cpp @@ -904,8 +904,7 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { break; } case IntID::MenusCanOverlapOSBar: - // we want XUL popups to be able to overlap the task bar. - aResult = 1; + aResult = 0; break; case IntID::SkipNavigatingDisabledMenuItem: aResult = 1; @@ -1975,10 +1974,7 @@ 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); + mTitlebarButtonSpacing = moz_gtk_get_titlebar_button_spacing(); } // We special-case the header bar color in Adwaita, Yaru and Breeze to be the diff --git a/widget/gtk/nsNativeThemeGTK.cpp b/widget/gtk/nsNativeThemeGTK.cpp index 78d4e925fe..5c858d2313 100644 --- a/widget/gtk/nsNativeThemeGTK.cpp +++ b/widget/gtk/nsNativeThemeGTK.cpp @@ -195,14 +195,6 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aAppearance == StyleAppearance::Menulist || aAppearance == StyleAppearance::MenulistButton) { aState->active &= aState->inHover; - } else if (aAppearance == StyleAppearance::Treetwisty || - aAppearance == StyleAppearance::Treetwistyopen) { - if (nsTreeBodyFrame* treeBodyFrame = do_QueryFrame(aFrame)) { - const mozilla::AtomArray& atoms = - treeBodyFrame->GetPropertyArrayForCurrentDrawingItem(); - aState->selected = atoms.Contains(nsGkAtoms::selected); - aState->inHover = atoms.Contains(nsGkAtoms::hover); - } } if (IsFrameContentNodeInNamespace(aFrame, kNameSpaceID_XUL)) { @@ -292,6 +284,7 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, break; } case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: aGtkWidgetType = MOZ_GTK_ENTRY; break; @@ -299,17 +292,8 @@ bool nsNativeThemeGTK::GetGtkWidgetAndState(StyleAppearance aAppearance, aGtkWidgetType = MOZ_GTK_TEXT_VIEW; break; case StyleAppearance::Listbox: - case StyleAppearance::Treeview: aGtkWidgetType = MOZ_GTK_TREEVIEW; break; - case StyleAppearance::Treetwisty: - aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER; - if (aWidgetFlags) *aWidgetFlags = GTK_EXPANDER_COLLAPSED; - break; - case StyleAppearance::Treetwistyopen: - aGtkWidgetType = MOZ_GTK_TREEVIEW_EXPANDER; - if (aWidgetFlags) *aWidgetFlags = GTK_EXPANDER_EXPANDED; - break; case StyleAppearance::MenulistButton: case StyleAppearance::Menulist: aGtkWidgetType = MOZ_GTK_DROPDOWN; @@ -1091,6 +1075,7 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize( result.height += border.TopBottom(); } break; case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: { gint contentHeight = 0; gint borderPaddingHeight = 0; @@ -1135,12 +1120,6 @@ LayoutDeviceIntSize nsNativeThemeGTK::GetMinimumWidgetSize( result.width = 14; result.height = 13; break; - case StyleAppearance::Treetwisty: - case StyleAppearance::Treetwistyopen: { - gint expander_size; - moz_gtk_get_treeview_expander_size(&expander_size); - result.width = result.height = expander_size; - } break; default: break; } @@ -1243,12 +1222,6 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::ButtonArrowNext: case StyleAppearance::ButtonArrowPrevious: case StyleAppearance::Listbox: - case StyleAppearance::Treeview: - // case StyleAppearance::Treeitem: - case StyleAppearance::Treetwisty: - // case StyleAppearance::Treeline: - // case StyleAppearance::Treeheader: - case StyleAppearance::Treetwistyopen: case StyleAppearance::ProgressBar: case StyleAppearance::Progresschunk: case StyleAppearance::Tab: @@ -1262,6 +1235,7 @@ nsNativeThemeGTK::ThemeSupportsWidget(nsPresContext* aPresContext, case StyleAppearance::SpinnerDownbutton: case StyleAppearance::SpinnerTextfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Range: @@ -1315,6 +1289,7 @@ bool nsNativeThemeGTK::ThemeDrawsFocusForWidget(nsIFrame* aFrame, case StyleAppearance::Textarea: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: return true; default: return false; diff --git a/widget/gtk/nsWaylandDisplay.cpp b/widget/gtk/nsWaylandDisplay.cpp index 1ea87b3bc5..c63c8f6564 100644 --- a/widget/gtk/nsWaylandDisplay.cpp +++ b/widget/gtk/nsWaylandDisplay.cpp @@ -28,7 +28,6 @@ void WaylandDisplayRelease() { MOZ_RELEASE_ASSERT(NS_IsMainThread(), "WaylandDisplay can be released in main thread only!"); if (!gWaylandDisplay) { - NS_WARNING("WaylandDisplayRelease(): Wayland display is missing!"); return; } delete gWaylandDisplay; @@ -151,7 +150,6 @@ static void global_registry_handler(void* data, wl_registry* registry, 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 = WaylandRegistryBind<wl_seat>(registry, id, &wl_seat_interface, 1); KeymapWrapper::SetSeat(seat, id); @@ -173,7 +171,7 @@ static void global_registry_remover(void* data, wl_registry* registry, static const struct wl_registry_listener registry_listener = { global_registry_handler, global_registry_remover}; -nsWaylandDisplay::~nsWaylandDisplay() {} +nsWaylandDisplay::~nsWaylandDisplay() = default; static void WlLogHandler(const char* format, va_list args) { char error[1000]; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index 0a78d0c8ec..2971497969 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -389,8 +389,7 @@ static void GtkWindowSetTransientFor(GtkWindow* aWindow, GtkWindow* aParent) { } nsWindow::nsWindow() - : mTitlebarRectMutex("nsWindow::mTitlebarRectMutex"), - mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"), + : mWindowVisibilityMutex("nsWindow::mWindowVisibilityMutex"), mIsMapped(false), mIsDestroyed(false), mIsShown(false), @@ -514,10 +513,6 @@ void nsWindow::MaybeDispatchResized() { << mBounds << " size state " << mSizeMode; } - if (IsTopLevelWindowType()) { - UpdateTopLevelOpaqueRegion(); - } - // Notify the GtkCompositorWidget of a ClientSizeChange if (mCompositorWidgetDelegate) { mCompositorWidgetDelegate->NotifyClientSizeChanged(GetClientSize()); @@ -591,6 +586,7 @@ void nsWindow::Destroy() { mWaylandVsyncSource = nullptr; } mWaylandVsyncDispatcher = nullptr; + UnlockNativePointer(); #endif // dragService will be null after shutdown of the service manager. @@ -3216,9 +3212,7 @@ LayoutDeviceIntRect nsWindow::GetScreenBounds() { return rect; } -LayoutDeviceIntSize nsWindow::GetClientSize() { - return LayoutDeviceIntSize(mBounds.width, mBounds.height); -} +LayoutDeviceIntSize nsWindow::GetClientSize() { return mBounds.Size(); } LayoutDeviceIntRect nsWindow::GetClientBounds() { // GetBounds returns a rect whose top left represents the top left of the @@ -5379,8 +5373,6 @@ void nsWindow::OnWindowStateEvent(GtkWidget* aWidget, } else { ClearTransparencyBitmap(); } - } else { - SetTitlebarRect(); } } @@ -5391,7 +5383,6 @@ void nsWindow::OnDPIChanged() { if (PresShell* presShell = mWidgetListener->GetPresShell()) { presShell->BackingScaleFactorChanged(); } - mWidgetListener->UIResolutionChanged(); } NotifyAPZOfDPIChange(); } @@ -6934,80 +6925,99 @@ LayoutDeviceIntCoord nsWindow::GetTitlebarRadius() { return GdkCoordToDevicePixels(cssCoord); } +LayoutDeviceIntRegion nsWindow::GetOpaqueRegion() const { + AutoReadLock r(mOpaqueRegionLock); + return mOpaqueRegion; +} + // See subtract_corners_from_region() at gtk/gtkwindow.c // We need to subtract corners from toplevel window opaque region // to draw transparent corners of default Gtk titlebar. // Both implementations (cairo_region_t and wl_region) needs to be synced. -static void SubtractTitlebarCorners(cairo_region_t* aRegion, int aX, int aY, - int aWindowWidth, int aWindowHeight, - int aTitlebarRadius) { - if (!aTitlebarRadius) { - return; - } - cairo_rectangle_int_t rect = {aX, aY, aTitlebarRadius, aTitlebarRadius}; - cairo_region_subtract_rectangle(aRegion, &rect); - rect = { - aX + aWindowWidth - aTitlebarRadius, - aY, - aTitlebarRadius, - aTitlebarRadius, - }; - cairo_region_subtract_rectangle(aRegion, &rect); - rect = { - aX, - aY + aWindowHeight - aTitlebarRadius, - aTitlebarRadius, - aTitlebarRadius, - }; - cairo_region_subtract_rectangle(aRegion, &rect); - rect = { - aX + aWindowWidth - aTitlebarRadius, - aY + aWindowHeight - aTitlebarRadius, - aTitlebarRadius, - aTitlebarRadius, - }; - cairo_region_subtract_rectangle(aRegion, &rect); +static void SubtractTitlebarCorners(LayoutDeviceIntRegion& aRegion, + const LayoutDeviceIntRect& aRect, + LayoutDeviceIntCoord aRadius) { + if (!aRadius) { + return; + } + const LayoutDeviceIntSize size(aRadius, aRadius); + aRegion.SubOut(LayoutDeviceIntRect(aRect.TopLeft(), size)); + aRegion.SubOut(LayoutDeviceIntRect( + aRect.TopRight() - LayoutDeviceIntPoint(aRadius, 0), size)); + aRegion.SubOut(LayoutDeviceIntRect( + aRect.BottomLeft() - LayoutDeviceIntPoint(0, aRadius), size)); + aRegion.SubOut(LayoutDeviceIntRect( + aRect.BottomRight() - LayoutDeviceIntPoint(aRadius, aRadius), size)); +} + +void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aRegion) { + LayoutDeviceIntRegion region = aRegion; + SubtractTitlebarCorners(region, LayoutDeviceIntRect({}, mBounds.Size()), + GetTitlebarRadius()); + { + AutoReadLock r(mOpaqueRegionLock); + if (mOpaqueRegion == region) { + return; + } + } + { + AutoWriteLock w(mOpaqueRegionLock); + mOpaqueRegion = region; + } + UpdateOpaqueRegionInternal(); } -void nsWindow::UpdateTopLevelOpaqueRegion() { +void nsWindow::UpdateOpaqueRegionInternal() { if (!mCompositedScreen) { return; } + if (!IsTopLevelWindowType()) { + // We need to clear target buffer alpha values of popup windows as + // SW-WR paints with alpha blending (see Bug 1674473). + return; + } + GdkWindow* window = GetToplevelGdkWindow(); if (!window) { return; } MOZ_ASSERT(gdk_window_get_window_type(window) == GDK_WINDOW_TOPLEVEL); - int x = 0; - int y = 0; - - gdk_window_get_position(mGdkWindow, &x, &y); - - int width = DevicePixelsToGdkCoordRoundDown(mBounds.width); - int height = DevicePixelsToGdkCoordRoundDown(mBounds.height); - - cairo_region_t* region = cairo_region_create(); - cairo_rectangle_int_t rect = {x, y, width, height}; - cairo_region_union_rectangle(region, &rect); - - // TODO: We actually could get a proper opaque region from layout, see - // nsIWidget::UpdateOpaqueRegion. This could simplify titlebar drawing. - int radius = DoDrawTilebarCorners() ? int(GetTitlebarRadius()) : 0; - SubtractTitlebarCorners(region, x, y, width, height, radius); - - gdk_window_set_opaque_region(window, region); - - cairo_region_destroy(region); + { + AutoReadLock lock(mOpaqueRegionLock); + cairo_region_t* region = nullptr; + if (!mOpaqueRegion.IsEmpty()) { + // NOTE(emilio): The opaque region is relative to our mContainer / + // mGdkWindow / inner window, but we're setting it on the top level + // GdkWindow / mShell. + // + // So we need to offset the rects by the position of mGdkWindow, in order + // for them to be in the right coordinate system. + GdkPoint offset{0, 0}; + gdk_window_get_position(mGdkWindow, &offset.x, &offset.y); + + region = cairo_region_create(); + + for (auto iter = mOpaqueRegion.RectIter(); !iter.Done(); iter.Next()) { + auto gdkRect = DevicePixelsToGdkRectRoundIn(iter.Get()); + cairo_rectangle_int_t rect = {gdkRect.x + offset.x, + gdkRect.y + offset.y, gdkRect.width, + gdkRect.height}; + cairo_region_union_rectangle(region, &rect); + } + } + gdk_window_set_opaque_region(window, region); + if (region) { + cairo_region_destroy(region); + } + } #ifdef MOZ_WAYLAND if (GdkIsWaylandDisplay()) { - moz_container_wayland_update_opaque_region(mContainer, radius); + moz_container_wayland_update_opaque_region(mContainer); } #endif - - SetTitlebarRect(); } bool nsWindow::IsChromeWindowTitlebar() { @@ -7181,24 +7191,6 @@ nsresult nsWindow::UpdateTranslucentWindowAlphaInternal(const nsIntRect& aRect, return NS_OK; } -#define TITLEBAR_HEIGHT 10 - -void nsWindow::SetTitlebarRect() { - MutexAutoLock lock(mTitlebarRectMutex); - - if (!mGdkWindow || !DoDrawTilebarCorners()) { - mTitlebarRect = LayoutDeviceIntRect(); - return; - } - mTitlebarRect = LayoutDeviceIntRect(0, 0, mBounds.width, - GdkCeiledScaleFactor() * TITLEBAR_HEIGHT); -} - -LayoutDeviceIntRect nsWindow::GetTitlebarRect() { - MutexAutoLock lock(mTitlebarRectMutex); - return mTitlebarRect; -} - void nsWindow::UpdateTitlebarTransparencyBitmap() { NS_ASSERTION(mTransparencyBitmapForTitlebar, "Transparency bitmap is already used to draw window shape"); @@ -8243,7 +8235,9 @@ static void toplevel_window_size_allocate_cb(GtkWidget* widget, return; } - window->UpdateTopLevelOpaqueRegion(); + // NOTE(emilio): We need to do this here to override GTK's own opaque region + // setting (which would clobber ours). + window->UpdateOpaqueRegionInternal(); } static gboolean delete_event_cb(GtkWidget* widget, GdkEventAny* event) { @@ -9180,9 +9174,11 @@ void nsWindow::SetDrawsInTitlebar(bool aState) { } else { ClearTransparencyBitmap(); } - } else { - SetTitlebarRect(); } + + // Recompute the input region (which should generally be null, but this is + // enough to work around bug 1844497, which is probably a gtk bug). + SetInputRegion(mInputRegion); } GtkWindow* nsWindow::GetCurrentTopmostWindow() const { @@ -9249,6 +9245,16 @@ GdkRectangle nsWindow::DevicePixelsToGdkRectRoundOut( return {x, y, right - x, bottom - y}; } +GdkRectangle nsWindow::DevicePixelsToGdkRectRoundIn( + const LayoutDeviceIntRect& aRect) { + double scale = FractionalScaleFactor(); + int x = ceil(aRect.x / scale); + int y = ceil(aRect.y / scale); + int right = floor((aRect.x + aRect.width) / scale); + int bottom = floor((aRect.y + aRect.height) / scale); + return {x, y, std::max(right - x, 0), std::max(bottom - y, 0)}; +} + GdkRectangle nsWindow::DevicePixelsToGdkSizeRoundUp( const LayoutDeviceIntSize& aSize) { double scale = FractionalScaleFactor(); @@ -9843,9 +9849,7 @@ void nsWindow::LockNativePointer() { return; } - if (mLockedPointer || mRelativePointer) { - UnlockNativePointer(); - } + UnlockNativePointer(); mLockedPointer = zwp_pointer_constraints_v1_lock_pointer( pointerConstraints, surface, pointer, nullptr, @@ -9869,9 +9873,6 @@ void nsWindow::LockNativePointer() { } void nsWindow::UnlockNativePointer() { - if (!GdkIsWaylandDisplay()) { - return; - } if (mRelativePointer) { zwp_relative_pointer_v1_destroy(mRelativePointer); mRelativePointer = nullptr; diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h index 25d68129d8..1dd1f1dce8 100644 --- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -19,6 +19,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/TouchEvents.h" #include "mozilla/UniquePtr.h" +#include "mozilla/RWLock.h" #include "mozilla/widget/WindowSurface.h" #include "mozilla/widget/WindowSurfaceProvider.h" #include "nsBaseWidget.h" @@ -257,7 +258,9 @@ class nsWindow final : public nsBaseWidget { gint GetInputRegionMarginInGdkCoords(); - void UpdateTopLevelOpaqueRegion(); + void UpdateOpaqueRegionInternal(); + void UpdateOpaqueRegion(const LayoutDeviceIntRegion&) override; + LayoutDeviceIntRegion GetOpaqueRegion() const; already_AddRefed<mozilla::gfx::DrawTarget> StartRemoteDrawingInRegion( const LayoutDeviceIntRegion& aInvalidRegion, @@ -369,9 +372,7 @@ class nsWindow final : public nsBaseWidget { nsresult SetNonClientMargins(const LayoutDeviceIntMargin&) override; void SetDrawsInTitlebar(bool aState); - void SetTitlebarRect(); mozilla::LayoutDeviceIntCoord GetTitlebarRadius(); - LayoutDeviceIntRect GetTitlebarRect(); void UpdateWindowDraggingRegion( const LayoutDeviceIntRegion& aRegion) override; @@ -389,6 +390,7 @@ class nsWindow final : public nsBaseWidget { GdkPoint DevicePixelsToGdkPointRoundDown(const LayoutDeviceIntPoint&); GdkRectangle DevicePixelsToGdkSizeRoundUp(const LayoutDeviceIntSize&); GdkRectangle DevicePixelsToGdkRectRoundOut(const LayoutDeviceIntRect&); + GdkRectangle DevicePixelsToGdkRectRoundIn(const LayoutDeviceIntRect&); // From GDK int GdkCoordToDevicePixels(gint); @@ -631,9 +633,6 @@ class nsWindow final : public nsBaseWidget { // If true, draw our own window titlebar. bool mDrawInTitlebar = false; - mozilla::Mutex mTitlebarRectMutex; - LayoutDeviceIntRect mTitlebarRect MOZ_GUARDED_BY(mTitlebarRectMutex); - // This mutex protect window visibility changes. mozilla::Mutex mWindowVisibilityMutex; @@ -1018,6 +1017,8 @@ class nsWindow final : public nsBaseWidget { // Running in kiosk mode and requested to stay on specified monitor. // If monitor is removed minimize the window. mozilla::Maybe<int> mKioskMonitor; + LayoutDeviceIntRegion mOpaqueRegion MOZ_GUARDED_BY(mOpaqueRegionLock); + mutable mozilla::RWLock mOpaqueRegionLock{"nsWindow::mOpaqueRegion"}; }; #endif /* __nsWindow_h__ */ diff --git a/widget/headless/HeadlessLookAndFeelGTK.cpp b/widget/headless/HeadlessLookAndFeelGTK.cpp index aa55bcc347..09938e1983 100644 --- a/widget/headless/HeadlessLookAndFeelGTK.cpp +++ b/widget/headless/HeadlessLookAndFeelGTK.cpp @@ -84,9 +84,6 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::TreeScrollLinesMax: aResult = 3; break; - case IntID::TabFocusModel: - aResult = nsIContent::eTabFocus_textControlsMask; - break; case IntID::ChosenMenuItemsShouldBlink: aResult = 1; break; diff --git a/widget/nsBaseClipboard.cpp b/widget/nsBaseClipboard.cpp index 9ffc53b4c2..1eb2e8e9e0 100644 --- a/widget/nsBaseClipboard.cpp +++ b/widget/nsBaseClipboard.cpp @@ -7,7 +7,6 @@ #include "ContentAnalysis.h" #include "mozilla/Components.h" -#include "mozilla/contentanalysis/ContentAnalysisIPCTypes.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/Document.h" @@ -19,14 +18,12 @@ #include "mozilla/MoveOnlyFunction.h" #include "mozilla/RefPtr.h" #include "mozilla/Services.h" -#include "mozilla/SpinEventLoopUntil.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_widget.h" #include "nsContentUtils.h" #include "nsFocusManager.h" #include "nsIClipboardOwner.h" #include "nsIPromptService.h" -#include "nsISupportsPrimitives.h" #include "nsError.h" #include "nsXPCOM.h" @@ -38,6 +35,8 @@ using mozilla::dom::CanonicalBrowsingContext; using mozilla::dom::ClipboardCapabilities; using mozilla::dom::Document; +mozilla::LazyLogModule gWidgetClipboardLog("WidgetClipboard"); + static const int32_t kGetAvailableFlavorsRetryCount = 5; namespace { @@ -211,9 +210,11 @@ NS_IMPL_ISUPPORTS(nsBaseClipboard::AsyncSetClipboardData, nsBaseClipboard::AsyncSetClipboardData::AsyncSetClipboardData( int32_t aClipboardType, nsBaseClipboard* aClipboard, + mozilla::dom::WindowContext* aSettingWindowContext, nsIAsyncClipboardRequestCallback* aCallback) : mClipboardType(aClipboardType), mClipboard(aClipboard), + mWindowContext(aSettingWindowContext), mCallback(aCallback) { MOZ_ASSERT(mClipboard); MOZ_ASSERT( @@ -247,7 +248,8 @@ nsBaseClipboard::AsyncSetClipboardData::SetData(nsITransferable* aTransferable, RefPtr<AsyncSetClipboardData> request = std::move(mClipboard->mPendingWriteRequests[mClipboardType]); - nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType); + nsresult rv = mClipboard->SetData(aTransferable, aOwner, mClipboardType, + mWindowContext); MaybeNotifyCallback(rv); return rv; @@ -292,7 +294,8 @@ void nsBaseClipboard::RejectPendingAsyncSetDataRequestIfAny( } NS_IMETHODIMP nsBaseClipboard::AsyncSetData( - int32_t aWhichClipboard, nsIAsyncClipboardRequestCallback* aCallback, + int32_t aWhichClipboard, mozilla::dom::WindowContext* aSettingWindowContext, + nsIAsyncClipboardRequestCallback* aCallback, nsIAsyncSetClipboardData** _retval) { MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); @@ -308,241 +311,13 @@ NS_IMETHODIMP nsBaseClipboard::AsyncSetData( // Create a new AsyncSetClipboardData. RefPtr<AsyncSetClipboardData> request = - mozilla::MakeRefPtr<AsyncSetClipboardData>(aWhichClipboard, this, - aCallback); + mozilla::MakeRefPtr<AsyncSetClipboardData>( + aWhichClipboard, this, aSettingWindowContext, aCallback); mPendingWriteRequests[aWhichClipboard] = request; request.forget(_retval); return NS_OK; } -namespace { -class SafeContentAnalysisResultCallback final - : public nsIContentAnalysisCallback { - public: - explicit SafeContentAnalysisResultCallback( - std::function<void(RefPtr<nsIContentAnalysisResult>&&)> aResolver) - : mResolver(std::move(aResolver)) {} - void Callback(RefPtr<nsIContentAnalysisResult>&& aResult) { - MOZ_ASSERT(mResolver, "Called SafeContentAnalysisResultCallback twice!"); - if (auto resolver = std::move(mResolver)) { - resolver(std::move(aResult)); - } - } - - NS_IMETHODIMP ContentResult(nsIContentAnalysisResponse* aResponse) override { - using namespace mozilla::contentanalysis; - RefPtr<ContentAnalysisResult> result = - ContentAnalysisResult::FromContentAnalysisResponse(aResponse); - Callback(result); - return NS_OK; - } - - NS_IMETHODIMP Error(nsresult aError) override { - using namespace mozilla::contentanalysis; - Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); - return NS_OK; - } - - NS_DECL_THREADSAFE_ISUPPORTS - private: - // Private destructor to force this to be allocated in a RefPtr, which is - // necessary for safe usage. - ~SafeContentAnalysisResultCallback() { - MOZ_ASSERT(!mResolver, "SafeContentAnalysisResultCallback never called!"); - } - mozilla::MoveOnlyFunction<void(RefPtr<nsIContentAnalysisResult>&&)> mResolver; -}; -NS_IMPL_ISUPPORTS(SafeContentAnalysisResultCallback, - nsIContentAnalysisCallback); -} // namespace - -// Returning: -// - true means a content analysis request was fired -// - false means there is no text data in the transferable -// - NoContentAnalysisResult means there was an error -static mozilla::Result<bool, mozilla::contentanalysis::NoContentAnalysisResult> -CheckClipboardContentAnalysisAsText( - uint64_t aInnerWindowId, SafeContentAnalysisResultCallback* aResolver, - nsIURI* aDocumentURI, nsIContentAnalysis* aContentAnalysis, - nsITransferable* aTextTrans) { - using namespace mozilla::contentanalysis; - - nsCOMPtr<nsISupports> transferData; - if (NS_FAILED(aTextTrans->GetTransferData(kTextMime, - getter_AddRefs(transferData)))) { - return false; - } - nsCOMPtr<nsISupportsString> textData = do_QueryInterface(transferData); - if (MOZ_UNLIKELY(!textData)) { - return false; - } - nsString text; - if (NS_FAILED(textData->GetData(text))) { - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - if (text.IsEmpty()) { - // Content Analysis doesn't expect to analyze an empty string. - // Just approve it. - return true; - } - RefPtr<mozilla::dom::WindowGlobalParent> window = - mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId); - if (!window) { - // The window has gone away in the meantime - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest = - new ContentAnalysisRequest( - nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry, - std::move(text), false, EmptyCString(), aDocumentURI, - nsIContentAnalysisRequest::OperationType::eClipboard, window); - nsresult rv = aContentAnalysis->AnalyzeContentRequestCallback( - contentAnalysisRequest, /* aAutoAcknowledge */ true, aResolver); - if (NS_FAILED(rv)) { - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - return true; -} - -// Returning: -// - true means a content analysis request was fired -// - false means there is no file data in the transferable -// - NoContentAnalysisResult means there was an error -static mozilla::Result<bool, mozilla::contentanalysis::NoContentAnalysisResult> -CheckClipboardContentAnalysisAsFile( - uint64_t aInnerWindowId, SafeContentAnalysisResultCallback* aResolver, - nsIURI* aDocumentURI, nsIContentAnalysis* aContentAnalysis, - nsITransferable* aFileTrans) { - using namespace mozilla::contentanalysis; - - nsCOMPtr<nsISupports> transferData; - nsresult rv = - aFileTrans->GetTransferData(kFileMime, getter_AddRefs(transferData)); - nsString filePath; - if (NS_SUCCEEDED(rv)) { - if (nsCOMPtr<nsIFile> file = do_QueryInterface(transferData)) { - rv = file->GetPath(filePath); - } else { - MOZ_ASSERT_UNREACHABLE("clipboard data had kFileMime but no nsIFile!"); - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - } - if (NS_FAILED(rv) || filePath.IsEmpty()) { - return false; - } - RefPtr<mozilla::dom::WindowGlobalParent> window = - mozilla::dom::WindowGlobalParent::GetByInnerWindowId(aInnerWindowId); - if (!window) { - // The window has gone away in the meantime - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - // Let the content analysis code calculate the digest - nsCOMPtr<nsIContentAnalysisRequest> contentAnalysisRequest = - new ContentAnalysisRequest( - nsIContentAnalysisRequest::AnalysisType::eBulkDataEntry, - std::move(filePath), true, EmptyCString(), aDocumentURI, - nsIContentAnalysisRequest::OperationType::eCustomDisplayString, - window); - rv = aContentAnalysis->AnalyzeContentRequestCallback( - contentAnalysisRequest, - /* aAutoAcknowledge */ true, aResolver); - if (NS_FAILED(rv)) { - return mozilla::Err(NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR); - } - return true; -} - -static void CheckClipboardContentAnalysis( - mozilla::dom::WindowGlobalParent* aWindow, nsITransferable* aTransferable, - SafeContentAnalysisResultCallback* aResolver) { - using namespace mozilla::contentanalysis; - - // Content analysis is only needed if an outside webpage has access to - // the data. So, skip content analysis if there is: - // - no associated window (for example, scripted clipboard read by system - // code) - // - the window is a chrome docshell - // - the window is being rendered in the parent process (for example, - // about:support and the like) - if (!aWindow || aWindow->GetBrowsingContext()->IsChrome() || - aWindow->IsInProcess()) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult:: - ALLOW_DUE_TO_CONTEXT_EXEMPT_FROM_CONTENT_ANALYSIS)); - return; - } - nsCOMPtr<nsIContentAnalysis> contentAnalysis = - mozilla::components::nsIContentAnalysis::Service(); - if (!contentAnalysis) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); - return; - } - - bool contentAnalysisIsActive; - nsresult rv = contentAnalysis->GetIsActive(&contentAnalysisIsActive); - if (MOZ_LIKELY(NS_FAILED(rv) || !contentAnalysisIsActive)) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ALLOW_DUE_TO_CONTENT_ANALYSIS_NOT_ACTIVE)); - return; - } - - nsCOMPtr<nsIURI> currentURI = aWindow->Canonical()->GetDocumentURI(); - uint64_t innerWindowId = aWindow->InnerWindowId(); - nsTArray<nsCString> flavors; - rv = aTransferable->FlavorsTransferableCanExport(flavors); - if (NS_WARN_IF(NS_FAILED(rv))) { - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::DENY_DUE_TO_OTHER_ERROR)); - return; - } - bool keepChecking = true; - if (flavors.Contains(kFileMime)) { - auto fileResult = CheckClipboardContentAnalysisAsFile( - innerWindowId, aResolver, currentURI, contentAnalysis, aTransferable); - - if (fileResult.isErr()) { - aResolver->Callback( - ContentAnalysisResult::FromNoResult(fileResult.unwrapErr())); - return; - } - keepChecking = !fileResult.unwrap(); - } - if (keepChecking) { - // Failed to get the clipboard data as a file, so try as text - auto textResult = CheckClipboardContentAnalysisAsText( - innerWindowId, aResolver, currentURI, contentAnalysis, aTransferable); - if (textResult.isErr()) { - aResolver->Callback( - ContentAnalysisResult::FromNoResult(textResult.unwrapErr())); - return; - } - if (!textResult.unwrap()) { - // Couldn't get file or text data from this - aResolver->Callback(ContentAnalysisResult::FromNoResult( - NoContentAnalysisResult::ALLOW_DUE_TO_COULD_NOT_GET_DATA)); - return; - } - } -} - -static bool CheckClipboardContentAnalysisSync( - mozilla::dom::WindowGlobalParent* aWindow, - const nsCOMPtr<nsITransferable>& trans) { - bool requestDone = false; - RefPtr<nsIContentAnalysisResult> result; - auto callback = mozilla::MakeRefPtr<SafeContentAnalysisResultCallback>( - [&requestDone, &result](RefPtr<nsIContentAnalysisResult>&& aResult) { - result = std::move(aResult); - requestDone = true; - }); - CheckClipboardContentAnalysis(aWindow, trans, callback); - mozilla::SpinEventLoopUntil("CheckClipboardContentAnalysisSync"_ns, - [&requestDone]() -> bool { return requestDone; }); - return result->GetShouldAllowContent(); -} - nsBaseClipboard::nsBaseClipboard(const ClipboardCapabilities& aClipboardCaps) : mClipboardCaps(aClipboardCaps) { using mozilla::MakeUnique; @@ -574,9 +349,9 @@ NS_IMPL_ISUPPORTS(nsBaseClipboard, nsIClipboard) * Sets the transferable object * */ -NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable, - nsIClipboardOwner* aOwner, - int32_t aWhichClipboard) { +NS_IMETHODIMP nsBaseClipboard::SetData( + nsITransferable* aTransferable, nsIClipboardOwner* aOwner, + int32_t aWhichClipboard, mozilla::dom::WindowContext* aWindowContext) { NS_ASSERTION(aTransferable, "clipboard given a null transferable"); MOZ_CLIPBOARD_LOG("%s: clipboard=%d", __FUNCTION__, aWhichClipboard); @@ -627,21 +402,22 @@ NS_IMETHODIMP nsBaseClipboard::SetData(nsITransferable* aTransferable, return result.unwrapErr(); } - clipboardCache->Update(aTransferable, aOwner, result.unwrap()); + clipboardCache->Update(aTransferable, aOwner, result.unwrap(), + aWindowContext + ? mozilla::Some(aWindowContext->InnerWindowId()) + : mozilla::Nothing()); return NS_OK; } nsresult nsBaseClipboard::GetDataFromClipboardCache( nsITransferable* aTransferable, int32_t aClipboardType) { MOZ_ASSERT(aTransferable); - MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); MOZ_ASSERT(mozilla::StaticPrefs::widget_clipboard_use_cached_data_enabled()); const auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType); if (!clipboardCache) { return NS_ERROR_FAILURE; } - return clipboardCache->GetData(aTransferable); } @@ -671,8 +447,10 @@ NS_IMETHODIMP nsBaseClipboard::GetData( if (NS_SUCCEEDED( GetDataFromClipboardCache(aTransferable, aWhichClipboard))) { // maybe try to fill in more types? Is there a point? - if (!CheckClipboardContentAnalysisSync(aWindowContext->Canonical(), - aTransferable)) { + if (!mozilla::contentanalysis::ContentAnalysis:: + CheckClipboardContentAnalysisSync( + this, aWindowContext->Canonical(), aTransferable, + aWhichClipboard)) { aTransferable->ClearAllData(); return NS_ERROR_CONTENT_BLOCKED; } @@ -686,8 +464,9 @@ NS_IMETHODIMP nsBaseClipboard::GetData( if (NS_FAILED(rv)) { return rv; } - if (!CheckClipboardContentAnalysisSync(aWindowContext->Canonical(), - aTransferable)) { + if (!mozilla::contentanalysis::ContentAnalysis:: + CheckClipboardContentAnalysisSync(this, aWindowContext->Canonical(), + aTransferable, aWhichClipboard)) { aTransferable->ClearAllData(); return NS_ERROR_CONTENT_BLOCKED; } @@ -795,7 +574,7 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetData( nsCOMPtr<nsITransferable> trans = clipboardCache->GetTransferable(); MOZ_ASSERT(trans); - if (nsCOMPtr<nsIPrincipal> principal = trans->GetRequestingPrincipal()) { + if (nsCOMPtr<nsIPrincipal> principal = trans->GetDataPrincipal()) { if (aRequestingPrincipal->Subsumes(principal)) { MOZ_CLIPBOARD_LOG("%s: native clipboard data is from same-origin page.", __FUNCTION__); @@ -1228,7 +1007,8 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( MOZ_ASSERT(mClipboard); auto contentAnalysisCallback = - mozilla::MakeRefPtr<SafeContentAnalysisResultCallback>( + mozilla::MakeRefPtr<mozilla::contentanalysis::ContentAnalysis:: + SafeContentAnalysisResultCallback>( [transferable = nsCOMPtr{aTransferable}, callback = nsCOMPtr{aCallback}]( RefPtr<nsIContentAnalysisResult>&& aResult) { @@ -1249,10 +1029,11 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( MOZ_DIAGNOSTIC_ASSERT(clipboardCache->GetSequenceNumber() == mSequenceNumber); if (NS_SUCCEEDED(clipboardCache->GetData(aTransferable))) { - CheckClipboardContentAnalysis(mRequestingWindowContext - ? mRequestingWindowContext->Canonical() - : nullptr, - aTransferable, contentAnalysisCallback); + mozilla::contentanalysis::ContentAnalysis::CheckClipboardContentAnalysis( + mClipboard, + mRequestingWindowContext ? mRequestingWindowContext->Canonical() + : nullptr, + aTransferable, mClipboardType, contentAnalysisCallback); return NS_OK; } @@ -1278,11 +1059,13 @@ NS_IMETHODIMP nsBaseClipboard::AsyncGetClipboardData::GetData( callback->OnComplete(NS_ERROR_FAILURE); return; } - CheckClipboardContentAnalysis( - self->mRequestingWindowContext - ? self->mRequestingWindowContext->Canonical() - : nullptr, - transferable, contentAnalysisCallback); + mozilla::contentanalysis::ContentAnalysis:: + CheckClipboardContentAnalysis( + self->mClipboard, + self->mRequestingWindowContext + ? self->mRequestingWindowContext->Canonical() + : nullptr, + transferable, self->mClipboardType, contentAnalysisCallback); }); return NS_OK; } @@ -1320,6 +1103,13 @@ bool nsBaseClipboard::AsyncGetClipboardData::IsValid() { return true; } +mozilla::Maybe<uint64_t> nsBaseClipboard::GetClipboardCacheInnerWindowId( + int32_t aClipboardType) { + auto* clipboardCache = GetClipboardCacheIfValid(aClipboardType); + return clipboardCache ? clipboardCache->GetInnerWindowId() + : mozilla::Nothing(); +} + nsBaseClipboard::ClipboardCache* nsBaseClipboard::GetClipboardCacheIfValid( int32_t aClipboardType) { MOZ_ASSERT(nsIClipboard::IsClipboardTypeSupported(aClipboardType)); diff --git a/widget/nsBaseClipboard.h b/widget/nsBaseClipboard.h index ffa68d6240..625421c342 100644 --- a/widget/nsBaseClipboard.h +++ b/widget/nsBaseClipboard.h @@ -14,11 +14,11 @@ #include "nsITransferable.h" #include "nsCOMPtr.h" -static mozilla::LazyLogModule sWidgetClipboardLog("WidgetClipboard"); +extern mozilla::LazyLogModule gWidgetClipboardLog; #define MOZ_CLIPBOARD_LOG(...) \ - MOZ_LOG(sWidgetClipboardLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) + MOZ_LOG(gWidgetClipboardLog, mozilla::LogLevel::Debug, (__VA_ARGS__)) #define MOZ_CLIPBOARD_LOG_ENABLED() \ - MOZ_LOG_TEST(sWidgetClipboardLog, mozilla::LogLevel::Debug) + MOZ_LOG_TEST(gWidgetClipboardLog, mozilla::LogLevel::Debug) class nsITransferable; class nsIClipboardOwner; @@ -42,9 +42,12 @@ class nsBaseClipboard : public nsIClipboard { NS_DECL_ISUPPORTS // nsIClipboard - NS_IMETHOD SetData(nsITransferable* aTransferable, nsIClipboardOwner* aOwner, - int32_t aWhichClipboard) override final; + NS_IMETHOD SetData( + nsITransferable* aTransferable, nsIClipboardOwner* aOwner, + int32_t aWhichClipboard, + mozilla::dom::WindowContext* aWindowContext) override final; NS_IMETHOD AsyncSetData(int32_t aWhichClipboard, + mozilla::dom::WindowContext* aSettingWindowContext, nsIAsyncClipboardRequestCallback* aCallback, nsIAsyncSetClipboardData** _retval) override final; NS_IMETHOD GetData( @@ -75,6 +78,9 @@ class nsBaseClipboard : public nsIClipboard { using HasMatchingFlavorsCallback = mozilla::MoveOnlyFunction<void( mozilla::Result<nsTArray<nsCString>, nsresult>)>; + mozilla::Maybe<uint64_t> GetClipboardCacheInnerWindowId( + int32_t aClipboardType); + protected: virtual ~nsBaseClipboard(); @@ -106,6 +112,7 @@ class nsBaseClipboard : public nsIClipboard { NS_DECL_NSIASYNCSETCLIPBOARDDATA AsyncSetClipboardData(int32_t aClipboardType, nsBaseClipboard* aClipboard, + mozilla::dom::WindowContext* aRequestingWindowContext, nsIAsyncClipboardRequestCallback* aCallback); private: @@ -123,6 +130,7 @@ class nsBaseClipboard : public nsIClipboard { // NotifyCallback()) once nsBaseClipboard stops tracking us. This is // also used to indicate whether this request is valid. nsBaseClipboard* mClipboard; + RefPtr<mozilla::dom::WindowContext> mWindowContext; // mCallback will be nullified once the callback is notified to ensure the // callback is only notified once. nsCOMPtr<nsIAsyncClipboardRequestCallback> mCallback; @@ -172,22 +180,26 @@ class nsBaseClipboard : public nsIClipboard { */ void Clear(); void Update(nsITransferable* aTransferable, - nsIClipboardOwner* aClipboardOwner, int32_t aSequenceNumber) { + nsIClipboardOwner* aClipboardOwner, int32_t aSequenceNumber, + mozilla::Maybe<uint64_t> aInnerWindowId) { // Clear first to notify the old clipboard owner. Clear(); mTransferable = aTransferable; mClipboardOwner = aClipboardOwner; mSequenceNumber = aSequenceNumber; + mInnerWindowId = aInnerWindowId; } nsITransferable* GetTransferable() const { return mTransferable; } nsIClipboardOwner* GetClipboardOwner() const { return mClipboardOwner; } int32_t GetSequenceNumber() const { return mSequenceNumber; } + mozilla::Maybe<uint64_t> GetInnerWindowId() const { return mInnerWindowId; } nsresult GetData(nsITransferable* aTransferable) const; private: nsCOMPtr<nsITransferable> mTransferable; nsCOMPtr<nsIClipboardOwner> mClipboardOwner; int32_t mSequenceNumber = -1; + mozilla::Maybe<uint64_t> mInnerWindowId; }; void MaybeRetryGetAvailableFlavors( @@ -203,7 +215,6 @@ class nsBaseClipboard : public nsIClipboard { int32_t aClipboardType); nsresult GetDataFromClipboardCache(nsITransferable* aTransferable, int32_t aClipboardType); - void RequestUserConfirmation(int32_t aClipboardType, const nsTArray<nsCString>& aFlavorList, mozilla::dom::WindowContext* aWindowContext, diff --git a/widget/nsBaseDragService.cpp b/widget/nsBaseDragService.cpp index 22ed844595..c79d5bbb6d 100644 --- a/widget/nsBaseDragService.cpp +++ b/widget/nsBaseDragService.cpp @@ -368,7 +368,7 @@ nsBaseDragService::InvokeDragSession( nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1"); trans->Init(nullptr); - trans->SetRequestingPrincipal(mSourceNode->NodePrincipal()); + trans->SetDataPrincipal(mSourceNode->NodePrincipal()); trans->SetContentPolicyType(mContentPolicyType); trans->SetCookieJarSettings(aCookieJarSettings); mutableArray->AppendElement(trans); @@ -378,8 +378,8 @@ nsBaseDragService::InvokeDragSession( nsCOMPtr<nsITransferable> trans = do_QueryElementAt(aTransferableArray, i); if (trans) { - // Set the requestingPrincipal on the transferable. - trans->SetRequestingPrincipal(mSourceNode->NodePrincipal()); + // Set the dataPrincipal on the transferable. + trans->SetDataPrincipal(mSourceNode->NodePrincipal()); trans->SetContentPolicyType(mContentPolicyType); trans->SetCookieJarSettings(aCookieJarSettings); } diff --git a/widget/nsClipboardHelper.cpp b/widget/nsClipboardHelper.cpp index 3439adef48..87e697a4c6 100644 --- a/widget/nsClipboardHelper.cpp +++ b/widget/nsClipboardHelper.cpp @@ -36,9 +36,10 @@ nsClipboardHelper::~nsClipboardHelper() { *****************************************************************************/ NS_IMETHODIMP -nsClipboardHelper::CopyStringToClipboard(const nsAString& aString, - int32_t aClipboardID, - SensitiveData aSensitive) { +nsClipboardHelper::CopyStringToClipboard( + const nsAString& aString, int32_t aClipboardID, + mozilla::dom::WindowContext* aSettingWindowContext, + SensitiveData aSensitive) { nsresult rv; // get the clipboard @@ -92,20 +93,22 @@ nsClipboardHelper::CopyStringToClipboard(const nsAString& aString, NS_ENSURE_SUCCESS(rv, rv); // put the transferable on the clipboard - rv = clipboard->SetData(trans, nullptr, aClipboardID); + rv = clipboard->SetData(trans, nullptr, aClipboardID, aSettingWindowContext); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP -nsClipboardHelper::CopyString(const nsAString& aString, - SensitiveData aSensitive) { +nsClipboardHelper::CopyString( + const nsAString& aString, + mozilla::dom::WindowContext* aSettingWindowContext, + SensitiveData aSensitive) { nsresult rv; // copy to the global clipboard. it's bad if this fails in any way. rv = CopyStringToClipboard(aString, nsIClipboard::kGlobalClipboard, - aSensitive); + aSettingWindowContext, aSensitive); NS_ENSURE_SUCCESS(rv, rv); // unix also needs us to copy to the selection clipboard. this will @@ -117,7 +120,8 @@ nsClipboardHelper::CopyString(const nsAString& aString, // if this fails in any way other than "not being unix", we'll get // the assertion we need in CopyStringToClipboard, and we needn't // assert again here. - CopyStringToClipboard(aString, nsIClipboard::kSelectionClipboard, aSensitive); + CopyStringToClipboard(aString, nsIClipboard::kSelectionClipboard, + aSettingWindowContext, aSensitive); return NS_OK; } diff --git a/widget/nsClipboardProxy.cpp b/widget/nsClipboardProxy.cpp index 3b27d5954d..5855374566 100644 --- a/widget/nsClipboardProxy.cpp +++ b/widget/nsClipboardProxy.cpp @@ -32,7 +32,8 @@ nsClipboardProxy::nsClipboardProxy() : mClipboardCaps(false, false, false) {} NS_IMETHODIMP nsClipboardProxy::SetData(nsITransferable* aTransferable, - nsIClipboardOwner* anOwner, int32_t aWhichClipboard) { + nsIClipboardOwner* anOwner, int32_t aWhichClipboard, + mozilla::dom::WindowContext* aWindowContext) { #if defined(ACCESSIBILITY) && defined(XP_WIN) a11y::Compatibility::SuppressA11yForClipboardCopy(); #endif @@ -41,17 +42,19 @@ nsClipboardProxy::SetData(nsITransferable* aTransferable, IPCTransferable ipcTransferable; nsContentUtils::TransferableToIPCTransferable(aTransferable, &ipcTransferable, false, nullptr); - child->SendSetClipboard(std::move(ipcTransferable), aWhichClipboard); + child->SendSetClipboard(std::move(ipcTransferable), aWhichClipboard, + aWindowContext); return NS_OK; } NS_IMETHODIMP nsClipboardProxy::AsyncSetData( - int32_t aWhichClipboard, nsIAsyncClipboardRequestCallback* aCallback, + int32_t aWhichClipboard, mozilla::dom::WindowContext* aSettingWindowContext, + nsIAsyncClipboardRequestCallback* aCallback, nsIAsyncSetClipboardData** _retval) { RefPtr<ClipboardWriteRequestChild> request = MakeRefPtr<ClipboardWriteRequestChild>(aCallback); ContentChild::GetSingleton()->SendPClipboardWriteRequestConstructor( - request, aWhichClipboard); + request, aWhichClipboard, aSettingWindowContext); request.forget(_retval); return NS_OK; } @@ -69,12 +72,18 @@ nsClipboardProxy::GetData(nsITransferable* aTransferable, nsTArray<nsCString> types; aTransferable->FlavorsTransferableCanImport(types); - IPCTransferableData transferable; - ContentChild::GetSingleton()->SendGetClipboard(types, aWhichClipboard, - aWindowContext, &transferable); + IPCTransferableDataOrError transferableOrError; + ContentChild::GetSingleton()->SendGetClipboard( + types, aWhichClipboard, aWindowContext, &transferableOrError); + + if (transferableOrError.type() == IPCTransferableDataOrError::Tnsresult) { + MOZ_ASSERT(NS_FAILED(transferableOrError.get_nsresult())); + return transferableOrError.get_nsresult(); + } + return nsContentUtils::IPCTransferableDataToTransferable( - transferable, false /* aAddDataFlavor */, aTransferable, - false /* aFilterUnknownFlavors */); + transferableOrError.get_IPCTransferableData(), false /* aAddDataFlavor */, + aTransferable, false /* aFilterUnknownFlavors */); } namespace { diff --git a/widget/nsIBaseWindow.idl b/widget/nsIBaseWindow.idl index 3cf7193788..3e0dadbdd5 100644 --- a/widget/nsIBaseWindow.idl +++ b/widget/nsIBaseWindow.idl @@ -161,7 +161,7 @@ interface nsIBaseWindow : nsISupports * * @see DimensionRequest */ - void setDimensions(in DimensionRequest aRequest); + [noscript] void setDimensions(in DimensionRequest aRequest); /** * Gets the dimensions of the window. The caller may pass nullptr for any @@ -178,7 +178,7 @@ interface nsIBaseWindow : nsISupports * * @see DimensionRequest */ - void getDimensions(in DimensionKind aDimensionKind, out long aX, out long aY, out long aCX, out long aCY); + [noscript] void getDimensions(in DimensionKind aDimensionKind, out long aX, out long aY, out long aCX, out long aCY); /** * Tell the window to repaint itself @@ -210,7 +210,7 @@ interface nsIBaseWindow : nsISupports On controls that don't support setting nativeWindow parents, setting this will return a NS_ERROR_NOT_IMPLEMENTED error. */ - attribute nativeWindow parentNativeWindow; + [noscript] attribute nativeWindow parentNativeWindow; /* This is the handle (HWND, GdkWindow*, ...) to the native window of the diff --git a/widget/nsIClipboard.idl b/widget/nsIClipboard.idl index a34f0f9298..fc1f8bf8c3 100644 --- a/widget/nsIClipboard.idl +++ b/widget/nsIClipboard.idl @@ -115,20 +115,28 @@ interface nsIClipboard : nsISupports * @param aTransferable The transferable * @param anOwner The owner of the transferable * @param aWhichClipboard Specifies the clipboard to which this operation applies. - * @result NS_Ok if no errors + * @param aSettingWindowContext [optional] + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. + * @result NS_OK if no errors */ - void setData ( in nsITransferable aTransferable, in nsIClipboardOwner anOwner, - in long aWhichClipboard ) ; + void setData (in nsITransferable aTransferable, in nsIClipboardOwner anOwner, + in long aWhichClipboard, [optional] in WindowContext aSettingWindowContext); /** - * Requests setting data to the native clipboard. The acutal set occur + * Requests setting data to the native clipboard. The actual set occurs * when the data is provided by calling nsIAsyncSetClipboardData::setData(). * The result will be notified by nsIClipboardCallback. A new set request * will cancel any prior pending requests, if any exist. * * @param aWhichClipboard * Specifies the clipboard to which this operation applies. + * @param aSettingWindowContext [optional] + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. * @param aCallback [optional] * The callback object that will be notified upon completion. * @return nsIAsyncSetClipboardData @@ -136,6 +144,7 @@ interface nsIClipboard : nsISupports * data is provided by calling nsIAsyncSetClipboardData::setData(). */ nsIAsyncSetClipboardData asyncSetData(in long aWhichClipboard, + [optional] in WindowContext aSettingWindowContext, [optional] in nsIAsyncClipboardRequestCallback aCallback); /** diff --git a/widget/nsIClipboardHelper.idl b/widget/nsIClipboardHelper.idl index ed4af112f1..370c652b31 100644 --- a/widget/nsIClipboardHelper.idl +++ b/widget/nsIClipboardHelper.idl @@ -29,17 +29,29 @@ interface nsIClipboardHelper : nsISupports * @param aString, the string to copy to the clipboard * @param aClipboardID, the ID of the clipboard to copy to * (eg. kSelectionClipboard -- see nsIClipboard.idl) + * @param aSettingWindowContext + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. * @param aSensitive, optional flag to indicate that data is sensitive, like a password. * That will exclude data from Cloud Clipboard/Clipboard History on Windows. */ void copyStringToClipboard(in AString aString, in long aClipboardID, + [optional] in WindowContext aSettingWindowContext, [optional, default(NotSensitive)] in nsIClipboardHelper_SensitiveData aSensitive); /** * copy string to (default) clipboard * * @param aString, the string to copy to the clipboard + * @param aSettingWindowContext + * The window context that is setting the clipboard, if any. This is used + * to possibly bypass Content Analysis if a set clipboard and get clipboard + * operation are done on the same page. + * @param aSensitive, optional flag to indicate that data is sensitive, like a password. + * That will exclude data from Cloud Clipboard/Clipboard History on Windows. */ void copyString(in AString aString, + [optional] in WindowContext aSettingWindowContext, [optional, default(NotSensitive)] in nsIClipboardHelper_SensitiveData aSensitive); }; diff --git a/widget/nsIDeviceContextSpec.cpp b/widget/nsIDeviceContextSpec.cpp index 5ab442415d..2ec5946bb0 100644 --- a/widget/nsIDeviceContextSpec.cpp +++ b/widget/nsIDeviceContextSpec.cpp @@ -58,8 +58,8 @@ gfxPoint nsIDeviceContextSpec::GetPrintingTranslate() { } RefPtr<PrintEndDocumentPromise> -nsIDeviceContextSpec::EndDocumentPromiseFromResult(nsresult aResult, - const char* aSite) { +nsIDeviceContextSpec::EndDocumentPromiseFromResult( + nsresult aResult, mozilla::StaticString aSite) { return NS_SUCCEEDED(aResult) ? PrintEndDocumentPromise::CreateAndResolve(true, aSite) : PrintEndDocumentPromise::CreateAndReject(aResult, aSite); diff --git a/widget/nsIDeviceContextSpec.h b/widget/nsIDeviceContextSpec.h index 6afe4ea850..33ebaecd62 100644 --- a/widget/nsIDeviceContextSpec.h +++ b/widget/nsIDeviceContextSpec.h @@ -95,7 +95,7 @@ class nsIDeviceContextSpec : public nsISupports { const char* aCallSite, AsyncEndDocumentFunction aFunction); static RefPtr<mozilla::gfx::PrintEndDocumentPromise> - EndDocumentPromiseFromResult(nsresult aResult, const char* aSite); + EndDocumentPromiseFromResult(nsresult aResult, mozilla::StaticString aSite); nsCOMPtr<nsIPrintSettings> mPrintSettings; diff --git a/widget/nsIDragService.idl b/widget/nsIDragService.idl index a35d3128a8..619c8f8a82 100644 --- a/widget/nsIDragService.idl +++ b/widget/nsIDragService.idl @@ -126,7 +126,7 @@ interface nsIDragService : nsISupports * * Note: This method is deprecated for non-native code. */ - [can_run_script] + [noscript, can_run_script] void invokeDragSessionWithSelection(in Selection aSelection, in nsIPrincipal aPrincipal, in nsIContentSecurityPolicy aCsp, diff --git a/widget/nsIJumpListBuilder.idl b/widget/nsIJumpListBuilder.idl index 1b7b93e5e7..a52f09d7da 100644 --- a/widget/nsIJumpListBuilder.idl +++ b/widget/nsIJumpListBuilder.idl @@ -100,9 +100,9 @@ interface nsIJumpListBuilder : nsISupports */ [implicit_jscontext] Promise populateJumpList( - in Array<jsval> aTaskDescriptions, + in jsval aTaskDescriptions, in AString aCustomTitle, - in Array<jsval> aCustomDescriptions + in jsval aCustomDescriptions ); /** diff --git a/widget/nsIScreen.idl b/widget/nsIScreen.idl index af8371edb3..619b8bc284 100644 --- a/widget/nsIScreen.idl +++ b/widget/nsIScreen.idl @@ -75,7 +75,7 @@ interface nsIScreen : nsISupports /** * ScreenColorGamut is native type, which cannot be declared [infallible]. */ - readonly attribute ScreenColorGamut colorGamut; + [noscript] readonly attribute ScreenColorGamut colorGamut; /** * The number of device pixels per desktop pixel for this screen (for diff --git a/widget/nsITransferable.idl b/widget/nsITransferable.idl index 0a8419c4bb..c6e424d7cb 100644 --- a/widget/nsITransferable.idl +++ b/widget/nsITransferable.idl @@ -203,9 +203,8 @@ interface nsITransferable : nsISupports * node principal of the source DOM node from which this transferable was * created, or the principal of the global from which this transferable was * created. - * XXXedgar: Rename it to something more generic, bug 1867636. */ - [notxpcom, nostdcall] attribute nsIPrincipal requestingPrincipal; + [notxpcom, nostdcall] attribute nsIPrincipal dataPrincipal; /** * the contentPolicyType for this transferable. diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 06eab558eb..29c127c146 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -1397,6 +1397,8 @@ class nsIWidget : public nsISupports { const nsAString& xulWinClass, const nsAString& xulWinName) = 0; + virtual void SetIsEarlyBlankWindow(bool) {} + /** * Enables/Disables system capture of any and all events that would cause a * popup to be rolled up. aListener should be set to a non-null value for diff --git a/widget/nsIWidgetListener.cpp b/widget/nsIWidgetListener.cpp index b9f236f159..13eba4eeeb 100644 --- a/widget/nsIWidgetListener.cpp +++ b/widget/nsIWidgetListener.cpp @@ -36,8 +36,6 @@ void nsIWidgetListener::SizeModeChanged(nsSizeMode aSizeMode) {} void nsIWidgetListener::SafeAreaInsetsChanged(const mozilla::ScreenIntMargin&) { } -void nsIWidgetListener::UIResolutionChanged() {} - #if defined(MOZ_WIDGET_ANDROID) void nsIWidgetListener::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) { } diff --git a/widget/nsIWidgetListener.h b/widget/nsIWidgetListener.h index aff753aaec..950d4b4622 100644 --- a/widget/nsIWidgetListener.h +++ b/widget/nsIWidgetListener.h @@ -81,12 +81,6 @@ class nsIWidgetListener { */ virtual void SizeModeChanged(nsSizeMode aSizeMode); - /** - * Called when the DPI (device resolution scaling factor) is changed, - * such that UI elements may need to be rescaled. - */ - virtual void UIResolutionChanged(); - #if defined(MOZ_WIDGET_ANDROID) virtual void DynamicToolbarMaxHeightChanged(mozilla::ScreenIntCoord aHeight); virtual void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset); diff --git a/widget/nsNativeTheme.cpp b/widget/nsNativeTheme.cpp index 6900414ff3..fcb30f5a7b 100644 --- a/widget/nsNativeTheme.cpp +++ b/widget/nsNativeTheme.cpp @@ -120,6 +120,7 @@ NS_IMPL_ISUPPORTS(nsNativeTheme, nsITimerCallback, nsINamed) case StyleAppearance::Menulist: case StyleAppearance::NumberInput: case StyleAppearance::Textfield: + case StyleAppearance::PasswordInput: case StyleAppearance::Searchfield: case StyleAppearance::Textarea: { if (CheckBooleanAttr(aFrame, nsGkAtoms::focused)) { diff --git a/widget/nsTransferable.cpp b/widget/nsTransferable.cpp index 58a8630801..b1e1f60ade 100644 --- a/widget/nsTransferable.cpp +++ b/widget/nsTransferable.cpp @@ -511,17 +511,16 @@ void nsTransferable::SetIsPrivateData(bool aIsPrivateData) { mPrivateData = aIsPrivateData; } -nsIPrincipal* nsTransferable::GetRequestingPrincipal() { +nsIPrincipal* nsTransferable::GetDataPrincipal() { MOZ_ASSERT(mInitialized); - return mRequestingPrincipal; + return mDataPrincipal; } -void nsTransferable::SetRequestingPrincipal( - nsIPrincipal* aRequestingPrincipal) { +void nsTransferable::SetDataPrincipal(nsIPrincipal* aDataPrincipal) { MOZ_ASSERT(mInitialized); - mRequestingPrincipal = aRequestingPrincipal; + mDataPrincipal = aDataPrincipal; } nsContentPolicyType nsTransferable::GetContentPolicyType() { diff --git a/widget/nsTransferable.h b/widget/nsTransferable.h index 02f2b0459c..191be3e74a 100644 --- a/widget/nsTransferable.h +++ b/widget/nsTransferable.h @@ -80,7 +80,7 @@ class nsTransferable : public nsITransferable { nsTArray<DataStruct> mDataArray; nsCOMPtr<nsIFormatConverter> mFormatConv; bool mPrivateData; - nsCOMPtr<nsIPrincipal> mRequestingPrincipal; + nsCOMPtr<nsIPrincipal> mDataPrincipal; nsContentPolicyType mContentPolicyType; nsCOMPtr<nsICookieJarSettings> mCookieJarSettings; nsCOMPtr<nsIReferrerInfo> mReferrerInfo; diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index 0c5b5207f2..cb34f7b067 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -146,7 +146,6 @@ static const char sIntPrefs[][45] = { "ui.treeLazyScrollDelay", "ui.treeScrollDelay", "ui.treeScrollLinesMax", - "accessibility.tabfocus", // Weird one... "ui.chosenMenuItemsShouldBlink", "ui.windowsAccentColorInTitlebar", "ui.macBigSurTheme", @@ -527,9 +526,6 @@ void nsXPLookAndFeel::Init() { // for each types. Then, we could reduce the unnecessary loop from // nsXPLookAndFeel::OnPrefChanged(). Preferences::RegisterPrefixCallback(OnPrefChanged, "ui."); - // We really do just want the accessibility.tabfocus pref, not other prefs - // that start with that string. - Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus"); for (const auto& pref : kMediaQueryPrefs) { Preferences::RegisterCallback( @@ -1237,8 +1233,7 @@ void LookAndFeel::DoHandleGlobalThemeChange() { // // We can use the *DoNotUseDirectly functions directly here, because we want // to notify all possible themes in a given process (but just once). - if (XRE_IsParentProcess() || - !StaticPrefs::widget_non_native_theme_enabled()) { + if (XRE_IsParentProcess()) { if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) { theme->ThemeChanged(); } diff --git a/widget/reftests/reftest.list b/widget/reftests/reftest.list index dc788a31aa..2170fec443 100644 --- a/widget/reftests/reftest.list +++ b/widget/reftests/reftest.list @@ -6,4 +6,4 @@ load 664925.xhtml pref(apz.allow_zooming,true) pref(ui.useOverlayScrollbars,0) skip-if(!cocoaWidget) != scaled-scrollbar.html about:blank # Test that scrollbar buttons are inhibited on Linux using the non-native theme. -skip-if(!gtkWidget) pref(widget.non-native-theme.enabled,true) test-pref(ui.scrollArrowStyle,4097) ref-pref(ui.scrollArrowStyle,0) == scrollbar-buttons.html scrollbar-buttons.html +skip-if(!gtkWidget) test-pref(ui.scrollArrowStyle,4097) ref-pref(ui.scrollArrowStyle,0) == scrollbar-buttons.html scrollbar-buttons.html diff --git a/widget/tests/browser/browser_test_InputContextURI.js b/widget/tests/browser/browser_test_InputContextURI.js index 52f05d90f9..ccdfc86def 100644 --- a/widget/tests/browser/browser_test_InputContextURI.js +++ b/widget/tests/browser/browser_test_InputContextURI.js @@ -86,6 +86,9 @@ add_task(async () => { await test_url_bar_url("check after remote content sets the URI"); }); add_task(async () => { + await SpecialPowers.pushPrefEnv({ + set: [["dom.security.https_first", false]], + }); await test_input_in_http_or_https(false); }); diff --git a/widget/tests/browser/browser_test_scrollbar_colors.js b/widget/tests/browser/browser_test_scrollbar_colors.js index 2152412071..458cadd84e 100644 --- a/widget/tests/browser/browser_test_scrollbar_colors.js +++ b/widget/tests/browser/browser_test_scrollbar_colors.js @@ -27,15 +27,6 @@ add_task(async () => { // == Native theme == - const WIN_REFERENCES = [ - // Yellow background - ["255,255,0", 6889], - // Blue scrollbar face - ["0,0,255", 540], - // Cyan scrollbar track - ["0,255,255", 2487], - ]; - const MAC_REFERENCES = [ // Yellow background ["255,255,0", 7225], @@ -45,19 +36,9 @@ add_task(async () => { ["0,255,255", 1760], ]; - // Values have been updated from 8100, 720, 1180 for linux1804 - const LINUX_REFERENCES = [ - // Yellow background - ["255,255,0", 7744], - // Blue scrollbar face - ["0,0,255", 1104], - // Cyan scrollbar track - ["0,255,255", 1152], - ]; - // == Non-native theme == - const WIN10_NNT_REFERENCES = [ + const WIN10_REFERENCES = [ // Yellow background ["255,255,0", 6889], // Blue scrollbar face @@ -66,7 +47,7 @@ add_task(async () => { ["0,255,255", 2355], ]; - const WIN11_NNT_REFERENCES = [ + const WIN11_REFERENCES = [ // Yellow background ["255,255,0", 6889], // Blue scrollbar face @@ -75,9 +56,7 @@ add_task(async () => { ["0,255,255", 2787], ]; - const MAC_NNT_REFERENCES = MAC_REFERENCES; - - const LINUX_NNT_REFERENCES = [ + const LINUX_REFERENCES = [ // Yellow background ["255,255,0", 7744], // Blue scrollbar face @@ -116,23 +95,19 @@ add_task(async () => { let canvas = snapshotRect(content.window, outerRect); let stats = countPixels(canvas); - let isNNT = SpecialPowers.getBoolPref("widget.non-native-theme.enabled"); - let references; if (content.navigator.platform.startsWith("Win")) { - if (!isNNT) { - references = WIN_REFERENCES; - } else if (WindowsVersionInfo.get().buildNumber >= 22000) { - // Windows 11 NNT - references = WIN11_NNT_REFERENCES; + if (WindowsVersionInfo.get().buildNumber >= 22000) { + // Windows 11 + references = WIN11_REFERENCES; } else { - // Windows 10 NNT - references = WIN10_NNT_REFERENCES; + // Windows 10 + references = WIN10_REFERENCES; } } else if (content.navigator.platform.startsWith("Mac")) { - references = isNNT ? MAC_NNT_REFERENCES : MAC_REFERENCES; + references = MAC_REFERENCES; } else if (content.navigator.platform.startsWith("Linux")) { - references = isNNT ? LINUX_NNT_REFERENCES : LINUX_REFERENCES; + references = LINUX_REFERENCES; } else { ok(false, "Unsupported platform"); } diff --git a/widget/tests/file_test_clipboard.js b/widget/tests/file_test_clipboard.js index 76bdbaa84d..4e720b6383 100644 --- a/widget/tests/file_test_clipboard.js +++ b/widget/tests/file_test_clipboard.js @@ -149,5 +149,20 @@ clipboardTypes.forEach(function (clipboardType) { // Clear all clipboard data. cleanupAllClipboard(); }); + + add_task(function test_unsupport_flavor() { + try { + is( + getClipboardData("foo/bar", clipboardType), + null, + `Test getData for clipboard type ${clipboardType}` + ); + } catch (e) { + ok( + false, + `getData should not throw error for clipboard type ${clipboardType}` + ); + } + }); } }); diff --git a/widget/tests/file_test_clipboard_asyncSetData.js b/widget/tests/file_test_clipboard_asyncSetData.js index cceecd2c44..5eb73f90fa 100644 --- a/widget/tests/file_test_clipboard_asyncSetData.js +++ b/widget/tests/file_test_clipboard_asyncSetData.js @@ -21,7 +21,7 @@ clipboardTypes.forEach(function (type) { let priorResult; let priorRequest; let priorPromise = new Promise(resolve => { - priorRequest = clipboard.asyncSetData(type, { + priorRequest = clipboard.asyncSetData(type, null, { QueryInterface: SpecialPowers.ChromeUtils.generateQI([ "nsIAsyncSetClipboardDataCallback", ]), @@ -119,7 +119,7 @@ clipboardTypes.forEach(function (type) { // Create a pending asyncSetData request let result; - let request = clipboard.asyncSetData(type, rv => { + let request = clipboard.asyncSetData(type, null, rv => { result = rv; }); diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm index 51a9a95b52..f720a8681e 100644 --- a/widget/uikit/nsLookAndFeel.mm +++ b/widget/uikit/nsLookAndFeel.mm @@ -282,9 +282,6 @@ nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) { case IntID::TreeScrollLinesMax: aResult = 3; break; - case IntID::TabFocusModel: - aResult = 1; // default to just textboxes - break; case IntID::ScrollToClick: aResult = 0; break; diff --git a/widget/windows/CompositorWidgetParent.cpp b/widget/windows/CompositorWidgetParent.cpp index b25d30d9d5..d38125f4ca 100644 --- a/widget/windows/CompositorWidgetParent.cpp +++ b/widget/windows/CompositorWidgetParent.cpp @@ -149,16 +149,12 @@ mozilla::ipc::IPCResult CompositorWidgetParent::RecvNotifyVisibilityUpdated( return IPC_OK(); } -nsSizeMode CompositorWidgetParent::CompositorWidgetParent::GetWindowSizeMode() - const { - nsSizeMode sizeMode = mSizeMode; - return sizeMode; +nsSizeMode CompositorWidgetParent::GetWindowSizeMode() const { + return mSizeMode; } -bool CompositorWidgetParent::CompositorWidgetParent::GetWindowIsFullyOccluded() - const { - bool isFullyOccluded = mIsFullyOccluded; - return isFullyOccluded; +bool CompositorWidgetParent::GetWindowIsFullyOccluded() const { + return mIsFullyOccluded; } mozilla::ipc::IPCResult CompositorWidgetParent::RecvClearTransparentWindow() { diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index 6d8429f1cc..bb9b19e3a1 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1808,6 +1808,12 @@ const nsTArray<GfxDriverInfo>& GfxInfo::GetGfxDriverInfo() { V(10, 18, 15, 4256), V(10, 18, 15, 4293), "FEATURE_FAILURE_BUG_1833809", "Intel driver 10.18.15.*"); + APPEND_TO_DRIVER_BLOCKLIST2( + OperatingSystem::Windows, DeviceFamily::IntelGen12, + nsIGfxInfo::FEATURE_REUSE_DECODER_DEVICE, + nsIGfxInfo::FEATURE_BLOCKED_DEVICE, DRIVER_COMPARISON_IGNORED, + V(0, 0, 0, 0), "FEATURE_FAILURE_BUG_1896823"); + //////////////////////////////////// // FEATURE_OVERLAY_VP_AUTO_HDR diff --git a/widget/windows/JumpListBuilder.cpp b/widget/windows/JumpListBuilder.cpp index e25c8c038f..69a46d5aad 100644 --- a/widget/windows/JumpListBuilder.cpp +++ b/widget/windows/JumpListBuilder.cpp @@ -18,6 +18,17 @@ #include "nsServiceManagerUtils.h" #include "WinUtils.h" +#ifdef __MINGW32__ +// The PKEY_Link_Arguments property key does not exist in the MINGW32 +// build configuration, so we define it ourselves here. +# define INITGUID // This alters the behavior of DEFINE_PROPERTYKEY so that + // we define PKEY_Link_Arguments rather than declare it. +# include <propkeydef.h> // For DEFINE_PROPERTYKEY() definition +DEFINE_PROPERTYKEY(PKEY_Link_Arguments, 0x436F2667, 0x14E2, 0x4FEB, 0xB3, 0x0A, + 0x14, 0x6C, 0x53, 0xB5, 0xB6, 0x74, 100); +# undef INITGUID +#endif + using mozilla::dom::Promise; using mozilla::dom::WindowsJumpListShortcutDescription; @@ -291,15 +302,35 @@ JumpListBuilder::CheckForRemovals(JSContext* aCx, Promise** aPromise) { } NS_IMETHODIMP -JumpListBuilder::PopulateJumpList( - const nsTArray<JS::Value>& aTaskDescriptions, const nsAString& aCustomTitle, - const nsTArray<JS::Value>& aCustomDescriptions, JSContext* aCx, - Promise** aPromise) { +JumpListBuilder::PopulateJumpList(JS::Handle<JS::Value> aTaskDescriptions, + const nsAString& aCustomTitle, + JS::Handle<JS::Value> aCustomDescriptions, + JSContext* aCx, Promise** aPromise) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aPromise); MOZ_ASSERT(mIOThread); - if (aCustomDescriptions.Length() && aCustomTitle.IsEmpty()) { + if (!aTaskDescriptions.isObject() || !aCustomDescriptions.isObject()) { + return NS_ERROR_INVALID_ARG; + } + + JS::Rooted<JSObject*> taskDescriptionsObj(aCx, &aTaskDescriptions.toObject()); + JS::Rooted<JSObject*> customDescriptionsObj(aCx, + &aCustomDescriptions.toObject()); + + uint32_t taskDescriptionsLength = 0; + uint32_t customDescriptionsLength = 0; + if (NS_WARN_IF(!JS::GetArrayLength(aCx, taskDescriptionsObj, + &taskDescriptionsLength))) { + return NS_ERROR_INVALID_ARG; + } + + if (NS_WARN_IF(!JS::GetArrayLength(aCx, customDescriptionsObj, + &customDescriptionsLength))) { + return NS_ERROR_INVALID_ARG; + } + + if (customDescriptionsLength && aCustomTitle.IsEmpty()) { return NS_ERROR_INVALID_ARG; } @@ -309,9 +340,11 @@ JumpListBuilder::PopulateJumpList( mIOThread->Dispatch(event, NS_DISPATCH_NORMAL); nsTArray<WindowsJumpListShortcutDescription> taskDescs; - for (auto& jsval : aTaskDescriptions) { + for (uint32_t arrayIndex = 0; arrayIndex < taskDescriptionsLength; + arrayIndex++) { JS::Rooted<JS::Value> rootedVal(aCx); - if (NS_WARN_IF(!dom::ToJSValue(aCx, jsval, &rootedVal))) { + if (NS_WARN_IF( + !JS_GetElement(aCx, taskDescriptionsObj, arrayIndex, &rootedVal))) { return NS_ERROR_INVALID_ARG; } @@ -324,9 +357,11 @@ JumpListBuilder::PopulateJumpList( } nsTArray<WindowsJumpListShortcutDescription> customDescs; - for (auto& jsval : aCustomDescriptions) { + for (uint32_t arrayIndex = 0; arrayIndex < customDescriptionsLength; + arrayIndex++) { JS::Rooted<JS::Value> rootedVal(aCx); - if (NS_WARN_IF(!dom::ToJSValue(aCx, jsval, &rootedVal))) { + if (NS_WARN_IF(!JS_GetElement(aCx, customDescriptionsObj, arrayIndex, + &rootedVal))) { return NS_ERROR_INVALID_ARG; } @@ -592,7 +627,14 @@ void JumpListBuilder::DoPopulateJumpList( reinterpret_cast<const wchar_t*>(aCustomTitle.BeginReading()), pCustomArray); - if (FAILED(hr)) { + // E_ACCESSDENIED might be returned if Windows is configured not to show + // recently opened items in the start menu or jump lists. In that case, we + // still want to populate the tasks, so we ignore the error and commit + // the list. + // + // See + // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-appendcategory + if (FAILED(hr) && hr != E_ACCESSDENIED) { rv = NS_ERROR_UNEXPECTED; return; } @@ -659,26 +701,47 @@ void JumpListBuilder::RemoveIconCacheAndGetJumplistShortcutURIs( if (FAILED(aObjArray->GetAt(idx, IID_IShellLinkW, static_cast<void**>(getter_AddRefs(pLink))))) { + NS_WARNING("Could not get a IShellLink from the IObjectArray"); continue; } - wchar_t buf[MAX_PATH]; - HRESULT hres = pLink->GetArguments(buf, MAX_PATH); - if (SUCCEEDED(hres)) { - LPWSTR* arglist; - int32_t numArgs; - - arglist = ::CommandLineToArgvW(buf, &numArgs); - if (arglist && numArgs > 0) { - nsString spec(arglist[0]); - aURISpecs.AppendElement(std::move(spec)); - ::LocalFree(arglist); - } + RefPtr<IPropertyStore> pPropStore = nullptr; + if (NS_WARN_IF(FAILED(pLink->QueryInterface( + IID_IPropertyStore, + static_cast<void**>(getter_AddRefs(pPropStore)))))) { + NS_WARNING("Could not get IPropertyStore from IShellLink"); + continue; + } + + PROPVARIANT pv; + PropVariantInit(&pv); + auto cleanupPropVariant = MakeScopeExit([&] { PropVariantClear(&pv); }); + if (NS_WARN_IF(FAILED(pPropStore->GetValue(PKEY_Link_Arguments, &pv)))) { + NS_WARNING("Could not get PKEY_Link_Arguments from IPropertyStore"); + continue; + } + + if (pv.vt != VT_LPWSTR) { + NS_WARNING("Unexpected type returned for PKEY_Link_Arguments"); + continue; + } + + LPCWSTR args(char16ptr_t(pv.pwszVal)); + LPWSTR* arglist; + int32_t numArgs; + + arglist = ::CommandLineToArgvW(args, &numArgs); + if (arglist && numArgs > 0) { + nsString spec(arglist[0]); + aURISpecs.AppendElement(std::move(spec)); + ::LocalFree(arglist); } int iconIdx = 0; - hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx); + wchar_t buf[MAX_PATH + 1]; + HRESULT hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx); if (SUCCEEDED(hres)) { + buf[MAX_PATH] = '\0'; nsDependentString spec(buf); DeleteIconFromDisk(spec); } diff --git a/widget/windows/RemoteBackbuffer.cpp b/widget/windows/RemoteBackbuffer.cpp index 56a8bae0fe..2d53570c3e 100644 --- a/widget/windows/RemoteBackbuffer.cpp +++ b/widget/windows/RemoteBackbuffer.cpp @@ -89,6 +89,8 @@ class SharedImage { } bool Initialize(int32_t aWidth, int32_t aHeight) { + MOZ_ASSERT(aWidth); + MOZ_ASSERT(aHeight); MOZ_ASSERT(aWidth > 0); MOZ_ASSERT(aHeight > 0); @@ -183,9 +185,9 @@ class SharedImage { } } - int32_t GetWidth() { return mWidth; } + int32_t GetWidth() const { return mWidth; } - int32_t GetHeight() { return mHeight; } + int32_t GetHeight() const { return mHeight; } SharedImage(const SharedImage&) = delete; SharedImage(SharedImage&&) = delete; @@ -515,7 +517,7 @@ void Provider::HandleBorrowRequest(BorrowResponseData* aResponseData, aResponseData->result = ResponseResult::Error; - RECT clientRect = {}; + RECT clientRect{}; if (!::GetClientRect(mWindowHandle, &clientRect)) { return; } @@ -523,12 +525,12 @@ void Provider::HandleBorrowRequest(BorrowResponseData* aResponseData, MOZ_ASSERT(clientRect.left == 0); MOZ_ASSERT(clientRect.top == 0); - int32_t width = clientRect.right ? clientRect.right : 1; - int32_t height = clientRect.bottom ? clientRect.bottom : 1; + const int32_t width = std::max(int32_t(clientRect.right), 1); + const int32_t height = std::max(int32_t(clientRect.bottom), 1); bool needNewBackbuffer = !aAllowSameBuffer || !mBackbuffer || - (mBackbuffer->GetWidth() != width) || - (mBackbuffer->GetHeight() != height); + mBackbuffer->GetWidth() != width || + mBackbuffer->GetHeight() != height; if (!needNewBackbuffer) { aResponseData->result = ResponseResult::BorrowSameBuffer; diff --git a/widget/windows/ToastNotification.cpp b/widget/windows/ToastNotification.cpp index afdffc19ac..83692b26a1 100644 --- a/widget/windows/ToastNotification.cpp +++ b/widget/windows/ToastNotification.cpp @@ -776,6 +776,9 @@ ToastNotification::CloseAlert(const nsAString& aAlertName, bool aContextClosed) { RefPtr<ToastNotificationHandler> handler; if (NS_WARN_IF(!mActiveHandlers.Get(aAlertName, getter_AddRefs(handler)))) { + // This can happen when the handler is gone but the closure signal is not + // yet reached to the content process, and then the process tries closing + // the same signal. return NS_OK; } diff --git a/widget/windows/WinCompositorWidget.h b/widget/windows/WinCompositorWidget.h index fef967380c..df5d443067 100644 --- a/widget/windows/WinCompositorWidget.h +++ b/widget/windows/WinCompositorWidget.h @@ -18,8 +18,7 @@ class nsWindow; -namespace mozilla { -namespace widget { +namespace mozilla::widget { class PlatformCompositorWidgetDelegate : public CompositorWidgetDelegate { public: @@ -74,7 +73,7 @@ class WinCompositorWidget : public CompositorWidget { void UpdateCompositorWndSizeIfNecessary(); void RequestFxrOutput(); - bool HasFxrOutputHandler() const { return mFxrHandler != nullptr; } + bool HasFxrOutputHandler() const { return !!mFxrHandler; } FxROutputHandler* GetFxrOutputHandler() const { return mFxrHandler.get(); } virtual nsSizeMode GetWindowSizeMode() const = 0; @@ -97,7 +96,6 @@ class WinCompositorWidget : public CompositorWidget { UniquePtr<FxROutputHandler> mFxrHandler; }; -} // namespace widget -} // namespace mozilla +} // namespace mozilla::widget #endif // widget_windows_WinCompositorWidget_h diff --git a/widget/windows/filedialog/PWinFileDialog.ipdl b/widget/windows/filedialog/PWinFileDialog.ipdl index 812db7e103..b61bfc0432 100644 --- a/widget/windows/filedialog/PWinFileDialog.ipdl +++ b/widget/windows/filedialog/PWinFileDialog.ipdl @@ -12,6 +12,16 @@ namespace mozilla { namespace widget { namespace filedialog { +union FileResult { + Results?; + RemoteError; +}; + +union FolderResult { + nsString?; + RemoteError; +}; + [ChildProc=Utility] protocol PWinFileDialog { @@ -19,12 +29,13 @@ child: // Exactly one Show function should be called per instance. Further calls will // result in IPC failure. // - // Each will return `Nothing` iff the operation was canceled by the user. + // Each will return `Ok(Nothing)` if the operation was canceled by the user, + // or `Err(...)` if the operation actually failed. async ShowFileDialog(WindowsHandle parentHwnd, FileDialogType type, Command[] commands) - returns (Results? results); + returns (FileResult result); async ShowFolderDialog(WindowsHandle parentHwnd, Command[] commands) - returns (nsString? path); + returns (FolderResult result); }; } // namespace filedialog diff --git a/widget/windows/filedialog/WinFileDialogChild.cpp b/widget/windows/filedialog/WinFileDialogChild.cpp index a41018ff0e..ddb972534a 100644 --- a/widget/windows/filedialog/WinFileDialogChild.cpp +++ b/widget/windows/filedialog/WinFileDialogChild.cpp @@ -46,34 +46,23 @@ WinFileDialogChild::IPCResult WinFileDialogChild::MakeIpcFailure( return IPC_FAIL(this, what); } -#define MOZ_IPC_ENSURE_HRESULT_OK(hr, what) \ - do { \ - MOZ_LOG(sLogFileDialog, LogLevel::Verbose, \ - ("checking HRESULT for %s", what)); \ - HRESULT const _hr_ = (hr); \ - if (FAILED(_hr_)) { \ - MOZ_LOG(sLogFileDialog, LogLevel::Error, \ - ("HRESULT %8lX while %s", (hr), (what))); \ - return MakeIpcFailure(_hr_, (what)); \ - } \ - } while (0) - WinFileDialogChild::IPCResult WinFileDialogChild::RecvShowFileDialog( uintptr_t parentHwnd, FileDialogType type, nsTArray<Command> commands, FileResolver&& resolver) { MOZ_ABORT_IF_ALREADY_USED(); - SpawnFilePicker(HWND(parentHwnd), type, std::move(commands)) - ->Then( - GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__, - [resolver = std::move(resolver)](Maybe<Results> const& res) { - resolver(res); - }, - [self = RefPtr(this)](HRESULT hr) { - // this doesn't need to be returned anywhere; it'll crash the - // process as a side effect of construction - self->MakeIpcFailure(hr, "SpawnFilePicker"); - }); + auto promise = SpawnFilePicker(HWND(parentHwnd), type, std::move(commands)); + using RRV = std::decay_t<decltype(*promise)>::ResolveOrRejectValue; + + promise->Then(GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__, + [resolver = std::move(resolver)](RRV&& val) -> void { + if (val.IsResolve()) { + resolver(val.ResolveValue()); + } else { + auto err = val.RejectValue(); + resolver(RemoteError(err.where.Serialize(), err.why)); + } + }); return IPC_OK(); } @@ -83,23 +72,22 @@ WinFileDialogChild::IPCResult WinFileDialogChild::RecvShowFolderDialog( FolderResolver&& resolver) { MOZ_ABORT_IF_ALREADY_USED(); - SpawnFolderPicker(HWND(parentHwnd), std::move(commands)) - ->Then( - GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__, - [resolver = std::move(resolver)](Maybe<nsString> const& res) { - resolver(res); - }, - [self = RefPtr(this), resolver](HRESULT hr) { - // this doesn't need to be returned anywhere; it'll crash the - // process as a side effect of construction - self->MakeIpcFailure(hr, "SpawnFolderPicker"); - }); + auto promise = SpawnFolderPicker(HWND(parentHwnd), std::move(commands)); + using RRV = std::decay_t<decltype(*promise)>::ResolveOrRejectValue; + + promise->Then(GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__, + [resolver = std::move(resolver)](RRV&& val) -> void { + if (val.IsResolve()) { + resolver(val.ResolveValue()); + } else { + auto err = val.RejectValue(); + resolver(RemoteError(err.where.Serialize(), err.why)); + } + }); return IPC_OK(); } -#undef MOZ_IPC_ENSURE_HRESULT_OK - void WinFileDialogChild::ProcessingError(Result aCode, const char* aReason) { detail::LogProcessingError(sLogFileDialog, this, aCode, aReason); } diff --git a/widget/windows/filedialog/WinFileDialogCommands.cpp b/widget/windows/filedialog/WinFileDialogCommands.cpp index 838856893d..75abbad343 100644 --- a/widget/windows/filedialog/WinFileDialogCommands.cpp +++ b/widget/windows/filedialog/WinFileDialogCommands.cpp @@ -22,6 +22,20 @@ namespace mozilla::widget::filedialog { +const char* Error::KindName(Error::Kind kind) { + switch (kind) { + case LocalError: + return "LocalError"; + case RemoteError: + return "RemoteError"; + case IPCError: + return "IPCError"; + default: + MOZ_ASSERT(false); + return "<bad value>"; + } +} + // Visitor to apply commands to the dialog. struct Applicator { IFileDialog* dialog = nullptr; @@ -98,13 +112,15 @@ static HRESULT GetShellItemPath(IShellItem* aItem, nsString& aResultString) { } } // namespace -#define MOZ_ENSURE_HRESULT_OK(call_) \ - do { \ - HRESULT const _tmp_hr_ = (call_); \ - if (FAILED(_tmp_hr_)) return Err(_tmp_hr_); \ +#define MOZ_ENSURE_HRESULT_OK(where, call_) \ + do { \ + HRESULT const _tmp_hr_ = (call_); \ + if (FAILED(_tmp_hr_)) { \ + return mozilla::Err(MOZ_FD_LOCAL_ERROR(where, _tmp_hr_)); \ + } \ } while (0) -mozilla::Result<RefPtr<IFileDialog>, HRESULT> MakeFileDialog( +mozilla::Result<RefPtr<IFileDialog>, Error> MakeFileDialog( FileDialogType type) { RefPtr<IFileDialog> dialog; @@ -112,43 +128,45 @@ mozilla::Result<RefPtr<IFileDialog>, HRESULT> MakeFileDialog( : CLSID_FileSaveDialog; HRESULT const hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, IID_IFileDialog, getter_AddRefs(dialog)); - MOZ_ENSURE_HRESULT_OK(hr); + // more properly: "CoCreateInstance(CLSID_...)", but this suffices + MOZ_ENSURE_HRESULT_OK("MakeFileDialog", hr); return std::move(dialog); } -HRESULT ApplyCommands(::IFileDialog* dialog, - nsTArray<Command> const& commands) { +mozilla::Result<Ok, Error> ApplyCommands(::IFileDialog* dialog, + nsTArray<Command> const& commands) { Applicator applicator{.dialog = dialog}; for (auto const& cmd : commands) { HRESULT const hr = applicator.Visit(cmd); - if (FAILED(hr)) { - return hr; - } + MOZ_ENSURE_HRESULT_OK("ApplyCommands", hr); } - return S_OK; + return Ok{}; } -mozilla::Result<Results, HRESULT> GetFileResults(::IFileDialog* dialog) { +mozilla::Result<Results, Error> GetFileResults(::IFileDialog* dialog) { FILEOPENDIALOGOPTIONS fos; - MOZ_ENSURE_HRESULT_OK(dialog->GetOptions(&fos)); + MOZ_ENSURE_HRESULT_OK("IFileDialog::GetOptions", dialog->GetOptions(&fos)); using widget::WinUtils; // Extract which filter type the user selected UINT index; - MOZ_ENSURE_HRESULT_OK(dialog->GetFileTypeIndex(&index)); + MOZ_ENSURE_HRESULT_OK("IFileDialog::GetFileTypeIndex", + dialog->GetFileTypeIndex(&index)); // single selection if ((fos & FOS_ALLOWMULTISELECT) == 0) { RefPtr<IShellItem> item; - MOZ_ENSURE_HRESULT_OK(dialog->GetResult(getter_AddRefs(item))); + MOZ_ENSURE_HRESULT_OK("IFileDialog::GetResult", + dialog->GetResult(getter_AddRefs(item))); if (!item) { - return Err(E_FAIL); + return Err(MOZ_FD_LOCAL_ERROR("IFileDialog::GetResult: item", E_POINTER)); } nsAutoString path; - MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, path)); + MOZ_ENSURE_HRESULT_OK("GetFileResults: GetShellItemPath (1)", + GetShellItemPath(item, path)); return Results({path}, index); } @@ -158,25 +176,29 @@ mozilla::Result<Results, HRESULT> GetFileResults(::IFileDialog* dialog) { dialog->QueryInterface(IID_IFileOpenDialog, getter_AddRefs(openDlg)); if (!openDlg) { MOZ_ASSERT(false, "a file-save dialog was given FOS_ALLOWMULTISELECT?"); - return Err(E_UNEXPECTED); + return Err(MOZ_FD_LOCAL_ERROR("Save + FOS_ALLOWMULTISELECT", E_UNEXPECTED)); } RefPtr<IShellItemArray> items; - MOZ_ENSURE_HRESULT_OK(openDlg->GetResults(getter_AddRefs(items))); + MOZ_ENSURE_HRESULT_OK("IFileOpenDialog::GetResults", + openDlg->GetResults(getter_AddRefs(items))); if (!items) { - return Err(E_FAIL); + return Err( + MOZ_FD_LOCAL_ERROR("IFileOpenDialog::GetResults: items", E_POINTER)); } nsTArray<nsString> paths; DWORD count = 0; - MOZ_ENSURE_HRESULT_OK(items->GetCount(&count)); + MOZ_ENSURE_HRESULT_OK("IShellItemArray::GetCount", items->GetCount(&count)); for (DWORD idx = 0; idx < count; idx++) { RefPtr<IShellItem> item; - MOZ_ENSURE_HRESULT_OK(items->GetItemAt(idx, getter_AddRefs(item))); + MOZ_ENSURE_HRESULT_OK("IShellItemArray::GetItemAt", + items->GetItemAt(idx, getter_AddRefs(item))); nsAutoString str; - MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, str)); + MOZ_ENSURE_HRESULT_OK("GetFileResults: GetShellItemPath (2)", + GetShellItemPath(item, str)); paths.EmplaceBack(str); } @@ -184,15 +206,17 @@ mozilla::Result<Results, HRESULT> GetFileResults(::IFileDialog* dialog) { return Results(std::move(paths), std::move(index)); } -mozilla::Result<nsString, HRESULT> GetFolderResults(::IFileDialog* dialog) { +mozilla::Result<nsString, Error> GetFolderResults(::IFileDialog* dialog) { RefPtr<IShellItem> item; - MOZ_ENSURE_HRESULT_OK(dialog->GetResult(getter_AddRefs(item))); + MOZ_ENSURE_HRESULT_OK("IFileDialog::GetResult", + dialog->GetResult(getter_AddRefs(item))); + if (!item) { // shouldn't happen -- probably a precondition failure on our part, but // might be due to misbehaving shell extensions? MOZ_ASSERT(false, "unexpected lack of item: was `Show`'s return value checked?"); - return Err(E_FAIL); + return Err(MOZ_FD_LOCAL_ERROR("IFileDialog::GetResult: item", E_POINTER)); } // If the user chose a Win7 Library, resolve to the library's @@ -200,6 +224,7 @@ mozilla::Result<nsString, HRESULT> GetFolderResults(::IFileDialog* dialog) { RefPtr<IShellLibrary> shellLib; RefPtr<IShellItem> folderPath; MOZ_ENSURE_HRESULT_OK( + "CoCreateInstance(CLSID_ShellLibrary)", CoCreateInstance(CLSID_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLibrary, getter_AddRefs(shellLib))); @@ -211,7 +236,7 @@ mozilla::Result<nsString, HRESULT> GetFolderResults(::IFileDialog* dialog) { // get the folder's file system path nsAutoString str; - MOZ_ENSURE_HRESULT_OK(GetShellItemPath(item, str)); + MOZ_ENSURE_HRESULT_OK("GetShellItemPath", GetShellItemPath(item, str)); return str; } @@ -312,12 +337,22 @@ void LogProcessingError(LogModule* aModule, ipc::IProtocol* aCaller, template <typename Res, typename Action, size_t N> RefPtr<Promise<Res>> SpawnFileDialogThread(const char (&where)[N], Action action) { + { + using ActionRetT = std::invoke_result_t<Action>; + using Info = detail::DestructureResult<ActionRetT>; + + MOZ_ASSERT_SAME_TYPE( + typename Info::ErrorT, Error, + "supplied Action must return Result<T, filedialog::Err>"); + } + RefPtr<nsIThread> thread; { nsresult rv = NS_NewNamedThread("File Dialog", getter_AddRefs(thread), nullptr, {.isUiThread = true}); if (NS_FAILED(rv)) { - return Promise<Res>::CreateAndReject((HRESULT)rv, where); + return Promise<Res>::CreateAndReject( + MOZ_FD_LOCAL_ERROR("NS_NewNamedThread", (HRESULT)rv), where); } } // `thread` is single-purpose, and should not perform any additional work @@ -393,7 +428,7 @@ RefPtr<Promise<Res>> SpawnFileDialogThread(const char (&where)[N], } // Actually invoke the action and report the result. - Result<Res, HRESULT> val = action(); + Result<Res, Error> val = action(); if (val.isErr()) { promise->Reject(val.unwrapErr(), where); } else { @@ -407,16 +442,16 @@ RefPtr<Promise<Res>> SpawnFileDialogThread(const char (&where)[N], // For F returning `Result<T, E>`, yields the type `T`. template <typename F, typename... Args> using inner_result_of = - typename std::remove_reference_t<decltype(std::declval<F>()( - std::declval<Args>()...))>::ok_type; + typename detail::DestructureResult<std::invoke_result_t<F, Args...>>::OkT; template <typename ExtractorF, typename RetT = inner_result_of<ExtractorF, IFileDialog*>> auto SpawnPickerT(HWND parent, FileDialogType type, ExtractorF&& extractor, nsTArray<Command> commands) -> RefPtr<Promise<Maybe<RetT>>> { + using ActionRetT = Result<Maybe<RetT>, Error>; + return detail::SpawnFileDialogThread<Maybe<RetT>>( - __PRETTY_FUNCTION__, - [=, commands = std::move(commands)]() -> Result<Maybe<RetT>, HRESULT> { + __PRETTY_FUNCTION__, [=, commands = std::move(commands)]() -> ActionRetT { // On Win10, the picker doesn't support per-monitor DPI, so we create it // with our context set temporarily to system-dpi-aware. WinUtils::AutoSystemDpiAware dpiAwareness; @@ -424,15 +459,13 @@ auto SpawnPickerT(HWND parent, FileDialogType type, ExtractorF&& extractor, RefPtr<IFileDialog> dialog; MOZ_TRY_VAR(dialog, MakeFileDialog(type)); - if (HRESULT const rv = ApplyCommands(dialog, commands); FAILED(rv)) { - return mozilla::Err(rv); - } + MOZ_TRY(ApplyCommands(dialog, commands)); if (HRESULT const rv = dialog->Show(parent); FAILED(rv)) { if (rv == HRESULT_FROM_WIN32(ERROR_CANCELLED)) { - return Result<Maybe<RetT>, HRESULT>(Nothing()); + return ActionRetT{Nothing()}; } - return mozilla::Err(rv); + return mozilla::Err(MOZ_FD_LOCAL_ERROR("IFileDialog::Show", rv)); } RetT res; diff --git a/widget/windows/filedialog/WinFileDialogCommands.h b/widget/windows/filedialog/WinFileDialogCommands.h index ca4561a8f2..800ce832d5 100644 --- a/widget/windows/filedialog/WinFileDialogCommands.h +++ b/widget/windows/filedialog/WinFileDialogCommands.h @@ -19,27 +19,200 @@ struct IFileOpenDialog; namespace mozilla::widget::filedialog { +namespace detail { + +template <typename T, typename E, bool B> +struct PromiseInfo { + using ResolveT = T; + using RejectT = E; + constexpr static const bool IsExclusive = B; + using Promise = MozPromise<T, E, B>; +}; + +template <typename P> +auto DestructurePromiseImpl(P&&) { + // Debugging hint: A type in the instantiation chain (here named `P`) was + // expected to be a `RefPtr<MozPromise<...>>`, but was some other type. + static_assert(false, "expected P = RefPtr<MozPromise< ... >>"); +} + +template <typename T, typename E, bool B> +auto DestructurePromiseImpl(RefPtr<MozPromise<T, E, B>>&&) + -> PromiseInfo<T, E, B>; + +template <typename P> +using DestructurePromise = + std::decay_t<decltype(DestructurePromiseImpl(std::declval<P>()))>; + +template <typename T, typename E> +struct ResultInfo { + using OkT = T; + using ErrorT = E; +}; + +template <typename R> +auto DestructureResultImpl(R&&) { + // Debugging hint: A type in the instantiation chain (here named `R`) was + // expected to be a `mozilla::Result<...>`, but was some other type. + static_assert(false, "expected R = mozilla::Result< ... >"); +} + +template <typename T, typename E> +auto DestructureResultImpl(mozilla::Result<T, E>&&) -> ResultInfo<T, E>; + +template <typename R> +using DestructureResult = + std::decay_t<decltype(DestructureResultImpl(std::declval<R>()))>; + +#define MOZ_ASSERT_SAME_TYPE(T1, T2, ...) \ + static_assert(std::is_same_v<T1, T2>, ##__VA_ARGS__) +} // namespace detail + extern LazyLogModule sLogFileDialog; +// Simple struct for reporting errors. +struct Error { + enum Kind { + // This error's source was within the local (current) process. + LocalError, + // This error's source was within the remote host process, and it was + // reported via noncatastrophic channels. + RemoteError, + // This error was reported via the IPC subsystem. (This includes unexpected + // remote host-process crashes.) + IPCError, + }; + + // "Enum" denoting error-location. Members are in `VALID_STRINGS`, and have no + // name other than their string. + // + // (Note: under C++20, this could reasonably be replaced with an `nsString` + // alongside a check that all constructors are either a) consteval or b) from + // IPC.) + class Location { + uint32_t value; + constexpr explicit Location(uint32_t value) : value(value) {} + + // Valid locations for errors. (Indices do not need to remain stable between + // releases; but -- where meaningful -- string values themselves should, for + // ease of telemetry-aggregation.) + constexpr static std::string_view const VALID_STRINGS[] = { + "ApplyCommands", + "CoCreateInstance(CLSID_ShellLibrary)", + "GetFileResults: GetShellItemPath (1)", + "GetFileResults: GetShellItemPath (2)", + "GetShellItemPath", + "IFileDialog::GetFileTypeIndex", + "IFileDialog::GetOptions", + "IFileDialog::GetResult", + "IFileDialog::GetResult: item", + "IFileDialog::Show", + "IFileOpenDialog::GetResults", + "IFileOpenDialog::GetResults: items", + "IPC", + "IShellItemArray::GetCount", + "IShellItemArray::GetItemAt", + "MakeFileDialog", + "NS_NewNamedThread", + "Save + FOS_ALLOWMULTISELECT", + "ShowFilePicker", + "ShowFolderPicker", + "ShowRemote: UtilityProcessManager::GetSingleton", + "ShowRemote: invocation of CreateWinFileDialogActor", + "UtilityProcessManager::CreateWinFileDialogActor", + "internal IPC failure?", + }; + constexpr static size_t VALID_STRINGS_COUNT = + std::extent_v<decltype(VALID_STRINGS)>; + + // Prevent duplicates from occurring in VALID_STRINGS by forcing it to be + // sorted. (Note that std::is_sorted is not constexpr until C++20.) + static_assert( + []() { + for (size_t i = 0; i + 1 < VALID_STRINGS_COUNT; ++i) { + if (!(VALID_STRINGS[i] < VALID_STRINGS[i + 1])) { + return false; + } + } + return true; + }(), + "VALID_STRINGS should be ASCIIbetically sorted"); + + public: + constexpr uint32_t Serialize() const { return value; } + constexpr static Location Deserialize(uint32_t val) { + return Location{val}; + } + + public: + constexpr static Location npos() { return Location{~uint32_t(0)}; } + + constexpr bool IsValid() const { return value < VALID_STRINGS_COUNT; } + + constexpr std::string_view ToString() const { + return value < VALID_STRINGS_COUNT ? VALID_STRINGS[value] + : "<bad filedialog::Error::Location?>"; + } + constexpr static Location FromString(std::string_view str) { + for (uint32_t i = 0; i < VALID_STRINGS_COUNT; ++i) { + if (str == VALID_STRINGS[i]) return Location{i}; + } + return npos(); + } + + constexpr char const* c_str() const { return ToString().data(); } + }; + + // Where and how (run-time) this error occurred. + Kind kind; + // Where (compile-time) this error occurred. + Location where; + // Why (run-time) this error occurred. Probably an HRESULT. + uint32_t why; + + // `impl Debug for Kind` + static const char* KindName(Kind); +}; + +// Create a filedialog::Error, confirming at compile-time that the supplied +// where-string is valid. +#define MOZ_FD_ERROR(kind_, where_, why_) \ + ([](HRESULT why_arg_) -> ::mozilla::widget::filedialog::Error { \ + using Error = ::mozilla::widget::filedialog::Error; \ + constexpr static const Error::Location loc = \ + Error::Location::FromString(where_); \ + static_assert( \ + loc.IsValid(), \ + "filedialog::Error: location not found in Error::VALID_STRINGS"); \ + return Error{ \ + .kind = Error::kind_, .where = loc, .why = (uint32_t)why_arg_}; \ + }(why_)) + +// Create a filedialog::Error of kind LocalError (the usual case). +#define MOZ_FD_LOCAL_ERROR(where_, why_) MOZ_FD_ERROR(LocalError, where_, why_) + +template <typename R> +using Promise = MozPromise<R, Error, true>; + enum class FileDialogType : uint8_t { Open, Save }; // Create a file-dialog of the relevant type. Requires MSCOM to be initialized. -mozilla::Result<RefPtr<IFileDialog>, HRESULT> MakeFileDialog(FileDialogType); +mozilla::Result<RefPtr<IFileDialog>, Error> MakeFileDialog(FileDialogType); // Apply the selected commands to the IFileDialog, in preparation for showing // it. (The actual showing step is left to the caller.) -[[nodiscard]] HRESULT ApplyCommands(::IFileDialog*, - nsTArray<Command> const& commands); +mozilla::Result<Ok, Error> ApplyCommands(::IFileDialog*, + nsTArray<Command> const& commands); // Extract one or more results from the file-picker dialog. // // Requires that Show() has been called and has returned S_OK. -mozilla::Result<Results, HRESULT> GetFileResults(::IFileDialog*); +mozilla::Result<Results, Error> GetFileResults(::IFileDialog*); // Extract the chosen folder from the folder-picker dialog. // // Requires that Show() has been called and has returned S_OK. -mozilla::Result<nsString, HRESULT> GetFolderResults(::IFileDialog*); +mozilla::Result<nsString, Error> GetFolderResults(::IFileDialog*); namespace detail { // Log the error. If it's a notable error, kill the child process. @@ -48,9 +221,6 @@ void LogProcessingError(LogModule* aModule, ipc::IProtocol* aCaller, } // namespace detail -template <typename R> -using Promise = MozPromise<R, HRESULT, true>; - // Show a file-picker on another thread in the current process. RefPtr<Promise<Maybe<Results>>> SpawnFilePicker(HWND parent, FileDialogType type, diff --git a/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh b/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh index dd85942f24..352b46df17 100644 --- a/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh +++ b/widget/windows/filedialog/WinFileDialogCommandsDefn.ipdlh @@ -44,6 +44,19 @@ struct Results { uint32_t selectedFileTypeIndex; }; +// Homolog of filedialog::Err. (Always Err::Kind::RemoteError, by definition.) +struct RemoteError { + // An enum (`filedialog::Error::Location`) describing the compile-time location + // where the error was detected. + // + // (This value is validated at use-sites; if the child process sends a bad + // value, nothing of import will happen.) + uint32_t where; + // An error code describing the error itself more precisely. Its semantics + // depend on the context provided by `where`, but it's probably an HRESULT. + uint32_t why; +}; + } // namespace filedialog } // namespace widget } // namespace mozilla diff --git a/widget/windows/filedialog/WinFileDialogParent.cpp b/widget/windows/filedialog/WinFileDialogParent.cpp index 2c256a1506..329c72cc94 100644 --- a/widget/windows/filedialog/WinFileDialogParent.cpp +++ b/widget/windows/filedialog/WinFileDialogParent.cpp @@ -54,6 +54,86 @@ PWinFileDialogParent::nsresult WinFileDialogParent::BindToUtilityProcess( return NS_OK; } +// Convert the raw IPC promise-type to a filedialog::Promise. +template <typename T, typename Ex, size_t N> +static auto ConvertToFDPromise( + const char (&aMethod)[N], // __func__ + Ex&& extractor, + RefPtr<MozPromise<T, mozilla::ipc::ResponseRejectReason, true>> + aSrcPromise) { + // The extractor must produce a `mozilla::Result<..., Error>` from `T`. + using SrcResultInfo = detail::DestructureResult<std::invoke_result_t<Ex, T>>; + using ResolveT = typename SrcResultInfo::OkT; + static_assert(std::is_same_v<typename SrcResultInfo::ErrorT, Error>, + "expected T to be a Result<..., Error>"); + + using SrcPromiseT = MozPromise<T, mozilla::ipc::ResponseRejectReason, true>; + using DstPromiseT = MozPromise<ResolveT, Error, true>; + + RefPtr<DstPromiseT> ret = aSrcPromise->Then( + mozilla::GetCurrentSerialEventTarget(), aMethod, + + [extractor, aMethod](T&& val) { + mozilla::Result<ResolveT, Error> result = extractor(std::move(val)); + if (result.isOk()) { + return DstPromiseT::CreateAndResolve(result.unwrap(), aMethod); + } + return DstPromiseT::CreateAndReject(result.unwrapErr(), aMethod); + }, + [aMethod](typename mozilla::ipc::ResponseRejectReason&& val) { + return DstPromiseT::CreateAndReject( + MOZ_FD_ERROR(IPCError, "IPC", (uint32_t)val), aMethod); + }); + + return ret; +} + +template <typename Input, typename Output> +struct Extractor { + template <typename Input::Type tag_, Output const& (Input::*getter_)() const> + static auto get() { + return [](Input&& res) -> Result<Output, Error> { + if (res.type() == tag_) { + return (res.*getter_)(); + } + if (res.type() == Input::TRemoteError) { + RemoteError err = res.get_RemoteError(); + return Err(Error{.kind = Error::RemoteError, + .where = Error::Location::Deserialize(err.where()), + .why = err.why()}); + } + MOZ_ASSERT_UNREACHABLE("internal IPC failure?"); + return Err(MOZ_FD_ERROR(IPCError, "internal IPC failure?", E_FAIL)); + }; + } +}; + +[[nodiscard]] RefPtr<WinFileDialogParent::ShowFileDialogPromise> +WinFileDialogParent::ShowFileDialogImpl(HWND parent, const FileDialogType& type, + mozilla::Span<Command const> commands) { + auto inner_promise = PWinFileDialogParent::SendShowFileDialog( + reinterpret_cast<WindowsHandle>(parent), type, std::move(commands)); + + return ConvertToFDPromise( + __func__, + Extractor<FileResult, Maybe<Results>>::get< + FileResult::TMaybeResults, &FileResult::get_MaybeResults>(), + std::move(inner_promise)); +} + +[[nodiscard]] RefPtr<WinFileDialogParent::ShowFolderDialogPromise> +WinFileDialogParent::ShowFolderDialogImpl( + HWND parent, mozilla::Span<Command const> commands) { + auto inner_promise = PWinFileDialogParent::SendShowFolderDialog( + reinterpret_cast<WindowsHandle>(parent), std::move(commands)); + + return ConvertToFDPromise( + __func__, + Extractor<FolderResult, Maybe<nsString>>::get< + FolderResult::TMaybensString, &FolderResult::get_MaybensString>(), + std::move(inner_promise)); +} + void WinFileDialogParent::ProcessingError(Result aCode, const char* aReason) { detail::LogProcessingError(sLogFileDialog, this, aCode, aReason); } diff --git a/widget/windows/filedialog/WinFileDialogParent.h b/widget/windows/filedialog/WinFileDialogParent.h index a2c1197c55..a1eb547444 100644 --- a/widget/windows/filedialog/WinFileDialogParent.h +++ b/widget/windows/filedialog/WinFileDialogParent.h @@ -21,11 +21,14 @@ namespace mozilla::widget::filedialog { -class WinFileDialogParent : public PWinFileDialogParent { +class WinFileDialogParent final : private PWinFileDialogParent { public: using UtilityActorName = ::mozilla::UtilityActorName; NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WinFileDialogParent, override); + using ShowFileDialogPromise = Promise<Maybe<Results>>; + using ShowFolderDialogPromise = Promise<Maybe<nsString>>; + public: WinFileDialogParent(); nsresult BindToUtilityProcess( @@ -35,6 +38,16 @@ class WinFileDialogParent : public PWinFileDialogParent { return UtilityActorName::WindowsFileDialog; } + bool CanSend() const { return PWinFileDialogParent::CanSend(); } + void Close() { return PWinFileDialogParent::Close(); } + + [[nodiscard]] RefPtr<ShowFileDialogPromise> ShowFileDialogImpl( + HWND parent, const FileDialogType& type, + mozilla::Span<Command const> commands); + + [[nodiscard]] RefPtr<ShowFolderDialogPromise> ShowFolderDialogImpl( + HWND parent, mozilla::Span<Command const> commands); + private: ~WinFileDialogParent(); diff --git a/widget/windows/nsDataObj.cpp b/widget/windows/nsDataObj.cpp index 88a2a2ad09..60e2784f37 100644 --- a/widget/windows/nsDataObj.cpp +++ b/widget/windows/nsDataObj.cpp @@ -346,10 +346,10 @@ HRESULT nsDataObj::CreateStream(IStream** outStream) { pStream->AddRef(); - // query the requestingPrincipal from the transferable and add it to the new - // channel + // query the dataPrincipal from the transferable and add it to the new + // channel. nsCOMPtr<nsIPrincipal> requestingPrincipal = - mTransferable->GetRequestingPrincipal(); + mTransferable->GetDataPrincipal(); MOZ_ASSERT(requestingPrincipal, "can not create channel without a principal"); // Note that the cookieJarSettings could be null if the data object is for the diff --git a/widget/windows/nsFilePicker.cpp b/widget/windows/nsFilePicker.cpp index 2a45937988..89b10ce4ee 100644 --- a/widget/windows/nsFilePicker.cpp +++ b/widget/windows/nsFilePicker.cpp @@ -44,15 +44,17 @@ #include "mozilla/widget/filedialog/WinFileDialogCommands.h" #include "mozilla/widget/filedialog/WinFileDialogParent.h" +using mozilla::LogLevel; using mozilla::UniquePtr; using namespace mozilla::widget; +template <typename Res> +using FDPromise = filedialog::Promise<Res>; + UniquePtr<char16_t[], nsFilePicker::FreeDeleter> nsFilePicker::sLastUsedUnicodeDirectory; -using mozilla::LogLevel; - #define MAX_EXTENSION_LENGTH 10 /////////////////////////////////////////////////////////////////////////////// @@ -106,27 +108,29 @@ NS_IMETHODIMP nsFilePicker::Init( } namespace mozilla::detail { +using Error = mozilla::widget::filedialog::Error; + // Boilerplate for remotely showing a file dialog. template <typename ActionType, typename ReturnType = typename decltype(std::declval<ActionType>()( nullptr))::element_type::ResolveValueType> -static auto ShowRemote(ActionType&& action) - -> RefPtr<MozPromise<ReturnType, HRESULT, true>> { - using RetPromise = MozPromise<ReturnType, HRESULT, true>; +static auto ShowRemote(ActionType&& action) -> RefPtr<FDPromise<ReturnType>> { + using RetPromise = FDPromise<ReturnType>; - constexpr static const auto fail = []() { - return RetPromise::CreateAndReject(E_FAIL, __PRETTY_FUNCTION__); - }; +// "function-local" #define +#define FAIL(where_, why_) \ + return RetPromise::CreateAndReject(MOZ_FD_LOCAL_ERROR(where_, why_), \ + __PRETTY_FUNCTION__) auto mgr = mozilla::ipc::UtilityProcessManager::GetSingleton(); if (!mgr) { MOZ_ASSERT(false); - return fail(); + FAIL("ShowRemote: UtilityProcessManager::GetSingleton", E_POINTER); } auto wfda = mgr->CreateWinFileDialogActor(); if (!wfda) { - return fail(); + FAIL("ShowRemote: invocation of CreateWinFileDialogActor", E_POINTER); } using mozilla::widget::filedialog::sLogFileDialog; @@ -135,32 +139,70 @@ static auto ShowRemote(ActionType&& action) mozilla::GetMainThreadSerialEventTarget(), "nsFilePicker ShowRemote acquire", [action = std::forward<ActionType>(action)]( - filedialog::ProcessProxy const& p) -> RefPtr<RetPromise> { + filedialog::ProcessProxy p) -> RefPtr<RetPromise> { MOZ_LOG(sLogFileDialog, LogLevel::Info, ("nsFilePicker ShowRemote first callback: p = [%p]", p.get())); // false positive: not actually redundant // NOLINTNEXTLINE(readability-redundant-smartptr-get) - return action(p.get())->Then( - mozilla::GetMainThreadSerialEventTarget(), - "nsFilePicker ShowRemote call", - [p](ReturnType ret) { - return RetPromise::CreateAndResolve(std::move(ret), - __PRETTY_FUNCTION__); - }, - [](mozilla::ipc::ResponseRejectReason error) { - MOZ_LOG(sLogFileDialog, LogLevel::Error, - ("IPC call rejected: %zu", size_t(error))); - return fail(); + auto promise = action(p.get()); + return promise->Map( + mozilla::GetMainThreadSerialEventTarget(), __func__, + [p = std::move(p)](typename RetPromise::ResolveValueType&& val) { + // explicitly retain the ProcessProxy until at least this point + return std::move(val); }); }, [](nsresult error) -> RefPtr<RetPromise> { MOZ_LOG(sLogFileDialog, LogLevel::Error, ("could not acquire WinFileDialog: %zu", size_t(error))); - return fail(); + // TODO: pipe more data up from utility-process creation + FAIL("UtilityProcessManager::CreateWinFileDialogActor", + (uint32_t)error); + }); + +#undef FAIL +} + +namespace { + +static RefPtr<FDPromise<Maybe<filedialog::Results>>> ShowFilePickerRemote( + HWND parent, filedialog::FileDialogType type, + nsTArray<filedialog::Command> const& commands) { + using mozilla::widget::filedialog::sLogFileDialog; + return mozilla::detail::ShowRemote( + [parent, type, + commands = commands.Clone()](filedialog::WinFileDialogParent* p) { + MOZ_LOG(sLogFileDialog, LogLevel::Info, + ("%s: p = [%p]", __PRETTY_FUNCTION__, p)); + return p->ShowFileDialogImpl(parent, type, commands); }); } +static RefPtr<FDPromise<Maybe<nsString>>> ShowFolderPickerRemote( + HWND parent, nsTArray<filedialog::Command> const& commands) { + using mozilla::widget::filedialog::sLogFileDialog; + return mozilla::detail::ShowRemote([parent, commands = commands.Clone()]( + filedialog::WinFileDialogParent* p) { + MOZ_LOG(sLogFileDialog, LogLevel::Info, + ("%s: p = [%p]", __PRETTY_FUNCTION__, p)); + return p->ShowFolderDialogImpl(parent, commands); + }); +} + +static RefPtr<FDPromise<Maybe<filedialog::Results>>> ShowFilePickerLocal( + HWND parent, filedialog::FileDialogType type, + nsTArray<filedialog::Command> const& commands) { + return filedialog::SpawnFilePicker(parent, type, commands.Clone()); +} + +static RefPtr<FDPromise<Maybe<nsString>>> ShowFolderPickerLocal( + HWND parent, nsTArray<filedialog::Command> const& commands) { + return filedialog::SpawnFolderPicker(parent, commands.Clone()); +} + +} // namespace + // fd_async // // Wrapper-namespace for the AsyncExecute() and AsyncAll() functions. @@ -296,31 +338,83 @@ static void RecordFailure(uint64_t (&&time)[3], HRESULT hrRemote, } } // namespace telemetry + +/* N.B.: L and R stand for Local and Remote, not just Left and Right */ +template <typename FnL, typename FnR, typename... Args> +struct AsyncExecuteInfo { + template <typename T> + using DestructurePromise = widget::filedialog::detail::DestructurePromise<T>; + + using Unit = ::mozilla::Ok; + + using RetL = std::invoke_result_t<FnL, Args...>; + using RetR = std::invoke_result_t<FnR, Args...>; + + using InfoL = DestructurePromise<RetL>; + using InfoR = DestructurePromise<RetR>; + + MOZ_ASSERT_SAME_TYPE( + typename InfoL::ResolveT, typename InfoR::ResolveT, + "local and remote promises must have identical resolve-types"); + + // At present, the local and remote promises have the same type, but this + // isn't logically necessary. (In particular, a future refactor may remove the + // redundant `.kind` from the local promises' return types.) + MOZ_ASSERT_SAME_TYPE(typename InfoL::RejectT, filedialog::Error, + "local promise must reject with a filedialog::Error"); + + MOZ_ASSERT_SAME_TYPE(typename InfoR::RejectT, filedialog::Error, + "remote promise must reject with a filedialog::Error"); + + using ResolveT = typename InfoL::ResolveT; + using PromiseT = MozPromise<ResolveT, Unit, true>; + + using RetT = RefPtr<PromiseT>; +}; + } // namespace details -// Invoke either or both of a "do locally" and "do remotely" function with the -// provided arguments, depending on the relevant preference-value and whether -// or not the remote version fails. +// Invoke either or both of a promise-returning "do locally" and "do remotely" +// function with the provided arguments, depending on the relevant preference's +// value and on whether or not the remote version fails (returns a rejection- +// promise). // -// Both functions must be asynchronous, returning a `RefPtr<MozPromise<...>>`. -// "Failure" is defined as the promise being rejected. +// Both provided functions must return a `RefPtr<filedialog::MozPromise<T>>`. As +// `AsyncExecute` reports failures itself, its rejection-type is `()`. template <typename Fn1, typename Fn2, typename... Args> -static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args) - -> std::invoke_result_t<Fn1, Args...> { +static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args) -> + typename details::AsyncExecuteInfo<Fn1, Fn2, Args...>::RetT { using namespace details; + using Info = AsyncExecuteInfo<Fn1, Fn2, Args...>; - static_assert(std::is_same_v<std::invoke_result_t<Fn1, Args...>, - std::invoke_result_t<Fn2, Args...>>); - using PromiseT = typename std::invoke_result_t<Fn1, Args...>::element_type; + using ResolveT = typename Info::ResolveT; + using PromiseT = typename Info::PromiseT; + using LPromiseT = typename Info::InfoL::Promise; + using RPromiseT = typename Info::InfoR::Promise; constexpr static char kFunctionName[] = "LocalAndOrRemote::AsyncExecute"; switch (GetStrategy()) { - case Local: - return local(args...); + case Local: { + return local(args...)->MapErr( + NS_GetCurrentThread(), __func__, [](Error const& err) { + MOZ_ASSERT(err.kind == Error::LocalError); + MOZ_LOG(filedialog::sLogFileDialog, LogLevel::Info, + ("local file-dialog failed: where=%s, why=%08" PRIX32, + err.where.c_str(), err.why)); + return Ok(); + }); + } case Remote: - return remote(args...); + return remote(args...)->MapErr( + NS_GetCurrentThread(), __func__, [](Error const& err) { + MOZ_LOG( + filedialog::sLogFileDialog, LogLevel::Info, + ("remote file-dialog failed: kind=%s, where=%s, why=%08" PRIX32, + Error::KindName(err.kind), err.where.c_str(), err.why)); + return Ok(); + }); case RemoteWithFallback: // more complicated; continue below @@ -337,34 +431,40 @@ static auto AsyncExecute(Fn1 local, Fn2 remote, Args const&... args) return remote(args...)->Then( NS_GetCurrentThread(), kFunctionName, - [t0](typename PromiseT::ResolveValueType result) -> RefPtr<PromiseT> { + [t0](typename RPromiseT::ResolveValueType result) -> RefPtr<PromiseT> { // success; stop here auto const t1 = GetTime(); // record success telemetry::RecordSuccess({t0, t1}); - return PromiseT::CreateAndResolve(result, kFunctionName); + return PromiseT::CreateAndResolve(std::move(result), kFunctionName); }, // initialized lambda pack captures are C++20 (clang 9, gcc 9); // `make_tuple` is just a C++17 workaround [=, tuple = std::make_tuple(Copy(args)...)]( - typename PromiseT::RejectValueType err) mutable -> RefPtr<PromiseT> { + typename RPromiseT::RejectValueType err) mutable -> RefPtr<PromiseT> { // failure; record time auto const t1 = GetTime(); - HRESULT const hrRemote = err; + // TODO: also propagate `err.where` into telemetry + HRESULT const hrRemote = err.why; // retry locally... auto p0 = std::apply(local, std::move(tuple)); // ...then record the telemetry event return p0->Then( NS_GetCurrentThread(), kFunctionName, - [t0, t1, - hrRemote](typename PromiseT::ResolveOrRejectValue const& val) + [t0, t1, hrRemote](typename LPromiseT::ResolveOrRejectValue&& val) -> RefPtr<PromiseT> { auto const t2 = GetTime(); - HRESULT const hrLocal = val.IsReject() ? val.RejectValue() : S_OK; + HRESULT const hrLocal = + val.IsReject() ? (HRESULT)val.RejectValue().why : S_OK; telemetry::RecordFailure({t0, t1, t2}, hrRemote, hrLocal); - return PromiseT::CreateAndResolveOrReject(val, kFunctionName); + using V = typename PromiseT::ResolveOrRejectValue; + return PromiseT::CreateAndResolveOrReject( + val.IsResolve() + ? V::MakeResolve(std::move(val).ResolveValue()) + : V::MakeReject(Ok{}), + kFunctionName); }); }); } @@ -393,45 +493,6 @@ using fd_async::AsyncExecute; } // namespace mozilla::detail -/* static */ -nsFilePicker::FPPromise<filedialog::Results> nsFilePicker::ShowFilePickerRemote( - HWND parent, filedialog::FileDialogType type, - nsTArray<filedialog::Command> const& commands) { - using mozilla::widget::filedialog::sLogFileDialog; - return mozilla::detail::ShowRemote( - [parent, type, - commands = commands.Clone()](filedialog::WinFileDialogParent* p) { - MOZ_LOG(sLogFileDialog, LogLevel::Info, - ("%s: p = [%p]", __PRETTY_FUNCTION__, p)); - return p->SendShowFileDialog((uintptr_t)parent, type, commands); - }); -} - -/* static */ -nsFilePicker::FPPromise<nsString> nsFilePicker::ShowFolderPickerRemote( - HWND parent, nsTArray<filedialog::Command> const& commands) { - using mozilla::widget::filedialog::sLogFileDialog; - return mozilla::detail::ShowRemote([parent, commands = commands.Clone()]( - filedialog::WinFileDialogParent* p) { - MOZ_LOG(sLogFileDialog, LogLevel::Info, - ("%s: p = [%p]", __PRETTY_FUNCTION__, p)); - return p->SendShowFolderDialog((uintptr_t)parent, commands); - }); -} - -/* static */ -nsFilePicker::FPPromise<filedialog::Results> nsFilePicker::ShowFilePickerLocal( - HWND parent, filedialog::FileDialogType type, - nsTArray<filedialog::Command> const& commands) { - return filedialog::SpawnFilePicker(parent, type, commands.Clone()); -} - -/* static */ -nsFilePicker::FPPromise<nsString> nsFilePicker::ShowFolderPickerLocal( - HWND parent, nsTArray<filedialog::Command> const& commands) { - return filedialog::SpawnFolderPicker(parent, commands.Clone()); -} - /* * Folder picker invocation */ @@ -447,16 +508,8 @@ nsFilePicker::FPPromise<nsString> nsFilePicker::ShowFolderPickerLocal( * - resolves to false if the dialog was cancelled by the user; * - is rejected with the associated HRESULT if some error occurred. */ -RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFolderPicker( - const nsString& aInitialDir) { - using Promise = mozilla::MozPromise<bool, HRESULT, true>; - constexpr static auto Ok = [](bool val) { - return Promise::CreateAndResolve(val, "nsFilePicker::ShowFolderPicker"); - }; - constexpr static auto NotOk = [](HRESULT val = E_FAIL) { - return Promise::CreateAndReject(val, "nsFilePicker::ShowFolderPicker"); - }; - +RefPtr<mozilla::MozPromise<bool, nsFilePicker::Unit, true>> +nsFilePicker::ShowFolderPicker(const nsString& aInitialDir) { namespace fd = ::mozilla::widget::filedialog; nsTArray<fd::Command> commands = { fd::SetOptions(FOS_PICKFOLDERS), @@ -474,23 +527,18 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFolderPicker( ScopedRtlShimWindow shim(mParentWidget.get()); AutoWidgetPickerState awps(mParentWidget); - return mozilla::detail::AsyncExecute(&ShowFolderPickerLocal, - &ShowFolderPickerRemote, shim.get(), - commands) - ->Then( - NS_GetCurrentThread(), __PRETTY_FUNCTION__, - [self = RefPtr(this), shim = std::move(shim), - awps = std::move(awps)](Maybe<nsString> val) { - if (val) { - self->mUnicodeFile = val.extract(); - return Ok(true); - } - return Ok(false); - }, - [](HRESULT err) { - NS_WARNING("ShowFolderPicker failed"); - return NotOk(err); - }); + return mozilla::detail::AsyncExecute(&mozilla::detail::ShowFolderPickerLocal, + &mozilla::detail::ShowFolderPickerRemote, + shim.get(), commands) + ->Map(NS_GetCurrentThread(), __PRETTY_FUNCTION__, + [self = RefPtr(this), shim = std::move(shim), + awps = std::move(awps)](Maybe<nsString> val) { + if (val) { + self->mUnicodeFile = val.extract(); + return true; + } + return false; + }); } /* @@ -508,16 +556,16 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFolderPicker( * - resolves to false if the dialog was cancelled by the user; * - is rejected with the associated HRESULT if some error occurred. */ -RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker( - const nsString& aInitialDir) { +RefPtr<mozilla::MozPromise<bool, nsFilePicker::Unit, true>> +nsFilePicker::ShowFilePicker(const nsString& aInitialDir) { AUTO_PROFILER_LABEL("nsFilePicker::ShowFilePicker", OTHER); - using Promise = mozilla::MozPromise<bool, HRESULT, true>; + using Promise = mozilla::MozPromise<bool, Unit, true>; constexpr static auto Ok = [](bool val) { return Promise::CreateAndResolve(val, "nsFilePicker::ShowFilePicker"); }; - constexpr static auto NotOk = [](HRESULT val = E_FAIL) { - return Promise::CreateAndReject(val, "nsFilePicker::ShowFilePicker"); + constexpr static auto NotOk = []() { + return Promise::CreateAndReject(Unit(), "nsFilePicker::ShowFilePicker"); }; namespace fd = ::mozilla::widget::filedialog; @@ -551,7 +599,7 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker( case modeGetFolder: MOZ_ASSERT(false, "file-picker opened in directory-picker mode"); - return NotOk(E_FAIL); + return NotOk(); } commands.AppendElement(fd::SetOptions(fos)); @@ -605,7 +653,8 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker( auto type = mMode == modeSave ? FileDialogType::Save : FileDialogType::Open; auto promise = mozilla::detail::AsyncExecute( - &ShowFilePickerLocal, &ShowFilePickerRemote, shim.get(), type, commands); + &mozilla::detail::ShowFilePickerLocal, + &mozilla::detail::ShowFilePickerRemote, shim.get(), type, commands); return promise->Then( mozilla::GetMainThreadSerialEventTarget(), __PRETTY_FUNCTION__, @@ -641,9 +690,9 @@ RefPtr<mozilla::MozPromise<bool, HRESULT, true>> nsFilePicker::ShowFilePicker( return Ok(true); }, - [](HRESULT err) { + [](Unit err) { NS_WARNING("ShowFilePicker failed"); - return NotOk(err); + return NotOk(); }); } @@ -752,43 +801,29 @@ nsFilePicker::CheckContentAnalysisService() { return promise; }; - // Since getting the files to analyze might be asynchronous, use a MozPromise - // to unify the logic below. - auto getFilesToAnalyzePromise = mozilla::MakeRefPtr<mozilla::MozPromise< - nsTArray<mozilla::PathString>, nsresult, true>::Private>(__func__); + nsCOMArray<nsIFile> files; if (mMode == modeGetFolder) { - nsCOMPtr<nsISupports> tmp; - nsresult rv = GetDomFileOrDirectory(getter_AddRefs(tmp)); + nsCOMPtr<nsIFile> file; + nsresult rv = GetFile(getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { - getFilesToAnalyzePromise->Reject(rv, __func__); return nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv, __func__); } - auto* directory = static_cast<mozilla::dom::Directory*>(tmp.get()); - mozilla::dom::OwningFileOrDirectory owningDirectory; - owningDirectory.SetAsDirectory() = directory; - nsTArray<mozilla::dom::OwningFileOrDirectory> directoryArray{ - std::move(owningDirectory)}; - - mozilla::ErrorResult error; - RefPtr<mozilla::dom::GetFilesHelper> helper = - mozilla::dom::GetFilesHelper::Create(directoryArray, true, error); - rv = error.StealNSResult(); + nsCOMPtr<nsIDirectoryEnumerator> iter; + rv = file->GetDirectoryEntries(getter_AddRefs(iter)); if (NS_WARN_IF(NS_FAILED(rv))) { - getFilesToAnalyzePromise->Reject(rv, __func__); return nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv, __func__); } - auto getFilesCallback = mozilla::MakeRefPtr<GetFilesInDirectoryCallback>( - getFilesToAnalyzePromise); - helper->AddCallback(getFilesCallback); + nsCOMPtr<nsIFile> entry; + while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(entry))) && entry) { + files.AppendElement(entry); + } } else { - nsCOMArray<nsIFile> files; if (!mUnicodeFile.IsEmpty()) { nsCOMPtr<nsIFile> file; rv = GetFile(getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { - getFilesToAnalyzePromise->Reject(rv, __func__); return nsFilePicker::ContentAnalysisResponse::CreateAndReject(rv, __func__); } @@ -796,22 +831,13 @@ nsFilePicker::CheckContentAnalysisService() { } else { files.AppendElements(mFiles); } - nsTArray<mozilla::PathString> paths(files.Length()); - std::transform(files.begin(), files.end(), MakeBackInserter(paths), - [](auto* entry) { return entry->NativePath(); }); - getFilesToAnalyzePromise->Resolve(std::move(paths), __func__); } + nsTArray<mozilla::PathString> paths(files.Length()); + std::transform(files.begin(), files.end(), MakeBackInserter(paths), + [](auto* entry) { return entry->NativePath(); }); - return getFilesToAnalyzePromise->Then( - mozilla::GetMainThreadSerialEventTarget(), __func__, - [processOneItem](nsTArray<mozilla::PathString> aPaths) mutable { - return mozilla::detail::AsyncAll<mozilla::PathString>(std::move(aPaths), - processOneItem); - }, - [](nsresult aError) { - return nsFilePicker::ContentAnalysisResponse::CreateAndReject(aError, - __func__); - }); + return mozilla::detail::AsyncAll<mozilla::PathString>(std::move(paths), + processOneItem); }; /////////////////////////////////////////////////////////////////////////////// @@ -891,10 +917,8 @@ nsresult nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) { callback->Done(retValue); }, - [callback = RefPtr(aCallback)](HRESULT err) { - using mozilla::widget::filedialog::sLogFileDialog; - MOZ_LOG(sLogFileDialog, LogLevel::Error, - ("nsFilePicker: Show failed with hr=0x%08lX", err)); + [callback = RefPtr(aCallback)](Unit _) { + // logging already handled callback->Done(ResultCode::returnCancel); }); diff --git a/widget/windows/nsFilePicker.h b/widget/windows/nsFilePicker.h index 59108bc0dd..5a4272d30f 100644 --- a/widget/windows/nsFilePicker.h +++ b/widget/windows/nsFilePicker.h @@ -56,8 +56,6 @@ class nsFilePicker final : public nsBaseWinFilePicker { using Maybe = mozilla::Maybe<T>; template <typename T> using Result = mozilla::Result<T, HRESULT>; - template <typename Res> - using FPPromise = RefPtr<mozilla::MozPromise<Maybe<Res>, HRESULT, true>>; using Command = mozilla::widget::filedialog::Command; using Results = mozilla::widget::filedialog::Results; @@ -89,23 +87,12 @@ class nsFilePicker final : public nsBaseWinFilePicker { NS_IMETHOD Open(nsIFilePickerShownCallback* aCallback) override; private: - RefPtr<mozilla::MozPromise<bool, HRESULT, true>> ShowFolderPicker( + using Unit = mozilla::Ok; + RefPtr<mozilla::MozPromise<bool, Unit, true>> ShowFolderPicker( const nsString& aInitialDir); - RefPtr<mozilla::MozPromise<bool, HRESULT, true>> ShowFilePicker( + RefPtr<mozilla::MozPromise<bool, Unit, true>> ShowFilePicker( const nsString& aInitialDir); - // Show the dialog out-of-process. - static FPPromise<Results> ShowFilePickerRemote( - HWND aParent, FileDialogType type, nsTArray<Command> const& commands); - static FPPromise<nsString> ShowFolderPickerRemote( - HWND aParent, nsTArray<Command> const& commands); - - // Show the dialog in-process. - static FPPromise<Results> ShowFilePickerLocal( - HWND aParent, FileDialogType type, nsTArray<Command> const& commands); - static FPPromise<nsString> ShowFolderPickerLocal( - HWND aParent, nsTArray<Command> const& commands); - void ClearFiles(); using ContentAnalysisResponse = mozilla::MozPromise<bool, nsresult, true>; RefPtr<ContentAnalysisResponse> CheckContentAnalysisService(); diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 7ef968baf6..b988fb4ec5 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -481,6 +481,7 @@ mozilla::Maybe<nsUXThemeClass> nsNativeThemeWin::GetThemeClass( case StyleAppearance::Button: return Some(eUXButton); case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: return Some(eUXEdit); @@ -500,12 +501,7 @@ mozilla::Maybe<nsUXThemeClass> nsNativeThemeWin::GetThemeClass( case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: return Some(eUXCombobox); - case StyleAppearance::Treeheadercell: - return Some(eUXHeader); case StyleAppearance::Listbox: - case StyleAppearance::Treeview: - case StyleAppearance::Treetwistyopen: - case StyleAppearance::Treeitem: return Some(eUXListview); default: return Nothing(); @@ -594,6 +590,7 @@ nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, return NS_OK; } case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: { ElementState elementState = GetContentState(aFrame, aAppearance); @@ -716,7 +713,6 @@ nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, } return NS_OK; } - case StyleAppearance::Treeview: case StyleAppearance::Listbox: { aPart = TREEVIEW_BODY; aState = TS_NORMAL; @@ -753,17 +749,6 @@ nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame, return NS_OK; } - case StyleAppearance::Treeheadercell: { - aPart = 1; - if (!aFrame) { - aState = TS_NORMAL; - return NS_OK; - } - - aState = StandardGetState(aFrame, aAppearance, true); - - return NS_OK; - } case StyleAppearance::MenulistButton: case StyleAppearance::Menulist: { nsIContent* content = aFrame->GetContent(); @@ -956,6 +941,7 @@ RENDER_AGAIN: DrawThemeBackground(theme, hdc, part, state, &contentRect, &clipRect); } else if (aAppearance == StyleAppearance::NumberInput || + aAppearance == StyleAppearance::PasswordInput || aAppearance == StyleAppearance::Textfield || aAppearance == StyleAppearance::Textarea) { DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect); @@ -1088,6 +1074,7 @@ LayoutDeviceIntMargin nsNativeThemeWin::GetWidgetBorder( } if (aFrame && (aAppearance == StyleAppearance::NumberInput || + aAppearance == StyleAppearance::PasswordInput || aAppearance == StyleAppearance::Textfield || aAppearance == StyleAppearance::Textarea)) { nsIContent* content = aFrame->GetContent(); @@ -1128,6 +1115,7 @@ bool nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext, * added, see bug 430212) */ if (aAppearance == StyleAppearance::NumberInput || + aAppearance == StyleAppearance::PasswordInput || aAppearance == StyleAppearance::Textfield || aAppearance == StyleAppearance::Textarea) { aResult->top = aResult->bottom = 2; @@ -1236,12 +1224,12 @@ LayoutDeviceIntSize nsNativeThemeWin::GetMinimumWidgetSize( switch (aAppearance) { case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Progresschunk: case StyleAppearance::Tabpanels: case StyleAppearance::Tabpanel: case StyleAppearance::Listbox: - case StyleAppearance::Treeview: return {}; // Don't worry about it. default: break; @@ -1386,6 +1374,7 @@ bool nsNativeThemeWin::ThemeDrawsFocusForWidget(nsIFrame* aFrame, case StyleAppearance::Textarea: case StyleAppearance::Textfield: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: return true; default: return false; @@ -1438,6 +1427,7 @@ bool nsNativeThemeWin::ClassicThemeSupportsWidget(nsIFrame* aFrame, switch (aAppearance) { case StyleAppearance::Button: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Range: @@ -1445,7 +1435,6 @@ bool nsNativeThemeWin::ClassicThemeSupportsWidget(nsIFrame* aFrame, case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: case StyleAppearance::Listbox: - case StyleAppearance::Treeview: case StyleAppearance::ProgressBar: case StyleAppearance::Progresschunk: case StyleAppearance::Tab: @@ -1465,11 +1454,11 @@ LayoutDeviceIntMargin nsNativeThemeWin::ClassicGetWidgetBorder( result.top = result.left = result.bottom = result.right = 2; break; case StyleAppearance::Listbox: - case StyleAppearance::Treeview: case StyleAppearance::Menulist: case StyleAppearance::MenulistButton: case StyleAppearance::Tab: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: result.top = result.left = result.bottom = result.right = 2; @@ -1516,8 +1505,8 @@ LayoutDeviceIntSize nsNativeThemeWin::ClassicGetMinimumWidgetSize( case StyleAppearance::MenulistButton: case StyleAppearance::Button: case StyleAppearance::Listbox: - case StyleAppearance::Treeview: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Progresschunk: @@ -1574,8 +1563,8 @@ nsresult nsNativeThemeWin::ClassicGetThemePartAndState( return NS_OK; } case StyleAppearance::Listbox: - case StyleAppearance::Treeview: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Menulist: @@ -1767,6 +1756,7 @@ RENDER_AGAIN: } // Draw controls with 2px 3D inset border case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Listbox: @@ -1787,15 +1777,6 @@ RENDER_AGAIN: break; } - case StyleAppearance::Treeview: { - // Draw inset edge - ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); - - // Fill in window color background - ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW + 1)); - - break; - } // Draw 3D face background controls case StyleAppearance::ProgressBar: // Draw 3D border @@ -1903,6 +1884,7 @@ uint32_t nsNativeThemeWin::GetWidgetNativeDrawingFlags( switch (aAppearance) { case StyleAppearance::Button: case StyleAppearance::NumberInput: + case StyleAppearance::PasswordInput: case StyleAppearance::Textfield: case StyleAppearance::Textarea: case StyleAppearance::Menulist: diff --git a/widget/windows/nsUXThemeData.cpp b/widget/windows/nsUXThemeData.cpp index ce8b0f3479..88546b01c6 100644 --- a/widget/windows/nsUXThemeData.cpp +++ b/widget/windows/nsUXThemeData.cpp @@ -72,8 +72,6 @@ const wchar_t* nsUXThemeData::GetClassName(nsUXThemeClass cls) { return L"Trackbar"; case eUXCombobox: return L"Combobox"; - case eUXHeader: - return L"Header"; case eUXListview: return L"Listview"; case eUXMenu: diff --git a/widget/windows/nsUXThemeData.h b/widget/windows/nsUXThemeData.h index 24fe07d128..b59c2aae04 100644 --- a/widget/windows/nsUXThemeData.h +++ b/widget/windows/nsUXThemeData.h @@ -24,7 +24,6 @@ enum nsUXThemeClass { eUXTab, eUXTrackbar, eUXCombobox, - eUXHeader, eUXListview, eUXMenu, eUXNumClasses diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 22d20d099e..ac393e8b09 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -166,7 +166,6 @@ #include "mozilla/StaticPrefs_ui.h" #include "mozilla/StaticPrefs_widget.h" #include "nsNativeAppSupportWin.h" -#include "mozilla/browser/NimbusFeatures.h" #include "nsIGfxInfo.h" #include "nsUXThemeConstants.h" @@ -188,7 +187,6 @@ # include "mozilla/a11y/DocAccessible.h" # include "mozilla/a11y/LazyInstantiator.h" # include "mozilla/a11y/Platform.h" -# include "mozilla/StaticPrefs_accessibility.h" # if !defined(WINABLEAPI) # include <winable.h> # endif // !defined(WINABLEAPI) @@ -348,6 +346,12 @@ static SystemTimeConverter<DWORD>& TimeConverter() { return timeConverterSingleton; } +static const wchar_t* GetMainWindowClass(); +static const wchar_t* ChooseWindowClass(mozilla::widget::WindowType); +// This method registers the given window class, and returns the class name. +static void RegisterWindowClass(const wchar_t* aClassName, UINT aExtraStyle, + LPWSTR aIconID); + // Global event hook for window cloaking. Never deregistered. // - `Nothing` if not yet set. // - `Some(nullptr)` if no attempt should be made to set it. @@ -990,13 +994,11 @@ nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, } if (aInitData->mIsPrivate) { - if (NimbusFeatures::GetBool("majorRelease2022"_ns, - "feltPrivacyWindowSeparation"_ns, true) && - // Although permanent Private Browsing mode is indeed Private Browsing, - // we choose to make it look like regular Firefox in terms of the icon - // it uses (which also means we shouldn't use the Private Browsing - // AUMID). - !StaticPrefs::browser_privatebrowsing_autostart()) { + // Although permanent Private Browsing mode is indeed Private Browsing, + // we choose to make it look like regular Firefox in terms of the icon + // it uses (which also means we shouldn't use the Private Browsing + // AUMID). + if (!StaticPrefs::browser_privatebrowsing_autostart()) { RefPtr<IPropertyStore> pPropStore; if (!FAILED(SHGetPropertyStoreForWindow(mWnd, IID_IPropertyStore, getter_AddRefs(pPropStore)))) { @@ -1189,40 +1191,28 @@ void nsWindow::Destroy() { * **************************************************************/ -/* static */ -const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName, - UINT aExtraStyle, LPWSTR aIconID) { - WNDCLASSW wc; +static void RegisterWindowClass(const wchar_t* aClassName, UINT aExtraStyle, + LPWSTR aIconID) { + WNDCLASSW wc = {}; if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName, &wc)) { // already registered - return aClassName; + return; } wc.style = CS_DBLCLKS | aExtraStyle; wc.lpfnWndProc = WinUtils::NonClientDpiScalingDefWindowProcW; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; wc.hInstance = nsToolkit::mDllInstance; wc.hIcon = aIconID ? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID) : nullptr; - wc.hCursor = nullptr; - wc.hbrBackground = nullptr; - wc.lpszMenuName = nullptr; wc.lpszClassName = aClassName; - if (!::RegisterClassW(&wc)) { - // For older versions of Win32 (i.e., not XP), the registration may - // fail with aExtraStyle, so we have to re-register without it. - wc.style = CS_DBLCLKS; - ::RegisterClassW(&wc); - } - return aClassName; + // Failures are ignored as they are handled when ::CreateWindow fails + ::RegisterClassW(&wc); } static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512); -/* static */ -const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType) { +static const wchar_t* ChooseWindowClass(WindowType aWindowType) { const wchar_t* className = [aWindowType] { switch (aWindowType) { case WindowType::Invisible: @@ -1235,7 +1225,8 @@ const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType) { return GetMainWindowClass(); } }(); - return RegisterWindowClass(className, 0, gStockApplicationIcon); + RegisterWindowClass(className, 0, gStockApplicationIcon); + return className; } /************************************************************** @@ -1246,24 +1237,62 @@ const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType) { * **************************************************************/ +const DWORD kTitlebarItemsWindowStyles = + WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; +const DWORD kAllBorderStyles = + kTitlebarItemsWindowStyles | WS_THICKFRAME | WS_DLGFRAME; + +static DWORD WindowStylesRemovedForBorderStyle(BorderStyle aStyle) { + if (aStyle == BorderStyle::Default || aStyle == BorderStyle::All) { + return 0; + } + if (aStyle == BorderStyle::None) { + return kAllBorderStyles; + } + DWORD toRemove = 0; + if (!(aStyle & BorderStyle::Border)) { + toRemove |= WS_BORDER; + } + if (!(aStyle & BorderStyle::Title)) { + toRemove |= WS_DLGFRAME; + } + if (!(aStyle & (BorderStyle::Menu | BorderStyle::Close))) { + // Looks like getting rid of the system menu also does away with the close + // box. So, we only get rid of the system menu and the close box if you + // want neither. How does the Windows "Dialog" window class get just + // closebox and no sysmenu? Who knows. + toRemove |= WS_SYSMENU; + } + if (!(aStyle & BorderStyle::ResizeH)) { + toRemove |= WS_THICKFRAME; + } + if (!(aStyle & BorderStyle::Minimize)) { + toRemove |= WS_MINIMIZEBOX; + } + if (!(aStyle & BorderStyle::Maximize)) { + toRemove |= WS_MAXIMIZEBOX; + } + return toRemove; +} + // Return nsWindow styles DWORD nsWindow::WindowStyle() { DWORD style; - switch (mWindowType) { case WindowType::Child: style = WS_OVERLAPPED; break; case WindowType::Dialog: - style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_3DLOOK | + style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_MODALFRAME | WS_CLIPCHILDREN; - if (mBorderStyle != BorderStyle::Default) + if (mBorderStyle != BorderStyle::Default) { style |= WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + } break; case WindowType::Popup: - style = WS_POPUP | WS_OVERLAPPED; + style = WS_OVERLAPPED | WS_POPUP; break; default: @@ -1272,48 +1301,12 @@ DWORD nsWindow::WindowStyle() { case WindowType::TopLevel: case WindowType::Invisible: - style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | - WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN; + style = WS_OVERLAPPED | WS_CLIPCHILDREN | WS_DLGFRAME | WS_BORDER | + WS_THICKFRAME | kTitlebarItemsWindowStyles; break; } - if (mBorderStyle != BorderStyle::Default && - mBorderStyle != BorderStyle::All) { - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & BorderStyle::Border)) - style &= ~WS_BORDER; - - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & BorderStyle::Title)) { - style &= ~WS_DLGFRAME; - } - - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & BorderStyle::Close)) - style &= ~0; - // XXX The close box can only be removed by changing the window class, - // as far as I know --- roc+moz@cs.cmu.edu - - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & (BorderStyle::Menu | BorderStyle::Close))) - style &= ~WS_SYSMENU; - // Looks like getting rid of the system menu also does away with the - // close box. So, we only get rid of the system menu if you want neither it - // nor the close box. How does the Windows "Dialog" window class get just - // closebox and no sysmenu? Who knows. - - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & BorderStyle::ResizeH)) - style &= ~WS_THICKFRAME; - - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & BorderStyle::Minimize)) - style &= ~WS_MINIMIZEBOX; - - if (mBorderStyle == BorderStyle::None || - !(mBorderStyle & BorderStyle::Maximize)) - style &= ~WS_MAXIMIZEBOX; - } + style &= ~WindowStylesRemovedForBorderStyle(mBorderStyle); if (mIsChildWindow) { style |= WS_CLIPCHILDREN; @@ -2471,46 +2464,6 @@ void nsWindow::ResetLayout() { Invalidate(); } -// Internally track the caption status via a window property. Required -// due to our internal handling of WM_NCACTIVATE when custom client -// margins are set. -static const wchar_t kManageWindowInfoProperty[] = L"ManageWindowInfoProperty"; -typedef BOOL(WINAPI* GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi); -static WindowsDllInterceptor::FuncHookType<GetWindowInfoPtr> - sGetWindowInfoPtrStub; - -BOOL WINAPI GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi) { - if (!sGetWindowInfoPtrStub) { - NS_ASSERTION(FALSE, "Something is horribly wrong in GetWindowInfoHook!"); - return FALSE; - } - int windowStatus = - reinterpret_cast<LONG_PTR>(GetPropW(hWnd, kManageWindowInfoProperty)); - // No property set, return the default data. - if (!windowStatus) return sGetWindowInfoPtrStub(hWnd, pwi); - // Call GetWindowInfo and update dwWindowStatus with our - // internally tracked value. - BOOL result = sGetWindowInfoPtrStub(hWnd, pwi); - if (result && pwi) - pwi->dwWindowStatus = (windowStatus == 1 ? 0 : WS_ACTIVECAPTION); - return result; -} - -void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption) { - if (!mWnd) return; - - sUser32Intercept.Init("user32.dll"); - sGetWindowInfoPtrStub.Set(sUser32Intercept, "GetWindowInfo", - &GetWindowInfoHook); - if (!sGetWindowInfoPtrStub) { - return; - } - - // Update our internally tracked caption status - SetPropW(mWnd, kManageWindowInfoProperty, - reinterpret_cast<HANDLE>(static_cast<INT_PTR>(aActiveCaption) + 1)); -} - #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 @@ -2703,6 +2656,8 @@ bool nsWindow::UpdateNonClientMargins(bool aReflowWindow) { mNonClientOffset = NormalWindowNonClientOffset(); } + UpdateOpaqueRegionInternal(); + if (aReflowWindow) { // Force a reflow of content based on the new client // dimensions. @@ -2713,8 +2668,9 @@ bool nsWindow::UpdateNonClientMargins(bool aReflowWindow) { } nsresult nsWindow::SetNonClientMargins(const LayoutDeviceIntMargin& margins) { - if (!mIsTopWidgetWindow || mBorderStyle == BorderStyle::None) + if (!mIsTopWidgetWindow || mBorderStyle == BorderStyle::None) { return NS_ERROR_INVALID_ARG; + } if (mHideChrome) { mFutureMarginsOnceChromeShows = margins; @@ -2731,19 +2687,13 @@ nsresult nsWindow::SetNonClientMargins(const LayoutDeviceIntMargin& margins) { // Force a reflow of content based on the new client // dimensions. ResetLayout(); - - int windowStatus = - reinterpret_cast<LONG_PTR>(GetPropW(mWnd, kManageWindowInfoProperty)); - if (windowStatus) { - ::SendMessageW(mWnd, WM_NCACTIVATE, 1 != windowStatus, 0); - } - return NS_OK; } if (margins.top < -1 || margins.bottom < -1 || margins.left < -1 || - margins.right < -1) + margins.right < -1) { return NS_ERROR_INVALID_ARG; + } mNonClientMargins = margins; mCustomNonClient = true; @@ -3999,10 +3949,8 @@ uint32_t nsWindow::GetMaxTouchPoints() const { return WinUtils::GetMaxTouchPoints(); } -void nsWindow::SetWindowClass(const nsAString& xulWinType, - const nsAString& xulWinClass, - const nsAString& xulWinName) { - mIsEarlyBlankWindow = xulWinType.EqualsLiteral("navigator:blank"); +void nsWindow::SetIsEarlyBlankWindow(bool aIsEarlyBlankWindow) { + mIsEarlyBlankWindow = aIsEarlyBlankWindow; } /************************************************************** @@ -5194,8 +5142,6 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting * through WM_NCPAINT via InvalidateNonClientRegion. */ - UpdateGetWindowInfoCaptionStatus(FALSE != wParam); - if (!mCustomNonClient) { break; } @@ -5684,9 +5630,6 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, if (WinUtils::LogToPhysFactor(mWnd) != mDefaultScale) { ChangedDPI(); ResetLayout(); - if (mWidgetListener) { - mWidgetListener->UIResolutionChanged(); - } } } break; @@ -5724,9 +5667,6 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, case WM_DISPLAYCHANGE: { ScreenHelperWin::RefreshScreens(); - if (mWidgetListener) { - mWidgetListener->UIResolutionChanged(); - } break; } @@ -5925,19 +5865,12 @@ bool nsWindow::ProcessMessageInternal(UINT msg, WPARAM& wParam, LPARAM& lParam, a11y::LazyInstantiator::EnableBlindAggregation(mWnd); result = true; } - } else if (objId == UiaRootObjectId && - StaticPrefs::accessibility_uia_enable()) { - if (a11y::LocalAccessible* acc = GetAccessible()) { - RefPtr<IAccessible> ia; - acc->GetNativeInterface(getter_AddRefs(ia)); - MOZ_ASSERT(ia); - RefPtr<IRawElementProviderSimple> uia; - ia->QueryInterface(IID_IRawElementProviderSimple, - getter_AddRefs(uia)); - if (uia) { - *aRetValue = UiaReturnRawElementProvider(mWnd, wParam, lParam, uia); - result = true; - } + } else if (objId == UiaRootObjectId) { + if (RefPtr<IRawElementProviderSimple> root = + a11y::LazyInstantiator::GetRootUia(mWnd)) { + *aRetValue = UiaReturnRawElementProvider(mWnd, wParam, lParam, root); + a11y::LazyInstantiator::EnableBlindAggregation(mWnd); + result = true; } } } break; @@ -7420,7 +7353,9 @@ a11y::LocalAccessible* nsWindow::GetAccessible() { **************************************************************/ void nsWindow::SetWindowTranslucencyInner(TransparencyMode aMode) { - if (aMode == mTransparencyMode) return; + if (aMode == mTransparencyMode) { + return; + } // stop on dialogs and popups! HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true); @@ -7458,10 +7393,11 @@ void nsWindow::SetWindowTranslucencyInner(TransparencyMode aMode) { } } - if (aMode == TransparencyMode::Transparent) + if (aMode == TransparencyMode::Transparent) { exStyle |= WS_EX_LAYERED; - else + } else { exStyle &= ~WS_EX_LAYERED; + } VERIFY_WINDOW_STYLE(style); ::SetWindowLongPtrW(hWnd, GWL_STYLE, style); @@ -7469,11 +7405,47 @@ void nsWindow::SetWindowTranslucencyInner(TransparencyMode aMode) { mTransparencyMode = aMode; + UpdateOpaqueRegionInternal(); + if (mCompositorWidgetDelegate) { mCompositorWidgetDelegate->UpdateTransparency(aMode); } } +void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aRegion) { + if (aRegion == mOpaqueRegion) { + return; + } + mOpaqueRegion = aRegion; + UpdateOpaqueRegionInternal(); +} + +void nsWindow::UpdateOpaqueRegionInternal() { + MARGINS margins{0}; + if (mTransparencyMode == TransparencyMode::Transparent) { + // If there is no opaque region, set margins to support a full sheet of + // glass. Comments in MSDN indicate all values must be set to -1 to get a + // full sheet of glass. + margins = {-1, -1, -1, -1}; + if (!mOpaqueRegion.IsEmpty()) { + LayoutDeviceIntRect clientBounds = GetClientBounds(); + // Find the largest rectangle and use that to calculate the inset. + LayoutDeviceIntRect largest = mOpaqueRegion.GetLargestRectangle(); + margins.cxLeftWidth = largest.X(); + margins.cxRightWidth = clientBounds.Width() - largest.XMost(); + margins.cyBottomHeight = clientBounds.Height() - largest.YMost(); + margins.cyTopHeight = largest.Y(); + + auto ncmargin = NonClientSizeMargin(); + margins.cxLeftWidth += ncmargin.left; + margins.cyTopHeight += ncmargin.top; + margins.cxRightWidth += ncmargin.right; + margins.cyBottomHeight += ncmargin.bottom; + } + } + DwmExtendFrameIntoClientArea(mWnd, &margins); +} + /************************************************************** ************************************************************** ** @@ -8200,7 +8172,7 @@ bool nsWindow::CanTakeFocus() { return false; } -/* static */ const wchar_t* nsWindow::GetMainWindowClass() { +static const wchar_t* GetMainWindowClass() { static const wchar_t* sMainWindowClass = nullptr; if (!sMainWindowClass) { nsAutoString className; diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index a2b2a701bd..9c4b4417f8 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -261,8 +261,7 @@ class nsWindow final : public nsBaseWidget { const LayoutDeviceIntRegion& aRegion) override; uint32_t GetMaxTouchPoints() const override; - void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass, - const nsAString& xulWinName) override; + void SetIsEarlyBlankWindow(bool) override; /** * Event helpers @@ -518,10 +517,8 @@ class nsWindow final : public nsBaseWidget { bool CanTakeFocus(); bool UpdateNonClientMargins(bool aReflowWindow = true); void UpdateDarkModeToolbar(); - void UpdateGetWindowInfoCaptionStatus(bool aActiveCaption); void ResetLayout(); void InvalidateNonClientRegion(); - static const wchar_t* GetMainWindowClass(); HWND GetOwnerWnd() const { return ::GetWindow(mWnd, GW_OWNER); } bool IsOwnerForegroundWindow() const { HWND owner = GetOwnerWnd(); @@ -592,11 +589,6 @@ class nsWindow final : public nsBaseWidget { DWORD WindowStyle(); DWORD WindowExStyle(); - static const wchar_t* ChooseWindowClass(WindowType); - // This method registers the given window class, and returns the class name. - static const wchar_t* RegisterWindowClass(const wchar_t* aClassName, - UINT aExtraStyle, LPWSTR aIconID); - /** * Popup hooks */ @@ -620,6 +612,9 @@ class nsWindow final : public nsBaseWidget { bool IsSimulatedClientArea(int32_t clientX, int32_t clientY); bool IsWindowButton(int32_t hitTestResult); + void UpdateOpaqueRegion(const LayoutDeviceIntRegion&) override; + void UpdateOpaqueRegionInternal(); + bool DispatchTouchEventFromWMPointer(UINT msg, LPARAM aLParam, const WinPointerInfo& aPointerInfo, mozilla::MouseButton aButton); @@ -808,6 +803,8 @@ class nsWindow final : public nsBaseWidget { // Draggable titlebar region maintained by UpdateWindowDraggingRegion LayoutDeviceIntRegion mDraggableRegion; + // Opaque region maintained by UpdateOpaqueRegion + LayoutDeviceIntRegion mOpaqueRegion; // Graphics LayoutDeviceIntRect mLastPaintBounds; @@ -816,7 +813,6 @@ class nsWindow final : public nsBaseWidget { // Transparency TransparencyMode mTransparencyMode = TransparencyMode::Opaque; - nsIntRegion mPossiblyTransparentRegion; // Win7 Gesture processing and management nsWinGesture mGesture; diff --git a/widget/windows/tests/gtest/TestJumpListBuilder.cpp b/widget/windows/tests/gtest/TestJumpListBuilder.cpp index 5494c42d37..531e326465 100644 --- a/widget/windows/tests/gtest/TestJumpListBuilder.cpp +++ b/widget/windows/tests/gtest/TestJumpListBuilder.cpp @@ -275,14 +275,14 @@ class TestingJumpListBackend : public JumpListBackend { * @param {nsTArray<WindowsJumpListShortcutDescription>&} aArray * The outparam for the array of generated * WindowsJumpListShortcutDescriptions. - * @param {nsTArray<JS::Value>&} aJSValArray + * @param {JS::Handle<JSObject*>} aJSArrayObj * The outparam for the array of JS::Value's representing the generated * WindowsJumpListShortcutDescriptions. */ void GenerateWindowsJumpListShortcutDescriptions( JSContext* aCx, uint32_t howMany, bool longDescription, nsTArray<WindowsJumpListShortcutDescription>& aArray, - nsTArray<JS::Value>& aJSValArray) { + JS::Handle<JSObject*> aJSArrayObj) { for (uint32_t i = 0; i < howMany; ++i) { WindowsJumpListShortcutDescription desc; nsAutoString title(u"Test Task #"); @@ -321,7 +321,8 @@ void GenerateWindowsJumpListShortcutDescriptions( aArray.AppendElement(desc); JS::Rooted<JS::Value> descJSValue(aCx); ASSERT_TRUE(ToJSValue(aCx, desc, &descJSValue)); - aJSValArray.AppendElement(std::move(descJSValue)); + + MOZ_ALWAYS_TRUE(JS_SetElement(aCx, aJSArrayObj, i, descJSValue)); } } @@ -393,15 +394,15 @@ TEST(JumpListBuilder, CheckForRemovals) EXPECT_CALL(*testBackend, BeginList) .WillOnce([](UINT* pcMinSlots, REFIID riid, void** ppv) { RefPtr<IObjectCollection> collection; - DebugOnly<HRESULT> hr = CoCreateInstance( + HRESULT hr = CoCreateInstance( CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER, IID_IObjectCollection, getter_AddRefs(collection)); - MOZ_ASSERT(SUCCEEDED(hr)); + MOZ_RELEASE_ASSERT(SUCCEEDED(hr)); RefPtr<IShellLinkW> link; hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLinkW, getter_AddRefs(link)); - MOZ_ASSERT(SUCCEEDED(hr)); + MOZ_RELEASE_ASSERT(SUCCEEDED(hr)); nsAutoString firstLinkHref(u"https://example.com"_ns); link->SetArguments(firstLinkHref.get()); @@ -421,7 +422,7 @@ TEST(JumpListBuilder, CheckForRemovals) RefPtr<IObjectArray> pArray; hr = collection->QueryInterface(IID_IObjectArray, getter_AddRefs(pArray)); - MOZ_ASSERT(SUCCEEDED(hr)); + MOZ_RELEASE_ASSERT(SUCCEEDED(hr)); *ppv = static_cast<IObjectArray*>(pArray); (static_cast<IUnknown*>(*ppv))->AddRef(); @@ -478,6 +479,115 @@ TEST(JumpListBuilder, CheckForRemovals) } /** + * Tests calling CheckForRemovals and receiving a jump list entry with a very + * long URL doesn't result in JumpListBuilder truncating the URL before handing + * it back to the caller. Expects the following calls in order: + * + * - SetAppID + * - AbortList + * - BeginList + * - AbortList + */ +TEST(JumpListBuilder, CheckForRemovalsLongURL) +{ + RefPtr<StrictMock<TestingJumpListBackend>> testBackend = + new StrictMock<TestingJumpListBackend>(); + nsAutoString aumid(u"TestApplicationID"); + // We set up this expectation here because SetAppID will be called soon + // after construction of the JumpListBuilder via the background thread. + EXPECT_CALL(*testBackend, SetAppID(_)).Times(1); + + nsCOMPtr<nsIJumpListBuilder> builder = + new JumpListBuilder(aumid, testBackend); + ASSERT_TRUE(builder); + + EXPECT_CALL(*testBackend, AbortList()).Times(2); + + constexpr static const nsLiteralString veryLongHref( + u"https://example.verylongurl.test/first/second/third/fourth/fifth/" + "sixth/seventh/eighth/ninth/tenth/eleventh/twelfth/thirteenth/" + "fourteenth/fifteenth-path-item/some/more/junk/after/that/more/more/" + "more/more/more/more/more/more/more/more/more/more/more/more/more/more/" + "more/more/more/more/more/more/more/more/more/more/more"_ns); + // This test ensures that URLs longer than MAX_PATH do not get truncated by + // JumpListBuilder or one of its utilities, so we must ensure that the static + // URL we just defined is actually longer than MAX_PATH. + static_assert(veryLongHref.Length() > MAX_PATH); + + // Let's prepare BeginList to return a one entry collection of IShellLinks. + // The IShellLink will have the URL be very long - over MAX_PATH characters. + EXPECT_CALL(*testBackend, BeginList) + .WillOnce([](UINT* pcMinSlots, REFIID riid, void** ppv) { + RefPtr<IObjectCollection> collection; + HRESULT hr = CoCreateInstance( + CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER, + IID_IObjectCollection, getter_AddRefs(collection)); + MOZ_RELEASE_ASSERT(SUCCEEDED(hr)); + + RefPtr<IShellLinkW> link; + hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + IID_IShellLinkW, getter_AddRefs(link)); + MOZ_RELEASE_ASSERT(SUCCEEDED(hr)); + + link->SetArguments(veryLongHref.get()); + + nsAutoString appPath(u"C:\\Tmp\\firefox.exe"_ns); + link->SetIconLocation(appPath.get(), 0); + + collection->AddObject(link); + + RefPtr<IObjectArray> pArray; + hr = collection->QueryInterface(IID_IObjectArray, + getter_AddRefs(pArray)); + MOZ_RELEASE_ASSERT(SUCCEEDED(hr)); + + *ppv = static_cast<IObjectArray*>(pArray); + (static_cast<IUnknown*>(*ppv))->AddRef(); + + // This is the default value to return, according to + // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-icustomdestinationlist-beginlist + *pcMinSlots = 10; + + return S_OK; + }); + + AutoJSAPI jsapi; + MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); + JSContext* cx = jsapi.cx(); + RefPtr<Promise> promise; + nsresult rv = builder->CheckForRemovals(cx, getter_AddRefs(promise)); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + ASSERT_TRUE(promise); + + RefPtr<WaitForResolver> resolver = new WaitForResolver(); + promise->AppendNativeHandler(resolver); + JS::Rooted<JS::Value> result(cx); + resolver->SpinUntilResolvedWithResult(&result); + + ASSERT_TRUE(result.isObject()); + JS::Rooted<JSObject*> obj(cx, result.toObjectOrNull()); + + bool isArray; + ASSERT_TRUE(JS::IsArrayObject(cx, obj, &isArray)); + ASSERT_TRUE(isArray); + + // We should expect to see 1 URL string returned in the array. + uint32_t length = 0; + ASSERT_TRUE(JS::GetArrayLength(cx, obj, &length)); + ASSERT_EQ(length, 1U); + + // The URL should match veryLongHref + JS::Rooted<JS::Value> returnedURLValue(cx); + ASSERT_TRUE(JS_GetElement(cx, obj, 0, &returnedURLValue)); + JS::Rooted<JSString*> returnedURLValueJSString(cx, + returnedURLValue.toString()); + nsAutoJSString returnedURLValueAutoString; + ASSERT_TRUE(returnedURLValueAutoString.init(cx, returnedURLValueJSString)); + + ASSERT_TRUE(returnedURLValueAutoString.Equals(veryLongHref)); +} + +/** * Tests calling PopulateJumpList with empty arguments, which should call the * following methods on the backend, in order: * @@ -506,9 +616,13 @@ TEST(JumpListBuilder, PopulateJumpListEmpty) JSContext* cx = jsapi.cx(); RefPtr<Promise> promise; - nsTArray<JS::Value> taskDescJSVals; + JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj)); + nsAutoString customTitle(u""); - nsTArray<JS::Value> customDescJSVals; + + JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj)); EXPECT_CALL(*testBackend, AbortList()).Times(1); EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1); @@ -516,7 +630,7 @@ TEST(JumpListBuilder, PopulateJumpListEmpty) EXPECT_CALL(*testBackend, DeleteList(_)).Times(0); nsresult rv = - builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals, + builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal, cx, getter_AddRefs(promise)); ASSERT_TRUE(NS_SUCCEEDED(rv)); ASSERT_TRUE(promise); @@ -558,13 +672,15 @@ TEST(JumpListBuilder, PopulateJumpListOnlyTasks) JSContext* cx = jsapi.cx(); RefPtr<Promise> promise; - nsTArray<JS::Value> taskDescJSVals; + JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj)); nsTArray<WindowsJumpListShortcutDescription> taskDescs; GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, taskDescs, - taskDescJSVals); + taskDescsObj); nsAutoString customTitle(u""); - nsTArray<JS::Value> customDescJSVals; + JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj)); EXPECT_CALL(*testBackend, AbortList()).Times(1); EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1); @@ -575,7 +691,7 @@ TEST(JumpListBuilder, PopulateJumpListOnlyTasks) EXPECT_CALL(*testBackend, DeleteList(_)).Times(0); nsresult rv = - builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals, + builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal, cx, getter_AddRefs(promise)); ASSERT_TRUE(NS_SUCCEEDED(rv)); ASSERT_TRUE(promise); @@ -617,13 +733,17 @@ TEST(JumpListBuilder, PopulateJumpListOnlyCustomItems) JSContext* cx = jsapi.cx(); RefPtr<Promise> promise; - nsTArray<WindowsJumpListShortcutDescription> descs; - nsTArray<JS::Value> customDescJSVals; - GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, descs, - customDescJSVals); + JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> taskDescsJSVal(cx); + taskDescsJSVal.setObject(*taskDescsObj); nsAutoString customTitle(u"My custom title"); - nsTArray<JS::Value> taskDescJSVals; + + JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj)); + nsTArray<WindowsJumpListShortcutDescription> descs; + GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, descs, + customDescsObj); EXPECT_CALL(*testBackend, AbortList()).Times(1); EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1); @@ -636,7 +756,7 @@ TEST(JumpListBuilder, PopulateJumpListOnlyCustomItems) EXPECT_CALL(*testBackend, DeleteList(_)).Times(0); nsresult rv = - builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals, + builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal, cx, getter_AddRefs(promise)); ASSERT_TRUE(NS_SUCCEEDED(rv)); ASSERT_TRUE(promise); @@ -679,30 +799,102 @@ TEST(JumpListBuilder, PopulateJumpList) JSContext* cx = jsapi.cx(); RefPtr<Promise> promise; + JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj)); nsTArray<WindowsJumpListShortcutDescription> taskDescs; - nsTArray<JS::Value> taskDescJSVals; GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, taskDescs, - taskDescJSVals); + taskDescsObj); + + nsAutoString customTitle(u"My custom title"); + JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj)); nsTArray<WindowsJumpListShortcutDescription> customDescs; - nsTArray<JS::Value> customDescJSVals; GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, customDescs, - customDescJSVals); + customDescsObj); + + EXPECT_CALL(*testBackend, AbortList()).Times(1); + EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1); + EXPECT_CALL(*testBackend, AddUserTasks(ShellLinksEq(&taskDescs))).Times(1); + + EXPECT_CALL(*testBackend, AppendCategory(LPCWSTREq(customTitle.get()), + ShellLinksEq(&customDescs))) + .Times(1); + EXPECT_CALL(*testBackend, CommitList()).Times(1); + EXPECT_CALL(*testBackend, DeleteList(_)).Times(0); + + nsresult rv = + builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal, + cx, getter_AddRefs(promise)); + ASSERT_TRUE(NS_SUCCEEDED(rv)); + ASSERT_TRUE(promise); + + RefPtr<WaitForResolver> resolver = new WaitForResolver(); + promise->AppendNativeHandler(resolver); + JS::Rooted<JS::Value> result(cx); + resolver->SpinUntilResolved(); +} + +/** + * Tests calling PopulateJumpList with tasks and custom items, but makes it so + * that AppendCategory returns E_ACCESSDENIED, which can occur if Windows is + * configured to not show recently opened items. The PopulateJumpList Promise + * should still resolve. + * + * - SetAppID + * - AbortList + * - BeginList + * - AddUserTasks + * - AppendCategory + * - CommitList + * + * This should result in a jump list with just built-in tasks. + */ +TEST(JumpListBuilder, PopulateJumpListNoOpenedItems) +{ + RefPtr<StrictMock<TestingJumpListBackend>> testBackend = + new StrictMock<TestingJumpListBackend>(); + nsAutoString aumid(u"TestApplicationID"); + // We set up this expectation here because SetAppID will be called soon + // after construction of the JumpListBuilder via the background thread. + EXPECT_CALL(*testBackend, SetAppID(_)).Times(1); + + nsCOMPtr<nsIJumpListBuilder> builder = + new JumpListBuilder(aumid, testBackend); + ASSERT_TRUE(builder); + + AutoJSAPI jsapi; + MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope())); + JSContext* cx = jsapi.cx(); + RefPtr<Promise> promise; + + JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj)); + nsTArray<WindowsJumpListShortcutDescription> taskDescs; + GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, taskDescs, + taskDescsObj); nsAutoString customTitle(u"My custom title"); + JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj)); + nsTArray<WindowsJumpListShortcutDescription> customDescs; + GenerateWindowsJumpListShortcutDescriptions(cx, 2, false, customDescs, + customDescsObj); + EXPECT_CALL(*testBackend, AbortList()).Times(1); EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1); EXPECT_CALL(*testBackend, AddUserTasks(ShellLinksEq(&taskDescs))).Times(1); EXPECT_CALL(*testBackend, AppendCategory(LPCWSTREq(customTitle.get()), ShellLinksEq(&customDescs))) - .Times(1); + .WillOnce([] { return E_ACCESSDENIED; }); + EXPECT_CALL(*testBackend, CommitList()).Times(1); EXPECT_CALL(*testBackend, DeleteList(_)).Times(0); nsresult rv = - builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals, + builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal, cx, getter_AddRefs(promise)); ASSERT_TRUE(NS_SUCCEEDED(rv)); ASSERT_TRUE(promise); @@ -780,15 +972,20 @@ TEST(JumpListBuilder, TruncateDescription) JSContext* cx = jsapi.cx(); RefPtr<Promise> promise; + JS::Rooted<JSObject*> taskDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> taskDescsJSVal(cx, JS::ObjectValue(*taskDescsObj)); nsTArray<WindowsJumpListShortcutDescription> taskDescs; - nsTArray<JS::Value> taskDescJSVals; GenerateWindowsJumpListShortcutDescriptions(cx, 2, true, taskDescs, - taskDescJSVals); + taskDescsObj); + nsAutoString customTitle(u"My custom title"); + + JS::Rooted<JSObject*> customDescsObj(cx, JS::NewArrayObject(cx, 0)); + JS::Rooted<JS::Value> customDescsJSVal(cx, JS::ObjectValue(*customDescsObj)); nsTArray<WindowsJumpListShortcutDescription> customDescs; - nsTArray<JS::Value> customDescJSVals; GenerateWindowsJumpListShortcutDescriptions(cx, 2, true, customDescs, - customDescJSVals); + customDescsObj); + // We expect the long descriptions to be truncated to 260 characters, so // we'll truncate the descriptions here ourselves. for (auto& taskDesc : taskDescs) { @@ -798,8 +995,6 @@ TEST(JumpListBuilder, TruncateDescription) customDesc.mDescription.SetLength(MAX_PATH - 1); } - nsAutoString customTitle(u"My custom title"); - EXPECT_CALL(*testBackend, AbortList()).Times(1); EXPECT_CALL(*testBackend, BeginList(_, _, _)).Times(1); EXPECT_CALL(*testBackend, AddUserTasks(ShellLinksEq(&taskDescs))).Times(1); @@ -811,7 +1006,7 @@ TEST(JumpListBuilder, TruncateDescription) EXPECT_CALL(*testBackend, DeleteList(_)).Times(0); nsresult rv = - builder->PopulateJumpList(taskDescJSVals, customTitle, customDescJSVals, + builder->PopulateJumpList(taskDescsJSVal, customTitle, customDescsJSVal, cx, getter_AddRefs(promise)); ASSERT_TRUE(NS_SUCCEEDED(rv)); ASSERT_TRUE(promise); diff --git a/widget/windows/tests/gtest/TestWinDND.cpp b/widget/windows/tests/gtest/TestWinDND.cpp index fb7849fd79..9a4b8c3db1 100644 --- a/widget/windows/tests/gtest/TestWinDND.cpp +++ b/widget/windows/tests/gtest/TestWinDND.cpp @@ -693,7 +693,7 @@ nsCOMPtr<nsIFile> GetTemporaryDirectory() { #define ENSURE(expr) NS_ENSURE_SUCCESS(expr, nullptr); ENSURE(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpdir))); - MOZ_ASSERT(tmpdir); + MOZ_RELEASE_ASSERT(tmpdir); ENSURE(tmpdir->AppendNative("TestWinDND"_ns)); ENSURE(tmpdir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0777)); |