summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <[email protected]>2023-11-13 11:08:31 +0100
committerTor Arne Vestbø <[email protected]>2023-11-15 18:25:04 +0100
commitc3a1fe7bd754abc1fc1370e574567aa79b237e31 (patch)
treea341ab05b2df264d6d25b5d1921f50dc0ee0b974
parent0a0f7b864b0902fcedcc8cd26dd28779d36242ff (diff)
Prevent reparenting of foreign window embedding container
A foreign window used to embed a Qt window into it should not end up with changes to its own parent, as its only job is to give the embedded Qt window a parent handle. Pick-to: 6.6 Change-Id: If1bc89658fedf449d266bc0cc750c90b6a841a68 Reviewed-by: Richard Moe Gustavsen <[email protected]> Reviewed-by: Oliver Wolff <[email protected]>
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm5
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm7
-rw-r--r--tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp45
-rw-r--r--tests/shared/nativewindow.h60
4 files changed, 108 insertions, 9 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 71d2cd8b2ae..05b9f19fec8 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -150,7 +150,10 @@ QCocoaWindow::~QCocoaWindow()
QMacAutoReleasePool pool;
[m_nsWindow makeFirstResponder:nil];
[m_nsWindow setContentView:nil];
- if ([m_view superview])
+
+ // Remove from superview only if we have a Qt window parent,
+ // as we don't want to affect window container foreign windows.
+ if (QPlatformWindow::parent())
[m_view removeFromSuperview];
// Make sure to disconnect observer in all case if view is valid
diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm
index 84a3f55f558..9f12122432e 100644
--- a/src/plugins/platforms/ios/qioswindow.mm
+++ b/src/plugins/platforms/ios/qioswindow.mm
@@ -91,7 +91,12 @@ QIOSWindow::~QIOSWindow()
clearAccessibleCache();
quiview_cast(m_view).platformWindow = 0;
- [m_view removeFromSuperview];
+
+ // Remove from superview only if we have a Qt window parent,
+ // as we don't want to affect window container foreign windows.
+ if (QPlatformWindow::parent())
+ [m_view removeFromSuperview];
+
[m_view release];
}
diff --git a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp
index a43939a21b4..ac0f78be2de 100644
--- a/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp
+++ b/tests/auto/gui/kernel/qwindow/tst_foreignwindow.cpp
@@ -25,6 +25,7 @@ private slots:
void initialState();
void embedForeignWindow();
+ void embedInForeignWindow();
};
void tst_ForeignWindow::fromWinId()
@@ -94,5 +95,49 @@ void tst_ForeignWindow::embedForeignWindow()
QTRY_COMPARE(nativeWindow.parentWinId(), originalParentWinId);
}
+void tst_ForeignWindow::embedInForeignWindow()
+{
+ // When a foreign window is used as a container to embed a Qt UI
+ // in a foreign window hierarchy, the foreign window merely acts
+ // as a parent, and should not be modified.
+
+ {
+ // At a minimum, we must be able to reparent into the window
+ NativeWindow nativeWindow;
+ QVERIFY(nativeWindow);
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(nativeWindow));
+
+ QWindow embeddedWindow;
+ embeddedWindow.setParent(foreignWindow.get());
+ QTRY_VERIFY(nativeWindow.isParentOf(embeddedWindow.winId()));
+ }
+
+ {
+ // The foreign window's native window should not be reparent as a
+ // result of creating the foreign window, adding and removing children,
+ // or destroying the foreign window.
+
+ NativeWindow topLevelNativeWindow;
+ NativeWindow childNativeWindow;
+ childNativeWindow.setParent(topLevelNativeWindow);
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ std::unique_ptr<QWindow> foreignWindow(QWindow::fromWinId(childNativeWindow));
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ QWindow embeddedWindow;
+ embeddedWindow.setParent(foreignWindow.get());
+ QTRY_VERIFY(childNativeWindow.isParentOf(embeddedWindow.winId()));
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ embeddedWindow.setParent(nullptr);
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+
+ foreignWindow.reset();
+ QVERIFY(topLevelNativeWindow.isParentOf(childNativeWindow));
+ }
+}
+
#include <tst_foreignwindow.moc>
QTEST_MAIN(tst_ForeignWindow)
diff --git a/tests/shared/nativewindow.h b/tests/shared/nativewindow.h
index 087b7da0fc6..8b5ad97836c 100644
--- a/tests/shared/nativewindow.h
+++ b/tests/shared/nativewindow.h
@@ -20,23 +20,29 @@ class NativeWindow
{
Q_DISABLE_COPY(NativeWindow)
public:
+#if defined(Q_OS_MACOS)
+ using Handle = NSView*;
+#elif defined(Q_OS_IOS)
+ using Handle = UIView*;
+#elif defined(Q_OS_WIN)
+ using Handle = HWND;
+#elif QT_CONFIG(xcb)
+ using Handle = xcb_window_t;
+#endif
+
NativeWindow();
~NativeWindow();
operator WId() const;
WId parentWinId() const;
+ bool isParentOf(WId childWinId);
+ void setParent(WId parent);
void setGeometry(const QRect &rect);
QRect geometry() const;
private:
-#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
- VIEW_BASE *m_handle = nullptr;
-#elif defined(Q_OS_WIN)
- HWND m_handle = nullptr;
-#elif QT_CONFIG(xcb)
- xcb_window_t m_handle = 0;
-#endif
+ Handle m_handle = {};
};
#if defined(Q_OS_MACOS) || defined(Q_OS_IOS)
@@ -92,6 +98,20 @@ WId NativeWindow::parentWinId() const
return WId(m_handle.superview);
}
+bool NativeWindow::isParentOf(WId childWinId)
+{
+ auto *subview = reinterpret_cast<Handle>(childWinId);
+ return subview.superview == m_handle;
+}
+
+void NativeWindow::setParent(WId parent)
+{
+ if (auto *superview = reinterpret_cast<Handle>(parent))
+ [superview addSubview:m_handle];
+ else
+ [m_handle removeFromSuperview];
+}
+
#elif defined(Q_OS_WIN)
NativeWindow::NativeWindow()
@@ -141,6 +161,16 @@ WId NativeWindow::parentWinId() const
return WId(GetAncestor(m_handle, GA_PARENT));
}
+bool NativeWindow::isParentOf(WId childWinId)
+{
+ return GetAncestor(Handle(childWinId), GA_PARENT) == m_handle;
+}
+
+void NativeWindow::setParent(WId parent)
+{
+ SetParent(m_handle, Handle(parent));
+}
+
#elif QT_CONFIG(xcb)
struct Connection
@@ -203,6 +233,22 @@ WId NativeWindow::parentWinId() const
return tree->parent;
}
+bool NativeWindow::isParentOf(WId childWinId)
+{
+ xcb_query_tree_reply_t *tree = xcb_query_tree_reply(
+ connection, xcb_query_tree(connection, Handle(childWinId)), nullptr);
+ const auto cleanup = qScopeGuard([&]{ free(tree); });
+ return tree->parent == m_handle;
+}
+
+void NativeWindow::setParent(WId parent)
+{
+ xcb_screen_t *screen = xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
+
+ xcb_reparent_window(connection, m_handle,
+ parent ? Handle(parent) : screen->root, 0, 0);
+}
+
#endif
#endif // NATIVEWINDOW_H