Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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 "nsBaseWidget.h"
#include <utility>
#include "GLConsts.h"
#include "InputData.h"
#include "LiveResizeListener.h"
#include "TouchEvents.h"
#include "WritingModes.h"
#include "X11UndefineNone.h"
#include "base/thread.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/GlobalKeyListener.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layers.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/TextEventDispatcher.h"
#include "mozilla/TextEventDispatcherListener.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/VsyncDispatcher.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Document.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/layers/APZCCallbackHelper.h"
#include "mozilla/layers/APZEventState.h"
#include "mozilla/layers/APZInputBridge.h"
#include "mozilla/layers/APZThreadUtils.h"
#include "mozilla/layers/ChromeProcessController.h"
#include "mozilla/layers/Compositor.h"
#include "mozilla/layers/CompositorBridgeChild.h"
#include "mozilla/layers/CompositorBridgeParent.h"
#include "mozilla/layers/CompositorOptions.h"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsDeviceContext.h"
#include "nsGfxCIID.h"
#include "nsIAppWindow.h"
#include "nsIBaseWindow.h"
#include "nsIContent.h"
#include "nsIScreenManager.h"
#include "nsISimpleEnumerator.h"
#include "nsIWidgetListener.h"
#include "nsRefPtrHashtable.h"
#include "nsServiceManagerUtils.h"
#include "nsWidgetsCID.h"
#include "nsXULPopupManager.h"
#include "prdtoa.h"
#include "prenv.h"
#ifdef ACCESSIBILITY
# include "nsAccessibilityService.h"
#endif
#include "gfxConfig.h"
#include "gfxUtils.h" // for ToDeviceColor
#include "mozilla/layers/CompositorSession.h"
#include "VRManagerChild.h"
#include "gfxConfig.h"
#include "nsView.h"
#include "nsViewManager.h"
#ifdef DEBUG
# include "nsIObserver.h"
static void debug_RegisterPrefCallbacks();
#endif
#ifdef NOISY_WIDGET_LEAKS
static int32_t gNumWidgets;
#endif
#ifdef XP_MACOSX
# include "nsCocoaFeatures.h"
#endif
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
static nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>* sPluginWidgetList;
#endif
nsIRollupListener* nsBaseWidget::gRollupListener = nullptr;
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::ipc;
using namespace mozilla::widget;
using namespace mozilla;
// Async pump timer during injected long touch taps
#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*/ nsTHashMap<uint64_t, nsCOMPtr<nsIObserver>>
AutoObserverNotifier::sSavedObservers;
// The maximum amount of time to let the EnableDragDrop runnable wait in the
// idle queue before timing out and moving it to the regular queue. Value is in
// milliseconds.
const uint32_t kAsyncDragDropTimeout = 1000;
namespace mozilla::widget {
void IMENotification::SelectionChangeDataBase::SetWritingMode(
const WritingMode& aWritingMode) {
mWritingMode = aWritingMode.mWritingMode.bits;
}
WritingMode IMENotification::SelectionChangeDataBase::GetWritingMode() const {
return WritingMode(mWritingMode);
}
} // namespace mozilla::widget
NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidget, nsISupportsWeakReference)
//-------------------------------------------------------------------------
//
// nsBaseWidget constructor
//
//-------------------------------------------------------------------------
nsBaseWidget::nsBaseWidget()
: mWidgetListener(nullptr),
mAttachedWidgetListener(nullptr),
mPreviouslyAttachedWidgetListener(nullptr),
mCompositorVsyncDispatcher(nullptr),
mBorderStyle(eBorderStyle_none),
mBounds(0, 0, 0, 0),
mOriginalBounds(nullptr),
mClipRectCount(0),
mSizeMode(nsSizeMode_Normal),
mIsTiled(false),
mPopupLevel(ePopupLevelTop),
mPopupType(ePopupTypeAny),
mHasRemoteContent(false),
mFissionWindow(false),
mUpdateCursor(true),
mUseAttachedEvents(false),
mIMEHasFocus(false),
mIMEHasQuit(false),
mIsFullyOccluded(false) {
#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
mShutdownObserver = new WidgetShutdownObserver(this);
}
NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver)
WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget)
: mWidget(aWidget), mRegistered(false) {
Register();
}
WidgetShutdownObserver::~WidgetShutdownObserver() {
// No need to call Unregister(), we can't be destroyed until nsBaseWidget
// gets torn down. The observer service and nsBaseWidget have a ref on us
// so nsBaseWidget has to call Unregister and then clear its ref.
}
NS_IMETHODIMP
WidgetShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!mWidget) {
return NS_OK;
}
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
RefPtr<nsBaseWidget> widget(mWidget);
widget->Shutdown();
} else if (!strcmp(aTopic, "quit-application")) {
RefPtr<nsBaseWidget> widget(mWidget);
widget->QuitIME();
}
return NS_OK;
}
void WidgetShutdownObserver::Register() {
if (!mRegistered) {
mRegistered = true;
nsContentUtils::RegisterShutdownObserver(this);
#ifndef MOZ_WIDGET_ANDROID
// The primary purpose of observing quit-application is
// to avoid leaking a widget on Windows when nothing else
// breaks the circular reference between the widget and
// TSFTextStore. However, our Android IME code crashes if
// doing this on Android, so let's not do this on Android.
// Doing this on Gtk and Mac just in case.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, "quit-application", false);
}
#endif
}
}
void WidgetShutdownObserver::Unregister() {
if (mRegistered) {
mWidget = nullptr;
#ifndef MOZ_WIDGET_ANDROID
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, "quit-application");
}
#endif
nsContentUtils::UnregisterShutdownObserver(this);
mRegistered = false;
}
}
#define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed"
#define L10N_PSEUDO_PREF "intl.l10n.pseudo"
static const char* kObservedPrefs[] = {L10N_PSEUDO_PREF, nullptr};
NS_IMPL_ISUPPORTS(LocalesChangedObserver, nsIObserver)
LocalesChangedObserver::LocalesChangedObserver(nsBaseWidget* aWidget)
: mWidget(aWidget), mRegistered(false) {
Register();
}
LocalesChangedObserver::~LocalesChangedObserver() {
// No need to call Unregister(), we can't be destroyed until nsBaseWidget
// gets torn down. The observer service and nsBaseWidget have a ref on us
// so nsBaseWidget has to call Unregister and then clear its ref.
}
NS_IMETHODIMP
LocalesChangedObserver::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
if (!mWidget) {
return NS_OK;
}
if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) {
RefPtr<nsBaseWidget> widget(mWidget);
widget->LocalesChanged();
} else {
MOZ_ASSERT(!strcmp("nsPref:changed", aTopic));
nsDependentString pref(aData);
if (pref.EqualsLiteral(L10N_PSEUDO_PREF)) {
RefPtr<nsBaseWidget> widget(mWidget);
widget->LocalesChanged();
}
}
return NS_OK;
}
void LocalesChangedObserver::Register() {
if (mRegistered) {
return;
}
DebugOnly<nsresult> rv =
Preferences::AddStrongObservers(this, kObservedPrefs);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Adding observers failed.");
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true);
}
// Locale might be update before registering
RefPtr<nsBaseWidget> widget(mWidget);
widget->LocalesChanged();
mRegistered = true;
}
void LocalesChangedObserver::Unregister() {
if (!mRegistered) {
return;
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, INTL_APP_LOCALES_CHANGED);
}
Preferences::RemoveObservers(this, kObservedPrefs);
mWidget = nullptr;
mRegistered = false;
}
void nsBaseWidget::Shutdown() {
NotifyLiveResizeStopped();
RevokeTransactionIdAllocator();
DestroyCompositor();
FreeLocalesChangedObserver();
FreeShutdownObserver();
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
if (sPluginWidgetList) {
delete sPluginWidgetList;
sPluginWidgetList = nullptr;
}
#endif
}
void nsBaseWidget::QuitIME() {
IMEStateManager::WidgetOnQuit(this);
this->mIMEHasQuit = true;
}
void nsBaseWidget::DestroyCompositor() {
// We release this before releasing the compositor, since it may hold the
// last reference to our ClientLayerManager. ClientLayerManager's dtor can
// trigger a paint, creating a new compositor, and we don't want to re-use
// the old vsync dispatcher.
if (mCompositorVsyncDispatcher) {
MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
mCompositorVsyncDispatcher->Shutdown();
mCompositorVsyncDispatcher = nullptr;
}
// The compositor shutdown sequence looks like this:
// 1. CompositorSession calls CompositorBridgeChild::Destroy.
// 2. CompositorBridgeChild synchronously sends WillClose.
// 3. CompositorBridgeParent releases some resources (such as the layer
// manager, compositor, and widget).
// 4. CompositorBridgeChild::Destroy returns.
// 5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the
// compositor thread when the I/O thread closes the IPC channel.
// 6. Step 5 will schedule DeferredDestroy on the compositor thread, which
// releases the reference CompositorBridgeParent holds to itself.
//
// When CompositorSession::Shutdown returns, we assume the compositor is gone
// or will be gone very soon.
if (mCompositorSession) {
ReleaseContentController();
mAPZC = nullptr;
SetCompositorWidgetDelegate(nullptr);
mCompositorBridgeChild = nullptr;
// XXX CompositorBridgeChild and CompositorBridgeParent might be re-created
// in ClientLayerManager destructor. See bug 1133426.
RefPtr<CompositorSession> session = std::move(mCompositorSession);
session->Shutdown();
}
}
// This prevents the layer manager from starting a new transaction during
// shutdown.
void nsBaseWidget::RevokeTransactionIdAllocator() {
if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) {
return;
}
mWindowRenderer->AsWebRender()->SetTransactionIdAllocator(nullptr);
}
void nsBaseWidget::ReleaseContentController() {
if (mRootContentController) {
mRootContentController->Destroy();
mRootContentController = nullptr;
}
}
void nsBaseWidget::DestroyLayerManager() {
if (mWindowRenderer) {
mWindowRenderer->Destroy();
mWindowRenderer = nullptr;
}
DestroyCompositor();
}
void nsBaseWidget::OnRenderingDeviceReset() { DestroyLayerManager(); }
void nsBaseWidget::FreeShutdownObserver() {
if (mShutdownObserver) {
mShutdownObserver->Unregister();
}
mShutdownObserver = nullptr;
}
void nsBaseWidget::FreeLocalesChangedObserver() {
if (mLocalesChangedObserver) {
mLocalesChangedObserver->Unregister();
}
mLocalesChangedObserver = nullptr;
}
//-------------------------------------------------------------------------
//
// nsBaseWidget destructor
//
//-------------------------------------------------------------------------
nsBaseWidget::~nsBaseWidget() {
IMEStateManager::WidgetDestroyed(this);
FreeLocalesChangedObserver();
FreeShutdownObserver();
RevokeTransactionIdAllocator();
DestroyLayerManager();
#ifdef NOISY_WIDGET_LEAKS
gNumWidgets--;
printf("WIDGETS- = %d\n", gNumWidgets);
#endif
delete mOriginalBounds;
}
//-------------------------------------------------------------------------
//
// Basic create.
//
//-------------------------------------------------------------------------
void nsBaseWidget::BaseCreate(nsIWidget* aParent, nsWidgetInitData* aInitData) {
// keep a reference to the device context
if (nullptr != aInitData) {
mWindowType = aInitData->mWindowType;
mBorderStyle = aInitData->mBorderStyle;
mPopupLevel = aInitData->mPopupLevel;
mPopupType = aInitData->mPopupHint;
mHasRemoteContent = aInitData->mHasRemoteContent;
mFissionWindow = aInitData->mFissionWindow;
}
if (aParent) {
aParent->AddChild(this);
}
}
//-------------------------------------------------------------------------
//
// Accessor functions to get/set the client data
//
//-------------------------------------------------------------------------
nsIWidgetListener* nsBaseWidget::GetWidgetListener() { return mWidgetListener; }
void nsBaseWidget::SetWidgetListener(nsIWidgetListener* aWidgetListener) {
mWidgetListener = aWidgetListener;
}
already_AddRefed<nsIWidget> nsBaseWidget::CreateChild(
const LayoutDeviceIntRect& aRect, nsWidgetInitData* aInitData,
bool aForceUseIWidgetParent) {
nsIWidget* parent = this;
nsNativeWidget nativeParent = nullptr;
if (!aForceUseIWidgetParent) {
// Use only either parent or nativeParent, not both, to match
// existing code. Eventually Create() should be divested of its
// nativeWidget parameter.
nativeParent = parent ? parent->GetNativeData(NS_NATIVE_WIDGET) : nullptr;
parent = nativeParent ? nullptr : parent;
MOZ_ASSERT(!parent || !nativeParent, "messed up logic");
}
nsCOMPtr<nsIWidget> widget;
if (aInitData && aInitData->mWindowType == eWindowType_popup) {
widget = AllocateChildPopupWidget();
} else {
widget = nsIWidget::CreateChildWindow();
}
if (widget &&
NS_SUCCEEDED(widget->Create(parent, nativeParent, aRect, aInitData))) {
return widget.forget();
}
return nullptr;
}
// Attach a view to our widget which we'll send events to.
void nsBaseWidget::AttachViewToTopLevel(bool aUseAttachedEvents) {
NS_ASSERTION((mWindowType == eWindowType_toplevel ||
mWindowType == eWindowType_dialog ||
mWindowType == eWindowType_invisible ||
mWindowType == eWindowType_child),
"Can't attach to window of that type");
mUseAttachedEvents = aUseAttachedEvents;
}
nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener() {
return mAttachedWidgetListener;
}
nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener() {
return mPreviouslyAttachedWidgetListener;
}
void nsBaseWidget::SetPreviouslyAttachedWidgetListener(
nsIWidgetListener* aListener) {
mPreviouslyAttachedWidgetListener = aListener;
}
void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener) {
mAttachedWidgetListener = aListener;
}
//-------------------------------------------------------------------------
//
// Close this nsBaseWidget
//
//-------------------------------------------------------------------------
void 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);
}
#if defined(XP_WIN)
// Allow our scroll capture container to be cleaned up, if we have one.
mScrollCaptureContainer = nullptr;
#endif
}
//-------------------------------------------------------------------------
//
// Get this nsBaseWidget parent
//
//-------------------------------------------------------------------------
nsIWidget* nsBaseWidget::GetParent(void) { return nullptr; }
//-------------------------------------------------------------------------
//
// Get this nsBaseWidget top level widget
//
//-------------------------------------------------------------------------
nsIWidget* nsBaseWidget::GetTopLevelWidget() {
nsIWidget *topLevelWidget = nullptr, *widget = this;
while (widget) {
topLevelWidget = widget;
widget = widget->GetParent();
}
return topLevelWidget;
}
//-------------------------------------------------------------------------
//
// Get this nsBaseWidget's top (non-sheet) parent (if it's a sheet)
//
//-------------------------------------------------------------------------
nsIWidget* nsBaseWidget::GetSheetWindowParent(void) { return nullptr; }
float nsBaseWidget::GetDPI() { return 96.0f; }
CSSToLayoutDeviceScale nsIWidget::GetDefaultScale() {
double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx();
if (devPixelsPerCSSPixel <= 0.0) {
devPixelsPerCSSPixel = GetDefaultScaleInternal();
}
return CSSToLayoutDeviceScale(devPixelsPerCSSPixel);
}
nsIntSize nsIWidget::CustomCursorSize(const Cursor& aCursor) {
MOZ_ASSERT(aCursor.IsCustom());
int32_t width = 0;
int32_t height = 0;
aCursor.mContainer->GetWidth(&width);
aCursor.mContainer->GetHeight(&height);
aCursor.mResolution.ApplyTo(width, height);
return {width, height};
}
//-------------------------------------------------------------------------
//
// Add a child to the list of children
//
//-------------------------------------------------------------------------
void nsBaseWidget::AddChild(nsIWidget* aChild) {
MOZ_ASSERT(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
"aChild not properly removed from its old child list");
if (!mFirstChild) {
mFirstChild = mLastChild = aChild;
} else {
// append to the list
MOZ_ASSERT(mLastChild);
MOZ_ASSERT(!mLastChild->GetNextSibling());
mLastChild->SetNextSibling(aChild);
aChild->SetPrevSibling(mLastChild);
mLastChild = aChild;
}
}
//-------------------------------------------------------------------------
//
// Remove a child from the list of children
//
//-------------------------------------------------------------------------
void nsBaseWidget::RemoveChild(nsIWidget* aChild) {
#ifdef DEBUG
# ifdef XP_MACOSX
// nsCocoaWindow doesn't implement GetParent, so in that case parent will be
// null and we'll just have to do without this assertion.
nsIWidget* parent = aChild->GetParent();
NS_ASSERTION(!parent || parent == this, "Not one of our kids!");
# else
MOZ_RELEASE_ASSERT(aChild->GetParent() == this, "Not one of our kids!");
# endif
#endif
if (mLastChild == aChild) {
mLastChild = mLastChild->GetPrevSibling();
}
if (mFirstChild == aChild) {
mFirstChild = mFirstChild->GetNextSibling();
}
// Now remove from the list. Make sure that we pass ownership of the tail
// of the list correctly before we have aChild let go of it.
nsIWidget* prev = aChild->GetPrevSibling();
nsIWidget* next = aChild->GetNextSibling();
if (prev) {
prev->SetNextSibling(next);
}
if (next) {
next->SetPrevSibling(prev);
}
aChild->SetNextSibling(nullptr);
aChild->SetPrevSibling(nullptr);
}
//-------------------------------------------------------------------------
//
// Sets widget's position within its parent's child list.
//
//-------------------------------------------------------------------------
void nsBaseWidget::SetZIndex(int32_t aZIndex) {
// Hold a ref to ourselves just in case, since we're going to remove
// from our parent.
nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
mZIndex = aZIndex;
// reorder this child in its parent's list.
auto* parent = static_cast<nsBaseWidget*>(GetParent());
if (parent) {
parent->RemoveChild(this);
// Scope sib outside the for loop so we can check it afterward
nsIWidget* sib = parent->GetFirstChild();
for (; sib; sib = sib->GetNextSibling()) {
int32_t childZIndex = GetZIndex();
if (aZIndex < childZIndex) {
// Insert ourselves before sib
nsIWidget* prev = sib->GetPrevSibling();
mNextSibling = sib;
mPrevSibling = prev;
sib->SetPrevSibling(this);
if (prev) {
prev->SetNextSibling(this);
} else {
NS_ASSERTION(sib == parent->mFirstChild, "Broken child list");
// We've taken ownership of sib, so it's safe to have parent let
// go of it
parent->mFirstChild = this;
}
PlaceBehind(eZPlacementBelow, sib, false);
break;
}
}
// were we added to the list?
if (!sib) {
parent->AddChild(this);
}
}
}
//-------------------------------------------------------------------------
//
// Maximize, minimize or restore the window. The BaseWidget implementation
// merely stores the state.
//
//-------------------------------------------------------------------------
void nsBaseWidget::SetSizeMode(nsSizeMode aMode) {
MOZ_ASSERT(aMode == nsSizeMode_Normal || aMode == nsSizeMode_Minimized ||
aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen);
mSizeMode = aMode;
}
void nsBaseWidget::GetWorkspaceID(nsAString& workspaceID) {
workspaceID.Truncate();
}
void nsBaseWidget::MoveToWorkspace(const nsAString& workspaceID) {
// Noop.
}
//-------------------------------------------------------------------------
//
// Get this component cursor
//
//-------------------------------------------------------------------------
void nsBaseWidget::SetCursor(const Cursor& aCursor) { mCursor = aCursor; }
//-------------------------------------------------------------------------
//
// Window transparency methods
//
//-------------------------------------------------------------------------
void nsBaseWidget::SetTransparencyMode(nsTransparencyMode aMode) {}
nsTransparencyMode nsBaseWidget::GetTransparencyMode() {
return eTransparencyOpaque;
}
bool nsBaseWidget::IsWindowClipRegionEqual(
const nsTArray<LayoutDeviceIntRect>& aRects) {
return mClipRects && mClipRectCount == aRects.Length() &&
memcmp(mClipRects.get(), aRects.Elements(),
sizeof(LayoutDeviceIntRect) * mClipRectCount) == 0;
}
void nsBaseWidget::StoreWindowClipRegion(
const nsTArray<LayoutDeviceIntRect>& aRects) {
mClipRectCount = aRects.Length();
mClipRects = MakeUnique<LayoutDeviceIntRect[]>(mClipRectCount);
if (mClipRects) {
memcpy(mClipRects.get(), aRects.Elements(),
sizeof(LayoutDeviceIntRect) * mClipRectCount);
}
}
void nsBaseWidget::GetWindowClipRegion(nsTArray<LayoutDeviceIntRect>* aRects) {
if (mClipRects) {
aRects->AppendElements(mClipRects.get(), mClipRectCount);
} else {
aRects->AppendElement(
LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height()));
}
}
const LayoutDeviceIntRegion nsBaseWidget::RegionFromArray(
const nsTArray<LayoutDeviceIntRect>& aRects) {
LayoutDeviceIntRegion region;
for (uint32_t i = 0; i < aRects.Length(); ++i) {
region.Or(region, aRects[i]);
}
return region;
}
void nsBaseWidget::ArrayFromRegion(const LayoutDeviceIntRegion& aRegion,
nsTArray<LayoutDeviceIntRect>& aRects) {
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
aRects.AppendElement(iter.Get());
}
}
nsresult nsBaseWidget::SetWindowClipRegion(
const nsTArray<LayoutDeviceIntRect>& aRects, bool aIntersectWithExisting) {
if (!aIntersectWithExisting) {
StoreWindowClipRegion(aRects);
} else {
// get current rects
nsTArray<LayoutDeviceIntRect> currentRects;
GetWindowClipRegion(&currentRects);
// create region from them
LayoutDeviceIntRegion currentRegion = RegionFromArray(currentRects);
// create region from new rects
LayoutDeviceIntRegion newRegion = RegionFromArray(aRects);
// intersect regions
LayoutDeviceIntRegion intersection;
intersection.And(currentRegion, newRegion);
// create int rect array from intersection
nsTArray<LayoutDeviceIntRect> rects;
ArrayFromRegion(intersection, rects);
// store
StoreWindowClipRegion(rects);
}
return NS_OK;
}
/* virtual */
void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage,
uint16_t aDuration,
nsISupports* aData,
nsIRunnable* aCallback) {
MOZ_ASSERT_UNREACHABLE(
"Should never call PerformFullscreenTransition on nsBaseWidget");
}
//-------------------------------------------------------------------------
//
// Put the window into full-screen mode
//
//-------------------------------------------------------------------------
void nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen,
nsIScreen* aScreen) {
HideWindowChrome(aFullScreen);
if (aFullScreen) {
if (!mOriginalBounds) {
mOriginalBounds = new LayoutDeviceIntRect();
}
*mOriginalBounds = GetScreenBounds();
// Move to top-left corner of screen and size to the screen dimensions
nsCOMPtr<nsIScreen> screen = aScreen;
if (!screen) {
screen = GetWidgetScreen();
}
if (screen) {
int32_t left, top, width, height;
if (NS_SUCCEEDED(
screen->GetRectDisplayPix(&left, &top, &width, &height))) {
Resize(left, top, width, height, true);
}
}
} else if (mOriginalBounds) {
if (BoundsUseDesktopPixels()) {
DesktopRect deskRect = *mOriginalBounds / GetDesktopToDeviceScale();
Resize(deskRect.X(), deskRect.Y(), deskRect.Width(), deskRect.Height(),
true);
} else {
Resize(mOriginalBounds->X(), mOriginalBounds->Y(),
mOriginalBounds->Width(), mOriginalBounds->Height(), true);
}
}
}
nsresult nsBaseWidget::MakeFullScreen(bool aFullScreen, nsIScreen* aScreen) {
InfallibleMakeFullScreen(aFullScreen, aScreen);
return NS_OK;
}
nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
nsBaseWidget* aWidget, gfxContext* aTarget, BufferMode aDoubleBuffering)
: mWidget(aWidget) {
WindowRenderer* renderer = mWidget->GetWindowRenderer();
if (renderer->AsFallback()) {
mRenderer = renderer->AsFallback();
mRenderer->SetTarget(aTarget, aDoubleBuffering);
}
}
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() {
if (mRenderer) {
mRenderer->SetTarget(nullptr, mozilla::layers::BufferMode::BUFFER_NONE);
}
}
bool nsBaseWidget::IsSmallPopup() const {
return mWindowType == eWindowType_popup && mPopupType != ePopupTypePanel;
}
bool nsBaseWidget::ComputeShouldAccelerate() {
return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
WidgetTypeSupportsAcceleration();
}
bool nsBaseWidget::UseAPZ() {
return (gfxPlatform::AsyncPanZoomEnabled() &&
(WindowType() == eWindowType_toplevel ||
WindowType() == eWindowType_child ||
((WindowType() == eWindowType_popup ||
WindowType() == eWindowType_dialog) &&
HasRemoteContent() && StaticPrefs::apz_popups_enabled())));
}
void nsBaseWidget::CreateCompositor() {
LayoutDeviceIntRect rect = GetBounds();
CreateCompositor(rect.Width(), rect.Height());
}
already_AddRefed<GeckoContentController>
nsBaseWidget::CreateRootContentController() {
RefPtr<GeckoContentController> controller =
new ChromeProcessController(this, mAPZEventState, mAPZC);
return controller.forget();
}
void nsBaseWidget::ConfigureAPZCTreeManager() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mAPZC);
ConfigureAPZControllerThread();
float dpi = GetDPI();
// On Android the main thread is not the controller thread
APZThreadUtils::RunOnControllerThread(
NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mAPZC,
&IAPZCTreeManager::SetDPI, dpi));
if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
KeyboardMap map = RootWindowGlobalKeyListener::CollectKeyboardShortcuts();
// On Android the main thread is not the controller thread
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
"layers::IAPZCTreeManager::SetKeyboardMap", mAPZC,
&IAPZCTreeManager::SetKeyboardMap, map));
}
RefPtr<IAPZCTreeManager> treeManager = mAPZC; // for capture by the lambdas
ContentReceivedInputBlockCallback callback(
[treeManager](uint64_t aInputBlockId, bool aPreventDefault) {
MOZ_ASSERT(NS_IsMainThread());
APZThreadUtils::RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
"layers::IAPZCTreeManager::ContentReceivedInputBlock", treeManager,
&IAPZCTreeManager::ContentReceivedInputBlock, aInputBlockId,
aPreventDefault));
});
mAPZEventState = new APZEventState(this, std::move(callback));
mSetAllowedTouchBehaviorCallback =
[treeManager](uint64_t aInputBlockId,
const nsTArray<TouchBehaviorFlags>& aFlags) {
MOZ_ASSERT(NS_IsMainThread());
APZThreadUtils::RunOnControllerThread(
NewRunnableMethod<
uint64_t, StoreCopyPassByLRef<nsTArray<TouchBehaviorFlags>>>(
"layers::IAPZCTreeManager::SetAllowedTouchBehavior",
treeManager, &IAPZCTreeManager::SetAllowedTouchBehavior,
aInputBlockId, aFlags.Clone()));
};
mRootContentController = CreateRootContentController();
if (mRootContentController) {
mCompositorSession->SetContentController(mRootContentController);
}
// When APZ is enabled, we can actually enable raw touch events because we
// have code that can deal with them properly. If APZ is not enabled, this
// function doesn't get called.
if (StaticPrefs::dom_w3c_touch_events_enabled()) {
RegisterTouchWindow();
}
}
void nsBaseWidget::ConfigureAPZControllerThread() {
// By default the controller thread is the main thread.
APZThreadUtils::SetControllerThread(NS_GetCurrentThread());
}
void nsBaseWidget::SetConfirmedTargetAPZC(
uint64_t aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets) const {
APZThreadUtils::RunOnControllerThread(
NewRunnableMethod<uint64_t,
StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
"layers::IAPZCTreeManager::SetTargetAPZC", mAPZC,
&IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets.Clone()));
}
void nsBaseWidget::UpdateZoomConstraints(
const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
const Maybe<ZoomConstraints>& aConstraints) {
if (!mCompositorSession || !mAPZC) {
if (mInitialZoomConstraints) {
MOZ_ASSERT(mInitialZoomConstraints->mPresShellID == aPresShellId);
MOZ_ASSERT(mInitialZoomConstraints->mViewID == aViewId);
if (!aConstraints) {
mInitialZoomConstraints.reset();
}
}
if (aConstraints) {
// We have some constraints, but the compositor and APZC aren't created
// yet. Save these so we can use them later.
mInitialZoomConstraints = Some(
InitialZoomConstraints(aPresShellId, aViewId, aConstraints.ref()));
}
return;
}
LayersId layersId = mCompositorSession->RootLayerTreeId();
mAPZC->UpdateZoomConstraints(
ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints);
}
bool nsBaseWidget::AsyncPanZoomEnabled() const { return !!mAPZC; }
nsEventStatus nsBaseWidget::ProcessUntransformedAPZEvent(
WidgetInputEvent* aEvent, const APZEventResult& aApzResult) {
MOZ_ASSERT(NS_IsMainThread());
ScrollableLayerGuid targetGuid = aApzResult.mTargetGuid;
uint64_t inputBlockId = aApzResult.mInputBlockId;
InputAPZContext context(aApzResult.mTargetGuid, inputBlockId,
aApzResult.GetStatus());
// Make a copy of the original event for the APZCCallbackHelper helpers that
// we call later, because the event passed to DispatchEvent can get mutated in
// ways that we don't want (i.e. touch points can get stripped out).
nsEventStatus status;
UniquePtr<WidgetEvent> original(aEvent->Duplicate());
DispatchEvent(aEvent, status);
if (mAPZC && !InputAPZContext::WasRoutedToChildProcess() && inputBlockId) {
// EventStateManager did not route the event into the child process.
// It's safe to communicate to APZ that the event has been processed.
// Note that here aGuid.mLayersId might be different from
// mCompositorSession->RootLayerTreeId() because the event might have gotten
// hit-tested by APZ to be targeted at a child process, but a parent process
// event listener called preventDefault on it. In that case aGuid.mLayersId
// would still be the layers id for the child process, but the event would
// not have actually gotten routed to the child process. The main-thread
// hit-test result therefore needs to use the parent process layers id.
LayersId rootLayersId = mCompositorSession->RootLayerTreeId();
RefPtr<DisplayportSetListener> postLayerization;
if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
nsTArray<TouchBehaviorFlags> allowedTouchBehaviors;
if (touchEvent->mMessage == eTouchStart) {
if (StaticPrefs::layout_css_touch_action_enabled()) {
allowedTouchBehaviors =
APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
this, GetDocument(), *(original->AsTouchEvent()),
inputBlockId, mSetAllowedTouchBehaviorCallback);
}
postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
this, GetDocument(), *(original->AsTouchEvent()), rootLayersId,
inputBlockId);
}
mAPZEventState->ProcessTouchEvent(*touchEvent, targetGuid, inputBlockId,
aApzResult.GetStatus(), status,
std::move(allowedTouchBehaviors));
} else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
MOZ_ASSERT(wheelEvent->mFlags.mHandledByAPZ);
postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
this, GetDocument(), *(original->AsWheelEvent()), rootLayersId,
inputBlockId);
if (wheelEvent->mCanTriggerSwipe) {
ReportSwipeStarted(inputBlockId, wheelEvent->TriggersSwipe());
}
mAPZEventState->ProcessWheelEvent(*wheelEvent, inputBlockId);
} else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
MOZ_ASSERT(mouseEvent->mFlags.mHandledByAPZ);
postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
this, GetDocument(), *(original->AsMouseEvent()), rootLayersId,
inputBlockId);
mAPZEventState->ProcessMouseEvent(*mouseEvent, inputBlockId);
}
if (postLayerization) {
postLayerization->Register();
}
}
return status;
}
template <class InputType, class EventType>
class DispatchEventOnMainThread : public Runnable {
public:
DispatchEventOnMainThread(const InputType& aInput, nsBaseWidget* aWidget,
const APZEventResult& aAPZResult)
: mozilla::Runnable("DispatchEventOnMainThread"),
mInput(aInput),
mWidget(aWidget),
mAPZResult(aAPZResult) {}
NS_IMETHOD Run() override {
EventType event = mInput.ToWidgetEvent(mWidget);
mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult);
return NS_OK;
}
private:
InputType mInput;
nsBaseWidget* mWidget;
APZEventResult mAPZResult;
};
template <class InputType, class EventType>
class DispatchInputOnControllerThread : public Runnable {
public:
DispatchInputOnControllerThread(const EventType& aEvent,
IAPZCTreeManager* aAPZC,
nsBaseWidget* aWidget)
: mozilla::Runnable("DispatchInputOnControllerThread"),
mMainMessageLoop(MessageLoop::current()),
mInput(aEvent),
mAPZC(aAPZC),
mWidget(aWidget) {}
NS_IMETHOD Run() override {
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(mInput);
if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
return NS_OK;
}
RefPtr<Runnable> r = new DispatchEventOnMainThread<InputType, EventType>(
mInput, mWidget, result);
mMainMessageLoop->PostTask(r.forget());
return NS_OK;
}
private:
MessageLoop* mMainMessageLoop;
InputType mInput;
RefPtr<IAPZCTreeManager> mAPZC;
nsBaseWidget* mWidget;
};
void nsBaseWidget::DispatchTouchInput(MultiTouchInput& aInput,
uint16_t aInputSource) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aInputSource ==
mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH ||
aInputSource == mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN);
if (mAPZC) {
MOZ_ASSERT(APZThreadUtils::IsControllerThread());
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
return;
}
WidgetTouchEvent event = aInput.ToWidgetEvent(this, aInputSource);
ProcessUntransformedAPZEvent(&event, result);
} else {
WidgetTouchEvent event = aInput.ToWidgetEvent(this, aInputSource);
nsEventStatus status;
DispatchEvent(&event, status);
}
}
void nsBaseWidget::DispatchPanGestureInput(PanGestureInput& aInput) {
MOZ_ASSERT(NS_IsMainThread());
if (mAPZC) {
MOZ_ASSERT(APZThreadUtils::IsControllerThread());
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
return;
}
WidgetWheelEvent event = aInput.ToWidgetEvent(this);
ProcessUntransformedAPZEvent(&event, result);
} else {
WidgetWheelEvent event = aInput.ToWidgetEvent(this);
nsEventStatus status;
DispatchEvent(&event, status);
}
}
void nsBaseWidget::DispatchPinchGestureInput(PinchGestureInput& aInput) {
MOZ_ASSERT(NS_IsMainThread());
if (mAPZC) {
MOZ_ASSERT(APZThreadUtils::IsControllerThread());
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
return;
}
WidgetWheelEvent event = aInput.ToWidgetEvent(this);
ProcessUntransformedAPZEvent(&event, result);
} else {
WidgetWheelEvent event = aInput.ToWidgetEvent(this);
nsEventStatus status;
DispatchEvent(&event, status);
}
}
nsIWidget::ContentAndAPZEventStatus nsBaseWidget::DispatchInputEvent(
WidgetInputEvent* aEvent) {
nsIWidget::ContentAndAPZEventStatus status;
MOZ_ASSERT(NS_IsMainThread());
if (mAPZC) {
if (APZThreadUtils::IsControllerThread()) {
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(*aEvent);
status.mApzStatus = result.GetStatus();
if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
return status;
}
status.mContentStatus = ProcessUntransformedAPZEvent(aEvent, result);
return status;
}
if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
RefPtr<Runnable> r =
new DispatchInputOnControllerThread<ScrollWheelInput,
WidgetWheelEvent>(*wheelEvent,
mAPZC, this);
APZThreadUtils::RunOnControllerThread(std::move(r));
status.mContentStatus = nsEventStatus_eConsumeDoDefault;
return status;
}
if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
RefPtr<Runnable> r =
new DispatchInputOnControllerThread<MouseInput, WidgetMouseEvent>(
*mouseEvent, mAPZC, this);
APZThreadUtils::RunOnControllerThread(std::move(r));
status.mContentStatus = nsEventStatus_eConsumeDoDefault;
return status;
}
if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
RefPtr<Runnable> r =
new DispatchInputOnControllerThread<MultiTouchInput,
WidgetTouchEvent>(*touchEvent,
mAPZC, this);
APZThreadUtils::RunOnControllerThread(std::move(r));
status.mContentStatus = nsEventStatus_eConsumeDoDefault;
return status;
}
// Allow dispatching keyboard events on Gecko thread.
MOZ_ASSERT(aEvent->AsKeyboardEvent());
}
DispatchEvent(aEvent, status.mContentStatus);
return status;
}
void nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) {
MOZ_ASSERT(NS_IsMainThread());
if (mAPZC) {
MOZ_ASSERT(APZThreadUtils::IsControllerThread());
mAPZC->InputBridge()->ReceiveInputEvent(*aEvent);
}
}
Document* nsBaseWidget::GetDocument() const {
if (mWidgetListener) {
if (PresShell* presShell = mWidgetListener->GetPresShell()) {
return presShell->GetDocument();
}
}
return nullptr;
}
void nsBaseWidget::CreateCompositorVsyncDispatcher() {
// Parent directly listens to the vsync source whereas
// child process communicate via IPC
// Should be called AFTER gfxPlatform is initialized
if (XRE_IsParentProcess()) {
if (!mCompositorVsyncDispatcherLock) {
mCompositorVsyncDispatcherLock =
MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
}
MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
if (!mCompositorVsyncDispatcher) {
mCompositorVsyncDispatcher = new CompositorVsyncDispatcher();
}
}
}
already_AddRefed<CompositorVsyncDispatcher>
nsBaseWidget::GetCompositorVsyncDispatcher() {
MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
RefPtr<CompositorVsyncDispatcher> dispatcher = mCompositorVsyncDispatcher;
return dispatcher.forget();
}
already_AddRefed<WebRenderLayerManager> nsBaseWidget::CreateCompositorSession(
int aWidth, int aHeight, CompositorOptions* aOptionsOut) {
MOZ_ASSERT(aOptionsOut);
do {
CreateCompositorVsyncDispatcher();
gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
// Make sure GPU process is ready for use.
// If it failed to connect to GPU process, GPU process usage is disabled in
// EnsureGPUReady(). It could update gfxVars and gfxConfigs.
gpu->EnsureGPUReady();
// If widget type does not supports acceleration, we may be allowed to use
// software WebRender instead. If not, then we use ClientLayerManager even
// when gfxVars::UseWebRender() is true. WebRender could coexist only with
// BasicCompositor.
bool supportsAcceleration = WidgetTypeSupportsAcceleration();
bool enableWR;
bool enableSWWR;
if (supportsAcceleration ||
StaticPrefs::gfx_webrender_unaccelerated_widget_force()) {
enableWR = gfx::gfxVars::UseWebRender();
enableSWWR = gfx::gfxVars::UseSoftwareWebRender();
} else {
enableWR = enableSWWR = gfx::gfxVars::UseWebRender();
}
MOZ_RELEASE_ASSERT(enableWR);
bool enableAPZ = UseAPZ();
CompositorOptions options(enableAPZ, enableSWWR);
#ifdef XP_WIN
if (supportsAcceleration) {
options.SetAllowSoftwareWebRenderD3D11(
gfx::gfxVars::AllowSoftwareWebRenderD3D11());
}
#elif defined(MOZ_WIDGET_ANDROID)
MOZ_ASSERT(supportsAcceleration);
options.SetAllowSoftwareWebRenderOGL(
StaticPrefs::gfx_webrender_software_opengl_AtStartup());
#elif defined(MOZ_WIDGET_GTK)
if (supportsAcceleration) {
options.SetAllowSoftwareWebRenderOGL(
StaticPrefs::gfx_webrender_software_opengl_AtStartup());
}
#endif
options.SetUseWebGPU(StaticPrefs::dom_webgpu_enabled());
#ifdef MOZ_WIDGET_ANDROID
if (!GetNativeData(NS_JAVA_SURFACE)) {
options.SetInitiallyPaused(true);
}
#else
options.SetInitiallyPaused(CompositorInitiallyPaused());
#endif
RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this);
bool retry = false;
mCompositorSession = gpu->CreateTopLevelCompositor(
this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(),
gfx::IntSize(aWidth, aHeight), &retry);
if (mCompositorSession) {
TextureFactoryIdentifier textureFactoryIdentifier;
nsCString error;
lm->Initialize(mCompositorSession->GetCompositorBridgeChild(),
wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
&textureFactoryIdentifier, error);
if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) {
retry = true;
DestroyCompositor();
// gfxVars::UseDoubleBufferingWithCompositor() is also disabled.
gfx::GPUProcessManager::Get()->DisableWebRender(
wr::WebRenderError::INITIALIZE, error);
}
}
// We need to retry in a loop because the act of failing to create the
// compositor can change our state (e.g. disable WebRender).
if (mCompositorSession || !retry) {
*aOptionsOut = options;
return lm.forget();
}
} while (true);
}
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(),
"This function assumes OMTC");
MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild,
"Should have properly cleaned up the previous PCompositor pair "
"beforehand");
if (mCompositorBridgeChild) {
mCompositorBridgeChild->Destroy();
}
// Recreating this is tricky, as we may still have an old and we need
// 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;
}
CompositorOptions options;
RefPtr<WebRenderLayerManager> lm =
CreateCompositorSession(aWidth, aHeight, &options);
if (!lm) {
return;
}
MOZ_ASSERT(mCompositorSession);
mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild();
SetCompositorWidgetDelegate(
mCompositorSession->GetCompositorWidgetDelegate());
if (options.UseAPZ()) {
mAPZC = mCompositorSession->GetAPZCTreeManager();
ConfigureAPZCTreeManager();
} else {
mAPZC = nullptr;
}
if (mInitialZoomConstraints) {
UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID,
mInitialZoomConstraints->mViewID,
Some(mInitialZoomConstraints->mConstraints));
mInitialZoomConstraints.reset();
}
TextureFactoryIdentifier textureFactoryIdentifier =
lm->GetTextureFactoryIdentifier();
MOZ_ASSERT(textureFactoryIdentifier.mParentBackend ==
LayersBackend::LAYERS_WR);
ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
WindowUsesOMTC();
mWindowRenderer = std::move(lm);
// Only track compositors for top-level windows, since other window types
// may use the basic compositor. Except on the OS X - see bug 1306383
#if defined(XP_MACOSX)
bool getCompositorFromThisWindow = true;
#else
bool getCompositorFromThisWindow = (mWindowType == eWindowType_toplevel);
#endif
if (getCompositorFromThisWindow) {
gfxPlatform::GetPlatform()->NotifyCompositorCreated(
mWindowRenderer->GetCompositorBackendType());
}
}
void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession* aSession) {
MOZ_ASSERT(aSession == mCompositorSession);
DestroyLayerManager();
}
bool nsBaseWidget::ShouldUseOffMainThreadCompositing() {
return gfxPlatform::UsesOffMainThreadCompositing();
}
WindowRenderer* nsBaseWidget::GetWindowRenderer() {
if (!mWindowRenderer) {
if (!mShutdownObserver) {
// We are shutting down, do not try to re-create a LayerManager
return nullptr;
}
// Try to use an async compositor first, if possible
if (ShouldUseOffMainThreadCompositing()) {
CreateCompositor();
}
if (!mWindowRenderer) {
mWindowRenderer = CreateBasicLayerManager();
}
}
return mWindowRenderer;
}
WindowRenderer* nsBaseWidget::CreateBasicLayerManager() {
return new FallbackRenderer;
}
CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer() {
return mCompositorBridgeChild;
}
void nsBaseWidget::ClearCachedWebrenderResources() {
if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) {
return;
}
mWindowRenderer->AsWebRender()->ClearCachedResources();
}
already_AddRefed<gfx::DrawTarget> nsBaseWidget::StartRemoteDrawing() {
return nullptr;
}
uint32_t nsBaseWidget::GetGLFrameBufferFormat() { return LOCAL_GL_RGBA; }
//-------------------------------------------------------------------------
//
// Destroy the window
//
//-------------------------------------------------------------------------
void nsBaseWidget::OnDestroy() {
if (mTextEventDispatcher) {
mTextEventDispatcher->OnDestroyWidget();
// Don't release it until this widget actually released because after this
// is called, TextEventDispatcher() may create it again.
}
// If this widget is being destroyed, let the APZ code know to drop references
// to this widget. Callers of this function all should be holding a deathgrip
// on this widget already.
ReleaseContentController();
}
void nsBaseWidget::MoveClient(const DesktopPoint& aOffset) {
LayoutDeviceIntPoint clientOffset(GetClientOffset());
// GetClientOffset returns device pixels; scale back to desktop pixels
// if that's what this widget uses for the Move/Resize APIs
if (BoundsUseDesktopPixels()) {
DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale();
Move(aOffset.x - desktopOffset.x, aOffset.y - desktopOffset.y);
} else {