Bug 1399956 - Track top level windows in headless mode for window ordering. r?jrmuizel
This better emulates a window manager and triggers the
required activated/deactivated events on the proper windows as
they are closed or hidden.
MozReview-Commit-ID: 1A2JTp8i4VE
--- a/widget/headless/HeadlessWidget.cpp
+++ b/widget/headless/HeadlessWidget.cpp
@@ -3,16 +3,17 @@
* 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 "HeadlessWidget.h"
#include "HeadlessCompositorWidget.h"
#include "Layers.h"
#include "BasicLayers.h"
#include "BasicEvents.h"
#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ClearOnShutdown.h"
using namespace mozilla::gfx;
using namespace mozilla::layers;
/*static*/ already_AddRefed<nsIWidget>
nsIWidget::CreateHeadlessWidget()
{
nsCOMPtr<nsIWidget> widget = new mozilla::widget::HeadlessWidget();
@@ -30,33 +31,79 @@ CreateDefaultTarget(IntSize aSize)
IntSize size = (aSize.width <= 0 || aSize.height <= 0) ? gfx::IntSize(1, 1) : aSize;
RefPtr<DrawTarget> target = Factory::CreateDrawTarget(gfxVars::ContentBackend(), size, SurfaceFormat::B8G8R8A8);
RefPtr<gfxContext> ctx = gfxContext::CreatePreservingTransformOrNull(target);
return ctx.forget();
}
NS_IMPL_ISUPPORTS_INHERITED0(HeadlessWidget, nsBaseWidget)
-HeadlessWidget* HeadlessWidget::sActiveWindow = nullptr;
+StaticAutoPtr<nsTArray<HeadlessWidget*>> HeadlessWidget::sActiveWindows;
+
+already_AddRefed<HeadlessWidget>
+HeadlessWidget::GetActiveWindow()
+{
+ if (!sActiveWindows) {
+ return nullptr;
+ }
+ auto length = sActiveWindows->Length();
+ if (length == 0) {
+ return nullptr;
+ }
+ RefPtr<HeadlessWidget> widget = sActiveWindows->ElementAt(length - 1);
+ return widget.forget();
+}
HeadlessWidget::HeadlessWidget()
: mEnabled(true)
, mVisible(false)
+ , mDestroyed(false)
, mTopLevel(nullptr)
, mCompositorWidget(nullptr)
, mLastSizeMode(nsSizeMode_Normal)
, mEffectiveSizeMode(nsSizeMode_Normal)
, mRestoreBounds(0,0,0,0)
{
+ if (!sActiveWindows) {
+ sActiveWindows = new nsTArray<HeadlessWidget*>();
+ ClearOnShutdown(&sActiveWindows);
+ }
}
HeadlessWidget::~HeadlessWidget()
{
- if (sActiveWindow == this)
- sActiveWindow = nullptr;
+ Destroy();
+}
+
+void
+HeadlessWidget::Destroy()
+{
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+
+ if (sActiveWindows) {
+ int32_t index = sActiveWindows->IndexOf(this);
+ if (index != -1) {
+ RefPtr<HeadlessWidget> activeWindow = GetActiveWindow();
+ sActiveWindows->RemoveElementAt(index);
+ // If this is the currently active widget and there's a previously active
+ // widget, activate the previous widget.
+ RefPtr<HeadlessWidget> previousActiveWindow = GetActiveWindow();
+ if (this == activeWindow && previousActiveWindow &&
+ previousActiveWindow->mWidgetListener) {
+ previousActiveWindow->mWidgetListener->WindowActivated();
+ }
+ }
+ }
+
+ nsBaseWidget::OnDestroy();
+
+ nsBaseWidget::Destroy();
}
nsresult
HeadlessWidget::Create(nsIWidget* aParent,
nsNativeWidget aNativeParent,
const LayoutDeviceIntRect& aRect,
nsWidgetInitData* aInitData)
{
@@ -100,45 +147,56 @@ nsIWidget*
HeadlessWidget::GetTopLevelWidget()
{
return mTopLevel;
}
void
HeadlessWidget::RaiseWindow()
{
- MOZ_ASSERT(mTopLevel == this, "Raising a non-toplevel window.");
+ MOZ_ASSERT(mTopLevel == this || mWindowType == eWindowType_dialog, "Raising a non-toplevel window.");
- if (sActiveWindow == this)
+ // Do nothing if this is the currently active window.
+ RefPtr<HeadlessWidget> activeWindow = GetActiveWindow();
+ if (activeWindow == this) {
return;
+ }
// Raise the window to the top of the stack.
nsWindowZ placement = nsWindowZTop;
nsCOMPtr<nsIWidget> actualBelow;
if (mWidgetListener)
mWidgetListener->ZLevelChanged(true, &placement, nullptr, getter_AddRefs(actualBelow));
// Deactivate the last active window.
- if (sActiveWindow && sActiveWindow->mWidgetListener)
- sActiveWindow->mWidgetListener->WindowDeactivated();
+ if (activeWindow && activeWindow->mWidgetListener) {
+ activeWindow->mWidgetListener->WindowDeactivated();
+ }
+
+ // Remove this window if it's already tracked.
+ int32_t index = sActiveWindows->IndexOf(this);
+ if (index != -1) {
+ sActiveWindows->RemoveElementAt(index);
+ }
// Activate this window.
- sActiveWindow = this;
+ sActiveWindows->AppendElement(this);
if (mWidgetListener)
mWidgetListener->WindowActivated();
}
void
HeadlessWidget::Show(bool aState)
{
mVisible = aState;
- // Top-level windows are activated/raised when shown.
- if (aState && mTopLevel == this)
+ // Top-level window and dialogs are activated/raised when shown.
+ if (aState && (mTopLevel == this || mWindowType == eWindowType_dialog)) {
RaiseWindow();
+ }
ApplySizeModeSideEffects();
}
bool
HeadlessWidget::IsVisible() const
{
return mVisible;
--- a/widget/headless/HeadlessWidget.h
+++ b/widget/headless/HeadlessWidget.h
@@ -35,16 +35,17 @@ public:
virtual already_AddRefed<nsIWidget> CreateChild(const LayoutDeviceIntRect& aRect,
nsWidgetInitData* aInitData = nullptr,
bool aForceUseIWidgetParent = false) override;
virtual nsIWidget* GetTopLevelWidget() override;
virtual void GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData) override;
+ virtual void Destroy() override;
virtual void Show(bool aState) override;
virtual bool IsVisible() const override;
virtual void Move(double aX, double aY) override;
virtual void Resize(double aWidth,
double aHeight,
bool aRepaint) override;
virtual void Resize(double aX,
double aY,
@@ -90,29 +91,35 @@ public:
virtual nsresult DispatchEvent(WidgetGUIEvent* aEvent,
nsEventStatus& aStatus) override;
private:
~HeadlessWidget();
bool mEnabled;
bool mVisible;
+ bool mDestroyed;
nsIWidget* mTopLevel;
HeadlessCompositorWidget* mCompositorWidget;
// The size mode before entering fullscreen mode.
nsSizeMode mLastSizeMode;
// The last size mode set while the window was visible.
nsSizeMode mEffectiveSizeMode;
InputContext mInputContext;
// In headless there is no window manager to track window bounds
// across size mode changes, so we must track it to emulate.
LayoutDeviceIntRect mRestoreBounds;
void ApplySizeModeSideEffects();
// Similarly, we must track the active window ourselves in order
// to dispatch (de)activation events properly.
void RaiseWindow();
- static HeadlessWidget* sActiveWindow;
+ // The top level widgets are tracked for window ordering. They are
+ // stored in order of activation where the last element is always the
+ // currently active widget.
+ static StaticAutoPtr<nsTArray<HeadlessWidget*>> sActiveWindows;
+ // Get the most recently activated widget or null if there are none.
+ static already_AddRefed<HeadlessWidget>GetActiveWindow();
};
} // namespace widget
} // namespace mozilla
#endif