/* -*- 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/. */
// 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;
MOZ_RUNINIT /*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;
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.
}
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;
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.
}
// 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());
// 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;
mCompositorSession->Shutdown();
mCompositorSession = nullptr;
}
}
// This prevents the layer manager from starting a new transaction during // shutdown. void nsBaseWidget::RevokeTransactionIdAllocator() { if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) { return;
}
mWindowRenderer->AsWebRender()->SetTransactionIdAllocator(nullptr);
}
//------------------------------------------------------------------------- // // Accessor functions to get/set the client data // //-------------------------------------------------------------------------
//------------------------------------------------------------------------- // // Close this nsBaseWidget // //------------------------------------------------------------------------- void nsBaseWidget::Destroy() {
DestroyCompositor();
// Just in case our parent is the only ref to us
nsCOMPtr<nsIWidget> kungFuDeathGrip(this); // disconnect from the parent if (mParent) {
mParent->RemoveFromChildList(this);
mParent = nullptr;
} // disconnect from the children
RemoveAllChildren();
}
nsIWidget* nsIWidget::GetTopLevelWidget() { auto* cur = this; while (true) { if (cur->IsTopLevelWidget()) { break;
}
nsIWidget* parent = cur->GetParent(); if (!parent) { break;
}
cur = parent;
} return cur;
}
float nsBaseWidget::GetDPI() { return 96.0f; }
void nsBaseWidget::NotifyAPZOfDPIChange() { if (mAPZC) {
mAPZC->SetDPI(GetDPI());
}
}
LayoutDeviceIntSize nsIWidget::ClientToWindowSizeDifference() { auto margin = ClientToWindowMargin();
MOZ_ASSERT(margin.top >= 0, "Window should be bigger than client area");
MOZ_ASSERT(margin.left >= 0, "Window should be bigger than client area");
MOZ_ASSERT(margin.right >= 0, "Window should be bigger than client area");
MOZ_ASSERT(margin.bottom >= 0, "Window should be bigger than client area"); return {margin.LeftRight(), margin.TopBottom()};
}
//------------------------------------------------------------------------- // // Add a child to the list of children // //------------------------------------------------------------------------- void nsIWidget::AddToChildList(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 nsIWidget::RemoveFromChildList(nsIWidget* aChild) {
MOZ_ASSERT(aChild->GetParent() == this, "Not one of our kids!");
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);
}
//------------------------------------------------------------------------- // // Get this component cursor // //-------------------------------------------------------------------------
//------------------------------------------------------------------------- // // Put the window into full-screen mode // //------------------------------------------------------------------------- void nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen) { #define MOZ_FORMAT_RECT(fmtstr) "[" fmtstr "," fmtstr " " fmtstr "x" fmtstr "]" #define MOZ_SPLAT_RECT(rect) \
(rect).X(), (rect).Y(), (rect).Width(), (rect).Height()
// Windows which can be made fullscreen are exactly those which are located on // the desktop, rather than being a child of some other window.
MOZ_DIAGNOSTIC_ASSERT(BoundsUseDesktopPixels(), "non-desktop windows cannot be made fullscreen");
// Ensure that the OS chrome is hidden/shown before we resize and/or exit the // function. // // HideWindowChrome() may (depending on platform, implementation details, and // OS-level user preferences) alter the reported size of the window. The // obvious and principled solution is socks-and-shoes: // - On entering fullscreen mode: hide window chrome, then perform resize. // - On leaving fullscreen mode: unperform resize, then show window chrome. // // ... unfortunately, HideWindowChrome() requires Resize() to be called // afterwards (see bug 498835), which prevents this from being done in a // straightforward way. // // Instead, we always call HideWindowChrome() just before we call Resize(). // This at least ensures that our measurements are consistently taken in a // pre-transition state. // // ... unfortunately again, coupling HideWindowChrome() to Resize() means that // we have to worry about the possibility of control flows that don't call // Resize() at all. (That shouldn't happen, but it's not trivial to rule out.) // We therefore set up a fallback to fix up the OS chrome if it hasn't been // done at exit time. bool hasAdjustedOSChrome = false; constauto adjustOSChrome = [&]() { if (hasAdjustedOSChrome) {
MOZ_ASSERT_UNREACHABLE("window chrome should only be adjusted once"); return;
}
HideWindowChrome(aFullScreen);
hasAdjustedOSChrome = true;
}; constauto adjustChromeOnScopeExit = MakeScopeExit([&]() { if (hasAdjustedOSChrome) { return;
}
MOZ_LOG(sBaseWidgetLog, LogLevel::Warning,
("window was not resized within InfallibleMakeFullScreen()"));
// Hide chrome and "resize" the window to its current size. auto rect = GetBounds();
adjustOSChrome();
Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(), true);
});
// Attempt to resize to `rect`. // // Returns the actual rectangle resized to. (This may differ from `rect`, if // the OS is unhappy with it. See bug 1786226.) constauto doReposition = [&](auto rect) -> void {
static_assert(std::is_base_of_v<DesktopPixel,
std::remove_reference_t<decltype(rect)>>, "doReposition requires a rectangle using desktop pixels");
if (MOZ_LOG_TEST(sBaseWidgetLog, LogLevel::Warning)) { // `rect` may have any underlying data type; coerce to float to // simplify printf-style logging const gfx::RectTyped<DesktopPixel, float> rectAsFloat{rect};
// The OS may have objected to the target position. That's not necessarily // a problem -- it'll happen regularly on Macs with camera notches in the // monitor, for instance (see bug 1786226) -- but it probably deserves to // be called out. // // Since there's floating-point math involved, the actual values may be // off by a few ulps -- as an upper bound, perhaps 8 * FLT_EPSILON * // max(MOZ_SPLAT_RECT(rect)) -- but 0.01 should be several orders of // magnitude bigger than that.
if (aFullScreen) { if (!mSavedBounds) {
mSavedBounds = Some(FullscreenSavedState());
} // save current position
mSavedBounds->windowRect = GetScreenBounds() / GetDesktopToDeviceScale();
nsCOMPtr<nsIScreen> screen = GetWidgetScreen(); if (!screen) { return;
}
// Move to fill the screen.
doReposition(screen->GetRectDisplayPix()); // Save off the new position. (This may differ from GetRectDisplayPix(), if // the OS was unhappy with it. See bug 1786226.)
mSavedBounds->screenRect = GetScreenBounds() / GetDesktopToDeviceScale();
} else { if (!mSavedBounds) { // This should never happen, at present, since we don't make windows // fullscreen at their creation time; but it's not logically impossible.
MOZ_ASSERT(false, "fullscreen window did not have saved position"); return;
}
// Figure out where to go from here. // // Fortunately, since we're currently fullscreen (and other code should be // handling _keeping_ us fullscreen even after display-layout changes), // there's an obvious choice for which display we should attach to; all we // need to determine is where on that display we should go.
// Optimization: if where we are is where we were, then where we originally // came from is where we're going to go. if (currentWinRect == DesktopRect(mSavedBounds->screenRect)) {
MOZ_LOG(sBaseWidgetLog, LogLevel::Debug,
("no location change detected; returning to saved location"));
doReposition(mSavedBounds->windowRect); return;
}
/* General case: figure out where we're going to go by dividing where we are by where we were, and then multiplying by where we originally came from.
Less abstrusely: resize so that we occupy the same proportional position on our current display after leaving fullscreen as we occupied on our previous display before entering fullscreen.
(N.B.: We do not clamp. If we were only partially on the old display, we'll be only partially on the new one, too.)
*/
MOZ_LOG(sBaseWidgetLog, LogLevel::Debug,
("location change detected; computing new destination"));
// splat: convert an arbitrary Rect into a tuple, for syntactic convenience. constauto splat = [](auto rect) { return std::tuple(rect.X(), rect.Y(), rect.Width(), rect.Height());
};
// remap: find the unique affine mapping which transforms `src` to `dst`, // and apply it to `val`. using Range = std::pair<float, float>; constauto remap = [](Range dst, Range src, float val) { // linear interpolation and its inverse: lerp(a, b, invlerp(a, b, t)) == t constauto lerp = [](float lo, float hi, float t) { return lo + t * (hi - lo);
}; constauto invlerp = [](float lo, float hi, float mid) { return (mid - lo) / (hi - lo);
};
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::UpdateZoomConstraints( const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, const Maybe<ZoomConstraints>& aConstraints) { if (!mCompositorSession || !mAPZC) {
MOZ_ASSERT_IF(mInitialZoomConstraints,
mInitialZoomConstraints->mViewID == aViewId); 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()));
} else {
mInitialZoomConstraints.reset();
} return;
}
LayersId layersId = mCompositorSession->RootLayerTreeId();
mAPZC->UpdateZoomConstraints(
ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints);
}
// 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() &&
!InputAPZContext::WasDropped() && inputBlockId) { // EventStateManager did not route the event into the child process and // the event was dispatched in the parent 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();
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;
} // Most drag events aren't able to converted to MouseEvent except to // eDragStart and eDragEnd. constbool canDispatchToApzc =
!aEvent->AsDragEvent() ||
aEvent->AsDragEvent()->CanConvertToInputData(); if (canDispatchToApzc) { 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 (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
MOZ_ASSERT(aEvent->mMessage == eContextMenu);
RefPtr<Runnable> r = new DispatchInputOnControllerThread<MouseInput, WidgetPointerEvent>(
*pointerEvent, 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/drag events on Gecko thread // without sending them to APZ
// FIXME: APZ can handle keyboard events now, we should // be sending them to APZ here
MOZ_ASSERT(aEvent->AsKeyboardEvent() || aEvent->AsDragEvent());
}
}
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) {
RefPtr<VsyncDispatcher> vsyncDispatcher =
gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher();
mCompositorVsyncDispatcher = new CompositorVsyncDispatcher(std::move(vsyncDispatcher));
}
}
}
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.
nsresult rv = gpu->EnsureGPUReady(); if (NS_WARN_IF(rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN)) { return nullptr;
}
// If widget type does not supports acceleration, we may be allowed to use // software WebRender instead. bool supportsAcceleration = WidgetTypeSupportsAcceleration(); bool enableSWWR = true; if (supportsAcceleration ||
StaticPrefs::gfx_webrender_unaccelerated_widget_force()) {
enableSWWR = gfx::gfxVars::UseSoftwareWebRender();
} bool enableAPZ = UseAPZ();
CompositorOptions options(enableAPZ, enableSWWR);
#ifdef XP_WIN if (supportsAcceleration) {
options.SetAllowSoftwareWebRenderD3D11(
gfx::gfxVars::AllowSoftwareWebRenderD3D11());
} if (mNeedFastSnaphot) {
options.SetNeedFastSnaphot(true);
} #elifdefined(MOZ_WIDGET_ANDROID)
MOZ_ASSERT(supportsAcceleration);
options.SetAllowSoftwareWebRenderOGL(
gfx::gfxVars::AllowSoftwareWebRenderOGL()); #elifdefined(MOZ_WIDGET_GTK) if (supportsAcceleration) {
options.SetAllowSoftwareWebRenderOGL(
gfx::gfxVars::AllowSoftwareWebRenderOGL());
} #endif
#ifdef MOZ_WIDGET_ANDROID // Unconditionally set the compositor as initially paused, as we have not // yet had a chance to send the compositor surface to the GPU process. We // will do so shortly once we have returned to nsWindow::CreateLayerManager, // where we will also resume the compositor if required.
options.SetInitiallyPaused(true); #else
options.SetInitiallyPaused(CompositorInitiallyPaused()); #endif
RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this);
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;
}
// The controller thread must be configured before the compositor // session is created, so that the input bridge runs on the right // thread.
ConfigureAPZControllerThread();
// Only track compositors for top-level windows, since other window types // may use the basic compositor. Except on the OS X - see bug 1306383 #ifdefined(XP_MACOSX) bool getCompositorFromThisWindow = true; #else bool getCompositorFromThisWindow = mWindowType == WindowType::TopLevel; #endif
if (getCompositorFromThisWindow) {
gfxPlatform::GetPlatform()->NotifyCompositorCreated(
mWindowRenderer->GetCompositorBackendType());
}
}
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();
}
//------------------------------------------------------------------------- // // 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();
}
// The maximum position to which the window can be moved while keeping its // bottom-right corner within screenRect. autoconst maxX = aScreenRect.XMost() - aSize.Width(); autoconst maxY = aScreenRect.YMost() - aSize.Height();
// Note that the conditional-pairs below are not exclusive with each other, // and cannot be replaced with a simple call to `std::clamp`! If the window // provided is too large to fit on the screen, they will both fire. Their // order has been chosen to ensure that the window's top left corner will be // onscreen.
if (point.x >= maxX) {
point.x = maxX;
} if (point.x < aScreenRect.x) {
point.x = aScreenRect.x;
}
if (point.y >= maxY) {
point.y = maxY;
} if (point.y < aScreenRect.y) {
point.y = aScreenRect.y;
}
/** * 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(); }
/** * 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.
*/ staticbool ResolveIconNameHelper(nsIFile* aFile, const nsAString& aIconName, const nsAString& aIconSuffix) {
aFile->Append(u"icons"_ns);
aFile->Append(u"default"_ns);
aFile->Append(aIconName + aIconSuffix);
/** * 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;
}
}
}
// 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 == WindowType::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);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.