2 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "nsBaseWidget.h"
13 #include "InputData.h"
14 #include "LiveResizeListener.h"
15 #include "SwipeTracker.h"
16 #include "TouchEvents.h"
17 #include "X11UndefineNone.h"
18 #include "base/thread.h"
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/Attributes.h"
21 #include "mozilla/GlobalKeyListener.h"
22 #include "mozilla/IMEStateManager.h"
23 #include "mozilla/MouseEvents.h"
24 #include "mozilla/NativeKeyBindingsType.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/Sprintf.h"
28 #include "mozilla/StaticPrefs_apz.h"
29 #include "mozilla/StaticPrefs_dom.h"
30 #include "mozilla/StaticPrefs_gfx.h"
31 #include "mozilla/StaticPrefs_layers.h"
32 #include "mozilla/StaticPrefs_layout.h"
33 #include "mozilla/TextEventDispatcher.h"
34 #include "mozilla/TextEventDispatcherListener.h"
35 #include "mozilla/UniquePtr.h"
36 #include "mozilla/Unused.h"
37 #include "mozilla/VsyncDispatcher.h"
38 #include "mozilla/dom/BrowserParent.h"
39 #include "mozilla/dom/ContentChild.h"
40 #include "mozilla/dom/Document.h"
41 #include "mozilla/dom/SimpleGestureEventBinding.h"
42 #include "mozilla/gfx/2D.h"
43 #include "mozilla/gfx/GPUProcessManager.h"
44 #include "mozilla/gfx/gfxVars.h"
45 #include "mozilla/layers/APZCCallbackHelper.h"
46 #include "mozilla/layers/TouchActionHelper.h"
47 #include "mozilla/layers/APZEventState.h"
48 #include "mozilla/layers/APZInputBridge.h"
49 #include "mozilla/layers/APZThreadUtils.h"
50 #include "mozilla/layers/ChromeProcessController.h"
51 #include "mozilla/layers/Compositor.h"
52 #include "mozilla/layers/CompositorBridgeChild.h"
53 #include "mozilla/layers/CompositorBridgeParent.h"
54 #include "mozilla/layers/CompositorOptions.h"
55 #include "mozilla/layers/IAPZCTreeManager.h"
56 #include "mozilla/layers/ImageBridgeChild.h"
57 #include "mozilla/layers/InputAPZContext.h"
58 #include "mozilla/layers/WebRenderLayerManager.h"
59 #include "mozilla/webrender/WebRenderTypes.h"
60 #include "nsAppDirectoryServiceDefs.h"
62 #include "nsContentUtils.h"
63 #include "nsDeviceContext.h"
64 #include "nsGfxCIID.h"
65 #include "nsIAppWindow.h"
66 #include "nsIBaseWindow.h"
67 #include "nsIContent.h"
68 #include "nsIScreenManager.h"
69 #include "nsISimpleEnumerator.h"
70 #include "nsIWidgetListener.h"
71 #include "nsRefPtrHashtable.h"
72 #include "nsServiceManagerUtils.h"
73 #include "nsWidgetsCID.h"
74 #include "nsXULPopupManager.h"
78 # include "nsAccessibilityService.h"
80 #include "gfxConfig.h"
81 #include "gfxUtils.h" // for ToDeviceColor
82 #include "mozilla/layers/CompositorSession.h"
83 #include "VRManagerChild.h"
84 #include "gfxConfig.h"
86 #include "nsViewManager.h"
89 # include "nsIObserver.h"
91 static void debug_RegisterPrefCallbacks();
95 #ifdef NOISY_WIDGET_LEAKS
96 static int32_t gNumWidgets
;
100 # include "nsCocoaFeatures.h"
103 nsIRollupListener
* nsBaseWidget::gRollupListener
= nullptr;
105 using namespace mozilla::dom
;
106 using namespace mozilla::layers
;
107 using namespace mozilla::ipc
;
108 using namespace mozilla::widget
;
109 using namespace mozilla
;
111 // Async pump timer during injected long touch taps
112 #define TOUCH_INJECT_PUMP_TIMER_MSEC 50
113 #define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500
114 int32_t nsIWidget::sPointerIdCounter
= 0;
116 // Some statics from nsIWidget.h
118 uint64_t AutoObserverNotifier::sObserverId
= 0;
119 /*static*/ nsTHashMap
<uint64_t, nsCOMPtr
<nsIObserver
>>
120 AutoObserverNotifier::sSavedObservers
;
122 // The maximum amount of time to let the EnableDragDrop runnable wait in the
123 // idle queue before timing out and moving it to the regular queue. Value is in
125 const uint32_t kAsyncDragDropTimeout
= 1000;
127 NS_IMPL_ISUPPORTS(nsBaseWidget
, nsIWidget
, nsISupportsWeakReference
)
129 //-------------------------------------------------------------------------
131 // nsBaseWidget constructor
133 //-------------------------------------------------------------------------
135 nsBaseWidget::nsBaseWidget() : nsBaseWidget(eBorderStyle_none
) {}
137 nsBaseWidget::nsBaseWidget(nsBorderStyle aBorderStyle
)
138 : mWidgetListener(nullptr),
139 mAttachedWidgetListener(nullptr),
140 mPreviouslyAttachedWidgetListener(nullptr),
141 mCompositorVsyncDispatcher(nullptr),
142 mBorderStyle(aBorderStyle
),
144 mOriginalBounds(nullptr),
146 mPopupLevel(ePopupLevelTop
),
147 mPopupType(ePopupTypeAny
),
148 mHasRemoteContent(false),
150 mUseAttachedEvents(false),
153 mIsFullyOccluded(false),
154 mNeedFastSnaphot(false),
155 mCurrentPanGestureBelongsToSwipe(false) {
156 #ifdef NOISY_WIDGET_LEAKS
158 printf("WIDGETS+ = %d\n", gNumWidgets
);
162 debug_RegisterPrefCallbacks();
165 mShutdownObserver
= new WidgetShutdownObserver(this);
168 NS_IMPL_ISUPPORTS(WidgetShutdownObserver
, nsIObserver
)
170 WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget
* aWidget
)
171 : mWidget(aWidget
), mRegistered(false) {
175 WidgetShutdownObserver::~WidgetShutdownObserver() {
176 // No need to call Unregister(), we can't be destroyed until nsBaseWidget
177 // gets torn down. The observer service and nsBaseWidget have a ref on us
178 // so nsBaseWidget has to call Unregister and then clear its ref.
182 WidgetShutdownObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
183 const char16_t
* aData
) {
187 if (!strcmp(aTopic
, NS_XPCOM_SHUTDOWN_OBSERVER_ID
)) {
188 RefPtr
<nsBaseWidget
> widget(mWidget
);
190 } else if (!strcmp(aTopic
, "quit-application")) {
191 RefPtr
<nsBaseWidget
> widget(mWidget
);
197 void WidgetShutdownObserver::Register() {
200 nsContentUtils::RegisterShutdownObserver(this);
202 #ifndef MOZ_WIDGET_ANDROID
203 // The primary purpose of observing quit-application is
204 // to avoid leaking a widget on Windows when nothing else
205 // breaks the circular reference between the widget and
206 // TSFTextStore. However, our Android IME code crashes if
207 // doing this on Android, so let's not do this on Android.
208 // Doing this on Gtk and Mac just in case.
209 nsCOMPtr
<nsIObserverService
> observerService
=
210 mozilla::services::GetObserverService();
211 if (observerService
) {
212 observerService
->AddObserver(this, "quit-application", false);
218 void WidgetShutdownObserver::Unregister() {
222 #ifndef MOZ_WIDGET_ANDROID
223 nsCOMPtr
<nsIObserverService
> observerService
=
224 mozilla::services::GetObserverService();
225 if (observerService
) {
226 observerService
->RemoveObserver(this, "quit-application");
230 nsContentUtils::UnregisterShutdownObserver(this);
235 #define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed"
237 NS_IMPL_ISUPPORTS(LocalesChangedObserver
, nsIObserver
)
239 LocalesChangedObserver::LocalesChangedObserver(nsBaseWidget
* aWidget
)
240 : mWidget(aWidget
), mRegistered(false) {
244 LocalesChangedObserver::~LocalesChangedObserver() {
245 // No need to call Unregister(), we can't be destroyed until nsBaseWidget
246 // gets torn down. The observer service and nsBaseWidget have a ref on us
247 // so nsBaseWidget has to call Unregister and then clear its ref.
251 LocalesChangedObserver::Observe(nsISupports
* aSubject
, const char* aTopic
,
252 const char16_t
* aData
) {
256 if (!strcmp(aTopic
, INTL_APP_LOCALES_CHANGED
)) {
257 RefPtr
<nsBaseWidget
> widget(mWidget
);
258 widget
->LocalesChanged();
263 void LocalesChangedObserver::Register() {
268 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
270 obs
->AddObserver(this, INTL_APP_LOCALES_CHANGED
, true);
273 // Locale might be update before registering
274 RefPtr
<nsBaseWidget
> widget(mWidget
);
275 widget
->LocalesChanged();
280 void LocalesChangedObserver::Unregister() {
285 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
287 obs
->RemoveObserver(this, INTL_APP_LOCALES_CHANGED
);
294 void nsBaseWidget::Shutdown() {
295 NotifyLiveResizeStopped();
296 RevokeTransactionIdAllocator();
298 FreeLocalesChangedObserver();
299 FreeShutdownObserver();
302 void nsBaseWidget::QuitIME() {
303 IMEStateManager::WidgetOnQuit(this);
304 this->mIMEHasQuit
= true;
307 void nsBaseWidget::DestroyCompositor() {
308 // We release this before releasing the compositor, since it may hold the
309 // last reference to our ClientLayerManager. ClientLayerManager's dtor can
310 // trigger a paint, creating a new compositor, and we don't want to re-use
311 // the old vsync dispatcher.
312 if (mCompositorVsyncDispatcher
) {
313 MOZ_ASSERT(mCompositorVsyncDispatcherLock
.get());
315 MutexAutoLock
lock(*mCompositorVsyncDispatcherLock
.get());
316 mCompositorVsyncDispatcher
->Shutdown();
317 mCompositorVsyncDispatcher
= nullptr;
320 // The compositor shutdown sequence looks like this:
321 // 1. CompositorSession calls CompositorBridgeChild::Destroy.
322 // 2. CompositorBridgeChild synchronously sends WillClose.
323 // 3. CompositorBridgeParent releases some resources (such as the layer
324 // manager, compositor, and widget).
325 // 4. CompositorBridgeChild::Destroy returns.
326 // 5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the
327 // compositor thread when the I/O thread closes the IPC channel.
328 // 6. Step 5 will schedule DeferredDestroy on the compositor thread, which
329 // releases the reference CompositorBridgeParent holds to itself.
331 // When CompositorSession::Shutdown returns, we assume the compositor is gone
332 // or will be gone very soon.
333 if (mCompositorSession
) {
334 ReleaseContentController();
336 SetCompositorWidgetDelegate(nullptr);
337 mCompositorBridgeChild
= nullptr;
338 mCompositorSession
->Shutdown();
339 mCompositorSession
= nullptr;
343 // This prevents the layer manager from starting a new transaction during
345 void nsBaseWidget::RevokeTransactionIdAllocator() {
346 if (!mWindowRenderer
|| !mWindowRenderer
->AsWebRender()) {
349 mWindowRenderer
->AsWebRender()->SetTransactionIdAllocator(nullptr);
352 void nsBaseWidget::ReleaseContentController() {
353 if (mRootContentController
) {
354 mRootContentController
->Destroy();
355 mRootContentController
= nullptr;
359 void nsBaseWidget::DestroyLayerManager() {
360 if (mWindowRenderer
) {
361 mWindowRenderer
->Destroy();
362 mWindowRenderer
= nullptr;
367 void nsBaseWidget::OnRenderingDeviceReset() { DestroyLayerManager(); }
369 void nsBaseWidget::FreeShutdownObserver() {
370 if (mShutdownObserver
) {
371 mShutdownObserver
->Unregister();
373 mShutdownObserver
= nullptr;
376 void nsBaseWidget::FreeLocalesChangedObserver() {
377 if (mLocalesChangedObserver
) {
378 mLocalesChangedObserver
->Unregister();
380 mLocalesChangedObserver
= nullptr;
383 //-------------------------------------------------------------------------
385 // nsBaseWidget destructor
387 //-------------------------------------------------------------------------
389 nsBaseWidget::~nsBaseWidget() {
391 mSwipeTracker
->Destroy();
392 mSwipeTracker
= nullptr;
395 IMEStateManager::WidgetDestroyed(this);
397 FreeLocalesChangedObserver();
398 FreeShutdownObserver();
399 RevokeTransactionIdAllocator();
400 DestroyLayerManager();
402 #ifdef NOISY_WIDGET_LEAKS
404 printf("WIDGETS- = %d\n", gNumWidgets
);
407 delete mOriginalBounds
;
410 //-------------------------------------------------------------------------
414 //-------------------------------------------------------------------------
415 void nsBaseWidget::BaseCreate(nsIWidget
* aParent
, nsWidgetInitData
* aInitData
) {
417 mWindowType
= aInitData
->mWindowType
;
418 mBorderStyle
= aInitData
->mBorderStyle
;
419 mPopupLevel
= aInitData
->mPopupLevel
;
420 mPopupType
= aInitData
->mPopupHint
;
421 mHasRemoteContent
= aInitData
->mHasRemoteContent
;
425 aParent
->AddChild(this);
429 //-------------------------------------------------------------------------
431 // Accessor functions to get/set the client data
433 //-------------------------------------------------------------------------
435 nsIWidgetListener
* nsBaseWidget::GetWidgetListener() const {
436 return mWidgetListener
;
439 void nsBaseWidget::SetWidgetListener(nsIWidgetListener
* aWidgetListener
) {
440 mWidgetListener
= aWidgetListener
;
443 already_AddRefed
<nsIWidget
> nsBaseWidget::CreateChild(
444 const LayoutDeviceIntRect
& aRect
, nsWidgetInitData
* aInitData
,
445 bool aForceUseIWidgetParent
) {
446 nsIWidget
* parent
= this;
447 nsNativeWidget nativeParent
= nullptr;
449 if (!aForceUseIWidgetParent
) {
450 // Use only either parent or nativeParent, not both, to match
451 // existing code. Eventually Create() should be divested of its
452 // nativeWidget parameter.
453 nativeParent
= parent
? parent
->GetNativeData(NS_NATIVE_WIDGET
) : nullptr;
454 parent
= nativeParent
? nullptr : parent
;
455 MOZ_ASSERT(!parent
|| !nativeParent
, "messed up logic");
458 nsCOMPtr
<nsIWidget
> widget
;
459 if (aInitData
&& aInitData
->mWindowType
== eWindowType_popup
) {
460 widget
= AllocateChildPopupWidget();
462 widget
= nsIWidget::CreateChildWindow();
465 if (widget
&& mNeedFastSnaphot
) {
466 widget
->SetNeedFastSnaphot();
470 NS_SUCCEEDED(widget
->Create(parent
, nativeParent
, aRect
, aInitData
))) {
471 return widget
.forget();
477 // Attach a view to our widget which we'll send events to.
478 void nsBaseWidget::AttachViewToTopLevel(bool aUseAttachedEvents
) {
479 NS_ASSERTION((mWindowType
== eWindowType_toplevel
||
480 mWindowType
== eWindowType_dialog
||
481 mWindowType
== eWindowType_invisible
||
482 mWindowType
== eWindowType_child
),
483 "Can't attach to window of that type");
485 mUseAttachedEvents
= aUseAttachedEvents
;
488 nsIWidgetListener
* nsBaseWidget::GetAttachedWidgetListener() const {
489 return mAttachedWidgetListener
;
492 nsIWidgetListener
* nsBaseWidget::GetPreviouslyAttachedWidgetListener() {
493 return mPreviouslyAttachedWidgetListener
;
496 void nsBaseWidget::SetPreviouslyAttachedWidgetListener(
497 nsIWidgetListener
* aListener
) {
498 mPreviouslyAttachedWidgetListener
= aListener
;
501 void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener
* aListener
) {
502 mAttachedWidgetListener
= aListener
;
505 //-------------------------------------------------------------------------
507 // Close this nsBaseWidget
509 //-------------------------------------------------------------------------
510 void nsBaseWidget::Destroy() {
511 // Just in case our parent is the only ref to us
512 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
513 // disconnect from the parent
514 nsIWidget
* parent
= GetParent();
516 parent
->RemoveChild(this);
520 //-------------------------------------------------------------------------
522 // Get this nsBaseWidget parent
524 //-------------------------------------------------------------------------
525 nsIWidget
* nsBaseWidget::GetParent(void) { return nullptr; }
527 //-------------------------------------------------------------------------
529 // Get this nsBaseWidget top level widget
531 //-------------------------------------------------------------------------
532 nsIWidget
* nsBaseWidget::GetTopLevelWidget() {
533 nsIWidget
*topLevelWidget
= nullptr, *widget
= this;
535 topLevelWidget
= widget
;
536 widget
= widget
->GetParent();
538 return topLevelWidget
;
541 //-------------------------------------------------------------------------
543 // Get this nsBaseWidget's top (non-sheet) parent (if it's a sheet)
545 //-------------------------------------------------------------------------
546 nsIWidget
* nsBaseWidget::GetSheetWindowParent(void) { return nullptr; }
548 float nsBaseWidget::GetDPI() { return 96.0f
; }
550 CSSToLayoutDeviceScale
nsIWidget::GetDefaultScale() {
551 double devPixelsPerCSSPixel
= StaticPrefs::layout_css_devPixelsPerPx();
553 if (devPixelsPerCSSPixel
<= 0.0) {
554 devPixelsPerCSSPixel
= GetDefaultScaleInternal();
557 return CSSToLayoutDeviceScale(devPixelsPerCSSPixel
);
560 nsIntSize
nsIWidget::CustomCursorSize(const Cursor
& aCursor
) {
561 MOZ_ASSERT(aCursor
.IsCustom());
564 aCursor
.mContainer
->GetWidth(&width
);
565 aCursor
.mContainer
->GetHeight(&height
);
566 aCursor
.mResolution
.ApplyTo(width
, height
);
567 return {width
, height
};
570 RefPtr
<mozilla::VsyncDispatcher
> nsIWidget::GetVsyncDispatcher() {
574 //-------------------------------------------------------------------------
576 // Add a child to the list of children
578 //-------------------------------------------------------------------------
579 void nsBaseWidget::AddChild(nsIWidget
* aChild
) {
580 MOZ_ASSERT(!aChild
->GetNextSibling() && !aChild
->GetPrevSibling(),
581 "aChild not properly removed from its old child list");
584 mFirstChild
= mLastChild
= aChild
;
586 // append to the list
587 MOZ_ASSERT(mLastChild
);
588 MOZ_ASSERT(!mLastChild
->GetNextSibling());
589 mLastChild
->SetNextSibling(aChild
);
590 aChild
->SetPrevSibling(mLastChild
);
595 //-------------------------------------------------------------------------
597 // Remove a child from the list of children
599 //-------------------------------------------------------------------------
600 void nsBaseWidget::RemoveChild(nsIWidget
* aChild
) {
603 // nsCocoaWindow doesn't implement GetParent, so in that case parent will be
604 // null and we'll just have to do without this assertion.
605 nsIWidget
* parent
= aChild
->GetParent();
606 NS_ASSERTION(!parent
|| parent
== this, "Not one of our kids!");
608 MOZ_RELEASE_ASSERT(aChild
->GetParent() == this, "Not one of our kids!");
612 if (mLastChild
== aChild
) {
613 mLastChild
= mLastChild
->GetPrevSibling();
615 if (mFirstChild
== aChild
) {
616 mFirstChild
= mFirstChild
->GetNextSibling();
619 // Now remove from the list. Make sure that we pass ownership of the tail
620 // of the list correctly before we have aChild let go of it.
621 nsIWidget
* prev
= aChild
->GetPrevSibling();
622 nsIWidget
* next
= aChild
->GetNextSibling();
624 prev
->SetNextSibling(next
);
627 next
->SetPrevSibling(prev
);
630 aChild
->SetNextSibling(nullptr);
631 aChild
->SetPrevSibling(nullptr);
634 //-------------------------------------------------------------------------
636 // Sets widget's position within its parent's child list.
638 //-------------------------------------------------------------------------
639 void nsBaseWidget::SetZIndex(int32_t aZIndex
) {
640 // Hold a ref to ourselves just in case, since we're going to remove
642 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
646 // reorder this child in its parent's list.
647 auto* parent
= static_cast<nsBaseWidget
*>(GetParent());
649 parent
->RemoveChild(this);
650 // Scope sib outside the for loop so we can check it afterward
651 nsIWidget
* sib
= parent
->GetFirstChild();
652 for (; sib
; sib
= sib
->GetNextSibling()) {
653 int32_t childZIndex
= GetZIndex();
654 if (aZIndex
< childZIndex
) {
655 // Insert ourselves before sib
656 nsIWidget
* prev
= sib
->GetPrevSibling();
659 sib
->SetPrevSibling(this);
661 prev
->SetNextSibling(this);
663 NS_ASSERTION(sib
== parent
->mFirstChild
, "Broken child list");
664 // We've taken ownership of sib, so it's safe to have parent let
666 parent
->mFirstChild
= this;
668 PlaceBehind(eZPlacementBelow
, sib
, false);
672 // were we added to the list?
674 parent
->AddChild(this);
679 void nsBaseWidget::GetWorkspaceID(nsAString
& workspaceID
) {
680 workspaceID
.Truncate();
683 void nsBaseWidget::MoveToWorkspace(const nsAString
& workspaceID
) {
687 //-------------------------------------------------------------------------
689 // Get this component cursor
691 //-------------------------------------------------------------------------
693 void nsBaseWidget::SetCursor(const Cursor
& aCursor
) { mCursor
= aCursor
; }
695 //-------------------------------------------------------------------------
697 // Window transparency methods
699 //-------------------------------------------------------------------------
701 void nsBaseWidget::SetTransparencyMode(nsTransparencyMode aMode
) {}
703 nsTransparencyMode
nsBaseWidget::GetTransparencyMode() {
704 return eTransparencyOpaque
;
708 void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage
,
711 nsIRunnable
* aCallback
) {
712 MOZ_ASSERT_UNREACHABLE(
713 "Should never call PerformFullscreenTransition on nsBaseWidget");
716 //-------------------------------------------------------------------------
718 // Put the window into full-screen mode
720 //-------------------------------------------------------------------------
721 void nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen
) {
722 HideWindowChrome(aFullScreen
);
725 if (!mOriginalBounds
) {
726 mOriginalBounds
= new LayoutDeviceIntRect();
728 *mOriginalBounds
= GetScreenBounds();
730 // Move to top-left corner of screen and size to the screen dimensions
731 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
733 int32_t left
, top
, width
, height
;
735 screen
->GetRectDisplayPix(&left
, &top
, &width
, &height
))) {
736 Resize(left
, top
, width
, height
, true);
739 } else if (mOriginalBounds
) {
740 if (BoundsUseDesktopPixels()) {
741 DesktopRect deskRect
= *mOriginalBounds
/ GetDesktopToDeviceScale();
742 Resize(deskRect
.X(), deskRect
.Y(), deskRect
.Width(), deskRect
.Height(),
745 Resize(mOriginalBounds
->X(), mOriginalBounds
->Y(),
746 mOriginalBounds
->Width(), mOriginalBounds
->Height(), true);
751 nsresult
nsBaseWidget::MakeFullScreen(bool aFullScreen
) {
752 InfallibleMakeFullScreen(aFullScreen
);
756 nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
757 nsBaseWidget
* aWidget
, gfxContext
* aTarget
, BufferMode aDoubleBuffering
)
759 WindowRenderer
* renderer
= mWidget
->GetWindowRenderer();
760 if (renderer
->AsFallback()) {
761 mRenderer
= renderer
->AsFallback();
762 mRenderer
->SetTarget(aTarget
, aDoubleBuffering
);
766 nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() {
768 mRenderer
->SetTarget(nullptr, mozilla::layers::BufferMode::BUFFER_NONE
);
772 bool nsBaseWidget::IsSmallPopup() const {
773 return mWindowType
== eWindowType_popup
&& mPopupType
!= ePopupTypePanel
;
776 bool nsBaseWidget::ComputeShouldAccelerate() {
777 return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING
) &&
778 (WidgetTypeSupportsAcceleration() ||
779 StaticPrefs::gfx_webrender_unaccelerated_widget_force());
782 bool nsBaseWidget::UseAPZ() {
783 return (gfxPlatform::AsyncPanZoomEnabled() &&
784 (WindowType() == eWindowType_toplevel
||
785 WindowType() == eWindowType_child
||
786 ((WindowType() == eWindowType_popup
||
787 WindowType() == eWindowType_dialog
) &&
788 HasRemoteContent() && StaticPrefs::apz_popups_enabled())));
791 void nsBaseWidget::CreateCompositor() {
792 LayoutDeviceIntRect rect
= GetBounds();
793 CreateCompositor(rect
.Width(), rect
.Height());
796 already_AddRefed
<GeckoContentController
>
797 nsBaseWidget::CreateRootContentController() {
798 RefPtr
<GeckoContentController
> controller
=
799 new ChromeProcessController(this, mAPZEventState
, mAPZC
);
800 return controller
.forget();
803 void nsBaseWidget::ConfigureAPZCTreeManager() {
804 MOZ_ASSERT(NS_IsMainThread());
807 mAPZC
->SetDPI(GetDPI());
809 if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
810 KeyboardMap map
= RootWindowGlobalKeyListener::CollectKeyboardShortcuts();
811 mAPZC
->SetKeyboardMap(map
);
814 ContentReceivedInputBlockCallback
callback(
815 [treeManager
= RefPtr
{mAPZC
.get()}](uint64_t aInputBlockId
,
816 bool aPreventDefault
) {
817 MOZ_ASSERT(NS_IsMainThread());
818 treeManager
->ContentReceivedInputBlock(aInputBlockId
, aPreventDefault
);
820 mAPZEventState
= new APZEventState(this, std::move(callback
));
822 mRootContentController
= CreateRootContentController();
823 if (mRootContentController
) {
824 mCompositorSession
->SetContentController(mRootContentController
);
827 // When APZ is enabled, we can actually enable raw touch events because we
828 // have code that can deal with them properly. If APZ is not enabled, this
829 // function doesn't get called.
830 if (StaticPrefs::dom_w3c_touch_events_enabled()) {
831 RegisterTouchWindow();
835 void nsBaseWidget::ConfigureAPZControllerThread() {
836 // By default the controller thread is the main thread.
837 APZThreadUtils::SetControllerThread(NS_GetCurrentThread());
840 void nsBaseWidget::SetConfirmedTargetAPZC(
841 uint64_t aInputBlockId
,
842 const nsTArray
<ScrollableLayerGuid
>& aTargets
) const {
843 mAPZC
->SetTargetAPZC(aInputBlockId
, aTargets
);
846 void nsBaseWidget::UpdateZoomConstraints(
847 const uint32_t& aPresShellId
, const ScrollableLayerGuid::ViewID
& aViewId
,
848 const Maybe
<ZoomConstraints
>& aConstraints
) {
849 if (!mCompositorSession
|| !mAPZC
) {
850 if (mInitialZoomConstraints
) {
851 MOZ_ASSERT(mInitialZoomConstraints
->mPresShellID
== aPresShellId
);
852 MOZ_ASSERT(mInitialZoomConstraints
->mViewID
== aViewId
);
854 mInitialZoomConstraints
.reset();
859 // We have some constraints, but the compositor and APZC aren't created
860 // yet. Save these so we can use them later.
861 mInitialZoomConstraints
= Some(
862 InitialZoomConstraints(aPresShellId
, aViewId
, aConstraints
.ref()));
866 LayersId layersId
= mCompositorSession
->RootLayerTreeId();
867 mAPZC
->UpdateZoomConstraints(
868 ScrollableLayerGuid(layersId
, aPresShellId
, aViewId
), aConstraints
);
871 bool nsBaseWidget::AsyncPanZoomEnabled() const { return !!mAPZC
; }
873 nsEventStatus
nsBaseWidget::ProcessUntransformedAPZEvent(
874 WidgetInputEvent
* aEvent
, const APZEventResult
& aApzResult
) {
875 MOZ_ASSERT(NS_IsMainThread());
876 ScrollableLayerGuid targetGuid
= aApzResult
.mTargetGuid
;
877 uint64_t inputBlockId
= aApzResult
.mInputBlockId
;
878 InputAPZContext
context(aApzResult
.mTargetGuid
, inputBlockId
,
879 aApzResult
.GetStatus());
881 // Make a copy of the original event for the APZCCallbackHelper helpers that
882 // we call later, because the event passed to DispatchEvent can get mutated in
883 // ways that we don't want (i.e. touch points can get stripped out).
884 nsEventStatus status
;
885 UniquePtr
<WidgetEvent
> original(aEvent
->Duplicate());
886 DispatchEvent(aEvent
, status
);
888 if (mAPZC
&& !InputAPZContext::WasRoutedToChildProcess() && inputBlockId
) {
889 // EventStateManager did not route the event into the child process.
890 // It's safe to communicate to APZ that the event has been processed.
891 // Note that here aGuid.mLayersId might be different from
892 // mCompositorSession->RootLayerTreeId() because the event might have gotten
893 // hit-tested by APZ to be targeted at a child process, but a parent process
894 // event listener called preventDefault on it. In that case aGuid.mLayersId
895 // would still be the layers id for the child process, but the event would
896 // not have actually gotten routed to the child process. The main-thread
897 // hit-test result therefore needs to use the parent process layers id.
898 LayersId rootLayersId
= mCompositorSession
->RootLayerTreeId();
900 RefPtr
<DisplayportSetListener
> postLayerization
;
901 if (WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent()) {
902 nsTArray
<TouchBehaviorFlags
> allowedTouchBehaviors
;
903 if (touchEvent
->mMessage
== eTouchStart
) {
904 auto& originalEvent
= *original
->AsTouchEvent();
905 MOZ_ASSERT(NS_IsMainThread());
906 allowedTouchBehaviors
= TouchActionHelper::GetAllowedTouchBehavior(
907 this, GetDocument(), originalEvent
);
908 if (!allowedTouchBehaviors
.IsEmpty()) {
909 mAPZC
->SetAllowedTouchBehavior(inputBlockId
, allowedTouchBehaviors
);
911 postLayerization
= APZCCallbackHelper::SendSetTargetAPZCNotification(
912 this, GetDocument(), originalEvent
, rootLayersId
, inputBlockId
);
914 mAPZEventState
->ProcessTouchEvent(*touchEvent
, targetGuid
, inputBlockId
,
915 aApzResult
.GetStatus(), status
,
916 std::move(allowedTouchBehaviors
));
917 } else if (WidgetWheelEvent
* wheelEvent
= aEvent
->AsWheelEvent()) {
918 MOZ_ASSERT(wheelEvent
->mFlags
.mHandledByAPZ
);
919 postLayerization
= APZCCallbackHelper::SendSetTargetAPZCNotification(
920 this, GetDocument(), *original
->AsWheelEvent(), rootLayersId
,
922 if (wheelEvent
->mCanTriggerSwipe
) {
923 ReportSwipeStarted(inputBlockId
, wheelEvent
->TriggersSwipe());
925 mAPZEventState
->ProcessWheelEvent(*wheelEvent
, inputBlockId
);
926 } else if (WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent()) {
927 MOZ_ASSERT(mouseEvent
->mFlags
.mHandledByAPZ
);
928 postLayerization
= APZCCallbackHelper::SendSetTargetAPZCNotification(
929 this, GetDocument(), *original
->AsMouseEvent(), rootLayersId
,
931 mAPZEventState
->ProcessMouseEvent(*mouseEvent
, inputBlockId
);
933 if (postLayerization
) {
934 postLayerization
->Register();
941 template <class InputType
, class EventType
>
942 class DispatchEventOnMainThread
: public Runnable
{
944 DispatchEventOnMainThread(const InputType
& aInput
, nsBaseWidget
* aWidget
,
945 const APZEventResult
& aAPZResult
)
946 : mozilla::Runnable("DispatchEventOnMainThread"),
949 mAPZResult(aAPZResult
) {}
951 NS_IMETHOD
Run() override
{
952 EventType event
= mInput
.ToWidgetEvent(mWidget
);
953 mWidget
->ProcessUntransformedAPZEvent(&event
, mAPZResult
);
959 nsBaseWidget
* mWidget
;
960 APZEventResult mAPZResult
;
963 template <class InputType
, class EventType
>
964 class DispatchInputOnControllerThread
: public Runnable
{
966 DispatchInputOnControllerThread(const EventType
& aEvent
,
967 IAPZCTreeManager
* aAPZC
,
968 nsBaseWidget
* aWidget
)
969 : mozilla::Runnable("DispatchInputOnControllerThread"),
970 mMainMessageLoop(MessageLoop::current()),
975 NS_IMETHOD
Run() override
{
976 APZEventResult result
= mAPZC
->InputBridge()->ReceiveInputEvent(mInput
);
977 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
980 RefPtr
<Runnable
> r
= new DispatchEventOnMainThread
<InputType
, EventType
>(
981 mInput
, mWidget
, result
);
982 mMainMessageLoop
->PostTask(r
.forget());
987 MessageLoop
* mMainMessageLoop
;
989 RefPtr
<IAPZCTreeManager
> mAPZC
;
990 nsBaseWidget
* mWidget
;
993 void nsBaseWidget::DispatchTouchInput(MultiTouchInput
& aInput
,
994 uint16_t aInputSource
) {
995 MOZ_ASSERT(NS_IsMainThread());
996 MOZ_ASSERT(aInputSource
==
997 mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH
||
998 aInputSource
== mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN
);
1000 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1002 APZEventResult result
= mAPZC
->InputBridge()->ReceiveInputEvent(aInput
);
1003 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
1007 WidgetTouchEvent event
= aInput
.ToWidgetEvent(this, aInputSource
);
1008 ProcessUntransformedAPZEvent(&event
, result
);
1010 WidgetTouchEvent event
= aInput
.ToWidgetEvent(this, aInputSource
);
1012 nsEventStatus status
;
1013 DispatchEvent(&event
, status
);
1017 void nsBaseWidget::DispatchPanGestureInput(PanGestureInput
& aInput
) {
1018 MOZ_ASSERT(NS_IsMainThread());
1020 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1022 APZEventResult result
= mAPZC
->InputBridge()->ReceiveInputEvent(aInput
);
1023 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
1027 WidgetWheelEvent event
= aInput
.ToWidgetEvent(this);
1028 ProcessUntransformedAPZEvent(&event
, result
);
1030 WidgetWheelEvent event
= aInput
.ToWidgetEvent(this);
1031 nsEventStatus status
;
1032 DispatchEvent(&event
, status
);
1036 void nsBaseWidget::DispatchPinchGestureInput(PinchGestureInput
& aInput
) {
1037 MOZ_ASSERT(NS_IsMainThread());
1039 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1040 APZEventResult result
= mAPZC
->InputBridge()->ReceiveInputEvent(aInput
);
1042 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
1045 WidgetWheelEvent event
= aInput
.ToWidgetEvent(this);
1046 ProcessUntransformedAPZEvent(&event
, result
);
1048 WidgetWheelEvent event
= aInput
.ToWidgetEvent(this);
1049 nsEventStatus status
;
1050 DispatchEvent(&event
, status
);
1054 nsIWidget::ContentAndAPZEventStatus
nsBaseWidget::DispatchInputEvent(
1055 WidgetInputEvent
* aEvent
) {
1056 nsIWidget::ContentAndAPZEventStatus status
;
1057 MOZ_ASSERT(NS_IsMainThread());
1059 if (APZThreadUtils::IsControllerThread()) {
1060 APZEventResult result
= mAPZC
->InputBridge()->ReceiveInputEvent(*aEvent
);
1061 status
.mApzStatus
= result
.GetStatus();
1062 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
1065 status
.mContentStatus
= ProcessUntransformedAPZEvent(aEvent
, result
);
1068 if (WidgetWheelEvent
* wheelEvent
= aEvent
->AsWheelEvent()) {
1069 RefPtr
<Runnable
> r
=
1070 new DispatchInputOnControllerThread
<ScrollWheelInput
,
1071 WidgetWheelEvent
>(*wheelEvent
,
1073 APZThreadUtils::RunOnControllerThread(std::move(r
));
1074 status
.mContentStatus
= nsEventStatus_eConsumeDoDefault
;
1077 if (WidgetMouseEvent
* mouseEvent
= aEvent
->AsMouseEvent()) {
1078 RefPtr
<Runnable
> r
=
1079 new DispatchInputOnControllerThread
<MouseInput
, WidgetMouseEvent
>(
1080 *mouseEvent
, mAPZC
, this);
1081 APZThreadUtils::RunOnControllerThread(std::move(r
));
1082 status
.mContentStatus
= nsEventStatus_eConsumeDoDefault
;
1085 if (WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent()) {
1086 RefPtr
<Runnable
> r
=
1087 new DispatchInputOnControllerThread
<MultiTouchInput
,
1088 WidgetTouchEvent
>(*touchEvent
,
1090 APZThreadUtils::RunOnControllerThread(std::move(r
));
1091 status
.mContentStatus
= nsEventStatus_eConsumeDoDefault
;
1094 // Allow dispatching keyboard events on Gecko thread.
1095 MOZ_ASSERT(aEvent
->AsKeyboardEvent());
1098 DispatchEvent(aEvent
, status
.mContentStatus
);
1102 void nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent
* aEvent
) {
1103 MOZ_ASSERT(NS_IsMainThread());
1105 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1106 mAPZC
->InputBridge()->ReceiveInputEvent(*aEvent
);
1110 bool nsBaseWidget::DispatchWindowEvent(WidgetGUIEvent
& event
) {
1111 nsEventStatus status
;
1112 DispatchEvent(&event
, status
);
1113 return ConvertStatus(status
);
1116 Document
* nsBaseWidget::GetDocument() const {
1117 if (mWidgetListener
) {
1118 if (PresShell
* presShell
= mWidgetListener
->GetPresShell()) {
1119 return presShell
->GetDocument();
1125 void nsBaseWidget::CreateCompositorVsyncDispatcher() {
1126 // Parent directly listens to the vsync source whereas
1127 // child process communicate via IPC
1128 // Should be called AFTER gfxPlatform is initialized
1129 if (XRE_IsParentProcess()) {
1130 if (!mCompositorVsyncDispatcherLock
) {
1131 mCompositorVsyncDispatcherLock
=
1132 MakeUnique
<Mutex
>("mCompositorVsyncDispatcherLock");
1134 MutexAutoLock
lock(*mCompositorVsyncDispatcherLock
.get());
1135 if (!mCompositorVsyncDispatcher
) {
1136 RefPtr
<VsyncDispatcher
> vsyncDispatcher
=
1137 gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher();
1138 mCompositorVsyncDispatcher
=
1139 new CompositorVsyncDispatcher(std::move(vsyncDispatcher
));
1144 already_AddRefed
<CompositorVsyncDispatcher
>
1145 nsBaseWidget::GetCompositorVsyncDispatcher() {
1146 MOZ_ASSERT(mCompositorVsyncDispatcherLock
.get());
1148 MutexAutoLock
lock(*mCompositorVsyncDispatcherLock
.get());
1149 RefPtr
<CompositorVsyncDispatcher
> dispatcher
= mCompositorVsyncDispatcher
;
1150 return dispatcher
.forget();
1153 already_AddRefed
<WebRenderLayerManager
> nsBaseWidget::CreateCompositorSession(
1154 int aWidth
, int aHeight
, CompositorOptions
* aOptionsOut
) {
1155 MOZ_ASSERT(aOptionsOut
);
1158 CreateCompositorVsyncDispatcher();
1160 gfx::GPUProcessManager
* gpu
= gfx::GPUProcessManager::Get();
1161 // Make sure GPU process is ready for use.
1162 // If it failed to connect to GPU process, GPU process usage is disabled in
1163 // EnsureGPUReady(). It could update gfxVars and gfxConfigs.
1164 gpu
->EnsureGPUReady();
1166 // If widget type does not supports acceleration, we may be allowed to use
1167 // software WebRender instead. If not, then we use ClientLayerManager even
1168 // when gfxVars::UseWebRender() is true. WebRender could coexist only with
1170 bool supportsAcceleration
= WidgetTypeSupportsAcceleration();
1173 if (supportsAcceleration
||
1174 StaticPrefs::gfx_webrender_unaccelerated_widget_force()) {
1175 enableWR
= gfx::gfxVars::UseWebRender();
1176 enableSWWR
= gfx::gfxVars::UseSoftwareWebRender();
1178 enableWR
= enableSWWR
= gfx::gfxVars::UseWebRender();
1180 MOZ_RELEASE_ASSERT(enableWR
);
1181 bool enableAPZ
= UseAPZ();
1182 CompositorOptions
options(enableAPZ
, enableSWWR
);
1185 if (supportsAcceleration
) {
1186 options
.SetAllowSoftwareWebRenderD3D11(
1187 gfx::gfxVars::AllowSoftwareWebRenderD3D11());
1189 if (mNeedFastSnaphot
) {
1190 options
.SetNeedFastSnaphot(true);
1192 #elif defined(MOZ_WIDGET_ANDROID)
1193 MOZ_ASSERT(supportsAcceleration
);
1194 options
.SetAllowSoftwareWebRenderOGL(
1195 StaticPrefs::gfx_webrender_software_opengl_AtStartup());
1196 #elif defined(MOZ_WIDGET_GTK)
1197 if (supportsAcceleration
) {
1198 options
.SetAllowSoftwareWebRenderOGL(
1199 StaticPrefs::gfx_webrender_software_opengl_AtStartup());
1203 #ifdef MOZ_WIDGET_ANDROID
1204 // Unconditionally set the compositor as initially paused, as we have not
1205 // yet had a chance to send the compositor surface to the GPU process. We
1206 // will do so shortly once we have returned to nsWindow::CreateLayerManager,
1207 // where we will also resume the compositor if required.
1208 options
.SetInitiallyPaused(true);
1210 options
.SetInitiallyPaused(CompositorInitiallyPaused());
1213 RefPtr
<WebRenderLayerManager
> lm
= new WebRenderLayerManager(this);
1215 uint64_t innerWindowId
= 0;
1216 if (Document
* doc
= GetDocument()) {
1217 innerWindowId
= doc
->InnerWindowID();
1221 mCompositorSession
= gpu
->CreateTopLevelCompositor(
1222 this, lm
, GetDefaultScale(), options
, UseExternalCompositingSurface(),
1223 gfx::IntSize(aWidth
, aHeight
), innerWindowId
, &retry
);
1225 if (mCompositorSession
) {
1226 TextureFactoryIdentifier textureFactoryIdentifier
;
1228 lm
->Initialize(mCompositorSession
->GetCompositorBridgeChild(),
1229 wr::AsPipelineId(mCompositorSession
->RootLayerTreeId()),
1230 &textureFactoryIdentifier
, error
);
1231 if (textureFactoryIdentifier
.mParentBackend
!= LayersBackend::LAYERS_WR
) {
1233 DestroyCompositor();
1234 // gfxVars::UseDoubleBufferingWithCompositor() is also disabled.
1235 gfx::GPUProcessManager::Get()->DisableWebRender(
1236 wr::WebRenderError::INITIALIZE
, error
);
1240 // We need to retry in a loop because the act of failing to create the
1241 // compositor can change our state (e.g. disable WebRender).
1242 if (mCompositorSession
|| !retry
) {
1243 *aOptionsOut
= options
;
1249 void nsBaseWidget::CreateCompositor(int aWidth
, int aHeight
) {
1250 // This makes sure that gfxPlatforms gets initialized if it hasn't by now.
1251 gfxPlatform::GetPlatform();
1253 MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
1254 "This function assumes OMTC");
1256 MOZ_ASSERT(!mCompositorSession
&& !mCompositorBridgeChild
,
1257 "Should have properly cleaned up the previous PCompositor pair "
1260 if (mCompositorBridgeChild
) {
1261 mCompositorBridgeChild
->Destroy();
1264 // Recreating this is tricky, as we may still have an old and we need
1265 // to make sure it's properly destroyed by calling DestroyCompositor!
1267 // If we've already received a shutdown notification, don't try
1268 // create a new compositor.
1269 if (!mShutdownObserver
) {
1273 // The controller thread must be configured before the compositor
1274 // session is created, so that the input bridge runs on the right
1276 ConfigureAPZControllerThread();
1278 CompositorOptions options
;
1279 RefPtr
<WebRenderLayerManager
> lm
=
1280 CreateCompositorSession(aWidth
, aHeight
, &options
);
1285 MOZ_ASSERT(mCompositorSession
);
1286 mCompositorBridgeChild
= mCompositorSession
->GetCompositorBridgeChild();
1287 SetCompositorWidgetDelegate(
1288 mCompositorSession
->GetCompositorWidgetDelegate());
1290 if (options
.UseAPZ()) {
1291 mAPZC
= mCompositorSession
->GetAPZCTreeManager();
1292 ConfigureAPZCTreeManager();
1297 if (mInitialZoomConstraints
) {
1298 UpdateZoomConstraints(mInitialZoomConstraints
->mPresShellID
,
1299 mInitialZoomConstraints
->mViewID
,
1300 Some(mInitialZoomConstraints
->mConstraints
));
1301 mInitialZoomConstraints
.reset();
1304 TextureFactoryIdentifier textureFactoryIdentifier
=
1305 lm
->GetTextureFactoryIdentifier();
1306 MOZ_ASSERT(textureFactoryIdentifier
.mParentBackend
==
1307 LayersBackend::LAYERS_WR
);
1308 ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier
);
1309 gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier
);
1313 mWindowRenderer
= std::move(lm
);
1315 // Only track compositors for top-level windows, since other window types
1316 // may use the basic compositor. Except on the OS X - see bug 1306383
1317 #if defined(XP_MACOSX)
1318 bool getCompositorFromThisWindow
= true;
1320 bool getCompositorFromThisWindow
= (mWindowType
== eWindowType_toplevel
);
1323 if (getCompositorFromThisWindow
) {
1324 gfxPlatform::GetPlatform()->NotifyCompositorCreated(
1325 mWindowRenderer
->GetCompositorBackendType());
1329 void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession
* aSession
) {
1330 MOZ_ASSERT(aSession
== mCompositorSession
);
1331 DestroyLayerManager();
1334 bool nsBaseWidget::ShouldUseOffMainThreadCompositing() {
1335 return gfxPlatform::UsesOffMainThreadCompositing();
1338 WindowRenderer
* nsBaseWidget::GetWindowRenderer() {
1339 if (!mWindowRenderer
) {
1340 if (!mShutdownObserver
) {
1341 // We are shutting down, do not try to re-create a LayerManager
1344 // Try to use an async compositor first, if possible
1345 if (ShouldUseOffMainThreadCompositing()) {
1349 if (!mWindowRenderer
) {
1350 mWindowRenderer
= CreateFallbackRenderer();
1353 return mWindowRenderer
;
1356 WindowRenderer
* nsBaseWidget::CreateFallbackRenderer() {
1357 return new FallbackRenderer
;
1360 CompositorBridgeChild
* nsBaseWidget::GetRemoteRenderer() {
1361 return mCompositorBridgeChild
;
1364 void nsBaseWidget::ClearCachedWebrenderResources() {
1365 if (!mWindowRenderer
|| !mWindowRenderer
->AsWebRender()) {
1368 mWindowRenderer
->AsWebRender()->ClearCachedResources();
1371 void nsBaseWidget::ClearWebrenderAnimationResources() {
1372 if (!mWindowRenderer
|| !mWindowRenderer
->AsWebRender()) {
1375 mWindowRenderer
->AsWebRender()->ClearAnimationResources();
1378 bool nsBaseWidget::SetNeedFastSnaphot() {
1379 MOZ_ASSERT(XRE_IsParentProcess());
1380 MOZ_ASSERT(!mCompositorSession
);
1382 if (!XRE_IsParentProcess() || mCompositorSession
) {
1386 mNeedFastSnaphot
= true;
1390 already_AddRefed
<gfx::DrawTarget
> nsBaseWidget::StartRemoteDrawing() {
1394 uint32_t nsBaseWidget::GetGLFrameBufferFormat() { return LOCAL_GL_RGBA
; }
1396 //-------------------------------------------------------------------------
1398 // Destroy the window
1400 //-------------------------------------------------------------------------
1401 void nsBaseWidget::OnDestroy() {
1402 if (mTextEventDispatcher
) {
1403 mTextEventDispatcher
->OnDestroyWidget();
1404 // Don't release it until this widget actually released because after this
1405 // is called, TextEventDispatcher() may create it again.
1408 // If this widget is being destroyed, let the APZ code know to drop references
1409 // to this widget. Callers of this function all should be holding a deathgrip
1410 // on this widget already.
1411 ReleaseContentController();
1414 void nsBaseWidget::MoveClient(const DesktopPoint
& aOffset
) {
1415 LayoutDeviceIntPoint
clientOffset(GetClientOffset());
1417 // GetClientOffset returns device pixels; scale back to desktop pixels
1418 // if that's what this widget uses for the Move/Resize APIs
1419 if (BoundsUseDesktopPixels()) {
1420 DesktopPoint desktopOffset
= clientOffset
/ GetDesktopToDeviceScale();
1421 Move(aOffset
.x
- desktopOffset
.x
, aOffset
.y
- desktopOffset
.y
);
1423 LayoutDevicePoint layoutOffset
= aOffset
* GetDesktopToDeviceScale();
1424 Move(layoutOffset
.x
- clientOffset
.x
, layoutOffset
.y
- clientOffset
.y
);
1428 void nsBaseWidget::ResizeClient(const DesktopSize
& aSize
, bool aRepaint
) {
1429 NS_ASSERTION((aSize
.width
>= 0), "Negative width passed to ResizeClient");
1430 NS_ASSERTION((aSize
.height
>= 0), "Negative height passed to ResizeClient");
1432 LayoutDeviceIntRect clientBounds
= GetClientBounds();
1434 // GetClientBounds and mBounds are device pixels; scale back to desktop pixels
1435 // if that's what this widget uses for the Move/Resize APIs
1436 if (BoundsUseDesktopPixels()) {
1437 DesktopSize desktopDelta
=
1438 (LayoutDeviceIntSize(mBounds
.Width(), mBounds
.Height()) -
1439 clientBounds
.Size()) /
1440 GetDesktopToDeviceScale();
1441 Resize(aSize
.width
+ desktopDelta
.width
, aSize
.height
+ desktopDelta
.height
,
1444 LayoutDeviceSize layoutSize
= aSize
* GetDesktopToDeviceScale();
1445 Resize(mBounds
.Width() + (layoutSize
.width
- clientBounds
.Width()),
1446 mBounds
.Height() + (layoutSize
.height
- clientBounds
.Height()),
1451 void nsBaseWidget::ResizeClient(const DesktopRect
& aRect
, bool aRepaint
) {
1452 NS_ASSERTION((aRect
.Width() >= 0), "Negative width passed to ResizeClient");
1453 NS_ASSERTION((aRect
.Height() >= 0), "Negative height passed to ResizeClient");
1455 LayoutDeviceIntRect clientBounds
= GetClientBounds();
1456 LayoutDeviceIntPoint clientOffset
= GetClientOffset();
1457 DesktopToLayoutDeviceScale scale
= GetDesktopToDeviceScale();
1459 if (BoundsUseDesktopPixels()) {
1460 DesktopPoint desktopOffset
= clientOffset
/ scale
;
1461 DesktopSize desktopDelta
=
1462 (LayoutDeviceIntSize(mBounds
.Width(), mBounds
.Height()) -
1463 clientBounds
.Size()) /
1465 Resize(aRect
.X() - desktopOffset
.x
, aRect
.Y() - desktopOffset
.y
,
1466 aRect
.Width() + desktopDelta
.width
,
1467 aRect
.Height() + desktopDelta
.height
, aRepaint
);
1469 LayoutDeviceRect layoutRect
= aRect
* scale
;
1470 Resize(layoutRect
.X() - clientOffset
.x
, layoutRect
.Y() - clientOffset
.y
,
1471 layoutRect
.Width() + mBounds
.Width() - clientBounds
.Width(),
1472 layoutRect
.Height() + mBounds
.Height() - clientBounds
.Height(),
1477 //-------------------------------------------------------------------------
1481 //-------------------------------------------------------------------------
1484 * If the implementation of nsWindow supports borders this method MUST be
1488 LayoutDeviceIntRect
nsBaseWidget::GetClientBounds() { return GetBounds(); }
1491 * If the implementation of nsWindow supports borders this method MUST be
1495 LayoutDeviceIntRect
nsBaseWidget::GetBounds() { return mBounds
; }
1498 * If the implementation of nsWindow uses a local coordinate system within the
1499 *window, this method must be overridden
1502 LayoutDeviceIntRect
nsBaseWidget::GetScreenBounds() { return GetBounds(); }
1504 nsresult
nsBaseWidget::GetRestoredBounds(LayoutDeviceIntRect
& aRect
) {
1505 if (SizeMode() != nsSizeMode_Normal
) {
1506 return NS_ERROR_FAILURE
;
1508 aRect
= GetScreenBounds();
1512 LayoutDeviceIntPoint
nsBaseWidget::GetClientOffset() {
1513 return LayoutDeviceIntPoint(0, 0);
1516 nsresult
nsBaseWidget::SetNonClientMargins(LayoutDeviceIntMargin
& margins
) {
1517 return NS_ERROR_NOT_IMPLEMENTED
;
1520 void nsBaseWidget::SetResizeMargin(LayoutDeviceIntCoord aResizeMargin
) {}
1522 uint32_t nsBaseWidget::GetMaxTouchPoints() const { return 0; }
1524 bool nsBaseWidget::HasPendingInputEvent() { return false; }
1526 bool nsBaseWidget::ShowsResizeIndicator(LayoutDeviceIntRect
* aResizerRect
) {
1531 * Modifies aFile to point at an icon file with the given name and suffix. The
1532 * suffix may correspond to a file extension with leading '.' if appropriate.
1533 * Returns true if the icon file exists and can be read.
1535 static bool ResolveIconNameHelper(nsIFile
* aFile
, const nsAString
& aIconName
,
1536 const nsAString
& aIconSuffix
) {
1537 aFile
->Append(u
"icons"_ns
);
1538 aFile
->Append(u
"default"_ns
);
1539 aFile
->Append(aIconName
+ aIconSuffix
);
1542 return NS_SUCCEEDED(aFile
->IsReadable(&readable
)) && readable
;
1546 * Resolve the given icon name into a local file object. This method is
1547 * intended to be called by subclasses of nsBaseWidget. aIconSuffix is a
1548 * platform specific icon file suffix (e.g., ".ico" under Win32).
1550 * If no file is found matching the given parameters, then null is returned.
1552 void nsBaseWidget::ResolveIconName(const nsAString
& aIconName
,
1553 const nsAString
& aIconSuffix
,
1554 nsIFile
** aResult
) {
1557 nsCOMPtr
<nsIProperties
> dirSvc
=
1558 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID
);
1559 if (!dirSvc
) return;
1561 // first check auxilary chrome directories
1563 nsCOMPtr
<nsISimpleEnumerator
> dirs
;
1564 dirSvc
->Get(NS_APP_CHROME_DIR_LIST
, NS_GET_IID(nsISimpleEnumerator
),
1565 getter_AddRefs(dirs
));
1568 while (NS_SUCCEEDED(dirs
->HasMoreElements(&hasMore
)) && hasMore
) {
1569 nsCOMPtr
<nsISupports
> element
;
1570 dirs
->GetNext(getter_AddRefs(element
));
1571 if (!element
) continue;
1572 nsCOMPtr
<nsIFile
> file
= do_QueryInterface(element
);
1573 if (!file
) continue;
1574 if (ResolveIconNameHelper(file
, aIconName
, aIconSuffix
)) {
1575 NS_ADDREF(*aResult
= file
);
1581 // then check the main app chrome directory
1583 nsCOMPtr
<nsIFile
> file
;
1584 dirSvc
->Get(NS_APP_CHROME_DIR
, NS_GET_IID(nsIFile
), getter_AddRefs(file
));
1585 if (file
&& ResolveIconNameHelper(file
, aIconName
, aIconSuffix
))
1586 NS_ADDREF(*aResult
= file
);
1589 void nsBaseWidget::SetSizeConstraints(const SizeConstraints
& aConstraints
) {
1590 mSizeConstraints
= aConstraints
;
1592 // Popups are constrained during layout, and we don't want to synchronously
1593 // paint from reflow, so bail out... This is not great, but it's no worse than
1594 // what we used to do.
1596 // The right fix here is probably making constraint changes go through the
1597 // view manager and such.
1598 if (mWindowType
== eWindowType_popup
) {
1602 // If the current size doesn't meet the new constraints, trigger a
1603 // resize to apply it. Note that, we don't want to invoke Resize if
1604 // the new constraints don't affect the current size, because Resize
1605 // implementation on some platforms may touch other geometry even if
1606 // the size don't need to change.
1607 LayoutDeviceIntSize curSize
= mBounds
.Size();
1608 LayoutDeviceIntSize clampedSize
=
1609 Max(aConstraints
.mMinSize
, Min(aConstraints
.mMaxSize
, curSize
));
1610 if (clampedSize
!= curSize
) {
1612 if (BoundsUseDesktopPixels()) {
1613 DesktopSize desktopSize
= clampedSize
/ GetDesktopToDeviceScale();
1614 size
= desktopSize
.ToUnknownSize();
1616 size
= gfx::Size(clampedSize
.ToUnknownSize());
1618 Resize(size
.width
, size
.height
, true);
1622 const widget::SizeConstraints
nsBaseWidget::GetSizeConstraints() {
1623 return mSizeConstraints
;
1627 nsIRollupListener
* nsBaseWidget::GetActiveRollupListener() {
1628 // If set, then this is likely an <html:select> dropdown.
1629 if (gRollupListener
) return gRollupListener
;
1631 return nsXULPopupManager::GetInstance();
1634 void nsBaseWidget::NotifyWindowDestroyed() {
1635 if (!mWidgetListener
) return;
1637 nsCOMPtr
<nsIAppWindow
> window
= mWidgetListener
->GetAppWindow();
1638 nsCOMPtr
<nsIBaseWindow
> appWindow(do_QueryInterface(window
));
1640 appWindow
->Destroy();
1644 void nsBaseWidget::NotifyWindowMoved(int32_t aX
, int32_t aY
) {
1645 if (mWidgetListener
) {
1646 mWidgetListener
->WindowMoved(this, aX
, aY
);
1649 if (mIMEHasFocus
&& IMENotificationRequestsRef().WantPositionChanged()) {
1650 NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE
));
1654 void nsBaseWidget::NotifySizeMoveDone() {
1655 if (!mWidgetListener
) {
1658 if (PresShell
* presShell
= mWidgetListener
->GetPresShell()) {
1659 presShell
->WindowSizeMoveDone();
1663 void nsBaseWidget::NotifyThemeChanged(ThemeChangeKind aKind
) {
1664 LookAndFeel::NotifyChangedAllWindows(aKind
);
1667 void nsBaseWidget::NotifyUIStateChanged(UIStateChangeType aShowFocusRings
) {
1668 if (Document
* doc
= GetDocument()) {
1669 if (nsPIDOMWindowOuter
* win
= doc
->GetWindow()) {
1670 win
->SetKeyboardIndicators(aShowFocusRings
);
1675 nsresult
nsBaseWidget::NotifyIME(const IMENotification
& aIMENotification
) {
1679 switch (aIMENotification
.mMessage
) {
1680 case REQUEST_TO_COMMIT_COMPOSITION
:
1681 case REQUEST_TO_CANCEL_COMPOSITION
:
1682 // We should send request to IME only when there is a TextEventDispatcher
1683 // instance (this means that this widget has dispatched at least one
1684 // composition event or keyboard event) and the it has composition.
1685 // Otherwise, there is nothing to do.
1686 // Note that if current input transaction is for native input events,
1687 // TextEventDispatcher::NotifyIME() will call
1688 // TextEventDispatcherListener::NotifyIME().
1689 if (mTextEventDispatcher
&& mTextEventDispatcher
->IsComposing()) {
1690 return mTextEventDispatcher
->NotifyIME(aIMENotification
);
1694 if (aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
) {
1695 mIMEHasFocus
= true;
1697 EnsureTextEventDispatcher();
1698 // TextEventDispatcher::NotifyIME() will always call
1699 // TextEventDispatcherListener::NotifyIME(). I.e., even if current
1700 // input transaction is for synthesized events for automated tests,
1701 // notifications will be sent to native IME.
1702 nsresult rv
= mTextEventDispatcher
->NotifyIME(aIMENotification
);
1703 if (aIMENotification
.mMessage
== NOTIFY_IME_OF_BLUR
) {
1704 mIMEHasFocus
= false;
1711 void nsBaseWidget::EnsureTextEventDispatcher() {
1712 if (mTextEventDispatcher
) {
1715 mTextEventDispatcher
= new TextEventDispatcher(this);
1718 nsIWidget::NativeIMEContext
nsBaseWidget::GetNativeIMEContext() {
1719 if (mTextEventDispatcher
&& mTextEventDispatcher
->GetPseudoIMEContext()) {
1720 // If we already have a TextEventDispatcher and it's working with
1721 // a TextInputProcessor, we need to return pseudo IME context since
1722 // TextCompositionArray::IndexOf(nsIWidget*) should return a composition
1723 // on the pseudo IME context in such case.
1724 NativeIMEContext pseudoIMEContext
;
1725 pseudoIMEContext
.InitWithRawNativeIMEContext(
1726 mTextEventDispatcher
->GetPseudoIMEContext());
1727 return pseudoIMEContext
;
1729 return NativeIMEContext(this);
1732 nsIWidget::TextEventDispatcher
* nsBaseWidget::GetTextEventDispatcher() {
1733 EnsureTextEventDispatcher();
1734 return mTextEventDispatcher
;
1737 void* nsBaseWidget::GetPseudoIMEContext() {
1738 TextEventDispatcher
* dispatcher
= GetTextEventDispatcher();
1742 return dispatcher
->GetPseudoIMEContext();
1745 TextEventDispatcherListener
*
1746 nsBaseWidget::GetNativeTextEventDispatcherListener() {
1747 // TODO: If all platforms supported use of TextEventDispatcher for handling
1748 // native IME and keyboard events, this method should be removed since
1749 // in such case, this is overridden by all the subclasses.
1753 void nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId
,
1754 const ScrollableLayerGuid::ViewID
& aViewId
,
1755 const CSSRect
& aRect
, const uint32_t& aFlags
) {
1756 if (!mCompositorSession
|| !mAPZC
) {
1759 LayersId layerId
= mCompositorSession
->RootLayerTreeId();
1760 mAPZC
->ZoomToRect(ScrollableLayerGuid(layerId
, aPresShellId
, aViewId
),
1761 ZoomTarget
{aRect
}, aFlags
);
1764 #ifdef ACCESSIBILITY
1766 a11y::LocalAccessible
* nsBaseWidget::GetRootAccessible() {
1767 NS_ENSURE_TRUE(mWidgetListener
, nullptr);
1769 PresShell
* presShell
= mWidgetListener
->GetPresShell();
1770 NS_ENSURE_TRUE(presShell
, nullptr);
1772 // If container is null then the presshell is not active. This often happens
1773 // when a preshell is being held onto for fastback.
1774 nsPresContext
* presContext
= presShell
->GetPresContext();
1775 NS_ENSURE_TRUE(presContext
->GetContainerWeak(), nullptr);
1777 // LocalAccessible creation might be not safe so use IsSafeToRunScript to
1778 // make sure it's not created at unsafe times.
1779 nsAccessibilityService
* accService
= GetOrCreateAccService();
1781 return accService
->GetRootDocumentAccessible(
1782 presShell
, nsContentUtils::IsSafeToRunScript());
1788 #endif // ACCESSIBILITY
1790 void nsBaseWidget::StartAsyncScrollbarDrag(
1791 const AsyncDragMetrics
& aDragMetrics
) {
1792 if (!AsyncPanZoomEnabled()) {
1796 MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession
);
1798 LayersId layersId
= mCompositorSession
->RootLayerTreeId();
1799 ScrollableLayerGuid
guid(layersId
, aDragMetrics
.mPresShellId
,
1800 aDragMetrics
.mViewId
);
1802 mAPZC
->StartScrollbarDrag(guid
, aDragMetrics
);
1805 bool nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint
& aAnchorLocation
,
1806 const ScrollableLayerGuid
& aGuid
) {
1807 MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
1809 return mAPZC
->StartAutoscroll(aGuid
, aAnchorLocation
);
1812 void nsBaseWidget::StopAsyncAutoscroll(const ScrollableLayerGuid
& aGuid
) {
1813 MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
1815 mAPZC
->StopAutoscroll(aGuid
);
1818 LayersId
nsBaseWidget::GetRootLayerTreeId() {
1819 return mCompositorSession
? mCompositorSession
->RootLayerTreeId()
1823 already_AddRefed
<nsIScreen
> nsBaseWidget::GetWidgetScreen() {
1824 nsCOMPtr
<nsIScreenManager
> screenManager
;
1825 screenManager
= do_GetService("@mozilla.org/gfx/screenmanager;1");
1826 if (!screenManager
) {
1830 LayoutDeviceIntRect bounds
= GetScreenBounds();
1831 DesktopIntRect deskBounds
= RoundedToInt(bounds
/ GetDesktopToDeviceScale());
1832 nsCOMPtr
<nsIScreen
> screen
;
1833 screenManager
->ScreenForRect(deskBounds
.X(), deskBounds
.Y(),
1834 deskBounds
.Width(), deskBounds
.Height(),
1835 getter_AddRefs(screen
));
1836 return screen
.forget();
1839 mozilla::DesktopToLayoutDeviceScale
1840 nsBaseWidget::GetDesktopToDeviceScaleByScreen() {
1841 return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext())
1842 ->GetDesktopToDeviceScale();
1845 nsresult
nsIWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint
,
1847 nsIObserver
* aObserver
) {
1848 AutoObserverNotifier
notifier(aObserver
, "touchtap");
1850 if (sPointerIdCounter
> TOUCH_INJECT_MAX_POINTS
) {
1851 sPointerIdCounter
= 0;
1853 int pointerId
= sPointerIdCounter
;
1854 sPointerIdCounter
++;
1855 nsresult rv
= SynthesizeNativeTouchPoint(pointerId
, TOUCH_CONTACT
, aPoint
,
1857 if (NS_FAILED(rv
)) {
1862 return SynthesizeNativeTouchPoint(pointerId
, TOUCH_REMOVE
, aPoint
, 0, 0,
1866 // initiate a long tap
1867 int elapse
= Preferences::GetInt("ui.click_hold_context_menus.delay",
1868 TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC
);
1869 if (!mLongTapTimer
) {
1870 mLongTapTimer
= NS_NewTimer();
1871 if (!mLongTapTimer
) {
1872 SynthesizeNativeTouchPoint(pointerId
, TOUCH_CANCEL
, aPoint
, 0, 0,
1874 return NS_ERROR_UNEXPECTED
;
1876 // Windows requires recuring events, so we set this to a smaller window
1877 // than the pref value.
1878 int timeout
= elapse
;
1879 if (timeout
> TOUCH_INJECT_PUMP_TIMER_MSEC
) {
1880 timeout
= TOUCH_INJECT_PUMP_TIMER_MSEC
;
1882 mLongTapTimer
->InitWithNamedFuncCallback(
1883 OnLongTapTimerCallback
, this, timeout
, nsITimer::TYPE_REPEATING_SLACK
,
1884 "nsIWidget::SynthesizeNativeTouchTap");
1887 // If we already have a long tap pending, cancel it. We only allow one long
1888 // tap to be active at a time.
1889 if (mLongTapTouchPoint
) {
1890 SynthesizeNativeTouchPoint(mLongTapTouchPoint
->mPointerId
, TOUCH_CANCEL
,
1891 mLongTapTouchPoint
->mPosition
, 0, 0, nullptr);
1894 mLongTapTouchPoint
= MakeUnique
<LongTapInfo
>(
1895 pointerId
, aPoint
, TimeDuration::FromMilliseconds(elapse
), aObserver
);
1896 notifier
.SkipNotification(); // we'll do it in the long-tap callback
1901 void nsIWidget::OnLongTapTimerCallback(nsITimer
* aTimer
, void* aClosure
) {
1902 auto* self
= static_cast<nsIWidget
*>(aClosure
);
1904 if ((self
->mLongTapTouchPoint
->mStamp
+ self
->mLongTapTouchPoint
->mDuration
) >
1907 // Windows needs us to keep pumping feedback to the digitizer, so update
1908 // the pointer id with the same position.
1909 self
->SynthesizeNativeTouchPoint(
1910 self
->mLongTapTouchPoint
->mPointerId
, TOUCH_CONTACT
,
1911 self
->mLongTapTouchPoint
->mPosition
, 1.0, 90, nullptr);
1916 AutoObserverNotifier
notifier(self
->mLongTapTouchPoint
->mObserver
,
1919 // finished, remove the touch point
1920 self
->mLongTapTimer
->Cancel();
1921 self
->mLongTapTimer
= nullptr;
1922 self
->SynthesizeNativeTouchPoint(
1923 self
->mLongTapTouchPoint
->mPointerId
, TOUCH_REMOVE
,
1924 self
->mLongTapTouchPoint
->mPosition
, 0, 0, nullptr);
1925 self
->mLongTapTouchPoint
= nullptr;
1928 nsresult
nsIWidget::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
1929 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
1931 // XXX This is odd. This is called by the constructor of nsIWidget. However,
1932 // at that point, nsIWidget::mLongTapTimer must be nullptr. Therefore,
1933 // this must do nothing at initializing the instance.
1934 if (!mLongTapTimer
) {
1937 mLongTapTimer
->Cancel();
1938 mLongTapTimer
= nullptr;
1939 SynthesizeNativeTouchPoint(mLongTapTouchPoint
->mPointerId
, TOUCH_CANCEL
,
1940 mLongTapTouchPoint
->mPosition
, 0, 0, nullptr);
1941 mLongTapTouchPoint
= nullptr;
1945 MultiTouchInput
nsBaseWidget::UpdateSynthesizedTouchState(
1946 MultiTouchInput
* aState
, uint32_t aTime
, mozilla::TimeStamp aTimeStamp
,
1947 uint32_t aPointerId
, TouchPointerState aPointerState
,
1948 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
1949 uint32_t aPointerOrientation
) {
1950 ScreenIntPoint pointerScreenPoint
= ViewAs
<ScreenPixel
>(
1951 aPoint
, PixelCastJustification::LayoutDeviceIsScreenForBounds
);
1953 // We can't dispatch *aState directly because (a) dispatching
1954 // it might inadvertently modify it and (b) in the case of touchend or
1955 // touchcancel events aState will hold the touches that are
1956 // still down whereas the input dispatched needs to hold the removed
1957 // touch(es). We use |inputToDispatch| for this purpose.
1958 MultiTouchInput inputToDispatch
;
1959 inputToDispatch
.mInputType
= MULTITOUCH_INPUT
;
1960 inputToDispatch
.mTime
= aTime
;
1961 inputToDispatch
.mTimeStamp
= aTimeStamp
;
1963 int32_t index
= aState
->IndexOfTouch((int32_t)aPointerId
);
1964 if (aPointerState
== TOUCH_CONTACT
) {
1966 // found an existing touch point, update it
1967 SingleTouchData
& point
= aState
->mTouches
[index
];
1968 point
.mScreenPoint
= pointerScreenPoint
;
1969 point
.mRotationAngle
= (float)aPointerOrientation
;
1970 point
.mForce
= (float)aPointerPressure
;
1971 inputToDispatch
.mType
= MultiTouchInput::MULTITOUCH_MOVE
;
1973 // new touch point, add it
1974 aState
->mTouches
.AppendElement(SingleTouchData(
1975 (int32_t)aPointerId
, pointerScreenPoint
, ScreenSize(0, 0),
1976 (float)aPointerOrientation
, (float)aPointerPressure
));
1977 inputToDispatch
.mType
= MultiTouchInput::MULTITOUCH_START
;
1979 inputToDispatch
.mTouches
= aState
->mTouches
;
1981 MOZ_ASSERT(aPointerState
== TOUCH_REMOVE
|| aPointerState
== TOUCH_CANCEL
);
1982 // a touch point is being lifted, so remove it from the stored list
1984 aState
->mTouches
.RemoveElementAt(index
);
1986 inputToDispatch
.mType
=
1987 (aPointerState
== TOUCH_REMOVE
? MultiTouchInput::MULTITOUCH_END
1988 : MultiTouchInput::MULTITOUCH_CANCEL
);
1989 inputToDispatch
.mTouches
.AppendElement(SingleTouchData(
1990 (int32_t)aPointerId
, pointerScreenPoint
, ScreenSize(0, 0),
1991 (float)aPointerOrientation
, (float)aPointerPressure
));
1994 return inputToDispatch
;
1997 void nsBaseWidget::NotifyLiveResizeStarted() {
1998 // If we have mLiveResizeListeners already non-empty, we should notify those
1999 // listeners that the resize stopped before starting anew. In theory this
2000 // should never happen because we shouldn't get nested live resize actions.
2001 NotifyLiveResizeStopped();
2002 MOZ_ASSERT(mLiveResizeListeners
.IsEmpty());
2004 // If we can get the active remote tab for the current widget, suppress
2005 // the displayport on it during the live resize.
2006 if (!mWidgetListener
) {
2009 nsCOMPtr
<nsIAppWindow
> appWindow
= mWidgetListener
->GetAppWindow();
2013 mLiveResizeListeners
= appWindow
->GetLiveResizeListeners();
2014 for (uint32_t i
= 0; i
< mLiveResizeListeners
.Length(); i
++) {
2015 mLiveResizeListeners
[i
]->LiveResizeStarted();
2019 void nsBaseWidget::NotifyLiveResizeStopped() {
2020 if (!mLiveResizeListeners
.IsEmpty()) {
2021 for (uint32_t i
= 0; i
< mLiveResizeListeners
.Length(); i
++) {
2022 mLiveResizeListeners
[i
]->LiveResizeStopped();
2024 mLiveResizeListeners
.Clear();
2028 nsresult
nsBaseWidget::AsyncEnableDragDrop(bool aEnable
) {
2029 RefPtr
<nsBaseWidget
> kungFuDeathGrip
= this;
2030 return NS_DispatchToCurrentThreadQueue(
2031 NS_NewRunnableFunction(
2032 "AsyncEnableDragDropFn",
2033 [this, aEnable
, kungFuDeathGrip
]() { EnableDragDrop(aEnable
); }),
2034 kAsyncDragDropTimeout
, EventQueuePriority::Idle
);
2037 void nsBaseWidget::SwipeFinished() { mSwipeTracker
= nullptr; }
2039 void nsBaseWidget::ReportSwipeStarted(uint64_t aInputBlockId
,
2041 if (mSwipeEventQueue
&& mSwipeEventQueue
->inputBlockId
== aInputBlockId
) {
2043 PanGestureInput
& startEvent
= mSwipeEventQueue
->queuedEvents
[0];
2044 TrackScrollEventAsSwipe(startEvent
, mSwipeEventQueue
->allowedDirections
);
2045 for (size_t i
= 1; i
< mSwipeEventQueue
->queuedEvents
.Length(); i
++) {
2046 mSwipeTracker
->ProcessEvent(mSwipeEventQueue
->queuedEvents
[i
]);
2049 mSwipeEventQueue
= nullptr;
2053 void nsBaseWidget::TrackScrollEventAsSwipe(
2054 const mozilla::PanGestureInput
& aSwipeStartEvent
,
2055 uint32_t aAllowedDirections
) {
2056 // If a swipe is currently being tracked kill it -- it's been interrupted
2057 // by another gesture event.
2058 if (mSwipeTracker
) {
2059 mSwipeTracker
->CancelSwipe(aSwipeStartEvent
.mTimeStamp
);
2060 mSwipeTracker
->Destroy();
2061 mSwipeTracker
= nullptr;
2064 uint32_t direction
=
2065 (aSwipeStartEvent
.mPanDisplacement
.x
> 0.0)
2066 ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT
2067 : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT
;
2070 new SwipeTracker(*this, aSwipeStartEvent
, aAllowedDirections
, direction
);
2073 mCurrentPanGestureBelongsToSwipe
= true;
2077 nsBaseWidget::SwipeInfo
nsBaseWidget::SendMayStartSwipe(
2078 const mozilla::PanGestureInput
& aSwipeStartEvent
) {
2079 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
2081 uint32_t direction
=
2082 (aSwipeStartEvent
.mPanDisplacement
.x
> 0.0)
2083 ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT
2084 : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT
;
2086 // We're ready to start the animation. Tell Gecko about it, and at the same
2087 // time ask it if it really wants to start an animation for this event.
2088 // This event also reports back the directions that we can swipe in.
2089 LayoutDeviceIntPoint position
= RoundedToInt(aSwipeStartEvent
.mPanStartPoint
*
2090 ScreenToLayoutDeviceScale(1));
2091 WidgetSimpleGestureEvent geckoEvent
= SwipeTracker::CreateSwipeGestureEvent(
2092 eSwipeGestureMayStart
, this, position
, aSwipeStartEvent
.mTimeStamp
);
2093 geckoEvent
.mDirection
= direction
;
2094 geckoEvent
.mDelta
= 0.0;
2095 geckoEvent
.mAllowedDirections
= 0;
2096 bool shouldStartSwipe
=
2097 DispatchWindowEvent(geckoEvent
); // event cancelled == swipe should start
2099 SwipeInfo result
= {shouldStartSwipe
, geckoEvent
.mAllowedDirections
};
2103 WidgetWheelEvent
nsBaseWidget::MayStartSwipeForAPZ(
2104 const PanGestureInput
& aPanInput
, const APZEventResult
& aApzResult
,
2105 CanTriggerSwipe aCanTriggerSwipe
) {
2106 WidgetWheelEvent event
= aPanInput
.ToWidgetEvent(this);
2107 if (aCanTriggerSwipe
== CanTriggerSwipe::Yes
&&
2108 aPanInput
.mOverscrollBehaviorAllowsSwipe
) {
2109 SwipeInfo swipeInfo
= SendMayStartSwipe(aPanInput
);
2110 event
.mCanTriggerSwipe
= swipeInfo
.wantsSwipe
;
2111 if (swipeInfo
.wantsSwipe
) {
2112 if (aApzResult
.GetStatus() == nsEventStatus_eIgnore
) {
2113 // APZ has determined and that scrolling horizontally in the
2114 // requested direction is impossible, so it didn't do any
2115 // scrolling for the event.
2116 // We know now that MayStartSwipe wants a swipe, so we can start
2118 TrackScrollEventAsSwipe(aPanInput
, swipeInfo
.allowedDirections
);
2120 // We don't know whether this event can start a swipe, so we need
2121 // to queue up events and wait for a call to ReportSwipeStarted.
2122 // APZ might already have started scrolling in response to the
2123 // event if it knew that it's the right thing to do. In that case
2124 // we'll still get a call to ReportSwipeStarted, and we will
2125 // discard the queued events at that point.
2126 mSwipeEventQueue
= MakeUnique
<SwipeEventQueue
>(
2127 swipeInfo
.allowedDirections
, aApzResult
.mInputBlockId
);
2132 if (mSwipeEventQueue
&&
2133 mSwipeEventQueue
->inputBlockId
== aApzResult
.mInputBlockId
) {
2134 mSwipeEventQueue
->queuedEvents
.AppendElement(aPanInput
);
2140 bool nsBaseWidget::MayStartSwipeForNonAPZ(const PanGestureInput
& aPanInput
,
2141 CanTriggerSwipe aCanTriggerSwipe
) {
2142 if (aPanInput
.mType
== PanGestureInput::PANGESTURE_MAYSTART
||
2143 aPanInput
.mType
== PanGestureInput::PANGESTURE_START
) {
2144 mCurrentPanGestureBelongsToSwipe
= false;
2146 if (mCurrentPanGestureBelongsToSwipe
) {
2147 // Ignore this event. It's a momentum event from a scroll gesture
2148 // that was processed as a swipe, and the swipe animation has
2149 // already finished (so mSwipeTracker is already null).
2150 MOZ_ASSERT(aPanInput
.IsMomentum(),
2151 "If the fingers are still on the touchpad, we should still have "
2153 "and it should have consumed this event.");
2157 if (aCanTriggerSwipe
== CanTriggerSwipe::No
) {
2161 SwipeInfo swipeInfo
= SendMayStartSwipe(aPanInput
);
2163 // We're in the non-APZ case here, but we still want to know whether
2164 // the event was routed to a child process, so we use InputAPZContext
2165 // to get that piece of information.
2166 ScrollableLayerGuid guid
;
2167 InputAPZContext
context(guid
, 0, nsEventStatus_eIgnore
);
2169 WidgetWheelEvent event
= aPanInput
.ToWidgetEvent(this);
2170 event
.mCanTriggerSwipe
= swipeInfo
.wantsSwipe
;
2171 nsEventStatus status
;
2172 DispatchEvent(&event
, status
);
2173 if (swipeInfo
.wantsSwipe
) {
2174 if (context
.WasRoutedToChildProcess()) {
2175 // We don't know whether this event can start a swipe, so we need
2176 // to queue up events and wait for a call to ReportSwipeStarted.
2178 MakeUnique
<SwipeEventQueue
>(swipeInfo
.allowedDirections
, 0);
2179 } else if (event
.TriggersSwipe()) {
2180 TrackScrollEventAsSwipe(aPanInput
, swipeInfo
.allowedDirections
);
2184 if (mSwipeEventQueue
&& mSwipeEventQueue
->inputBlockId
== 0) {
2185 mSwipeEventQueue
->queuedEvents
.AppendElement(aPanInput
);
2191 const IMENotificationRequests
& nsIWidget::IMENotificationRequestsRef() {
2192 TextEventDispatcher
* dispatcher
= GetTextEventDispatcher();
2193 return dispatcher
->IMENotificationRequestsRef();
2196 void nsIWidget::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent
* aEvent
) {}
2198 bool nsIWidget::GetEditCommands(NativeKeyBindingsType aType
,
2199 const WidgetKeyboardEvent
& aEvent
,
2200 nsTArray
<CommandInt
>& aCommands
) {
2201 MOZ_ASSERT(aEvent
.IsTrusted());
2202 MOZ_ASSERT(aCommands
.IsEmpty());
2206 already_AddRefed
<nsIBidiKeyboard
> nsIWidget::CreateBidiKeyboard() {
2207 if (XRE_IsContentProcess()) {
2208 return CreateBidiKeyboardContentProcess();
2210 return CreateBidiKeyboardInner();
2214 already_AddRefed
<nsIBidiKeyboard
> nsIWidget::CreateBidiKeyboardInner() {
2215 // no bidi keyboard implementation
2220 namespace mozilla::widget
{
2222 const char* ToChar(InputContext::Origin aOrigin
) {
2224 case InputContext::ORIGIN_MAIN
:
2225 return "ORIGIN_MAIN";
2226 case InputContext::ORIGIN_CONTENT
:
2227 return "ORIGIN_CONTENT";
2229 return "Unexpected value";
2233 const char* ToChar(IMEMessage aIMEMessage
) {
2234 switch (aIMEMessage
) {
2235 case NOTIFY_IME_OF_NOTHING
:
2236 return "NOTIFY_IME_OF_NOTHING";
2237 case NOTIFY_IME_OF_FOCUS
:
2238 return "NOTIFY_IME_OF_FOCUS";
2239 case NOTIFY_IME_OF_BLUR
:
2240 return "NOTIFY_IME_OF_BLUR";
2241 case NOTIFY_IME_OF_SELECTION_CHANGE
:
2242 return "NOTIFY_IME_OF_SELECTION_CHANGE";
2243 case NOTIFY_IME_OF_TEXT_CHANGE
:
2244 return "NOTIFY_IME_OF_TEXT_CHANGE";
2245 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
2246 return "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED";
2247 case NOTIFY_IME_OF_POSITION_CHANGE
:
2248 return "NOTIFY_IME_OF_POSITION_CHANGE";
2249 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
2250 return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT";
2251 case REQUEST_TO_COMMIT_COMPOSITION
:
2252 return "REQUEST_TO_COMMIT_COMPOSITION";
2253 case REQUEST_TO_CANCEL_COMPOSITION
:
2254 return "REQUEST_TO_CANCEL_COMPOSITION";
2256 return "Unexpected value";
2260 void NativeIMEContext::Init(nsIWidget
* aWidget
) {
2262 mRawNativeIMEContext
= reinterpret_cast<uintptr_t>(nullptr);
2263 mOriginProcessID
= static_cast<uint64_t>(-1);
2266 if (!XRE_IsContentProcess()) {
2267 mRawNativeIMEContext
= reinterpret_cast<uintptr_t>(
2268 aWidget
->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT
));
2269 mOriginProcessID
= 0;
2272 // If this is created in a child process, aWidget is an instance of
2273 // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT.
2274 // Instead of that PuppetWidget::GetNativeIMEContext() returns cached
2275 // native IME context of the parent process.
2276 *this = aWidget
->GetNativeIMEContext();
2279 void NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext
) {
2280 if (NS_WARN_IF(!aRawNativeIMEContext
)) {
2281 mRawNativeIMEContext
= reinterpret_cast<uintptr_t>(nullptr);
2282 mOriginProcessID
= static_cast<uint64_t>(-1);
2285 mRawNativeIMEContext
= reinterpret_cast<uintptr_t>(aRawNativeIMEContext
);
2287 XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0;
2290 void IMENotification::TextChangeDataBase::MergeWith(
2291 const IMENotification::TextChangeDataBase
& aOther
) {
2292 MOZ_ASSERT(aOther
.IsValid(), "Merging data must store valid data");
2293 MOZ_ASSERT(aOther
.mStartOffset
<= aOther
.mRemovedEndOffset
,
2294 "end of removed text must be same or larger than start");
2295 MOZ_ASSERT(aOther
.mStartOffset
<= aOther
.mAddedEndOffset
,
2296 "end of added text must be same or larger than start");
2303 // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed
2304 // text ranges. I.e., mStartOffset should be the smallest offset of all
2305 // modified text ranges in old text. |mRemovedEndOffset| should be the
2306 // largest end offset in old text of all modified text ranges.
2307 // |mAddedEndOffset| represents the end offset of all inserted text ranges.
2308 // I.e., only this is an offset in new text.
2309 // In other words, between mStartOffset and |mRemovedEndOffset| of the
2310 // premodified text was already removed. And some text whose length is
2311 // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|. I.e.,
2312 // this allows IME to mark dirty the modified text range with |mStartOffset|
2313 // and |mRemovedEndOffset| if IME stores all text of the focused editor and
2314 // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|.
2315 // Additionally, IME can retrieve only the text between |mStartOffset| and
2316 // |mAddedEndOffset| for updating stored text.
2318 // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they
2319 // should be adjusted to be in same text. The |newData.mStartOffset| and
2320 // |newData.mRemovedEndOffset| should be computed as in old text because
2321 // |mStartOffset| and |mRemovedEndOffset| represent the modified text range
2322 // in the old text but even if some text before the values of the newData
2323 // has already been modified, the values don't include the changes.
2325 // For comparing new and old |mAddedEndOffset| values, they should be
2326 // adjusted to be in same text. The |oldData.mAddedEndOffset| should be
2327 // computed as in the new text because |mAddedEndOffset| indicates the end
2328 // offset of inserted text in the new text but |oldData.mAddedEndOffset|
2329 // doesn't include any changes of the text before |newData.mAddedEndOffset|.
2331 const TextChangeDataBase
& newData
= aOther
;
2332 const TextChangeDataBase oldData
= *this;
2334 // mCausedOnlyByComposition should be true only when all changes are caused
2336 mCausedOnlyByComposition
=
2337 newData
.mCausedOnlyByComposition
&& oldData
.mCausedOnlyByComposition
;
2339 // mIncludingChangesWithoutComposition should be true if at least one of
2340 // merged changes occurred without composition.
2341 mIncludingChangesWithoutComposition
=
2342 newData
.mIncludingChangesWithoutComposition
||
2343 oldData
.mIncludingChangesWithoutComposition
;
2345 // mIncludingChangesDuringComposition should be true when at least one of
2346 // the merged non-composition changes occurred during the latest composition.
2347 if (!newData
.mCausedOnlyByComposition
&&
2348 !newData
.mIncludingChangesDuringComposition
) {
2349 MOZ_ASSERT(newData
.mIncludingChangesWithoutComposition
);
2350 MOZ_ASSERT(mIncludingChangesWithoutComposition
);
2351 // If new change is neither caused by composition nor occurred during
2352 // composition, set mIncludingChangesDuringComposition to false because
2353 // IME doesn't want outdated text changes as text change during current
2355 mIncludingChangesDuringComposition
= false;
2357 // Otherwise, set mIncludingChangesDuringComposition to true if either
2358 // oldData or newData includes changes during composition.
2359 mIncludingChangesDuringComposition
=
2360 newData
.mIncludingChangesDuringComposition
||
2361 oldData
.mIncludingChangesDuringComposition
;
2364 if (newData
.mStartOffset
>= oldData
.mAddedEndOffset
) {
2366 // If new start is after old end offset of added text, it means that text
2367 // after the modified range is modified. Like:
2368 // added range of old change: +----------+
2369 // removed range of new change: +----------+
2370 // So, the old start offset is always the smaller offset.
2371 mStartOffset
= oldData
.mStartOffset
;
2372 // The new end offset of removed text is moved by the old change and we
2373 // need to cancel the move of the old change for comparing the offsets in
2374 // same text because it doesn't make sensce to compare offsets in different
2376 uint32_t newRemovedEndOffsetInOldText
=
2377 newData
.mRemovedEndOffset
- oldData
.Difference();
2379 std::max(newRemovedEndOffsetInOldText
, oldData
.mRemovedEndOffset
);
2380 // The new end offset of added text is always the larger offset.
2381 mAddedEndOffset
= newData
.mAddedEndOffset
;
2385 if (newData
.mStartOffset
>= oldData
.mStartOffset
) {
2386 // If new start is in the modified range, it means that new data changes
2387 // a part or all of the range.
2388 mStartOffset
= oldData
.mStartOffset
;
2389 if (newData
.mRemovedEndOffset
>= oldData
.mAddedEndOffset
) {
2391 // If new end of removed text is greater than old end of added text, it
2392 // means that all or a part of modified range modified again and text
2393 // after the modified range is also modified. Like:
2394 // added range of old change: +----------+
2395 // removed range of new change: +----------+
2396 // So, the new removed end offset is moved by the old change and we need
2397 // to cancel the move of the old change for comparing the offsets in the
2398 // same text because it doesn't make sense to compare the offsets in
2400 uint32_t newRemovedEndOffsetInOldText
=
2401 newData
.mRemovedEndOffset
- oldData
.Difference();
2403 std::max(newRemovedEndOffsetInOldText
, oldData
.mRemovedEndOffset
);
2404 // The old end of added text is replaced by new change. So, it should be
2405 // same as the new start. On the other hand, the new added end offset is
2406 // always same or larger. Therefore, the merged end offset of added
2407 // text should be the new end offset of added text.
2408 mAddedEndOffset
= newData
.mAddedEndOffset
;
2413 // If new end of removed text is less than old end of added text, it means
2414 // that only a part of the modified range is modified again. Like:
2415 // added range of old change: +------------+
2416 // removed range of new change: +-----+
2417 // So, the new end offset of removed text should be same as the old end
2418 // offset of removed text. Therefore, the merged end offset of removed
2419 // text should be the old text change's |mRemovedEndOffset|.
2420 mRemovedEndOffset
= oldData
.mRemovedEndOffset
;
2421 // The old end of added text is moved by new change. So, we need to cancel
2422 // the move of the new change for comparing the offsets in same text.
2423 uint32_t oldAddedEndOffsetInNewText
=
2424 oldData
.mAddedEndOffset
+ newData
.Difference();
2426 std::max(newData
.mAddedEndOffset
, oldAddedEndOffsetInNewText
);
2430 if (newData
.mRemovedEndOffset
>= oldData
.mStartOffset
) {
2431 // If new end of removed text is greater than old start (and new start is
2432 // less than old start), it means that a part of modified range is modified
2433 // again and some new text before the modified range is also modified.
2434 MOZ_ASSERT(newData
.mStartOffset
< oldData
.mStartOffset
,
2435 "new start offset should be less than old one here");
2436 mStartOffset
= newData
.mStartOffset
;
2437 if (newData
.mRemovedEndOffset
>= oldData
.mAddedEndOffset
) {
2439 // If new end of removed text is greater than old end of added text, it
2440 // means that all modified text and text after the modified range is
2442 // added range of old change: +----------+
2443 // removed range of new change: +------------------+
2444 // So, the new end of removed text is moved by the old change. Therefore,
2445 // we need to cancel the move of the old change for comparing the offsets
2446 // in same text because it doesn't make sense to compare the offsets in
2448 uint32_t newRemovedEndOffsetInOldText
=
2449 newData
.mRemovedEndOffset
- oldData
.Difference();
2451 std::max(newRemovedEndOffsetInOldText
, oldData
.mRemovedEndOffset
);
2452 // The old end of added text is replaced by new change. So, the old end
2453 // offset of added text is same as new text change's start offset. Then,
2454 // new change's end offset of added text is always same or larger than
2455 // it. Therefore, merged end offset of added text is always the new end
2456 // offset of added text.
2457 mAddedEndOffset
= newData
.mAddedEndOffset
;
2462 // If new end of removed text is less than old end of added text, it
2463 // means that only a part of the modified range is modified again. Like:
2464 // added range of old change: +----------+
2465 // removed range of new change: +----------+
2466 // So, the new end of removed text should be same as old end of removed
2467 // text for preventing end of removed text to be modified. Therefore,
2468 // merged end offset of removed text is always the old end offset of removed
2470 mRemovedEndOffset
= oldData
.mRemovedEndOffset
;
2471 // The old end of added text is moved by this change. So, we need to
2472 // cancel the move of the new change for comparing the offsets in same text
2473 // because it doesn't make sense to compare the offsets in different text.
2474 uint32_t oldAddedEndOffsetInNewText
=
2475 oldData
.mAddedEndOffset
+ newData
.Difference();
2477 std::max(newData
.mAddedEndOffset
, oldAddedEndOffsetInNewText
);
2482 // Otherwise, i.e., both new end of added text and new start are less than
2483 // old start, text before the modified range is modified. Like:
2484 // added range of old change: +----------+
2485 // removed range of new change: +----------+
2486 MOZ_ASSERT(newData
.mStartOffset
< oldData
.mStartOffset
,
2487 "new start offset should be less than old one here");
2488 mStartOffset
= newData
.mStartOffset
;
2489 MOZ_ASSERT(newData
.mRemovedEndOffset
< oldData
.mRemovedEndOffset
,
2490 "new removed end offset should be less than old one here");
2491 mRemovedEndOffset
= oldData
.mRemovedEndOffset
;
2492 // The end of added text should be adjusted with the new difference.
2493 uint32_t oldAddedEndOffsetInNewText
=
2494 oldData
.mAddedEndOffset
+ newData
.Difference();
2496 std::max(newData
.mAddedEndOffset
, oldAddedEndOffsetInNewText
);
2501 // Let's test the code of merging multiple text change data in debug build
2502 // and crash if one of them fails because this feature is very complex but
2503 // cannot be tested with mochitest.
2504 void IMENotification::TextChangeDataBase::Test() {
2505 static bool gTestTextChangeEvent
= true;
2506 if (!gTestTextChangeEvent
) {
2509 gTestTextChangeEvent
= false;
2511 /****************************************************************************
2513 ****************************************************************************/
2516 MergeWith(TextChangeData(10, 10, 20, false, false));
2517 MergeWith(TextChangeData(20, 20, 35, false, false));
2518 MOZ_ASSERT(mStartOffset
== 10,
2519 "Test 1-1-1: mStartOffset should be the first offset");
2521 mRemovedEndOffset
== 10, // 20 - (20 - 10)
2522 "Test 1-1-2: mRemovedEndOffset should be the first end of removed text");
2524 mAddedEndOffset
== 35,
2525 "Test 1-1-3: mAddedEndOffset should be the last end of added text");
2528 // Removing text (longer line -> shorter line)
2529 MergeWith(TextChangeData(10, 20, 10, false, false));
2530 MergeWith(TextChangeData(10, 30, 10, false, false));
2531 MOZ_ASSERT(mStartOffset
== 10,
2532 "Test 1-2-1: mStartOffset should be the first offset");
2533 MOZ_ASSERT(mRemovedEndOffset
== 40, // 30 + (10 - 20)
2534 "Test 1-2-2: mRemovedEndOffset should be the the last end of "
2536 "with already removed length");
2538 mAddedEndOffset
== 10,
2539 "Test 1-2-3: mAddedEndOffset should be the last end of added text");
2542 // Removing text (shorter line -> longer line)
2543 MergeWith(TextChangeData(10, 20, 10, false, false));
2544 MergeWith(TextChangeData(10, 15, 10, false, false));
2545 MOZ_ASSERT(mStartOffset
== 10,
2546 "Test 1-3-1: mStartOffset should be the first offset");
2547 MOZ_ASSERT(mRemovedEndOffset
== 25, // 15 + (10 - 20)
2548 "Test 1-3-2: mRemovedEndOffset should be the the last end of "
2550 "with already removed length");
2552 mAddedEndOffset
== 10,
2553 "Test 1-3-3: mAddedEndOffset should be the last end of added text");
2556 // Appending text at different point (not sure if actually occurs)
2557 MergeWith(TextChangeData(10, 10, 20, false, false));
2558 MergeWith(TextChangeData(55, 55, 60, false, false));
2559 MOZ_ASSERT(mStartOffset
== 10,
2560 "Test 1-4-1: mStartOffset should be the smallest offset");
2562 mRemovedEndOffset
== 45, // 55 - (10 - 20)
2563 "Test 1-4-2: mRemovedEndOffset should be the the largest end of removed "
2564 "text without already added length");
2566 mAddedEndOffset
== 60,
2567 "Test 1-4-3: mAddedEndOffset should be the last end of added text");
2570 // Removing text at different point (not sure if actually occurs)
2571 MergeWith(TextChangeData(10, 20, 10, false, false));
2572 MergeWith(TextChangeData(55, 68, 55, false, false));
2573 MOZ_ASSERT(mStartOffset
== 10,
2574 "Test 1-5-1: mStartOffset should be the smallest offset");
2576 mRemovedEndOffset
== 78, // 68 - (10 - 20)
2577 "Test 1-5-2: mRemovedEndOffset should be the the largest end of removed "
2578 "text with already removed length");
2580 mAddedEndOffset
== 55,
2581 "Test 1-5-3: mAddedEndOffset should be the largest end of added text");
2584 // Replacing text and append text (becomes longer)
2585 MergeWith(TextChangeData(30, 35, 32, false, false));
2586 MergeWith(TextChangeData(32, 32, 40, false, false));
2587 MOZ_ASSERT(mStartOffset
== 30,
2588 "Test 1-6-1: mStartOffset should be the smallest offset");
2590 mRemovedEndOffset
== 35, // 32 - (32 - 35)
2591 "Test 1-6-2: mRemovedEndOffset should be the the first end of removed "
2594 mAddedEndOffset
== 40,
2595 "Test 1-6-3: mAddedEndOffset should be the last end of added text");
2598 // Replacing text and append text (becomes shorter)
2599 MergeWith(TextChangeData(30, 35, 32, false, false));
2600 MergeWith(TextChangeData(32, 32, 33, false, false));
2601 MOZ_ASSERT(mStartOffset
== 30,
2602 "Test 1-7-1: mStartOffset should be the smallest offset");
2604 mRemovedEndOffset
== 35, // 32 - (32 - 35)
2605 "Test 1-7-2: mRemovedEndOffset should be the the first end of removed "
2608 mAddedEndOffset
== 33,
2609 "Test 1-7-3: mAddedEndOffset should be the last end of added text");
2612 // Removing text and replacing text after first range (not sure if actually
2614 MergeWith(TextChangeData(30, 35, 30, false, false));
2615 MergeWith(TextChangeData(32, 34, 48, false, false));
2616 MOZ_ASSERT(mStartOffset
== 30,
2617 "Test 1-8-1: mStartOffset should be the smallest offset");
2618 MOZ_ASSERT(mRemovedEndOffset
== 39, // 34 - (30 - 35)
2619 "Test 1-8-2: mRemovedEndOffset should be the the first end of "
2621 "without already removed text");
2623 mAddedEndOffset
== 48,
2624 "Test 1-8-3: mAddedEndOffset should be the last end of added text");
2627 // Removing text and replacing text after first range (not sure if actually
2629 MergeWith(TextChangeData(30, 35, 30, false, false));
2630 MergeWith(TextChangeData(32, 38, 36, false, false));
2631 MOZ_ASSERT(mStartOffset
== 30,
2632 "Test 1-9-1: mStartOffset should be the smallest offset");
2633 MOZ_ASSERT(mRemovedEndOffset
== 43, // 38 - (30 - 35)
2634 "Test 1-9-2: mRemovedEndOffset should be the the first end of "
2636 "without already removed text");
2638 mAddedEndOffset
== 36,
2639 "Test 1-9-3: mAddedEndOffset should be the last end of added text");
2642 /****************************************************************************
2644 ****************************************************************************/
2646 // Replacing text in around end of added text (becomes shorter) (not sure
2647 // if actually occurs)
2648 MergeWith(TextChangeData(50, 50, 55, false, false));
2649 MergeWith(TextChangeData(53, 60, 54, false, false));
2650 MOZ_ASSERT(mStartOffset
== 50,
2651 "Test 2-1-1: mStartOffset should be the smallest offset");
2652 MOZ_ASSERT(mRemovedEndOffset
== 55, // 60 - (55 - 50)
2653 "Test 2-1-2: mRemovedEndOffset should be the the last end of "
2655 "without already added text length");
2657 mAddedEndOffset
== 54,
2658 "Test 2-1-3: mAddedEndOffset should be the last end of added text");
2661 // Replacing text around end of added text (becomes longer) (not sure
2662 // if actually occurs)
2663 MergeWith(TextChangeData(50, 50, 55, false, false));
2664 MergeWith(TextChangeData(54, 62, 68, false, false));
2665 MOZ_ASSERT(mStartOffset
== 50,
2666 "Test 2-2-1: mStartOffset should be the smallest offset");
2667 MOZ_ASSERT(mRemovedEndOffset
== 57, // 62 - (55 - 50)
2668 "Test 2-2-2: mRemovedEndOffset should be the the last end of "
2670 "without already added text length");
2672 mAddedEndOffset
== 68,
2673 "Test 2-2-3: mAddedEndOffset should be the last end of added text");
2676 // Replacing text around end of replaced text (became shorter) (not sure if
2678 MergeWith(TextChangeData(36, 48, 45, false, false));
2679 MergeWith(TextChangeData(43, 50, 49, false, false));
2680 MOZ_ASSERT(mStartOffset
== 36,
2681 "Test 2-3-1: mStartOffset should be the smallest offset");
2682 MOZ_ASSERT(mRemovedEndOffset
== 53, // 50 - (45 - 48)
2683 "Test 2-3-2: mRemovedEndOffset should be the the last end of "
2685 "without already removed text length");
2687 mAddedEndOffset
== 49,
2688 "Test 2-3-3: mAddedEndOffset should be the last end of added text");
2691 // Replacing text around end of replaced text (became longer) (not sure if
2693 MergeWith(TextChangeData(36, 52, 53, false, false));
2694 MergeWith(TextChangeData(43, 68, 61, false, false));
2695 MOZ_ASSERT(mStartOffset
== 36,
2696 "Test 2-4-1: mStartOffset should be the smallest offset");
2697 MOZ_ASSERT(mRemovedEndOffset
== 67, // 68 - (53 - 52)
2698 "Test 2-4-2: mRemovedEndOffset should be the the last end of "
2700 "without already added text length");
2702 mAddedEndOffset
== 61,
2703 "Test 2-4-3: mAddedEndOffset should be the last end of added text");
2706 /****************************************************************************
2708 ****************************************************************************/
2710 // Appending text in already added text (not sure if actually occurs)
2711 MergeWith(TextChangeData(10, 10, 20, false, false));
2712 MergeWith(TextChangeData(15, 15, 30, false, false));
2713 MOZ_ASSERT(mStartOffset
== 10,
2714 "Test 3-1-1: mStartOffset should be the smallest offset");
2715 MOZ_ASSERT(mRemovedEndOffset
== 10,
2716 "Test 3-1-2: mRemovedEndOffset should be the the first end of "
2719 mAddedEndOffset
== 35, // 20 + (30 - 15)
2720 "Test 3-1-3: mAddedEndOffset should be the first end of added text with "
2721 "added text length by the new change");
2724 // Replacing text in added text (not sure if actually occurs)
2725 MergeWith(TextChangeData(50, 50, 55, false, false));
2726 MergeWith(TextChangeData(52, 53, 56, false, false));
2727 MOZ_ASSERT(mStartOffset
== 50,
2728 "Test 3-2-1: mStartOffset should be the smallest offset");
2729 MOZ_ASSERT(mRemovedEndOffset
== 50,
2730 "Test 3-2-2: mRemovedEndOffset should be the the first end of "
2733 mAddedEndOffset
== 58, // 55 + (56 - 53)
2734 "Test 3-2-3: mAddedEndOffset should be the first end of added text with "
2735 "added text length by the new change");
2738 // Replacing text in replaced text (became shorter) (not sure if actually
2740 MergeWith(TextChangeData(36, 48, 45, false, false));
2741 MergeWith(TextChangeData(37, 38, 50, false, false));
2742 MOZ_ASSERT(mStartOffset
== 36,
2743 "Test 3-3-1: mStartOffset should be the smallest offset");
2744 MOZ_ASSERT(mRemovedEndOffset
== 48,
2745 "Test 3-3-2: mRemovedEndOffset should be the the first end of "
2748 mAddedEndOffset
== 57, // 45 + (50 - 38)
2749 "Test 3-3-3: mAddedEndOffset should be the first end of added text with "
2750 "added text length by the new change");
2753 // Replacing text in replaced text (became longer) (not sure if actually
2755 MergeWith(TextChangeData(32, 48, 53, false, false));
2756 MergeWith(TextChangeData(43, 50, 52, false, false));
2757 MOZ_ASSERT(mStartOffset
== 32,
2758 "Test 3-4-1: mStartOffset should be the smallest offset");
2759 MOZ_ASSERT(mRemovedEndOffset
== 48,
2760 "Test 3-4-2: mRemovedEndOffset should be the the last end of "
2762 "without already added text length");
2764 mAddedEndOffset
== 55, // 53 + (52 - 50)
2765 "Test 3-4-3: mAddedEndOffset should be the first end of added text with "
2766 "added text length by the new change");
2769 // Replacing text in replaced text (became shorter) (not sure if actually
2771 MergeWith(TextChangeData(36, 48, 50, false, false));
2772 MergeWith(TextChangeData(37, 49, 47, false, false));
2773 MOZ_ASSERT(mStartOffset
== 36,
2774 "Test 3-5-1: mStartOffset should be the smallest offset");
2776 mRemovedEndOffset
== 48,
2777 "Test 3-5-2: mRemovedEndOffset should be the the first end of removed "
2779 MOZ_ASSERT(mAddedEndOffset
== 48, // 50 + (47 - 49)
2780 "Test 3-5-3: mAddedEndOffset should be the first end of added "
2782 "removed text length by the new change");
2785 // Replacing text in replaced text (became longer) (not sure if actually
2787 MergeWith(TextChangeData(32, 48, 53, false, false));
2788 MergeWith(TextChangeData(43, 50, 47, false, false));
2789 MOZ_ASSERT(mStartOffset
== 32,
2790 "Test 3-6-1: mStartOffset should be the smallest offset");
2791 MOZ_ASSERT(mRemovedEndOffset
== 48,
2792 "Test 3-6-2: mRemovedEndOffset should be the the last end of "
2794 "without already added text length");
2795 MOZ_ASSERT(mAddedEndOffset
== 50, // 53 + (47 - 50)
2796 "Test 3-6-3: mAddedEndOffset should be the first end of added "
2798 "removed text length by the new change");
2801 /****************************************************************************
2803 ****************************************************************************/
2805 // Replacing text all of already append text (not sure if actually occurs)
2806 MergeWith(TextChangeData(50, 50, 55, false, false));
2807 MergeWith(TextChangeData(44, 66, 68, false, false));
2808 MOZ_ASSERT(mStartOffset
== 44,
2809 "Test 4-1-1: mStartOffset should be the smallest offset");
2810 MOZ_ASSERT(mRemovedEndOffset
== 61, // 66 - (55 - 50)
2811 "Test 4-1-2: mRemovedEndOffset should be the the last end of "
2813 "without already added text length");
2815 mAddedEndOffset
== 68,
2816 "Test 4-1-3: mAddedEndOffset should be the last end of added text");
2819 // Replacing text around a point in which text was removed (not sure if
2821 MergeWith(TextChangeData(50, 62, 50, false, false));
2822 MergeWith(TextChangeData(44, 66, 68, false, false));
2823 MOZ_ASSERT(mStartOffset
== 44,
2824 "Test 4-2-1: mStartOffset should be the smallest offset");
2825 MOZ_ASSERT(mRemovedEndOffset
== 78, // 66 - (50 - 62)
2826 "Test 4-2-2: mRemovedEndOffset should be the the last end of "
2828 "without already removed text length");
2830 mAddedEndOffset
== 68,
2831 "Test 4-2-3: mAddedEndOffset should be the last end of added text");
2834 // Replacing text all replaced text (became shorter) (not sure if actually
2836 MergeWith(TextChangeData(50, 62, 60, false, false));
2837 MergeWith(TextChangeData(49, 128, 130, false, false));
2838 MOZ_ASSERT(mStartOffset
== 49,
2839 "Test 4-3-1: mStartOffset should be the smallest offset");
2840 MOZ_ASSERT(mRemovedEndOffset
== 130, // 128 - (60 - 62)
2841 "Test 4-3-2: mRemovedEndOffset should be the the last end of "
2843 "without already removed text length");
2845 mAddedEndOffset
== 130,
2846 "Test 4-3-3: mAddedEndOffset should be the last end of added text");
2849 // Replacing text all replaced text (became longer) (not sure if actually
2851 MergeWith(TextChangeData(50, 61, 73, false, false));
2852 MergeWith(TextChangeData(44, 100, 50, false, false));
2853 MOZ_ASSERT(mStartOffset
== 44,
2854 "Test 4-4-1: mStartOffset should be the smallest offset");
2855 MOZ_ASSERT(mRemovedEndOffset
== 88, // 100 - (73 - 61)
2856 "Test 4-4-2: mRemovedEndOffset should be the the last end of "
2858 "with already added text length");
2860 mAddedEndOffset
== 50,
2861 "Test 4-4-3: mAddedEndOffset should be the last end of added text");
2864 /****************************************************************************
2866 ****************************************************************************/
2868 // Replacing text around start of added text (not sure if actually occurs)
2869 MergeWith(TextChangeData(50, 50, 55, false, false));
2870 MergeWith(TextChangeData(48, 52, 49, false, false));
2871 MOZ_ASSERT(mStartOffset
== 48,
2872 "Test 5-1-1: mStartOffset should be the smallest offset");
2874 mRemovedEndOffset
== 50,
2875 "Test 5-1-2: mRemovedEndOffset should be the the first end of removed "
2878 mAddedEndOffset
== 52, // 55 + (52 - 49)
2879 "Test 5-1-3: mAddedEndOffset should be the first end of added text with "
2880 "added text length by the new change");
2883 // Replacing text around start of replaced text (became shorter) (not sure if
2885 MergeWith(TextChangeData(50, 60, 58, false, false));
2886 MergeWith(TextChangeData(43, 50, 48, false, false));
2887 MOZ_ASSERT(mStartOffset
== 43,
2888 "Test 5-2-1: mStartOffset should be the smallest offset");
2890 mRemovedEndOffset
== 60,
2891 "Test 5-2-2: mRemovedEndOffset should be the the first end of removed "
2893 MOZ_ASSERT(mAddedEndOffset
== 56, // 58 + (48 - 50)
2894 "Test 5-2-3: mAddedEndOffset should be the first end of added "
2896 "removed text length by the new change");
2899 // Replacing text around start of replaced text (became longer) (not sure if
2901 MergeWith(TextChangeData(50, 60, 68, false, false));
2902 MergeWith(TextChangeData(43, 55, 53, false, false));
2903 MOZ_ASSERT(mStartOffset
== 43,
2904 "Test 5-3-1: mStartOffset should be the smallest offset");
2906 mRemovedEndOffset
== 60,
2907 "Test 5-3-2: mRemovedEndOffset should be the the first end of removed "
2909 MOZ_ASSERT(mAddedEndOffset
== 66, // 68 + (53 - 55)
2910 "Test 5-3-3: mAddedEndOffset should be the first end of added "
2912 "removed text length by the new change");
2915 // Replacing text around start of replaced text (became shorter) (not sure if
2917 MergeWith(TextChangeData(50, 60, 58, false, false));
2918 MergeWith(TextChangeData(43, 50, 128, false, false));
2919 MOZ_ASSERT(mStartOffset
== 43,
2920 "Test 5-4-1: mStartOffset should be the smallest offset");
2922 mRemovedEndOffset
== 60,
2923 "Test 5-4-2: mRemovedEndOffset should be the the first end of removed "
2926 mAddedEndOffset
== 136, // 58 + (128 - 50)
2927 "Test 5-4-3: mAddedEndOffset should be the first end of added text with "
2928 "added text length by the new change");
2931 // Replacing text around start of replaced text (became longer) (not sure if
2933 MergeWith(TextChangeData(50, 60, 68, false, false));
2934 MergeWith(TextChangeData(43, 55, 65, false, false));
2935 MOZ_ASSERT(mStartOffset
== 43,
2936 "Test 5-5-1: mStartOffset should be the smallest offset");
2938 mRemovedEndOffset
== 60,
2939 "Test 5-5-2: mRemovedEndOffset should be the the first end of removed "
2942 mAddedEndOffset
== 78, // 68 + (65 - 55)
2943 "Test 5-5-3: mAddedEndOffset should be the first end of added text with "
2944 "added text length by the new change");
2947 /****************************************************************************
2949 ****************************************************************************/
2951 // Appending text before already added text (not sure if actually occurs)
2952 MergeWith(TextChangeData(30, 30, 45, false, false));
2953 MergeWith(TextChangeData(10, 10, 20, false, false));
2954 MOZ_ASSERT(mStartOffset
== 10,
2955 "Test 6-1-1: mStartOffset should be the smallest offset");
2957 mRemovedEndOffset
== 30,
2958 "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed "
2961 mAddedEndOffset
== 55, // 45 + (20 - 10)
2962 "Test 6-1-3: mAddedEndOffset should be the first end of added text with "
2963 "added text length by the new change");
2966 // Removing text before already removed text (not sure if actually occurs)
2967 MergeWith(TextChangeData(30, 35, 30, false, false));
2968 MergeWith(TextChangeData(10, 25, 10, false, false));
2969 MOZ_ASSERT(mStartOffset
== 10,
2970 "Test 6-2-1: mStartOffset should be the smallest offset");
2972 mRemovedEndOffset
== 35,
2973 "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed "
2976 mAddedEndOffset
== 15, // 30 - (25 - 10)
2977 "Test 6-2-3: mAddedEndOffset should be the first end of added text with "
2978 "removed text length by the new change");
2981 // Replacing text before already replaced text (not sure if actually occurs)
2982 MergeWith(TextChangeData(50, 65, 70, false, false));
2983 MergeWith(TextChangeData(13, 24, 15, false, false));
2984 MOZ_ASSERT(mStartOffset
== 13,
2985 "Test 6-3-1: mStartOffset should be the smallest offset");
2987 mRemovedEndOffset
== 65,
2988 "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed "
2990 MOZ_ASSERT(mAddedEndOffset
== 61, // 70 + (15 - 24)
2991 "Test 6-3-3: mAddedEndOffset should be the first end of added "
2993 "removed text length by the new change");
2996 // Replacing text before already replaced text (not sure if actually occurs)
2997 MergeWith(TextChangeData(50, 65, 70, false, false));
2998 MergeWith(TextChangeData(13, 24, 36, false, false));
2999 MOZ_ASSERT(mStartOffset
== 13,
3000 "Test 6-4-1: mStartOffset should be the smallest offset");
3002 mRemovedEndOffset
== 65,
3003 "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed "
3005 MOZ_ASSERT(mAddedEndOffset
== 82, // 70 + (36 - 24)
3006 "Test 6-4-3: mAddedEndOffset should be the first end of added "
3008 "removed text length by the new change");
3012 #endif // #ifdef DEBUG
3014 } // namespace mozilla::widget
3017 //////////////////////////////////////////////////////////////
3019 // Convert a GUI event message code to a string.
3020 // Makes it a lot easier to debug events.
3022 // See gtk/nsWidget.cpp and windows/nsWindow.cpp
3023 // for a DebugPrintEvent() function that uses
3026 //////////////////////////////////////////////////////////////
3028 nsAutoString
nsBaseWidget::debug_GuiEventToString(WidgetGUIEvent
* aGuiEvent
) {
3029 NS_ASSERTION(nullptr != aGuiEvent
, "cmon, null gui event.");
3031 nsAutoString
eventName(u
"UNKNOWN"_ns
);
3033 # define _ASSIGN_eventName(_value, _name) \
3035 eventName.AssignLiteral(_name); \
3038 switch (aGuiEvent
->mMessage
) {
3039 _ASSIGN_eventName(eBlur
, "eBlur");
3040 _ASSIGN_eventName(eDrop
, "eDrop");
3041 _ASSIGN_eventName(eDragEnter
, "eDragEnter");
3042 _ASSIGN_eventName(eDragExit
, "eDragExit");
3043 _ASSIGN_eventName(eDragOver
, "eDragOver");
3044 _ASSIGN_eventName(eEditorInput
, "eEditorInput");
3045 _ASSIGN_eventName(eFocus
, "eFocus");
3046 _ASSIGN_eventName(eFocusIn
, "eFocusIn");
3047 _ASSIGN_eventName(eFocusOut
, "eFocusOut");
3048 _ASSIGN_eventName(eFormSelect
, "eFormSelect");
3049 _ASSIGN_eventName(eFormChange
, "eFormChange");
3050 _ASSIGN_eventName(eFormReset
, "eFormReset");
3051 _ASSIGN_eventName(eFormSubmit
, "eFormSubmit");
3052 _ASSIGN_eventName(eImageAbort
, "eImageAbort");
3053 _ASSIGN_eventName(eLoadError
, "eLoadError");
3054 _ASSIGN_eventName(eKeyDown
, "eKeyDown");
3055 _ASSIGN_eventName(eKeyPress
, "eKeyPress");
3056 _ASSIGN_eventName(eKeyUp
, "eKeyUp");
3057 _ASSIGN_eventName(eMouseEnterIntoWidget
, "eMouseEnterIntoWidget");
3058 _ASSIGN_eventName(eMouseExitFromWidget
, "eMouseExitFromWidget");
3059 _ASSIGN_eventName(eMouseDown
, "eMouseDown");
3060 _ASSIGN_eventName(eMouseUp
, "eMouseUp");
3061 _ASSIGN_eventName(eMouseClick
, "eMouseClick");
3062 _ASSIGN_eventName(eMouseAuxClick
, "eMouseAuxClick");
3063 _ASSIGN_eventName(eMouseDoubleClick
, "eMouseDoubleClick");
3064 _ASSIGN_eventName(eMouseMove
, "eMouseMove");
3065 _ASSIGN_eventName(eLoad
, "eLoad");
3066 _ASSIGN_eventName(ePopState
, "ePopState");
3067 _ASSIGN_eventName(eBeforeScriptExecute
, "eBeforeScriptExecute");
3068 _ASSIGN_eventName(eAfterScriptExecute
, "eAfterScriptExecute");
3069 _ASSIGN_eventName(eUnload
, "eUnload");
3070 _ASSIGN_eventName(eHashChange
, "eHashChange");
3071 _ASSIGN_eventName(eReadyStateChange
, "eReadyStateChange");
3072 _ASSIGN_eventName(eXULBroadcast
, "eXULBroadcast");
3073 _ASSIGN_eventName(eXULCommandUpdate
, "eXULCommandUpdate");
3075 # undef _ASSIGN_eventName
3078 eventName
.AssignLiteral("UNKNOWN: ");
3079 eventName
.AppendInt(aGuiEvent
->mMessage
);
3083 return nsAutoString(eventName
);
3085 //////////////////////////////////////////////////////////////
3087 // Code to deal with paint and event debug prefs.
3089 //////////////////////////////////////////////////////////////
3095 static PrefPair debug_PrefValues
[] = {
3096 {"nglayout.debug.crossing_event_dumping", false},
3097 {"nglayout.debug.event_dumping", false},
3098 {"nglayout.debug.invalidate_dumping", false},
3099 {"nglayout.debug.motion_event_dumping", false},
3100 {"nglayout.debug.paint_dumping", false}};
3102 //////////////////////////////////////////////////////////////
3103 bool nsBaseWidget::debug_GetCachedBoolPref(const char* aPrefName
) {
3104 NS_ASSERTION(nullptr != aPrefName
, "cmon, pref name is null.");
3106 for (uint32_t i
= 0; i
< ArrayLength(debug_PrefValues
); i
++) {
3107 if (strcmp(debug_PrefValues
[i
].name
, aPrefName
) == 0) {
3108 return debug_PrefValues
[i
].value
;
3114 //////////////////////////////////////////////////////////////
3115 static void debug_SetCachedBoolPref(const char* aPrefName
, bool aValue
) {
3116 NS_ASSERTION(nullptr != aPrefName
, "cmon, pref name is null.");
3118 for (uint32_t i
= 0; i
< ArrayLength(debug_PrefValues
); i
++) {
3119 if (strcmp(debug_PrefValues
[i
].name
, aPrefName
) == 0) {
3120 debug_PrefValues
[i
].value
= aValue
;
3126 NS_ASSERTION(false, "cmon, this code is not reached dude.");
3129 //////////////////////////////////////////////////////////////
3130 class Debug_PrefObserver final
: public nsIObserver
{
3131 ~Debug_PrefObserver() = default;
3138 NS_IMPL_ISUPPORTS(Debug_PrefObserver
, nsIObserver
)
3141 Debug_PrefObserver::Observe(nsISupports
* subject
, const char* topic
,
3142 const char16_t
* data
) {
3143 NS_ConvertUTF16toUTF8
prefName(data
);
3145 bool value
= Preferences::GetBool(prefName
.get(), false);
3146 debug_SetCachedBoolPref(prefName
.get(), value
);
3150 //////////////////////////////////////////////////////////////
3151 /* static */ void debug_RegisterPrefCallbacks() {
3152 static bool once
= true;
3160 nsCOMPtr
<nsIObserver
> obs(new Debug_PrefObserver());
3161 for (uint32_t i
= 0; i
< ArrayLength(debug_PrefValues
); i
++) {
3162 // Initialize the pref values
3163 debug_PrefValues
[i
].value
=
3164 Preferences::GetBool(debug_PrefValues
[i
].name
, false);
3167 // Register callbacks for when these change
3169 name
.AssignLiteral(debug_PrefValues
[i
].name
,
3170 strlen(debug_PrefValues
[i
].name
));
3171 Preferences::AddStrongObserver(obs
, name
);
3175 //////////////////////////////////////////////////////////////
3176 static int32_t _GetPrintCount() {
3177 static int32_t sCount
= 0;
3181 //////////////////////////////////////////////////////////////
3183 void nsBaseWidget::debug_DumpEvent(FILE* aFileOut
, nsIWidget
* aWidget
,
3184 WidgetGUIEvent
* aGuiEvent
,
3185 const char* aWidgetName
, int32_t aWindowID
) {
3186 if (aGuiEvent
->mMessage
== eMouseMove
) {
3187 if (!debug_GetCachedBoolPref("nglayout.debug.motion_event_dumping")) return;
3190 if (aGuiEvent
->mMessage
== eMouseEnterIntoWidget
||
3191 aGuiEvent
->mMessage
== eMouseExitFromWidget
) {
3192 if (!debug_GetCachedBoolPref("nglayout.debug.crossing_event_dumping"))
3196 if (!debug_GetCachedBoolPref("nglayout.debug.event_dumping")) return;
3198 NS_LossyConvertUTF16toASCII
tempString(
3199 debug_GuiEventToString(aGuiEvent
).get());
3201 fprintf(aFileOut
, "%4d %-26s widget=%-8p name=%-12s id=0x%-6x refpt=%d,%d\n",
3202 _GetPrintCount(), tempString
.get(), (void*)aWidget
, aWidgetName
,
3203 aWindowID
, aGuiEvent
->mRefPoint
.x
, aGuiEvent
->mRefPoint
.y
);
3205 //////////////////////////////////////////////////////////////
3207 void nsBaseWidget::debug_DumpPaintEvent(FILE* aFileOut
, nsIWidget
* aWidget
,
3208 const nsIntRegion
& aRegion
,
3209 const char* aWidgetName
,
3210 int32_t aWindowID
) {
3211 NS_ASSERTION(nullptr != aFileOut
, "cmon, null output FILE");
3212 NS_ASSERTION(nullptr != aWidget
, "cmon, the widget is null");
3214 if (!debug_GetCachedBoolPref("nglayout.debug.paint_dumping")) return;
3216 nsIntRect rect
= aRegion
.GetBounds();
3218 "%4d PAINT widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d "
3220 _GetPrintCount(), (void*)aWidget
, aWidgetName
, aWindowID
, rect
.X(),
3221 rect
.Y(), rect
.Width(), rect
.Height());
3223 fprintf(aFileOut
, "\n");
3225 //////////////////////////////////////////////////////////////
3227 void nsBaseWidget::debug_DumpInvalidate(FILE* aFileOut
, nsIWidget
* aWidget
,
3228 const LayoutDeviceIntRect
* aRect
,
3229 const char* aWidgetName
,
3230 int32_t aWindowID
) {
3231 if (!debug_GetCachedBoolPref("nglayout.debug.invalidate_dumping")) return;
3233 NS_ASSERTION(nullptr != aFileOut
, "cmon, null output FILE");
3234 NS_ASSERTION(nullptr != aWidget
, "cmon, the widget is null");
3236 fprintf(aFileOut
, "%4d Invalidate widget=%p name=%-12s id=0x%-6x",
3237 _GetPrintCount(), (void*)aWidget
, aWidgetName
, aWindowID
);
3240 fprintf(aFileOut
, " rect=%3d,%-3d %3d,%-3d", aRect
->X(), aRect
->Y(),
3241 aRect
->Width(), aRect
->Height());
3243 fprintf(aFileOut
, " rect=%-15s", "none");
3246 fprintf(aFileOut
, "\n");
3248 //////////////////////////////////////////////////////////////