From 86b963dd22928e1ffb71b19c79fd3ddbfd5eefb8 Mon Sep 17 00:00:00 2001 From: Martin Stransky Date: Mon, 30 Apr 2018 13:51:36 +0200 Subject: [PATCH] Build fix --- ...te-csd-window-offset-mozilla-1457691.patch | 427 ++++++++++++++++-- firefox.spec | 6 +- 2 files changed, 402 insertions(+), 31 deletions(-) diff --git a/complete-csd-window-offset-mozilla-1457691.patch b/complete-csd-window-offset-mozilla-1457691.patch index e37d02d..a43a518 100644 --- a/complete-csd-window-offset-mozilla-1457691.patch +++ b/complete-csd-window-offset-mozilla-1457691.patch @@ -1,5 +1,6 @@ This is a composition of these patches for Firefox 60: +https://bugzilla.mozilla.org/show_bug.cgi?id=1441873 https://bugzilla.mozilla.org/show_bug.cgi?id=1441665 https://bugzilla.mozilla.org/show_bug.cgi?id=1456898 https://bugzilla.mozilla.org/show_bug.cgi?id=1457309 @@ -7,9 +8,377 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1457691 which fix popup window placement at CSD window mode. -diff -up firefox-60.0/widget/gtk/gtk3drawing.cpp.old firefox-60.0/widget/gtk/gtk3drawing.cpp ---- firefox-60.0/widget/gtk/gtk3drawing.cpp.old 2018-04-26 22:07:36.000000000 +0200 -+++ firefox-60.0/widget/gtk/gtk3drawing.cpp 2018-04-30 11:59:06.750866104 +0200 + +diff --git a/widget/gtk/nsLookAndFeel.cpp b/widget/gtk/nsLookAndFeel.cpp +--- a/widget/gtk/nsLookAndFeel.cpp ++++ b/widget/gtk/nsLookAndFeel.cpp +@@ -1076,19 +1076,18 @@ nsLookAndFeel::EnsureInit() + nullptr); + + GetSystemFontInfo(gtk_widget_get_style_context(entry), + &mFieldFontName, &mFieldFontStyle); + + gtk_widget_destroy(window); + g_object_unref(labelWidget); + +- // Require GTK 3.10 for GtkHeaderBar support and compatible window manager. +- mCSDAvailable = (gtk_check_version(3, 10, 0) == nullptr && +- nsWindow::GetCSDSupportLevel() != nsWindow::CSD_SUPPORT_NONE); ++ mCSDAvailable = ++ nsWindow::GetSystemCSDSupportLevel() != nsWindow::CSD_SUPPORT_NONE; + + mCSDCloseButton = false; + mCSDMinimizeButton = false; + mCSDMaximizeButton = false; + + // We need to initialize whole CSD config explicitly because it's queried + // as -moz-gtk* media features. + WidgetNodeType buttonLayout[TOOLBAR_BUTTONS]; +diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h +--- a/widget/gtk/nsWindow.h ++++ b/widget/gtk/nsWindow.h +@@ -395,28 +395,26 @@ public: + // From GDK + int GdkCoordToDevicePixels(gint coord); + LayoutDeviceIntPoint GdkPointToDevicePixels(GdkPoint point); + LayoutDeviceIntPoint GdkEventCoordsToDevicePixels(gdouble x, gdouble y); + LayoutDeviceIntRect GdkRectToDevicePixels(GdkRectangle rect); + + virtual bool WidgetTypeSupportsAcceleration() override; + +- bool DoDrawTitlebar() const; +- + typedef enum { CSD_SUPPORT_SYSTEM, // CSD including shadows + CSD_SUPPORT_CLIENT, // CSD without shadows + CSD_SUPPORT_NONE, // WM does not support CSD at all + CSD_SUPPORT_UNKNOWN + } CSDSupportLevel; + /** + * Get the support of Client Side Decoration by checking + * the XDG_CURRENT_DESKTOP environment variable. + */ +- static CSDSupportLevel GetCSDSupportLevel(); ++ static CSDSupportLevel GetSystemCSDSupportLevel(); + + protected: + virtual ~nsWindow(); + + // event handling code + void DispatchActivateEvent(void); + void DispatchDeactivateEvent(void); + void DispatchResized(); +@@ -512,19 +510,21 @@ private: + int mXDepth; + mozilla::widget::WindowSurfaceProvider mSurfaceProvider; + #endif + + // Upper bound on pending ConfigureNotify events to be dispatched to the + // window. See bug 1225044. + unsigned int mPendingConfigures; + +- bool mIsCSDAvailable; ++ // Window titlebar rendering mode, CSD_SUPPORT_NONE if it's disabled ++ // for this window. ++ CSDSupportLevel mCSDSupportLevel; + // If true, draw our own window titlebar. +- bool mIsCSDEnabled; ++ bool mDrawInTitlebar; + // Draggable titlebar region maintained by UpdateWindowDraggingRegion + LayoutDeviceIntRegion mDraggableRegion; + + #ifdef ACCESSIBILITY + RefPtr mRootAccessible; + + /** + * Request to create the accessible for this window if it is top level. + +diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp +--- a/widget/gtk/nsWindow.cpp ++++ b/widget/gtk/nsWindow.cpp +@@ -474,18 +474,18 @@ nsWindow::nsWindow() + + mTransparencyBitmapWidth = 0; + mTransparencyBitmapHeight = 0; + + #if GTK_CHECK_VERSION(3,4,0) + mLastScrollEventTime = GDK_CURRENT_TIME; + #endif + mPendingConfigures = 0; +- mIsCSDAvailable = false; +- mIsCSDEnabled = false; ++ mCSDSupportLevel = CSD_SUPPORT_NONE; ++ mDrawInTitlebar = false; + } + + nsWindow::~nsWindow() + { + LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this)); + + delete[] mTransparencyBitmap; + mTransparencyBitmap = nullptr; +@@ -2814,17 +2814,17 @@ nsWindow::OnButtonReleaseEvent(GdkEventB + LayoutDeviceIntPoint pos = event.mRefPoint; + + nsEventStatus eventStatus = DispatchInputEvent(&event); + + bool defaultPrevented = (eventStatus == nsEventStatus_eConsumeNoDefault); + // Check if mouse position in titlebar and doubleclick happened to + // trigger restore/maximize. + if (!defaultPrevented +- && mIsCSDEnabled ++ && mDrawInTitlebar + && event.button == WidgetMouseEvent::eLeftButton + && event.mClickCount == 2 + && mDraggableRegion.Contains(pos.x, pos.y)) { + + if (mSizeState == nsSizeMode_Maximized) { + SetSizeMode(nsSizeMode_Normal); + } else { + SetSizeMode(nsSizeMode_Maximized); +@@ -3758,22 +3758,18 @@ nsWindow::Create(nsIWidget* aParent, + gtk_window_set_wmclass(GTK_WINDOW(mShell), "Toplevel", + gdk_get_program_class()); + + // each toplevel window gets its own window group + GtkWindowGroup *group = gtk_window_group_new(); + gtk_window_group_add_window(group, GTK_WINDOW(mShell)); + g_object_unref(group); + +- int32_t isCSDAvailable = false; +- nsresult rv = LookAndFeel::GetInt(LookAndFeel::eIntID_GTKCSDAvailable, +- &isCSDAvailable); +- if (NS_SUCCEEDED(rv)) { +- mIsCSDAvailable = isCSDAvailable; +- } ++ // We enable titlebar rendering for toplevel windows only. ++ mCSDSupportLevel = GetSystemCSDSupportLevel(); + } + + // Create a container to hold child windows and child GtkWidgets. + GtkWidget *container = moz_container_new(); + mContainer = MOZ_CONTAINER(container); + + // "csd" style is set when widget is realized so we need to call + // it explicitly now. +@@ -3788,17 +3784,17 @@ nsWindow::Create(nsIWidget* aParent, + * are drawn by Gtk+ to mShell. Content is rendered to mContainer + * and we listen to the Gtk+ events on mContainer. + * 3) We're running on Wayland. All gecko content is rendered + * to mContainer and we listen to the Gtk+ events on mContainer. + */ + GtkStyleContext* style = gtk_widget_get_style_context(mShell); + drawToContainer = + !mIsX11Display || +- (mIsCSDAvailable && GetCSDSupportLevel() == CSD_SUPPORT_CLIENT) || ++ (mCSDSupportLevel == CSD_SUPPORT_CLIENT) || + gtk_style_context_has_class(style, "csd"); + eventWidget = (drawToContainer) ? container : mShell; + + gtk_widget_add_events(eventWidget, kEvents); + if (drawToContainer) + gtk_widget_add_events(mShell, GDK_PROPERTY_CHANGE_MASK); + + // Prevent GtkWindow from painting a background to avoid flickering. +@@ -6581,90 +6577,91 @@ nsWindow::ClearCachedResources() + window->ClearCachedResources(); + } + } + } + + nsresult + nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins) + { +- SetDrawsInTitlebar(aMargins.top == 0); +- return NS_OK; ++ SetDrawsInTitlebar(aMargins.top == 0); ++ return NS_OK; + } + + void + nsWindow::SetDrawsInTitlebar(bool aState) + { +- if (!mIsCSDAvailable || aState == mIsCSDEnabled) +- return; +- +- if (mShell) { +- if (GetCSDSupportLevel() == CSD_SUPPORT_SYSTEM) { +- SetWindowDecoration(aState ? eBorderStyle_border : mBorderStyle); +- } +- else { +- /* Window manager does not support GDK_DECOR_BORDER, +- * emulate it by CSD. +- * +- * gtk_window_set_titlebar() works on unrealized widgets only, +- * we need to handle mShell carefully here. +- * When CSD is enabled mGdkWindow is owned by mContainer which is good +- * as we can't delete our mGdkWindow. To make mShell unrealized while +- * mContainer is preserved we temporary reparent mContainer to an +- * invisible GtkWindow. +- */ +- NativeShow(false); +- +- // Using GTK_WINDOW_POPUP rather than +- // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less +- // initialization and window manager interaction. +- GtkWidget* tmpWindow = gtk_window_new(GTK_WINDOW_POPUP); +- gtk_widget_realize(tmpWindow); +- +- gtk_widget_reparent(GTK_WIDGET(mContainer), tmpWindow); +- gtk_widget_unrealize(GTK_WIDGET(mShell)); +- +- // Available as of GTK 3.10+ +- static auto sGtkWindowSetTitlebar = (void (*)(GtkWindow*, GtkWidget*)) +- dlsym(RTLD_DEFAULT, "gtk_window_set_titlebar"); +- MOZ_ASSERT(sGtkWindowSetTitlebar, +- "Missing gtk_window_set_titlebar(), old Gtk+ library?"); +- +- if (aState) { +- // Add a hidden titlebar widget to trigger CSD, but disable the default +- // titlebar. GtkFixed is a somewhat random choice for a simple unused +- // widget. gtk_window_set_titlebar() takes ownership of the titlebar +- // widget. +- sGtkWindowSetTitlebar(GTK_WINDOW(mShell), gtk_fixed_new()); +- } else { +- sGtkWindowSetTitlebar(GTK_WINDOW(mShell), nullptr); +- } +- +- /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=791081 +- * gtk_widget_realize() throws: +- * "In pixman_region32_init_rect: Invalid rectangle passed" +- * when mShell has default 1x1 size. +- */ +- GtkAllocation allocation = {0, 0, 0, 0}; +- gtk_widget_get_preferred_width(GTK_WIDGET(mShell), nullptr, +- &allocation.width); +- gtk_widget_get_preferred_height(GTK_WIDGET(mShell), nullptr, +- &allocation.height); +- gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation); +- +- gtk_widget_realize(GTK_WIDGET(mShell)); +- gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell)); +- mNeedsShow = true; +- NativeResize(); +- +- gtk_widget_destroy(tmpWindow); +- } +- } +- +- mIsCSDEnabled = aState; ++ if (!mShell || ++ mCSDSupportLevel == CSD_SUPPORT_NONE || ++ aState == mDrawInTitlebar) { ++ return; ++ } ++ ++ if (mCSDSupportLevel == CSD_SUPPORT_SYSTEM) { ++ SetWindowDecoration(aState ? eBorderStyle_border : mBorderStyle); ++ } ++ else if (mCSDSupportLevel == CSD_SUPPORT_CLIENT) { ++ /* Window manager does not support GDK_DECOR_BORDER, ++ * emulate it by CSD. ++ * ++ * gtk_window_set_titlebar() works on unrealized widgets only, ++ * we need to handle mShell carefully here. ++ * When CSD is enabled mGdkWindow is owned by mContainer which is good ++ * as we can't delete our mGdkWindow. To make mShell unrealized while ++ * mContainer is preserved we temporary reparent mContainer to an ++ * invisible GtkWindow. ++ */ ++ NativeShow(false); ++ ++ // Using GTK_WINDOW_POPUP rather than ++ // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less ++ // initialization and window manager interaction. ++ GtkWidget* tmpWindow = gtk_window_new(GTK_WINDOW_POPUP); ++ gtk_widget_realize(tmpWindow); ++ ++ gtk_widget_reparent(GTK_WIDGET(mContainer), tmpWindow); ++ gtk_widget_unrealize(GTK_WIDGET(mShell)); ++ ++ // Available as of GTK 3.10+ ++ static auto sGtkWindowSetTitlebar = (void (*)(GtkWindow*, GtkWidget*)) ++ dlsym(RTLD_DEFAULT, "gtk_window_set_titlebar"); ++ MOZ_ASSERT(sGtkWindowSetTitlebar, ++ "Missing gtk_window_set_titlebar(), old Gtk+ library?"); ++ ++ if (aState) { ++ // Add a hidden titlebar widget to trigger CSD, but disable the default ++ // titlebar. GtkFixed is a somewhat random choice for a simple unused ++ // widget. gtk_window_set_titlebar() takes ownership of the titlebar ++ // widget. ++ sGtkWindowSetTitlebar(GTK_WINDOW(mShell), gtk_fixed_new()); ++ } else { ++ sGtkWindowSetTitlebar(GTK_WINDOW(mShell), nullptr); ++ } ++ ++ /* A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=791081 ++ * gtk_widget_realize() throws: ++ * "In pixman_region32_init_rect: Invalid rectangle passed" ++ * when mShell has default 1x1 size. ++ */ ++ GtkAllocation allocation = {0, 0, 0, 0}; ++ gtk_widget_get_preferred_width(GTK_WIDGET(mShell), nullptr, ++ &allocation.width); ++ gtk_widget_get_preferred_height(GTK_WIDGET(mShell), nullptr, ++ &allocation.height); ++ gtk_widget_size_allocate(GTK_WIDGET(mShell), &allocation); ++ ++ gtk_widget_realize(GTK_WIDGET(mShell)); ++ gtk_widget_reparent(GTK_WIDGET(mContainer), GTK_WIDGET(mShell)); ++ mNeedsShow = true; ++ NativeResize(); ++ ++ gtk_widget_destroy(tmpWindow); ++ } ++ ++ mDrawInTitlebar = aState; + } + + gint + nsWindow::GdkScaleFactor() + { + #if (MOZ_WIDGET_GTK >= 3) + // Available as of GTK 3.10+ + static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) +@@ -6923,28 +6920,28 @@ nsWindow::SynthesizeNativeTouchPoint(uin + event.touch.y = DevicePixelsToGdkCoordRoundDown(pointInWindow.y); + + gdk_event_put(&event); + + return NS_OK; + } + #endif + +-bool +-nsWindow::DoDrawTitlebar() const +-{ +- return mIsCSDEnabled && mSizeState == nsSizeMode_Normal; +-} +- + nsWindow::CSDSupportLevel +-nsWindow::GetCSDSupportLevel() { ++nsWindow::GetSystemCSDSupportLevel() { + if (sCSDSupportLevel != CSD_SUPPORT_UNKNOWN) { + return sCSDSupportLevel; + } + ++ // Require GTK 3.10 for GtkHeaderBar support and compatible window manager. ++ if (gtk_check_version(3, 10, 0) != nullptr) { ++ sCSDSupportLevel = CSD_SUPPORT_NONE; ++ return sCSDSupportLevel; ++ } ++ + const char* currentDesktop = getenv("XDG_CURRENT_DESKTOP"); + if (currentDesktop) { + // GNOME Flashback (fallback) + if (strstr(currentDesktop, "GNOME-Flashback:GNOME") != nullptr) { + sCSDSupportLevel = CSD_SUPPORT_CLIENT; + // gnome-shell + } else if (strstr(currentDesktop, "GNOME") != nullptr) { + sCSDSupportLevel = CSD_SUPPORT_SYSTEM; +diff -up firefox-60.0/widget/gtk/gtk3drawing.cpp.orig firefox-60.0/widget/gtk/gtk3drawing.cpp +--- firefox-60.0/widget/gtk/gtk3drawing.cpp.orig 2018-04-26 22:07:36.000000000 +0200 ++++ firefox-60.0/widget/gtk/gtk3drawing.cpp 2018-04-30 13:38:19.083949868 +0200 @@ -38,6 +38,16 @@ static ToolbarGTKMetrics sToolbarMetrics #define GTK_STATE_FLAG_CHECKED (1 << 11) #endif @@ -104,9 +473,9 @@ diff -up firefox-60.0/widget/gtk/gtk3drawing.cpp.old firefox-60.0/widget/gtk/gtk /* cairo_t *cr argument has to be a system-cairo. */ gint moz_gtk_widget_paint(WidgetNodeType widget, cairo_t *cr, -diff -up firefox-60.0/widget/gtk/gtkdrawing.h.old firefox-60.0/widget/gtk/gtkdrawing.h ---- firefox-60.0/widget/gtk/gtkdrawing.h.old 2018-04-26 22:07:35.000000000 +0200 -+++ firefox-60.0/widget/gtk/gtkdrawing.h 2018-04-30 11:59:06.750866104 +0200 +diff -up firefox-60.0/widget/gtk/gtkdrawing.h.orig firefox-60.0/widget/gtk/gtkdrawing.h +--- firefox-60.0/widget/gtk/gtkdrawing.h.orig 2018-04-26 22:07:35.000000000 +0200 ++++ firefox-60.0/widget/gtk/gtkdrawing.h 2018-04-30 13:38:19.083949868 +0200 @@ -334,6 +334,10 @@ typedef enum { */ MOZ_GTK_HEADER_BAR_BUTTON_MAXIMIZE_RESTORE, @@ -136,9 +505,9 @@ diff -up firefox-60.0/widget/gtk/gtkdrawing.h.old firefox-60.0/widget/gtk/gtkdra +GetCSDDecorationSize(GtkWindow *aGtkWindow, GtkBorder* aDecorationSize); + #endif -diff -up firefox-60.0/widget/gtk/nsWindow.cpp.old firefox-60.0/widget/gtk/nsWindow.cpp ---- firefox-60.0/widget/gtk/nsWindow.cpp.old 2018-04-30 12:01:38.788343254 +0200 -+++ firefox-60.0/widget/gtk/nsWindow.cpp 2018-04-30 12:00:01.012679502 +0200 +diff -up firefox-60.0/widget/gtk/nsWindow.cpp.orig firefox-60.0/widget/gtk/nsWindow.cpp +--- firefox-60.0/widget/gtk/nsWindow.cpp.orig 2018-04-30 13:37:32.145122854 +0200 ++++ firefox-60.0/widget/gtk/nsWindow.cpp 2018-04-30 13:39:12.593752681 +0200 @@ -127,6 +127,7 @@ using namespace mozilla::widget; #endif @@ -158,7 +527,7 @@ diff -up firefox-60.0/widget/gtk/nsWindow.cpp.old firefox-60.0/widget/gtk/nsWind } void -@@ -6556,6 +6561,32 @@ nsWindow::ClearCachedResources() +@@ -6552,6 +6557,32 @@ nsWindow::ClearCachedResources() } } @@ -191,24 +560,24 @@ diff -up firefox-60.0/widget/gtk/nsWindow.cpp.old firefox-60.0/widget/gtk/nsWind nsresult nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &aMargins) { -@@ -6628,6 +6659,13 @@ nsWindow::SetDrawsInTitlebar(bool aState - mNeedsShow = true; - NativeResize(); +@@ -6626,6 +6657,13 @@ nsWindow::SetDrawsInTitlebar(bool aState + mNeedsShow = true; + NativeResize(); -+ // When we use system titlebar setup managed by Gtk+ we also get -+ // _NET_FRAME_EXTENTS property for our toplevel window so we can't -+ // update the client offset it here. -+ if (aState) { -+ UpdateClientOffsetForCSDWindow(); -+ } ++ // When we use system titlebar setup managed by Gtk+ we also get ++ // _NET_FRAME_EXTENTS property for our toplevel window so we can't ++ // update the client offset it here. ++ if (aState) { ++ UpdateClientOffsetForCSDWindow(); ++ } + - gtk_widget_destroy(tmpWindow); - } - } -diff -up firefox-60.0/widget/gtk/nsWindow.h.old firefox-60.0/widget/gtk/nsWindow.h ---- firefox-60.0/widget/gtk/nsWindow.h.old 2018-04-26 22:07:35.000000000 +0200 -+++ firefox-60.0/widget/gtk/nsWindow.h 2018-04-30 11:57:33.656146337 +0200 -@@ -456,6 +456,8 @@ private: + gtk_widget_destroy(tmpWindow); + } + +diff -up firefox-60.0/widget/gtk/nsWindow.h.orig firefox-60.0/widget/gtk/nsWindow.h +--- firefox-60.0/widget/gtk/nsWindow.h.orig 2018-04-30 13:37:32.143122861 +0200 ++++ firefox-60.0/widget/gtk/nsWindow.h 2018-04-30 13:38:19.085949861 +0200 +@@ -454,6 +454,8 @@ private: nsIWidgetListener* GetListener(); bool IsComposited() const; @@ -217,9 +586,9 @@ diff -up firefox-60.0/widget/gtk/nsWindow.h.old firefox-60.0/widget/gtk/nsWindow GtkWidget *mShell; MozContainer *mContainer; GdkWindow *mGdkWindow; -diff -up firefox-60.0/widget/gtk/WidgetStyleCache.cpp.old firefox-60.0/widget/gtk/WidgetStyleCache.cpp ---- firefox-60.0/widget/gtk/WidgetStyleCache.cpp.old 2018-04-26 22:07:35.000000000 +0200 -+++ firefox-60.0/widget/gtk/WidgetStyleCache.cpp 2018-04-30 13:07:04.170056312 +0200 +diff -up firefox-60.0/widget/gtk/WidgetStyleCache.cpp.orig firefox-60.0/widget/gtk/WidgetStyleCache.cpp +--- firefox-60.0/widget/gtk/WidgetStyleCache.cpp.orig 2018-04-26 22:07:35.000000000 +0200 ++++ firefox-60.0/widget/gtk/WidgetStyleCache.cpp 2018-04-30 13:38:19.085949861 +0200 @@ -1285,6 +1285,22 @@ GetCssNodeStyleInternal(WidgetNodeType a "MOZ_GTK_HEADER_BAR_BUTTON_RESTORE is used as an icon only!"); return nullptr; diff --git a/firefox.spec b/firefox.spec index 01d81a9..ab10326 100644 --- a/firefox.spec +++ b/firefox.spec @@ -154,7 +154,8 @@ Patch413: mozilla-1353817.patch Patch414: mozilla-1435212-ffmpeg-4.0.patch Patch415: Bug-1238661---fix-mozillaSignalTrampoline-to-work-.patch Patch416: mozilla-1424422.patch -Patch417: complete-csd-window-offset-mozilla-1457691.patch + +Patch421: complete-csd-window-offset-mozilla-1457691.patch # Debian patches Patch500: mozilla-440908.patch @@ -324,7 +325,8 @@ This package contains results of tests executed during build. %patch415 -p1 -b .mozilla-1238661 %endif %patch416 -p1 -b .1424422 -%patch417 -p1 -b .complete-csd-1457691 + +%patch421 -p1 -b .mozilla-1457691 # Patch for big endian platforms only %if 0%{?big_endian}