Skip to content

Commit

Permalink
Bug 1373739 - Simulate window activation events in headless mode. r=j…
Browse files Browse the repository at this point in the history
…rmuizel

This is necessary for focus events to propagate correctly. Otherwise,
/2dcontext/drawing-paths-to-the-canvas/drawFocusIfNeeded_004.html in the
Web Platform Tests fails, for example.

MozReview-Commit-ID: 69GItIaQWfr
  • Loading branch information
spinda committed Jun 21, 2017
1 parent d49ad09 commit 9d116ab
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 9 deletions.
58 changes: 51 additions & 7 deletions widget/headless/HeadlessWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ CreateDefaultTarget(IntSize aSize)

NS_IMPL_ISUPPORTS_INHERITED0(HeadlessWidget, nsBaseWidget)

HeadlessWidget* HeadlessWidget::sActiveWindow = nullptr;

HeadlessWidget::~HeadlessWidget()
{
if (sActiveWindow == this)
sActiveWindow = nullptr;
}

nsresult
HeadlessWidget::Create(nsIWidget* aParent,
nsNativeWidget aNativeParent,
Expand All @@ -43,10 +51,18 @@ HeadlessWidget::Create(nsIWidget* aParent,
MOZ_ASSERT(!aNativeParent, "No native parents for headless widgets.");

BaseCreate(nullptr, aInitData);

mBounds = aRect;
mRestoreBounds = aRect;
mVisible = true;
mEnabled = true;

if (aParent) {
mTopLevel = aParent->GetTopLevelWidget();
} else {
mTopLevel = this;
}

return NS_OK;
}

Expand All @@ -59,28 +75,50 @@ HeadlessWidget::CreateChild(const LayoutDeviceIntRect& aRect,
if (!widget) {
return nullptr;
}
if (NS_FAILED(widget->Create(nullptr, nullptr, aRect, aInitData))) {
if (NS_FAILED(widget->Create(this, nullptr, aRect, aInitData))) {
return nullptr;
}
return widget.forget();
}

nsIWidget*
HeadlessWidget::GetTopLevelWidget()
{
return mTopLevel;
}

void
HeadlessWidget::SendSetZLevelEvent()
HeadlessWidget::RaiseWindow()
{
MOZ_ASSERT(mTopLevel == this, "Raising a non-toplevel window.");

if (sActiveWindow == 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();

// Activate this window.
sActiveWindow = this;
if (mWidgetListener)
mWidgetListener->WindowActivated();
}

void
HeadlessWidget::Show(bool aState)
{
mVisible = aState;
if (aState) {
SendSetZLevelEvent();
}

// Top-level windows are activated/raised when shown.
if (aState && mTopLevel == this)
RaiseWindow();
}

bool
Expand All @@ -92,8 +130,14 @@ HeadlessWidget::IsVisible() const
nsresult
HeadlessWidget::SetFocus(bool aRaise)
{
if (aRaise && mVisible) {
SendSetZLevelEvent();
// aRaise == true means we request activation of our toplevel window.
if (aRaise) {
HeadlessWidget* topLevel = (HeadlessWidget*) GetTopLevelWidget();

// The toplevel only becomes active if it's currently visible; otherwise, it
// will be activated anyway when it's shown.
if (topLevel->IsVisible())
topLevel->RaiseWindow();
}
return NS_OK;
}
Expand Down
10 changes: 8 additions & 2 deletions widget/headless/HeadlessWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class HeadlessWidget : public nsBaseWidget
nsWidgetInitData* aInitData = nullptr,
bool aForceUseIWidgetParent = false) override;

virtual nsIWidget* GetTopLevelWidget() override;

virtual void Show(bool aState) override;
virtual bool IsVisible() const override;
virtual void Move(double aX, double aY) override;
Expand Down Expand Up @@ -85,16 +87,20 @@ class HeadlessWidget : public nsBaseWidget
nsEventStatus& aStatus) override;

private:
~HeadlessWidget() {}
~HeadlessWidget();
bool mEnabled;
bool mVisible;
nsIWidget* mTopLevel;
// The size mode before entering fullscreen mode.
nsSizeMode mLastSizeMode;
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 SendSetZLevelEvent();
// Similarly, we must track the active window ourselves in order
// to dispatch (de)activation events properly.
void RaiseWindow();
static HeadlessWidget* sActiveWindow;
};

} // namespace widget
Expand Down

0 comments on commit 9d116ab

Please sign in to comment.