--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -4,16 +4,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 "mozilla/ArrayUtils.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEventDispatcherListener.h"
+#include "mozilla/Atomics.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "nsBaseWidget.h"
#include "nsDeviceContext.h"
#include "nsCOMPtr.h"
#include "nsGfxCIID.h"
#include "nsWidgetsCID.h"
@@ -39,41 +40,53 @@
#include "npapi.h"
#include "base/thread.h"
#include "prdtoa.h"
#include "prenv.h"
#include "mozilla/Attributes.h"
#include "mozilla/unused.h"
#include "nsContentUtils.h"
#include "gfxPrefs.h"
+#include "gfxVR.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/MouseEvents.h"
#include "GLConsts.h"
#include "mozilla/unused.h"
#include "mozilla/IMEStateManager.h"
-#include "mozilla/VsyncDispatcher.h"
+#include "gfxVsync.h"
#include "mozilla/layers/APZCTreeManager.h"
#include "mozilla/layers/APZEventState.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/ChromeProcessController.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/Move.h"
#include "mozilla/Services.h"
#include "mozilla/Snprintf.h"
#include "nsRefPtrHashtable.h"
#include "TouchEvents.h"
#include "WritingModes.h"
#include "InputData.h"
#include "FrameLayerBuilder.h"
+#include "BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "mozilla/layout/VsyncChild.h"
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
+#ifdef MOZ_NUWA_PROCESS
+#include "ipc/Nuwa.h"
+#endif
+
+PRLogModuleInfo *gVsyncLog = nullptr;
+#define VSYNC_LOG(...) MOZ_LOG(gVsyncLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define PARENT_OR_CHILD (XRE_IsParentProcess() ? "Parent" : "Child")
#ifdef DEBUG
#include "nsIObserver.h"
static void debug_RegisterPrefCallbacks();
#endif
@@ -107,16 +120,18 @@ bool gDisableNativeTheme
#define TOUCH_INJECT_PUMP_TIMER_MSEC 50
#define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500
int32_t nsIWidget::sPointerIdCounter = 0;
// Some statics from nsIWidget.h
/*static*/ uint64_t AutoObserverNotifier::sObserverId = 0;
/*static*/ nsDataHashtable<nsUint64HashKey, nsCOMPtr<nsIObserver>> AutoObserverNotifier::sSavedObservers;
+static nsRefPtrHashtable<nsIDHashKey, layout::VsyncChild>* sVsyncChildTable;
+
namespace mozilla {
namespace widget {
void
IMENotification::SelectionChangeDataBase::SetWritingMode(
const WritingMode& aWritingMode)
{
mWritingMode = aWritingMode.mWritingMode;
@@ -150,51 +165,61 @@ NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidge
//-------------------------------------------------------------------------
//
// nsBaseWidget constructor
//
//-------------------------------------------------------------------------
nsBaseWidget::nsBaseWidget()
-: mWidgetListener(nullptr)
+: mVsyncObserversLock("Widget vsync observer lock")
+, mWidgetListener(nullptr)
, mAttachedWidgetListener(nullptr)
, mPreviouslyAttachedWidgetListener(nullptr)
, mLayerManager(nullptr)
-, mCompositorVsyncDispatcher(nullptr)
, mCursor(eCursor_standard)
, mBorderStyle(eBorderStyle_none)
, mBounds(0,0,0,0)
, mOriginalBounds(nullptr)
, mClipRectCount(0)
, mSizeMode(nsSizeMode_Normal)
, mPopupLevel(ePopupLevelTop)
, mPopupType(ePopupTypeAny)
, mUpdateCursor(true)
, mUseAttachedEvents(false)
, mIMEHasFocus(false)
#if defined(XP_WIN) || defined(XP_MACOSX)
, mAccessibilityInUseFlag(false)
#endif
{
+ if (!gVsyncLog) {
+ gVsyncLog = PR_NewLogModule("Vsync");
+ }
+
#ifdef NOISY_WIDGET_LEAKS
gNumWidgets++;
printf("WIDGETS+ = %d\n", gNumWidgets);
#endif
#ifdef DEBUG
debug_RegisterPrefCallbacks();
#endif
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
if (!sPluginWidgetList) {
sPluginWidgetList = new nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>();
}
#endif
+
+ if (!XRE_IsParentProcess() && !sVsyncChildTable) {
+ sVsyncChildTable = new nsRefPtrHashtable<nsIDHashKey, layout::VsyncChild>();
+ }
+
mShutdownObserver = new WidgetShutdownObserver(this);
+ mDesiredVsyncSourceID = kWidgetDefaultVsyncSourceID;
}
NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver)
WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget) :
mWidget(aWidget),
mRegistered(false)
{
@@ -249,16 +274,17 @@ PRTimeToSeconds(PRTime t_usec)
PRTime usec_per_sec = PR_USEC_PER_SEC;
return uint32_t(t_usec /= usec_per_sec);
}
#endif
void
nsBaseWidget::Shutdown()
{
+ ShutdownVsync();
DestroyCompositor();
FreeShutdownObserver();
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
if (sPluginWidgetList) {
delete sPluginWidgetList;
sPluginWidgetList = nullptr;
}
#endif
@@ -269,23 +295,16 @@ void nsBaseWidget::DestroyCompositor()
if (mCompositorBridgeChild) {
// XXX CompositorBridgeChild and CompositorBridgeParent might be re-created in
// ClientLayerManager destructor. See bug 1133426.
RefPtr<CompositorBridgeChild> compositorChild = mCompositorBridgeChild;
RefPtr<CompositorBridgeParent> compositorParent = mCompositorBridgeParent;
mCompositorBridgeChild->Destroy();
mCompositorBridgeChild = nullptr;
}
-
- // Can have base widgets that are things like tooltips
- // which don't have CompositorVsyncDispatchers
- if (mCompositorVsyncDispatcher) {
- mCompositorVsyncDispatcher->Shutdown();
- mCompositorVsyncDispatcher = nullptr;
- }
}
void nsBaseWidget::DestroyLayerManager()
{
if (mLayerManager) {
mLayerManager->Destroy();
mLayerManager = nullptr;
}
@@ -339,42 +358,16 @@ nsBaseWidget::FreeShutdownObserver()
if (mShutdownObserver) {
mShutdownObserver->Unregister();
}
mShutdownObserver = nullptr;
}
//-------------------------------------------------------------------------
//
-// nsBaseWidget destructor
-//
-//-------------------------------------------------------------------------
-
-nsBaseWidget::~nsBaseWidget()
-{
- IMEStateManager::WidgetDestroyed(this);
-
- if (mLayerManager &&
- mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
- static_cast<BasicLayerManager*>(mLayerManager.get())->ClearRetainerWidget();
- }
-
- FreeShutdownObserver();
- DestroyLayerManager();
-
-#ifdef NOISY_WIDGET_LEAKS
- gNumWidgets--;
- printf("WIDGETS- = %d\n", gNumWidgets);
-#endif
-
- delete mOriginalBounds;
-}
-
-//-------------------------------------------------------------------------
-//
// Basic create.
//
//-------------------------------------------------------------------------
void nsBaseWidget::BaseCreate(nsIWidget* aParent,
nsWidgetInitData* aInitData)
{
static bool gDisableNativeThemeCached = false;
if (!gDisableNativeThemeCached) {
@@ -390,16 +383,18 @@ void nsBaseWidget::BaseCreate(nsIWidget*
mBorderStyle = aInitData->mBorderStyle;
mPopupLevel = aInitData->mPopupLevel;
mPopupType = aInitData->mPopupHint;
}
if (aParent) {
aParent->AddChild(this);
}
+
+ UpdateVsyncObserver();
}
NS_IMETHODIMP nsBaseWidget::CaptureMouse(bool aCapture)
{
return NS_OK;
}
//-------------------------------------------------------------------------
@@ -483,35 +478,16 @@ void nsBaseWidget::SetPreviouslyAttached
void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener)
{
mAttachedWidgetListener = aListener;
}
//-------------------------------------------------------------------------
//
-// Close this nsBaseWidget
-//
-//-------------------------------------------------------------------------
-NS_METHOD nsBaseWidget::Destroy()
-{
- // Just in case our parent is the only ref to us
- nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
- // disconnect from the parent
- nsIWidget *parent = GetParent();
- if (parent) {
- parent->RemoveChild(this);
- }
-
- return NS_OK;
-}
-
-
-//-------------------------------------------------------------------------
-//
// Set this nsBaseWidget's parent
//
//-------------------------------------------------------------------------
NS_IMETHODIMP nsBaseWidget::SetParent(nsIWidget* aNewParent)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
@@ -598,16 +574,17 @@ void nsBaseWidget::AddChild(nsIWidget* a
} else {
// append to the list
MOZ_RELEASE_ASSERT(mLastChild);
MOZ_RELEASE_ASSERT(!mLastChild->GetNextSibling());
mLastChild->SetNextSibling(aChild);
aChild->SetPrevSibling(mLastChild);
mLastChild = aChild;
}
+ aChild->ParentChanged();
}
//-------------------------------------------------------------------------
//
// Remove a child from the list of children
//
//-------------------------------------------------------------------------
@@ -639,16 +616,17 @@ void nsBaseWidget::RemoveChild(nsIWidget
prev->SetNextSibling(next);
}
if (next) {
next->SetPrevSibling(prev);
}
aChild->SetNextSibling(nullptr);
aChild->SetPrevSibling(nullptr);
+ aChild->ParentChanged();
}
//-------------------------------------------------------------------------
//
// Sets widget's position within its parent's child list.
//
//-------------------------------------------------------------------------
@@ -1217,30 +1195,403 @@ nsBaseWidget::GetDocument() const
if (mWidgetListener) {
if (nsIPresShell* presShell = mWidgetListener->GetPresShell()) {
return presShell->GetDocument();
}
}
return nullptr;
}
-void nsBaseWidget::CreateCompositorVsyncDispatcher()
+class VsyncForwardingObserver;
+
+namespace {
+
+// The PBackground protocol connection callback. It will be called when
+// PBackground is ready. Then we create the PVsync sub-protocol for our
+// vsync-base RefreshTimer.
+class VsyncChildCreateCallback final : public nsIIPCBackgroundChildCreateCallback
+{
+ NS_DECL_ISUPPORTS
+
+public:
+ VsyncChildCreateCallback(const nsID& aSourceID, VsyncForwardingObserver *aObserver)
+ : mSourceID(aSourceID)
+ , mObserver(aObserver)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ }
+
+ static void CreateVsyncActor(PBackgroundChild* aPBackgroundChild, const nsID& aSourceID, VsyncForwardingObserver *aObserver);
+
+protected:
+ virtual ~VsyncChildCreateCallback() {}
+
+ virtual void ActorCreated(PBackgroundChild* aPBackgroundChild) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPBackgroundChild);
+ CreateVsyncActor(aPBackgroundChild, mSourceID, mObserver);
+ }
+
+ virtual void ActorFailed() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_CRASH("Failed To Create VsyncChild Actor");
+ }
+
+ nsID mSourceID;
+ RefPtr<VsyncForwardingObserver> mObserver;
+}; // VsyncChildCreateCallback
+NS_IMPL_ISUPPORTS(VsyncChildCreateCallback, nsIIPCBackgroundChildCreateCallback)
+
+} // namespace anon
+
+/**
+ * VsyncForwardingObserver is both a VsyncObserver and a VsyncSource,
+ * and is owned by a nsBaseWidget. There's a 1:1 relationship between
+ * a widget and the VsyncForwardingObserver.
+ *
+ * This observer establishes a vsync forwarding graph -- a vsync signal
+ * is given to the topmost widget's VsyncForwardingObserver, which
+ * will then notify any observers listening to it (which may be other
+ * widgets' VFOs) and so on.
+ *
+ * This allows us to switch the source at a single point (the toplevel
+ * widget in this case -- such as when the window moves to a different
+ * monitor) and have everything further down the line get a new vsync
+ * signal without needing to be aware of the change.
+ */
+class VsyncForwardingObserver final :
+ public gfx::VsyncObserver,
+ public gfx::VsyncSource
{
- // Parent directly listens to the vsync source whereas
- // child process communicate via IPC
- // Should be called AFTER gfxPlatform is initialized
- if (XRE_IsParentProcess()) {
- mCompositorVsyncDispatcher = new CompositorVsyncDispatcher();
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncForwardingObserver, override)
+
+public:
+ explicit VsyncForwardingObserver()
+ : mPaused(true)
+ , mObservedWidget(nullptr)
+ {
+ }
+
+ void NotifyVsync(mozilla::TimeStamp aVsyncTimestamp) override {
+ // incoming notification, pass it along
+ VSYNC_LOG("VFO[%p].(%d->%d)\n", this, (bool) mPaused, mVsyncObservers.Length());
+ if (!mPaused) {
+ OnVsync(aVsyncTimestamp);
+ }
+ }
+
+ bool IsVsyncEnabled() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ return !mPaused;
+ }
+
+ void DisableVsync() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mPaused) {
+ return;
+ }
+
+ mPaused = true;
+
+ PauseInternal();
+ }
+
+ void EnableVsync() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mPaused) {
+ return;
+ }
+
+ mPaused = false;
+
+ if (mObservedWidget) {
+ mObservedWidget->AddVsyncObserver(this);
+ return;
+ }
+
+ ObserveSourceID(mSourceID);
+ }
+
+ TimeDuration GetVsyncInterval() override {
+ if (AttachedSource()) {
+ return AttachedSource()->GetVsyncInterval();
+ }
+
+ return VsyncSource::GetVsyncInterval();
+ }
+
+ void ObserveWidget(nsIWidget *aWidget) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mObservedWidget == aWidget) {
+ return;
+ }
+
+ Unobserve();
+
+ mObservedWidget = aWidget;
+
+ if (!mPaused) {
+ mObservedWidget->AddVsyncObserver(this);
+ }
+ }
+
+ void ObserveSource(gfx::VsyncSource* aSource) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (AttachedSource() == aSource) {
+ return;
+ }
+
+ Unobserve();
+
+ mSourceID = aSource->ID();
+
+ if (!mPaused) {
+ aSource->AddVsyncObserver(this);
+ }
+ }
+
+ void ObserveSourceID(const nsID& aVsyncSourceID) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!(aVsyncSourceID == nsIWidget::kWidgetDefaultVsyncSourceID));
+
+ Unobserve();
+
+ mSourceID = aVsyncSourceID;
+
+#ifdef MOZ_NUWA_PROCESS
+ // If we're the NUWA process, we don't actually observe anything here. We can't
+ // set up a PVSync connection because we don't have Clone set up (and we don't need
+ // it anyway).
+ if (IsNuwaProcess()) {
+ return;
+ }
+#endif
+
+ RefPtr<gfx::VsyncSource> display;
+ if (XRE_IsParentProcess()) {
+ // Parent; we can grab the vsync display directly
+ display = gfxPlatform::GetPlatform()->GetHardwareVsync()->GetSource(aVsyncSourceID);
+ ObserveSource(display);
+ } else {
+ // Child
+
+ // first check if we already have a VsyncChild for the display id
+ display = sVsyncChildTable->GetWeak(aVsyncSourceID);
+ if (display) {
+ // Yes? Great; observe it and move on.
+ ObserveSource(display);
+ return;
+ }
+
+ // No? Set up a new VsyncChild via PBackground.
+ PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
+ if (backgroundChild) {
+ // If we already have PBackground connection, create Vsync actor directly without going
+ // through a callback
+ VsyncChildCreateCallback::CreateVsyncActor(backgroundChild, aVsyncSourceID, this);
+ } else {
+ // Otherwise, set up a callback for when PBackground is created
+ RefPtr<nsIIPCBackgroundChildCreateCallback> callback = new VsyncChildCreateCallback(aVsyncSourceID, this);
+ if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
+ MOZ_CRASH("PVsync actor create failed!");
+ }
+ }
+ }
+ }
+
+ void Destroy() {
+ VSYNC_LOG("VFO[%p]::Destroy()\n", this);
+ mPaused = true;
+ Unobserve();
+ }
+
+ nsID GetObservedSourceID() {
+ return mSourceID;
+ }
+
+protected:
+ virtual ~VsyncForwardingObserver() {
+ VSYNC_LOG("VFO[%p]::~VFO()\n", this);
+ Destroy();
+ }
+
+ void Unobserve() {
+ // Call PauseInternal directly, because we don't
+ // want to actually changed mPaused
+ PauseInternal();
+
+ mObservedWidget = nullptr;
+ mSourceID = gfx::VsyncManager::kGlobalDisplaySourceID;
+ }
+
+ void PauseInternal() {
+ if (mObservedWidget) {
+ VSYNC_LOG("VFO[%p]::RVOw(%p)\n", this, mObservedWidget);
+ mObservedWidget->RemoveVsyncObserver(this);
+ return;
+ }
+
+ if (AttachedSource()) {
+ VSYNC_LOG("VFO[%p]::RVOs(%p)\n", this, AttachedSource());
+ AttachedSource()->RemoveVsyncObserver(this);
+ return;
+ }
+ }
+
+ Atomic<bool> mPaused;
+ nsID mSourceID;
+ // If we're observing a widget. It will always be a parent, which means
+ // that it will hold a ref to us in its children.
+ nsIWidget* mObservedWidget;
+};
+
+/* static */ void
+VsyncChildCreateCallback::CreateVsyncActor(PBackgroundChild* aPBackgroundChild,
+ const nsID& aSourceID,
+ VsyncForwardingObserver *aObserver)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPBackgroundChild);
+ MOZ_ASSERT(!XRE_IsParentProcess());
+
+ // There is a chance we issued multiple callbacks for the same display ID,
+ // especially on startup while PBackground is still being set up. Check that
+ // here, and avoid creating an unnecessary child.
+ RefPtr<layout::VsyncChild> child = sVsyncChildTable->GetWeak(aSourceID);
+ if (!child) {
+ // no, we need to construct it
+ layout::PVsyncChild* actor = aPBackgroundChild->SendPVsyncConstructor(aSourceID);
+ child = static_cast<layout::VsyncChild*>(actor);
+ // add it to the table
+ sVsyncChildTable->Put(aSourceID, child);
+
+ VSYNC_LOG("[%s]: Vsync actor %p created for display ID %s\n", PARENT_OR_CHILD, child.get(), nsIDToCString(aSourceID).get());
+ }
+
+ gfx::VsyncSource* display = static_cast<gfx::VsyncSource*>(child.get());
+
+ aObserver->ObserveSource(display);
+}
+
+/* static */ const nsID nsIWidget::kWidgetDefaultVsyncSourceID =
+ { 0x9e25db7c, 0x5d67, 0x4449, { 0x86, 0xa2, 0x66, 0x12, 0x07, 0x1a, 0x48, 0xdc } };
+
+// impl for nsIWidget's generic version
+nsID
+nsIWidget::GetVsyncSourceIdentifier()
+{
+ return nsIWidget::kWidgetDefaultVsyncSourceID;
+}
+
+// the actual nsBaseWidget version
+nsID
+nsBaseWidget::GetVsyncSourceIdentifier()
+{
+ if (mIncomingVsyncObserver) {
+ return mIncomingVsyncObserver->GetObservedSourceID();
+ }
+
+ return mDesiredVsyncSourceID;
+}
+
+/* static */ void
+nsBaseWidget::ForceUpdateVSyncObserver(nsBaseWidget *aWidget)
+{
+ aWidget->UpdateVsyncObserver();
+}
+
+/**
+ * UpdateVsyncObserver will create the actual incoming vsync observer
+ * if one doesn't exist, and attach it to the right source for this widget.
+ * If this widget is not the toplevel widget, it will listen to the toplevel
+ * widget. Otherwise, it will listen to whatever desired vsync source ID
+ * was set for this widget.
+ *
+ * This will get called after this one widget's vsync source was modified, e.g.
+ * if this particular widget was made fullscreen on a special display (such as
+ * a VR HMD) and needs to listen to its vsync source. When those circumstances
+ * end, UpdateVsyncObserver brings it back to the "normal" situation.
+ */
+void
+nsBaseWidget::UpdateVsyncObserver()
+{
+ // If we were already notified of shutdown, don't try to set up vsync
+ if (!mShutdownObserver) {
+ return;
+ }
+
+ if (!mIncomingVsyncObserver) {
+ mIncomingVsyncObserver = new VsyncForwardingObserver();
+ VSYNC_LOG("[%s]: Widget %p creating incoming vsync observer %p\n", PARENT_OR_CHILD, this, mIncomingVsyncObserver.get());
+#ifdef MOZ_NUWA_PROCESS
+ // After NUWA fork, make sure we set up the observer connection properly
+ NuwaAddConstructor((void (*)(void *))&ForceUpdateVSyncObserver, this);
+#endif
+ }
+
+ if (mDesiredVsyncSourceID == kWidgetDefaultVsyncSourceID) {
+ if (GetTopLevelWidget() != this) {
+ // if we're not the root widget, then just observe the root widget which will
+ // forward to our own listeners.
+ mIncomingVsyncObserver->ObserveWidget(GetTopLevelWidget());
+
+ VSYNC_LOG("[%s]: Widget %p observing root widget %p (vsync ID %s)\n", PARENT_OR_CHILD, this, GetTopLevelWidget(),
+ nsIDToCString(mIncomingVsyncObserver->GetObservedSourceID()).get());
+ } else {
+ // XXX this should be source-id-for-monitor, once we have multimonitor
+ mIncomingVsyncObserver->ObserveSourceID(gfx::VsyncManager::kGlobalDisplaySourceID);
+ }
+ } else {
+ mIncomingVsyncObserver->ObserveSourceID(mDesiredVsyncSourceID);
+ }
+
+ VSYNC_LOG("[%s]: Widget %p observing vsync ID %s\n", PARENT_OR_CHILD, this, nsIDToCString(mDesiredVsyncSourceID).get());
+}
+
+void
+nsBaseWidget::AddVsyncObserver(gfx::VsyncObserver *aObserver)
+{
+ // we might not have one if Destroy() was already called but something
+ // (such as a compositor) might still have some refs and try to enable
+ // vsync
+ if (mIncomingVsyncObserver) {
+ mIncomingVsyncObserver->AddVsyncObserver(aObserver);
}
}
-CompositorVsyncDispatcher*
-nsBaseWidget::GetCompositorVsyncDispatcher()
+void
+nsBaseWidget::RemoveVsyncObserver(gfx::VsyncObserver *aObserver)
+{
+ if (mIncomingVsyncObserver) {
+ mIncomingVsyncObserver->RemoveVsyncObserver(aObserver);
+ }
+}
+
+void
+nsBaseWidget::ShutdownVsync()
{
- return mCompositorVsyncDispatcher;
+ DestroyVsync();
+
+ if (sVsyncChildTable) {
+ delete sVsyncChildTable;
+ sVsyncChildTable = nullptr;
+ }
+}
+
+void
+nsBaseWidget::DestroyVsync()
+{
+ VSYNC_LOG("BW[%p]::DestroyVsync(obs %p)\n", this, mIncomingVsyncObserver.get());
+
+ if (mIncomingVsyncObserver) {
+ mIncomingVsyncObserver->Destroy();
+ mIncomingVsyncObserver = nullptr;
+ }
}
void nsBaseWidget::CreateCompositor(int aWidth, int aHeight)
{
// This makes sure that gfxPlatforms gets initialized if it hasn't by now.
gfxPlatform::GetPlatform();
MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
@@ -1257,17 +1608,16 @@ void nsBaseWidget::CreateCompositor(int
// to make sure it's properly destroyed by calling DestroyCompositor!
// If we've already received a shutdown notification, don't try
// create a new compositor.
if (!mShutdownObserver) {
return;
}
- CreateCompositorVsyncDispatcher();
mCompositorBridgeParent = NewCompositorBridgeParent(aWidth, aHeight);
RefPtr<ClientLayerManager> lm = new ClientLayerManager(this);
mCompositorBridgeChild = new CompositorBridgeChild(lm);
mCompositorBridgeChild->OpenSameProcess(mCompositorBridgeParent);
// Make sure the parent knows it is same process.
mCompositorBridgeParent->SetOtherProcessId(base::GetCurrentProcId());
@@ -1304,17 +1654,16 @@ void nsBaseWidget::CreateCompositor(int
if (mRootContentController) {
mRootContentController->Destroy();
mRootContentController = nullptr;
}
DestroyCompositor();
mLayerManager = nullptr;
mCompositorBridgeChild = nullptr;
mCompositorBridgeParent = nullptr;
- mCompositorVsyncDispatcher = nullptr;
return;
}
lf->SetShadowManager(shadowManager);
lf->IdentifyTextureHost(textureFactoryIdentifier);
ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
WindowUsesOMTC();
@@ -2085,16 +2434,78 @@ nsBaseWidget::UnregisterPluginWindowForR
NS_WARNING("This is not a valid native widget!");
return;
}
MOZ_ASSERT(sPluginWidgetList);
sPluginWidgetList->Remove(id);
#endif
}
+void
+nsBaseWidget::ParentChanged()
+{
+ // Our hierarchy changed, so make sure we're still observing
+ // the right widget (if we have a parent) or source (if we don't).
+ if (mIncomingVsyncObserver) {
+ UpdateVsyncObserver();
+ }
+}
+
+//-------------------------------------------------------------------------
+//
+// Close this nsBaseWidget
+//
+//-------------------------------------------------------------------------
+NS_METHOD nsBaseWidget::Destroy()
+{
+ // Just in case our parent is the only ref to us
+ nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
+
+ // Do this before disconnecting from parent, to avoid
+ // churning the vsync observer during destruction
+ DestroyVsync();
+
+ // disconnect from the parent
+ nsIWidget *parent = GetParent();
+ if (parent) {
+ parent->RemoveChild(this);
+ }
+
+ return NS_OK;
+}
+
+//-------------------------------------------------------------------------
+//
+// nsBaseWidget destructor
+//
+//-------------------------------------------------------------------------
+nsBaseWidget::~nsBaseWidget()
+{
+ VSYNC_LOG("BW[%p]::~nsBW()\n", this);
+ VSYNC_LOG("%p nsBaseWidget::~nsBaseWidget\n", this);
+
+ IMEStateManager::WidgetDestroyed(this);
+
+ if (mLayerManager &&
+ mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
+ static_cast<BasicLayerManager*>(mLayerManager.get())->ClearRetainerWidget();
+ }
+
+ DestroyVsync();
+ FreeShutdownObserver();
+ DestroyLayerManager();
+
+#ifdef NOISY_WIDGET_LEAKS
+ gNumWidgets--;
+ printf("WIDGETS- = %d\n", gNumWidgets);
+#endif
+
+ delete mOriginalBounds;
+}
+
// static
nsIWidget*
nsIWidget::LookupRegisteredPluginWindow(uintptr_t aWindowID)
{
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
NS_NOTREACHED("nsBaseWidget::LookupRegisteredPluginWindow not implemented!");
return nullptr;
#else
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -3,29 +3,31 @@
* 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 nsBaseWidget_h__
#define nsBaseWidget_h__
#include "mozilla/EventForwards.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
+#include "mozilla/Mutex.h"
#include "mozilla/WidgetUtils.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "nsRect.h"
#include "nsIWidget.h"
#include "nsWidgetsCID.h"
#include "nsIFile.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIRollupListener.h"
#include "nsIObserver.h"
#include "nsIWidgetListener.h"
#include "nsPIDOMWindow.h"
#include "nsWeakReference.h"
+
#include <algorithm>
class nsIContent;
class nsAutoRollup;
class gfxContext;
namespace mozilla {
#ifdef ACCESSIBILITY
namespace a11y {
@@ -42,28 +44,36 @@ class BasicLayerManager;
class CompositorBridgeChild;
class CompositorBridgeParent;
class APZCTreeManager;
class GeckoContentController;
class APZEventState;
struct ScrollableLayerGuid;
} // namespace layers
-class CompositorVsyncDispatcher;
+namespace layout {
+class VsyncChild;
+} // namespace layout (XXX to move from layout to gfx)
+
+namespace gfx {
+class VsyncObserver;
+} // namespace gfx
} // namespace mozilla
namespace base {
class Thread;
} // namespace base
// Windows specific constant indicating the maximum number of touch points the
// inject api will allow. This also sets the maximum numerical value for touch
// ids we can use when injecting touch points on Windows.
#define TOUCH_INJECT_MAX_POINTS 256
+class VsyncForwardingObserver;
+class WidgetVsyncSource;
class nsBaseWidget;
// Helper class used in shutting down gfx related code.
class WidgetShutdownObserver final : public nsIObserver
{
~WidgetShutdownObserver();
public:
@@ -123,16 +133,17 @@ public:
NS_IMETHOD Destroy() override;
NS_IMETHOD SetParent(nsIWidget* aNewParent) override;
virtual nsIWidget* GetParent(void) override;
virtual nsIWidget* GetTopLevelWidget() override;
virtual nsIWidget* GetSheetWindowParent(void) override;
virtual float GetDPI() override;
virtual void AddChild(nsIWidget* aChild) override;
virtual void RemoveChild(nsIWidget* aChild) override;
+ virtual void ParentChanged() override;
void SetZIndex(int32_t aZIndex) override;
NS_IMETHOD PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
nsIWidget *aWidget, bool aActivate) override;
NS_IMETHOD SetSizeMode(nsSizeMode aMode) override;
virtual nsSizeMode SizeMode() override
{
@@ -159,18 +170,16 @@ public:
nsIRunnable* aCallback) override;
NS_IMETHOD MakeFullScreen(bool aFullScreen, nsIScreen* aScreen = nullptr) override;
virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
bool* aAllowRetaining = nullptr) override;
- CompositorVsyncDispatcher* GetCompositorVsyncDispatcher() override;
- void CreateCompositorVsyncDispatcher();
virtual CompositorBridgeParent* NewCompositorBridgeParent(int aSurfaceWidth, int aSurfaceHeight);
virtual void CreateCompositor();
virtual void CreateCompositor(int aWidth, int aHeight);
virtual void PrepareWindowEffects() override {}
virtual void CleanupWindowEffects() override {}
virtual bool PreRender(LayerManagerComposite* aManager) override { return true; }
virtual void PostRender(LayerManagerComposite* aManager) override {}
virtual void DrawWindowUnderlay(LayerManagerComposite* aManager, LayoutDeviceIntRect aRect) override {}
@@ -282,16 +291,39 @@ public:
void NotifyWindowMoved(int32_t aX, int32_t aY);
// Register plugin windows for remote updates from the compositor
virtual void RegisterPluginWindowForRemoteUpdates() override;
virtual void UnregisterPluginWindowForRemoteUpdates() override;
virtual void SetNativeData(uint32_t aDataType, uintptr_t aVal) override {};
+ /*
+ * Vsync
+ */
+ void AddVsyncObserver(mozilla::gfx::VsyncObserver *aObserver) override;
+ void RemoveVsyncObserver(mozilla::gfx::VsyncObserver *aObserver) override;
+ nsID GetVsyncSourceIdentifier() override;
+
+protected:
+ friend class VsyncForwardingObserver;
+ friend class WidgetVsyncSource;
+
+ void UpdateVsyncObserver();
+ static void ForceUpdateVSyncObserver(nsBaseWidget *aWidget);
+ void ShutdownVsync();
+ void DestroyVsync();
+
+ RefPtr<mozilla::layout::VsyncChild> mVsyncChild;
+ RefPtr<VsyncForwardingObserver> mIncomingVsyncObserver;
+ nsTArray<RefPtr<mozilla::gfx::VsyncObserver>> mVsyncObservers;
+ mozilla::Mutex mVsyncObserversLock;
+ nsID mDesiredVsyncSourceID;
+
+public:
// Should be called by derived implementations to notify on system color and
// theme changes.
void NotifySysColorChanged();
void NotifyThemeChanged();
void NotifyUIStateChanged(UIStateChangeType aShowAccelerators,
UIStateChangeType aShowFocusRings);
#ifdef ACCESSIBILITY
@@ -518,17 +550,16 @@ protected:
void FreeShutdownObserver();
nsIWidgetListener* mWidgetListener;
nsIWidgetListener* mAttachedWidgetListener;
nsIWidgetListener* mPreviouslyAttachedWidgetListener;
RefPtr<LayerManager> mLayerManager;
RefPtr<CompositorBridgeChild> mCompositorBridgeChild;
RefPtr<CompositorBridgeParent> mCompositorBridgeParent;
- RefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher;
RefPtr<APZCTreeManager> mAPZC;
RefPtr<GeckoContentController> mRootContentController;
RefPtr<APZEventState> mAPZEventState;
// Back buffer of BasicCompositor
RefPtr<DrawTarget> mLastBackBuffer;
SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
RefPtr<WidgetShutdownObserver> mShutdownObserver;
RefPtr<TextEventDispatcher> mTextEventDispatcher;