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 "BasicLayers.h"
#include "ClientLayerManager.h"
#include "FrameLayerBuilder.h"
#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_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/PLayerTransactionChild.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/webrender/WebRenderTypes.h"
#include "npapi.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 "nsIKeyEventInPluginCallback.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*/ nsDataHashtable<nsUint64HashKey, 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),
mLayerManager(nullptr),
mCompositorVsyncDispatcher(nullptr),
mCursor(eCursor_standard),
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;
}
}
void nsBaseWidget::Shutdown() {
NotifyLiveResizeStopped();
RevokeTransactionIdAllocator();
DestroyCompositor();
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 (!mLayerManager) {
return;
}
mLayerManager->SetTransactionIdAllocator(nullptr);
}
void nsBaseWidget::ReleaseContentController() {
if (mRootContentController) {
mRootContentController->Destroy();
mRootContentController = nullptr;
}
}
void nsBaseWidget::DestroyLayerManager() {
if (mLayerManager) {
mLayerManager->Destroy();
mLayerManager = nullptr;
}
DestroyCompositor();
}
void nsBaseWidget::OnRenderingDeviceReset() { DestroyLayerManager(); }
void nsBaseWidget::FreeShutdownObserver() {
if (mShutdownObserver) {
mShutdownObserver->Unregister();
}
mShutdownObserver = nullptr;
}
//-------------------------------------------------------------------------
//
// nsBaseWidget destructor
//
//-------------------------------------------------------------------------
nsBaseWidget::~nsBaseWidget() {
IMEStateManager::WidgetDestroyed(this);
if (mLayerManager) {
if (BasicLayerManager* mgr = mLayerManager->AsBasicLayerManager()) {
mgr->ClearRetainerWidget();
}
}
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);
}
//-------------------------------------------------------------------------
//
// 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(nsCursor aCursor, imgIContainer*, uint32_t,
uint32_t) {
// We don't support the cursor image.
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,
ScreenRotation aRotation)
: mWidget(aWidget) {
LayerManager* lm = mWidget->GetLayerManager();
NS_ASSERTION(
!lm || lm->GetBackendType() == LayersBackend::LAYERS_BASIC,
"AutoLayerManagerSetup instantiated for non-basic layer backend!");
if (lm) {
mLayerManager = lm->AsBasicLayerManager();
if (mLayerManager) {
mLayerManager->SetDefaultTarget(aTarget);
mLayerManager->SetDefaultTargetConfiguration(aDoubleBuffering, aRotation);
}
}
}
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() {
if (mLayerManager) {
mLayerManager->SetDefaultTarget(nullptr);
mLayerManager->SetDefaultTargetConfiguration(
mozilla::layers::BufferMode::BUFFER_NONE, ROTATION_0);
}
}
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 && 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() ||
StaticPrefs::dom_w3c_pointer_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.mStatus);
// 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();
UniquePtr<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.mStatus, 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()) {
Unused << postLayerization.release();
}
}
return status;
}
class DispatchWheelEventOnMainThread : public Runnable {
public:
DispatchWheelEventOnMainThread(const ScrollWheelInput& aWheelInput,
nsBaseWidget* aWidget,
const APZEventResult& aAPZResult)
: mozilla::Runnable("DispatchWheelEventOnMainThread"),
mWheelInput(aWheelInput),
mWidget(aWidget),
mAPZResult(aAPZResult) {}
NS_IMETHOD Run() override {
WidgetWheelEvent wheelEvent = mWheelInput.ToWidgetWheelEvent(mWidget);
mWidget->ProcessUntransformedAPZEvent(&wheelEvent, mAPZResult);
return NS_OK;
}
private:
ScrollWheelInput mWheelInput;
nsBaseWidget* mWidget;
APZEventResult mAPZResult;
};
class DispatchWheelInputOnControllerThread : public Runnable {
public:
DispatchWheelInputOnControllerThread(const WidgetWheelEvent& aWheelEvent,
IAPZCTreeManager* aAPZC,
nsBaseWidget* aWidget)
: mozilla::Runnable("DispatchWheelInputOnControllerThread"),
mMainMessageLoop(MessageLoop::current()),
mWheelInput(aWheelEvent),
mAPZC(aAPZC),
mWidget(aWidget) {}
NS_IMETHOD Run() override {
APZEventResult result =
mAPZC->InputBridge()->ReceiveInputEvent(mWheelInput);
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return NS_OK;
}
RefPtr<Runnable> r =
new DispatchWheelEventOnMainThread(mWheelInput, mWidget, result);
mMainMessageLoop->PostTask(r.forget());
return NS_OK;
}
private:
MessageLoop* mMainMessageLoop;
ScrollWheelInput mWheelInput;
RefPtr<IAPZCTreeManager> mAPZC;
nsBaseWidget* mWidget;
};
void nsBaseWidget::DispatchTouchInput(MultiTouchInput& aInput) {
MOZ_ASSERT(NS_IsMainThread());
if (mAPZC) {
MOZ_ASSERT(APZThreadUtils::IsControllerThread());
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return;
}
WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
ProcessUntransformedAPZEvent(&event, result);
} else {
WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
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.mStatus == nsEventStatus_eConsumeNoDefault) {
return;
}
WidgetWheelEvent event = aInput.ToWidgetWheelEvent(this);
ProcessUntransformedAPZEvent(&event, result);
} else {
WidgetWheelEvent event = aInput.ToWidgetWheelEvent(this);
nsEventStatus status;
DispatchEvent(&event, status);
}
}
nsEventStatus nsBaseWidget::DispatchInputEvent(WidgetInputEvent* aEvent) {
MOZ_ASSERT(NS_IsMainThread());
if (mAPZC) {
if (APZThreadUtils::IsControllerThread()) {
APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(*aEvent);
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return result.mStatus;
}
return ProcessUntransformedAPZEvent(aEvent, result);
}
WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
if (wheelEvent) {
RefPtr<Runnable> r =
new DispatchWheelInputOnControllerThread(*wheelEvent, mAPZC, this);
APZThreadUtils::RunOnControllerThread(std::move(r));
return nsEventStatus_eConsumeDoDefault;
}
// Allow dispatching keyboard events on Gecko thread.
MOZ_ASSERT(aEvent->AsKeyboardEvent());
}
nsEventStatus status;
DispatchEvent(aEvent, status);
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<LayerManager> 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 use ClientLayerManager
// even when gfxVars::UseWebRender() is true. WebRender could coexist only
// with BasicCompositor.
bool enableWR =
gfx::gfxVars::UseWebRender() && WidgetTypeSupportsAcceleration();
bool enableAPZ = UseAPZ();
CompositorOptions options(enableAPZ, enableWR);
// Bug 1588484 - Advanced Layers is currently disabled for fission windows,
// since it doesn't properly support nested RefLayers.
bool enableAL =
gfx::gfxConfig::IsEnabled(gfx::Feature::ADVANCED_LAYERS) &&
(!mFissionWindow || StaticPrefs::layers_advanced_fission_enabled());
options.SetUseAdvancedLayers(enableAL);
#ifdef MOZ_WIDGET_ANDROID
if (!GetNativeData(NS_JAVA_SURFACE)) {
options.SetInitiallyPaused(true);
}
#else
options.SetInitiallyPaused(CompositorInitiallyPaused());
#endif
RefPtr<LayerManager> lm;
if (options.UseWebRender()) {
lm = new WebRenderLayerManager(this);
} else {
lm = new ClientLayerManager(this);
}
bool retry = false;
mCompositorSession = gpu->CreateTopLevelCompositor(
this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(),
gfx::IntSize(aWidth, aHeight), &retry);
if (lm->AsWebRenderLayerManager() && mCompositorSession) {
TextureFactoryIdentifier textureFactoryIdentifier;
nsCString error;
lm->AsWebRenderLayerManager()->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);
}
} else if (lm->AsClientLayerManager() && mCompositorSession) {
bool shouldAccelerate = ComputeShouldAccelerate();
TextureFactoryIdentifier textureFactoryIdentifier;
lm->AsClientLayerManager()->Initialize(
mCompositorSession->GetCompositorBridgeChild(), shouldAccelerate,
&textureFactoryIdentifier);
if (textureFactoryIdentifier.mParentBackend ==
LayersBackend::LAYERS_NONE) {
DestroyCompositor();
lm = nullptr;
}
}
// 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<LayerManager> 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();
}
if (lm->AsWebRenderLayerManager()) {
TextureFactoryIdentifier textureFactoryIdentifier =
lm->GetTextureFactoryIdentifier();
MOZ_ASSERT(textureFactoryIdentifier.mParentBackend ==
LayersBackend::LAYERS_WR);
ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
} else if (lm->AsClientLayerManager()) {
TextureFactoryIdentifier textureFactoryIdentifier =
lm->GetTextureFactoryIdentifier();
// Some popup or transparent widgets may use a different backend than the
// compositors used with ImageBridge and VR (and more generally web
// content).
if (WidgetTypeSupportsAcceleration()) {
ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
}
}
WindowUsesOMTC();
mLayerManager = 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(
mLayerManager->GetCompositorBackendType());
}
}
void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession* aSession) {
MOZ_ASSERT(aSession == mCompositorSession);
DestroyLayerManager();
}
bool nsBaseWidget::ShouldUseOffMainThreadCompositing() {
return gfxPlatform::UsesOffMainThreadCompositing();
}
LayerManager* nsBaseWidget::GetLayerManager(
PLayerTransactionChild* aShadowManager, LayersBackend aBackendHint,
LayerManagerPersistence aPersistence) {
if (!mLayerManager) {
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()) {
// e10s uses the parameter to pass in the shadow manager from the
// BrowserChild so we don't expect to see it there since this doesn't
// support e10s.
NS_ASSERTION(aShadowManager == nullptr,
"Async Compositor not supported with e10s");
CreateCompositor();
}
if (!mLayerManager) {
mLayerManager = CreateBasicLayerManager();
}
}
return mLayerManager;
}
LayerManager* nsBaseWidget::CreateBasicLayerManager() {
return new BasicLayerManager(this);
}
CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer() {
return mCompositorBridgeChild;
}
void nsBaseWidget::ClearCachedWebrenderResources() {
if (!mLayerManager || !mLayerManager->AsWebRenderLayerManager()) {
return;
}
mLayerManager->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 {
LayoutDevicePoint layoutOffset = aOffset * GetDesktopToDeviceScale();
Move(layoutOffset.x - clientOffset.x, layoutOffset.y - clientOffset.y);
}
}
void nsBaseWidget::ResizeClient(const DesktopSize& aSize, bool aRepaint) {
NS_ASSERTION((aSize.width >= 0), "Negative width passed to ResizeClient");
NS_ASSERTION((aSize.height >= 0), "Negative height passed to ResizeClient");
LayoutDeviceIntRect clientBounds = GetClientBounds();
// GetClientBounds and mBounds are device pixels; scale back to desktop pixels
// if that's what this widget uses for the Move/Resize APIs
if (BoundsUseDesktopPixels()) {
DesktopSize desktopDelta =
(LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
clientBounds.Size()) /
GetDesktopToDeviceScale();
Resize(aSize.width + desktopDelta.width, aSize.height + desktopDelta.height,
aRepaint);
} else {
LayoutDeviceSize layoutSize = aSize * GetDesktopToDeviceScale();
Resize(mBounds.Width() + (layoutSize.width - clientBounds.Width()),
mBounds.Height() + (layoutSize.height - clientBounds.Height()),
aRepaint);
}
}
void nsBaseWidget::ResizeClient(const DesktopRect& aRect, bool aRepaint) {
NS_ASSERTION((aRect.Width() >= 0), "Negative width passed to ResizeClient");
NS_ASSERTION((aRect.Height() >= 0), "Negative height passed to ResizeClient");
LayoutDeviceIntRect clientBounds = GetClientBounds();
LayoutDeviceIntPoint clientOffset = GetClientOffset();
DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale();
if (BoundsUseDesktopPixels()) {
DesktopPoint desktopOffset = clientOffset / scale;
DesktopSize desktopDelta =
(LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
clientBounds.Size()) /
scale;
Resize(aRect.X() - desktopOffset.x, aRect.Y() - desktopOffset.y,
aRect.Width() + desktopDelta.width,
aRect.Height() + desktopDelta.height, aRepaint);
} else {
LayoutDeviceRect layoutRect = aRect * scale;
Resize(layoutRect.X() - clientOffset.x, layoutRect.Y() - clientOffset.y,
layoutRect.Width() + mBounds.Width() - clientBounds.Width(),
layoutRect.Height() + mBounds.Height() - clientBounds.Height(),
aRepaint);
}
}
//-------------------------------------------------------------------------
//
// Bounds
//
//-------------------------------------------------------------------------
/**
* If the implementation of nsWindow supports borders this method MUST be
* overridden
*
**/
LayoutDeviceIntRect nsBaseWidget::GetClientBounds() { return GetBounds(); }
/**
* If the implementation of nsWindow supports borders this method MUST be
* overridden
*
**/
LayoutDeviceIntRect nsBaseWidget::GetBounds() { return mBounds; }
/**
* If the implementation of nsWindow uses a local coordinate system within the
*window, this method must be overridden
*
**/
LayoutDeviceIntRect nsBaseWidget::GetScreenBounds() { return GetBounds(); }
nsresult nsBaseWidget::GetRestoredBounds(LayoutDeviceIntRect& aRect) {
if (SizeMode() != nsSizeMode_Normal) {
return NS_ERROR_FAILURE;
}
aRect = GetScreenBounds();
return NS_OK;
}
LayoutDeviceIntPoint nsBaseWidget::GetClientOffset() {
return LayoutDeviceIntPoint(0, 0);
}
nsresult nsBaseWidget::SetNonClientMargins(LayoutDeviceIntMargin& margins) {
return NS_ERROR_NOT_IMPLEMENTED;
}
uint32_t nsBaseWidget::GetMaxTouchPoints() const { return 0; }
bool nsBaseWidget::HasPendingInputEvent() { return false; }
bool nsBaseWidget::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) {
return false;
}
/**
* Modifies aFile to point at an icon file with the given name and suffix. The
* suffix may correspond to a file extension with leading '.' if appropriate.
* Returns true if the icon file exists and can be read.
*/
static bool ResolveIconNameHelper(nsIFile* aFile, const nsAString& aIconName,
const nsAString& aIconSuffix) {
aFile->Append(u"icons"_ns);
aFile->Append(u"default"_ns);
aFile->Append(aIconName + aIconSuffix);
bool readable;
return NS_SUCCEEDED(aFile->IsReadable(&readable)) && readable;
}
/**
* Resolve the given icon name into a local file object. This method is
* intended to be called by subclasses of nsBaseWidget. aIconSuffix is a
* platform specific icon file suffix (e.g., ".ico" under Win32).
*
* If no file is found matching the given parameters, then null is returned.
*/
void nsBaseWidget::ResolveIconName(const nsAString& aIconName,
const nsAString& aIconSuffix,
nsIFile** aResult) {
*aResult = nullptr;
nsCOMPtr<nsIProperties> dirSvc =
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
if (!dirSvc) return;
// first check auxilary chrome directories
nsCOMPtr<nsISimpleEnumerator> dirs;
dirSvc->Get(NS_APP_CHROME_DIR_LIST, NS_GET_IID(nsISimpleEnumerator),
getter_AddRefs(dirs));
if (dirs) {
bool hasMore;
while (NS_SUCCEEDED(dirs->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> element;
dirs->GetNext(getter_AddRefs(element));
if (!element) continue;
nsCOMPtr<nsIFile> file = do_QueryInterface(element);
if (!file) continue;
if (ResolveIconNameHelper(file, aIconName, aIconSuffix)) {
NS_ADDREF(*aResult = file);
return;
}
}
}
// then check the main app chrome directory
nsCOMPtr<nsIFile> file;
dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(file));
if (file && ResolveIconNameHelper(file, aIconName, aIconSuffix))
NS_ADDREF(*aResult = file);
}
void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints) {
mSizeConstraints = aConstraints;
// Popups are constrained during layout, and we don't want to synchronously
// paint from reflow, so bail out... This is not great, but it's no worse than
// what we used to do.
//
// The right fix here is probably making constraint changes go through the
// view manager and such.
if (mWindowType == eWindowType_popup) {
return;
}
// If the current size doesn't meet the new constraints, trigger a
// resize to apply it. Note that, we don't want to invoke Resize if
// the new constraints don't affect the current size, because Resize
// implementation on some platforms may touch other geometry even if
// the size don't need to change.
LayoutDeviceIntSize curSize = mBounds.Size();
LayoutDeviceIntSize clampedSize =
Max(aConstraints.mMinSize, Min(aConstraints.mMaxSize, curSize));
if (clampedSize != curSize) {
gfx::Size size;
if (BoundsUseDesktopPixels()) {
DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale();
size = desktopSize.ToUnknownSize();
} else {
size = gfx::Size(clampedSize.ToUnknownSize());
}
Resize(size.width, size.height, true);
}
}
const widget::SizeConstraints nsBaseWidget::GetSizeConstraints() {
return mSizeConstraints;
}
// static
nsIRollupListener* nsBaseWidget::GetActiveRollupListener() {
// If set, then this is likely an <html:select> dropdown.
if (gRollupListener) return gRollupListener;
return nsXULPopupManager::GetInstance();
}
void nsBaseWidget::NotifyWindowDestroyed() {
if (!mWidgetListener) return;
nsCOMPtr<nsIAppWindow> window = mWidgetListener->GetAppWindow();
nsCOMPtr<nsIBaseWindow> appWindow(do_QueryInterface(window));
if (appWindow) {
appWindow->Destroy();
}
}
void nsBaseWidget::NotifyWindowMoved(int32_t aX, int32_t aY) {
if (mWidgetListener) {
mWidgetListener->WindowMoved(this, aX, aY);
}
if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) {
NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE));
}
}
void nsBaseWidget::NotifySizeMoveDone() {
if (!mWidgetListener) {
return;
}
if (PresShell* presShell = mWidgetListener->GetPresShell()) {
presShell->WindowSizeMoveDone();
}
}
void nsBaseWidget::NotifyThemeChanged() {
if (!mWidgetListener) {
return;
}
if (PresShell* presShell = mWidgetListener->GetPresShell()) {
presShell->ThemeChanged();
}
}
void nsBaseWidget::NotifyUIStateChanged(UIStateChangeType aShowFocusRings) {
if (Document* doc = GetDocument()) {
if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
win->SetKeyboardIndicators(aShowFocusRings);
}
}
}
nsresult nsBaseWidget::NotifyIME(const IMENotification& aIMENotification) {
if (mIMEHasQuit) {
return NS_OK;
}
switch (aIMENotification.mMessage) {
case REQUEST_TO_COMMIT_COMPOSITION:
case REQUEST_TO_CANCEL_COMPOSITION:
// We should send request to IME only when there is a TextEventDispatcher
// instance (this means that this widget has dispatched at least one
// composition event or keyboard event) and the it has composition.
// Otherwise, there is nothing to do.
// Note that if current input transaction is for native input events,
// TextEventDispatcher::NotifyIME() will call
// TextEventDispatcherListener::NotifyIME().
if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) {
return mTextEventDispatcher->NotifyIME(aIMENotification);
}
return NS_OK;
default: {
if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
mIMEHasFocus = true;
}
EnsureTextEventDispatcher();
// TextEventDispatcher::NotifyIME() will always call
// TextEventDispatcherListener::NotifyIME(). I.e., even if current
// input transaction is for synthesized events for automated tests,
// notifications will be sent to native IME.
nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification);
if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
mIMEHasFocus = false;
}
return rv;
}
}
}
void nsBaseWidget::EnsureTextEventDispatcher() {
if (mTextEventDispatcher) {
return;
}
mTextEventDispatcher = new TextEventDispatcher(this);
}
nsIWidget::NativeIMEContext nsBaseWidget::GetNativeIMEContext() {
if (mTextEventDispatcher && mTextEventDispatcher->GetPseudoIMEContext()) {
// If we already have a TextEventDispatcher and it's working with
// a TextInputProcessor, we need to return pseudo IME context since
// TextCompositionArray::IndexOf(nsIWidget*) should return a composition
// on the pseudo IME context in such case.
NativeIMEContext pseudoIMEContext;
pseudoIMEContext.InitWithRawNativeIMEContext(
mTextEventDispatcher->GetPseudoIMEContext());
return pseudoIMEContext;
}
return NativeIMEContext(this);
}
nsIWidget::TextEventDispatcher* nsBaseWidget::GetTextEventDispatcher() {
EnsureTextEventDispatcher();
return mTextEventDispatcher;
}
void* nsBaseWidget::GetPseudoIMEContext() {
TextEventDispatcher* dispatcher = GetTextEventDispatcher();
if (!dispatcher) {
return nullptr;
}
return dispatcher->GetPseudoIMEContext();
}
TextEventDispatcherListener*
nsBaseWidget::GetNativeTextEventDispatcherListener() {
// TODO: If all platforms supported use of TextEventDispatcher for handling
// native IME and keyboard events, this method should be removed since
// in such case, this is overridden by all the subclasses.
return nullptr;
}
void nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId,
const ScrollableLayerGuid::ViewID& aViewId,
const CSSRect& aRect, const uint32_t& aFlags) {
if (!mCompositorSession || !mAPZC) {
return;
}
LayersId layerId = mCompositorSession->RootLayerTreeId();
APZThreadUtils::RunOnControllerThread(
NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
"layers::IAPZCTreeManager::ZoomToRect", mAPZC,
&IAPZCTreeManager::ZoomToRect,
ScrollableLayerGuid(layerId, aPresShellId, aViewId), aRect, aFlags));
}
#ifdef ACCESSIBILITY
a11y::Accessible* nsBaseWidget::GetRootAccessible() {
NS_ENSURE_TRUE(mWidgetListener, nullptr);
PresShell* presShell = mWidgetListener->GetPresShell();
NS_ENSURE_TRUE(presShell, nullptr);
// If container is null then the presshell is not active. This often happens
// when a preshell is being held onto for fastback.
nsPresContext* presContext = presShell->GetPresContext();
NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr);
// Accessible creation might be not safe so use IsSafeToRunScript to
// make sure it's not created at unsafe times.
nsAccessibilityService* accService = GetOrCreateAccService();
if (accService) {
return accService->GetRootDocumentAccessible(
presShell, nsContentUtils::IsSafeToRunScript());
}
return nullptr;
}
#endif // ACCESSIBILITY
void nsBaseWidget::StartAsyncScrollbarDrag(
const AsyncDragMetrics& aDragMetrics) {
if (!AsyncPanZoomEnabled()) {
return;
}
MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession);
LayersId layersId = mCompositorSession->RootLayerTreeId();
ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId,
aDragMetrics.mViewId);
APZThreadUtils::RunOnControllerThread(
NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
"layers::IAPZCTreeManager::StartScrollbarDrag", mAPZC,
&IAPZCTreeManager::StartScrollbarDrag, guid, aDragMetrics));
}
bool nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
const ScrollableLayerGuid& aGuid) {
MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
return mAPZC->StartAutoscroll(aGuid, aAnchorLocation);
}
void nsBaseWidget::StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) {
MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
mAPZC->StopAutoscroll(aGuid);
}
already_AddRefed<nsIScreen> nsBaseWidget::GetWidgetScreen() {
nsCOMPtr<nsIScreenManager> screenManager;
screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
if (!screenManager) {
return nullptr;
}
LayoutDeviceIntRect bounds = GetScreenBounds();
DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale());
nsCOMPtr<nsIScreen> screen;
screenManager->ScreenForRect(deskBounds.X(), deskBounds.Y(),
deskBounds.Width(), deskBounds.Height(),
getter_AddRefs(screen));
return screen.forget();
}
mozilla::DesktopToLayoutDeviceScale
nsBaseWidget::GetDesktopToDeviceScaleByScreen() {
return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext())
->GetDesktopToDeviceScale();
}
nsresult nsIWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
bool aLongTap,
nsIObserver* aObserver) {
AutoObserverNotifier notifier(aObserver, "touchtap");
if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) {
sPointerIdCounter = 0;
}
int pointerId = sPointerIdCounter;
sPointerIdCounter++;
nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT, aPoint,
1.0, 90, nullptr);
if (NS_FAILED(rv)) {
return rv;
}
if (!aLongTap) {
return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, aPoint, 0, 0,
nullptr);
}
// initiate a long tap
int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay",
TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC);
if (!mLongTapTimer) {
mLongTapTimer = NS_NewTimer();
if (!mLongTapTimer) {
SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL, aPoint, 0, 0,
nullptr);
return NS_ERROR_UNEXPECTED;
}
// Windows requires recuring events, so we set this to a smaller window
// than the pref value.
int timeout = elapse;
if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) {
timeout = TOUCH_INJECT_PUMP_TIMER_MSEC;
}
mLongTapTimer->InitWithNamedFuncCallback(
OnLongTapTimerCallback, this, timeout, nsITimer::TYPE_REPEATING_SLACK,
"nsIWidget::SynthesizeNativeTouchTap");
}
// If we already have a long tap pending, cancel it. We only allow one long
// tap to be active at a time.