Bug 1779627 - Migrate toolkit/components/mozintl/mozIntl.jsm to esm; r=nordzilla
[gecko.git] / widget / nsBaseWidget.cpp
blob12cd01d6b2e9df49781e0c1deca271d60d7e09c4
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"
10 #include <utility>
12 #include "GLConsts.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"
61 #include "nsCOMPtr.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"
75 #include "prdtoa.h"
76 #include "prenv.h"
77 #ifdef ACCESSIBILITY
78 # include "nsAccessibilityService.h"
79 #endif
80 #include "gfxConfig.h"
81 #include "gfxUtils.h" // for ToDeviceColor
82 #include "mozilla/layers/CompositorSession.h"
83 #include "VRManagerChild.h"
84 #include "gfxConfig.h"
85 #include "nsView.h"
86 #include "nsViewManager.h"
88 #ifdef DEBUG
89 # include "nsIObserver.h"
91 static void debug_RegisterPrefCallbacks();
93 #endif
95 #ifdef NOISY_WIDGET_LEAKS
96 static int32_t gNumWidgets;
97 #endif
99 #ifdef XP_MACOSX
100 # include "nsCocoaFeatures.h"
101 #endif
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
117 /*static*/
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
124 // milliseconds.
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),
143 mBounds(0, 0, 0, 0),
144 mOriginalBounds(nullptr),
145 mIsTiled(false),
146 mPopupLevel(ePopupLevelTop),
147 mPopupType(ePopupTypeAny),
148 mHasRemoteContent(false),
149 mUpdateCursor(true),
150 mUseAttachedEvents(false),
151 mIMEHasFocus(false),
152 mIMEHasQuit(false),
153 mIsFullyOccluded(false),
154 mNeedFastSnaphot(false),
155 mCurrentPanGestureBelongsToSwipe(false) {
156 #ifdef NOISY_WIDGET_LEAKS
157 gNumWidgets++;
158 printf("WIDGETS+ = %d\n", gNumWidgets);
159 #endif
161 #ifdef DEBUG
162 debug_RegisterPrefCallbacks();
163 #endif
165 mShutdownObserver = new WidgetShutdownObserver(this);
168 NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver)
170 WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget)
171 : mWidget(aWidget), mRegistered(false) {
172 Register();
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.
181 NS_IMETHODIMP
182 WidgetShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
183 const char16_t* aData) {
184 if (!mWidget) {
185 return NS_OK;
187 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
188 RefPtr<nsBaseWidget> widget(mWidget);
189 widget->Shutdown();
190 } else if (!strcmp(aTopic, "quit-application")) {
191 RefPtr<nsBaseWidget> widget(mWidget);
192 widget->QuitIME();
194 return NS_OK;
197 void WidgetShutdownObserver::Register() {
198 if (!mRegistered) {
199 mRegistered = true;
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);
214 #endif
218 void WidgetShutdownObserver::Unregister() {
219 if (mRegistered) {
220 mWidget = nullptr;
222 #ifndef MOZ_WIDGET_ANDROID
223 nsCOMPtr<nsIObserverService> observerService =
224 mozilla::services::GetObserverService();
225 if (observerService) {
226 observerService->RemoveObserver(this, "quit-application");
228 #endif
230 nsContentUtils::UnregisterShutdownObserver(this);
231 mRegistered = false;
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) {
241 Register();
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.
250 NS_IMETHODIMP
251 LocalesChangedObserver::Observe(nsISupports* aSubject, const char* aTopic,
252 const char16_t* aData) {
253 if (!mWidget) {
254 return NS_OK;
256 if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) {
257 RefPtr<nsBaseWidget> widget(mWidget);
258 widget->LocalesChanged();
260 return NS_OK;
263 void LocalesChangedObserver::Register() {
264 if (mRegistered) {
265 return;
268 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
269 if (obs) {
270 obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true);
273 // Locale might be update before registering
274 RefPtr<nsBaseWidget> widget(mWidget);
275 widget->LocalesChanged();
277 mRegistered = true;
280 void LocalesChangedObserver::Unregister() {
281 if (!mRegistered) {
282 return;
285 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
286 if (obs) {
287 obs->RemoveObserver(this, INTL_APP_LOCALES_CHANGED);
290 mWidget = nullptr;
291 mRegistered = false;
294 void nsBaseWidget::Shutdown() {
295 NotifyLiveResizeStopped();
296 RevokeTransactionIdAllocator();
297 DestroyCompositor();
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();
335 mAPZC = nullptr;
336 SetCompositorWidgetDelegate(nullptr);
337 mCompositorBridgeChild = nullptr;
338 mCompositorSession->Shutdown();
339 mCompositorSession = nullptr;
343 // This prevents the layer manager from starting a new transaction during
344 // shutdown.
345 void nsBaseWidget::RevokeTransactionIdAllocator() {
346 if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) {
347 return;
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;
364 DestroyCompositor();
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() {
390 if (mSwipeTracker) {
391 mSwipeTracker->Destroy();
392 mSwipeTracker = nullptr;
395 IMEStateManager::WidgetDestroyed(this);
397 FreeLocalesChangedObserver();
398 FreeShutdownObserver();
399 RevokeTransactionIdAllocator();
400 DestroyLayerManager();
402 #ifdef NOISY_WIDGET_LEAKS
403 gNumWidgets--;
404 printf("WIDGETS- = %d\n", gNumWidgets);
405 #endif
407 delete mOriginalBounds;
410 //-------------------------------------------------------------------------
412 // Basic create.
414 //-------------------------------------------------------------------------
415 void nsBaseWidget::BaseCreate(nsIWidget* aParent, nsWidgetInitData* aInitData) {
416 if (aInitData) {
417 mWindowType = aInitData->mWindowType;
418 mBorderStyle = aInitData->mBorderStyle;
419 mPopupLevel = aInitData->mPopupLevel;
420 mPopupType = aInitData->mPopupHint;
421 mHasRemoteContent = aInitData->mHasRemoteContent;
424 if (aParent) {
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();
461 } else {
462 widget = nsIWidget::CreateChildWindow();
465 if (widget && mNeedFastSnaphot) {
466 widget->SetNeedFastSnaphot();
469 if (widget &&
470 NS_SUCCEEDED(widget->Create(parent, nativeParent, aRect, aInitData))) {
471 return widget.forget();
474 return nullptr;
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();
515 if (parent) {
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;
534 while (widget) {
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());
562 int32_t width = 0;
563 int32_t height = 0;
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() {
571 return nullptr;
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");
583 if (!mFirstChild) {
584 mFirstChild = mLastChild = aChild;
585 } else {
586 // append to the list
587 MOZ_ASSERT(mLastChild);
588 MOZ_ASSERT(!mLastChild->GetNextSibling());
589 mLastChild->SetNextSibling(aChild);
590 aChild->SetPrevSibling(mLastChild);
591 mLastChild = aChild;
595 //-------------------------------------------------------------------------
597 // Remove a child from the list of children
599 //-------------------------------------------------------------------------
600 void nsBaseWidget::RemoveChild(nsIWidget* aChild) {
601 #ifdef DEBUG
602 # ifdef XP_MACOSX
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!");
607 # else
608 MOZ_RELEASE_ASSERT(aChild->GetParent() == this, "Not one of our kids!");
609 # endif
610 #endif
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();
623 if (prev) {
624 prev->SetNextSibling(next);
626 if (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
641 // from our parent.
642 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
644 mZIndex = aZIndex;
646 // reorder this child in its parent's list.
647 auto* parent = static_cast<nsBaseWidget*>(GetParent());
648 if (parent) {
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();
657 mNextSibling = sib;
658 mPrevSibling = prev;
659 sib->SetPrevSibling(this);
660 if (prev) {
661 prev->SetNextSibling(this);
662 } else {
663 NS_ASSERTION(sib == parent->mFirstChild, "Broken child list");
664 // We've taken ownership of sib, so it's safe to have parent let
665 // go of it
666 parent->mFirstChild = this;
668 PlaceBehind(eZPlacementBelow, sib, false);
669 break;
672 // were we added to the list?
673 if (!sib) {
674 parent->AddChild(this);
679 void nsBaseWidget::GetWorkspaceID(nsAString& workspaceID) {
680 workspaceID.Truncate();
683 void nsBaseWidget::MoveToWorkspace(const nsAString& workspaceID) {
684 // Noop.
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;
707 /* virtual */
708 void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage,
709 uint16_t aDuration,
710 nsISupports* aData,
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);
724 if (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();
732 if (screen) {
733 int32_t left, top, width, height;
734 if (NS_SUCCEEDED(
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(),
743 true);
744 } else {
745 Resize(mOriginalBounds->X(), mOriginalBounds->Y(),
746 mOriginalBounds->Width(), mOriginalBounds->Height(), true);
751 nsresult nsBaseWidget::MakeFullScreen(bool aFullScreen) {
752 InfallibleMakeFullScreen(aFullScreen);
753 return NS_OK;
756 nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
757 nsBaseWidget* aWidget, gfxContext* aTarget, BufferMode aDoubleBuffering)
758 : mWidget(aWidget) {
759 WindowRenderer* renderer = mWidget->GetWindowRenderer();
760 if (renderer->AsFallback()) {
761 mRenderer = renderer->AsFallback();
762 mRenderer->SetTarget(aTarget, aDoubleBuffering);
766 nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() {
767 if (mRenderer) {
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());
805 MOZ_ASSERT(mAPZC);
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);
853 if (!aConstraints) {
854 mInitialZoomConstraints.reset();
858 if (aConstraints) {
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()));
864 return;
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,
921 inputBlockId);
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,
930 inputBlockId);
931 mAPZEventState->ProcessMouseEvent(*mouseEvent, inputBlockId);
933 if (postLayerization) {
934 postLayerization->Register();
938 return status;
941 template <class InputType, class EventType>
942 class DispatchEventOnMainThread : public Runnable {
943 public:
944 DispatchEventOnMainThread(const InputType& aInput, nsBaseWidget* aWidget,
945 const APZEventResult& aAPZResult)
946 : mozilla::Runnable("DispatchEventOnMainThread"),
947 mInput(aInput),
948 mWidget(aWidget),
949 mAPZResult(aAPZResult) {}
951 NS_IMETHOD Run() override {
952 EventType event = mInput.ToWidgetEvent(mWidget);
953 mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult);
954 return NS_OK;
957 private:
958 InputType mInput;
959 nsBaseWidget* mWidget;
960 APZEventResult mAPZResult;
963 template <class InputType, class EventType>
964 class DispatchInputOnControllerThread : public Runnable {
965 public:
966 DispatchInputOnControllerThread(const EventType& aEvent,
967 IAPZCTreeManager* aAPZC,
968 nsBaseWidget* aWidget)
969 : mozilla::Runnable("DispatchInputOnControllerThread"),
970 mMainMessageLoop(MessageLoop::current()),
971 mInput(aEvent),
972 mAPZC(aAPZC),
973 mWidget(aWidget) {}
975 NS_IMETHOD Run() override {
976 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(mInput);
977 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
978 return NS_OK;
980 RefPtr<Runnable> r = new DispatchEventOnMainThread<InputType, EventType>(
981 mInput, mWidget, result);
982 mMainMessageLoop->PostTask(r.forget());
983 return NS_OK;
986 private:
987 MessageLoop* mMainMessageLoop;
988 InputType mInput;
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);
999 if (mAPZC) {
1000 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1002 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
1003 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1004 return;
1007 WidgetTouchEvent event = aInput.ToWidgetEvent(this, aInputSource);
1008 ProcessUntransformedAPZEvent(&event, result);
1009 } else {
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());
1019 if (mAPZC) {
1020 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1022 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
1023 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1024 return;
1027 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1028 ProcessUntransformedAPZEvent(&event, result);
1029 } else {
1030 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1031 nsEventStatus status;
1032 DispatchEvent(&event, status);
1036 void nsBaseWidget::DispatchPinchGestureInput(PinchGestureInput& aInput) {
1037 MOZ_ASSERT(NS_IsMainThread());
1038 if (mAPZC) {
1039 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1040 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
1042 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1043 return;
1045 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1046 ProcessUntransformedAPZEvent(&event, result);
1047 } else {
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());
1058 if (mAPZC) {
1059 if (APZThreadUtils::IsControllerThread()) {
1060 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(*aEvent);
1061 status.mApzStatus = result.GetStatus();
1062 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1063 return status;
1065 status.mContentStatus = ProcessUntransformedAPZEvent(aEvent, result);
1066 return status;
1068 if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
1069 RefPtr<Runnable> r =
1070 new DispatchInputOnControllerThread<ScrollWheelInput,
1071 WidgetWheelEvent>(*wheelEvent,
1072 mAPZC, this);
1073 APZThreadUtils::RunOnControllerThread(std::move(r));
1074 status.mContentStatus = nsEventStatus_eConsumeDoDefault;
1075 return status;
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;
1083 return status;
1085 if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
1086 RefPtr<Runnable> r =
1087 new DispatchInputOnControllerThread<MultiTouchInput,
1088 WidgetTouchEvent>(*touchEvent,
1089 mAPZC, this);
1090 APZThreadUtils::RunOnControllerThread(std::move(r));
1091 status.mContentStatus = nsEventStatus_eConsumeDoDefault;
1092 return status;
1094 // Allow dispatching keyboard events on Gecko thread.
1095 MOZ_ASSERT(aEvent->AsKeyboardEvent());
1098 DispatchEvent(aEvent, status.mContentStatus);
1099 return status;
1102 void nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) {
1103 MOZ_ASSERT(NS_IsMainThread());
1104 if (mAPZC) {
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();
1122 return nullptr;
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);
1157 do {
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
1169 // BasicCompositor.
1170 bool supportsAcceleration = WidgetTypeSupportsAcceleration();
1171 bool enableWR;
1172 bool enableSWWR;
1173 if (supportsAcceleration ||
1174 StaticPrefs::gfx_webrender_unaccelerated_widget_force()) {
1175 enableWR = gfx::gfxVars::UseWebRender();
1176 enableSWWR = gfx::gfxVars::UseSoftwareWebRender();
1177 } else {
1178 enableWR = enableSWWR = gfx::gfxVars::UseWebRender();
1180 MOZ_RELEASE_ASSERT(enableWR);
1181 bool enableAPZ = UseAPZ();
1182 CompositorOptions options(enableAPZ, enableSWWR);
1184 #ifdef XP_WIN
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());
1201 #endif
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);
1209 #else
1210 options.SetInitiallyPaused(CompositorInitiallyPaused());
1211 #endif
1213 RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this);
1215 uint64_t innerWindowId = 0;
1216 if (Document* doc = GetDocument()) {
1217 innerWindowId = doc->InnerWindowID();
1220 bool retry = false;
1221 mCompositorSession = gpu->CreateTopLevelCompositor(
1222 this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(),
1223 gfx::IntSize(aWidth, aHeight), innerWindowId, &retry);
1225 if (mCompositorSession) {
1226 TextureFactoryIdentifier textureFactoryIdentifier;
1227 nsCString error;
1228 lm->Initialize(mCompositorSession->GetCompositorBridgeChild(),
1229 wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
1230 &textureFactoryIdentifier, error);
1231 if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) {
1232 retry = true;
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;
1244 return lm.forget();
1246 } while (true);
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 "
1258 "beforehand");
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) {
1270 return;
1273 // The controller thread must be configured before the compositor
1274 // session is created, so that the input bridge runs on the right
1275 // thread.
1276 ConfigureAPZControllerThread();
1278 CompositorOptions options;
1279 RefPtr<WebRenderLayerManager> lm =
1280 CreateCompositorSession(aWidth, aHeight, &options);
1281 if (!lm) {
1282 return;
1285 MOZ_ASSERT(mCompositorSession);
1286 mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild();
1287 SetCompositorWidgetDelegate(
1288 mCompositorSession->GetCompositorWidgetDelegate());
1290 if (options.UseAPZ()) {
1291 mAPZC = mCompositorSession->GetAPZCTreeManager();
1292 ConfigureAPZCTreeManager();
1293 } else {
1294 mAPZC = nullptr;
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);
1311 WindowUsesOMTC();
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;
1319 #else
1320 bool getCompositorFromThisWindow = (mWindowType == eWindowType_toplevel);
1321 #endif
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
1342 return nullptr;
1344 // Try to use an async compositor first, if possible
1345 if (ShouldUseOffMainThreadCompositing()) {
1346 CreateCompositor();
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()) {
1366 return;
1368 mWindowRenderer->AsWebRender()->ClearCachedResources();
1371 void nsBaseWidget::ClearWebrenderAnimationResources() {
1372 if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) {
1373 return;
1375 mWindowRenderer->AsWebRender()->ClearAnimationResources();
1378 bool nsBaseWidget::SetNeedFastSnaphot() {
1379 MOZ_ASSERT(XRE_IsParentProcess());
1380 MOZ_ASSERT(!mCompositorSession);
1382 if (!XRE_IsParentProcess() || mCompositorSession) {
1383 return false;
1386 mNeedFastSnaphot = true;
1387 return true;
1390 already_AddRefed<gfx::DrawTarget> nsBaseWidget::StartRemoteDrawing() {
1391 return nullptr;
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);
1422 } else {
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,
1442 aRepaint);
1443 } else {
1444 LayoutDeviceSize layoutSize = aSize * GetDesktopToDeviceScale();
1445 Resize(mBounds.Width() + (layoutSize.width - clientBounds.Width()),
1446 mBounds.Height() + (layoutSize.height - clientBounds.Height()),
1447 aRepaint);
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()) /
1464 scale;
1465 Resize(aRect.X() - desktopOffset.x, aRect.Y() - desktopOffset.y,
1466 aRect.Width() + desktopDelta.width,
1467 aRect.Height() + desktopDelta.height, aRepaint);
1468 } else {
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(),
1473 aRepaint);
1477 //-------------------------------------------------------------------------
1479 // Bounds
1481 //-------------------------------------------------------------------------
1484 * If the implementation of nsWindow supports borders this method MUST be
1485 * overridden
1488 LayoutDeviceIntRect nsBaseWidget::GetClientBounds() { return GetBounds(); }
1491 * If the implementation of nsWindow supports borders this method MUST be
1492 * overridden
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();
1509 return NS_OK;
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) {
1527 return false;
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);
1541 bool readable;
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) {
1555 *aResult = nullptr;
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));
1566 if (dirs) {
1567 bool hasMore;
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);
1576 return;
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) {
1599 return;
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) {
1611 gfx::Size size;
1612 if (BoundsUseDesktopPixels()) {
1613 DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale();
1614 size = desktopSize.ToUnknownSize();
1615 } else {
1616 size = gfx::Size(clampedSize.ToUnknownSize());
1618 Resize(size.width, size.height, true);
1622 const widget::SizeConstraints nsBaseWidget::GetSizeConstraints() {
1623 return mSizeConstraints;
1626 // static
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));
1639 if (appWindow) {
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) {
1656 return;
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) {
1676 if (mIMEHasQuit) {
1677 return NS_OK;
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);
1692 return NS_OK;
1693 default: {
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;
1706 return rv;
1711 void nsBaseWidget::EnsureTextEventDispatcher() {
1712 if (mTextEventDispatcher) {
1713 return;
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();
1739 if (!dispatcher) {
1740 return nullptr;
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.
1750 return nullptr;
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) {
1757 return;
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();
1780 if (accService) {
1781 return accService->GetRootDocumentAccessible(
1782 presShell, nsContentUtils::IsSafeToRunScript());
1785 return nullptr;
1788 #endif // ACCESSIBILITY
1790 void nsBaseWidget::StartAsyncScrollbarDrag(
1791 const AsyncDragMetrics& aDragMetrics) {
1792 if (!AsyncPanZoomEnabled()) {
1793 return;
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()
1820 : LayersId{0};
1823 already_AddRefed<nsIScreen> nsBaseWidget::GetWidgetScreen() {
1824 nsCOMPtr<nsIScreenManager> screenManager;
1825 screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
1826 if (!screenManager) {
1827 return nullptr;
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,
1846 bool aLongTap,
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,
1856 1.0, 90, nullptr);
1857 if (NS_FAILED(rv)) {
1858 return rv;
1861 if (!aLongTap) {
1862 return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, aPoint, 0, 0,
1863 nullptr);
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,
1873 nullptr);
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
1897 return NS_OK;
1900 // static
1901 void nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) {
1902 auto* self = static_cast<nsIWidget*>(aClosure);
1904 if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) >
1905 TimeStamp::Now()) {
1906 #ifdef XP_WIN
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);
1912 #endif
1913 return;
1916 AutoObserverNotifier notifier(self->mLongTapTouchPoint->mObserver,
1917 "touchtap");
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) {
1935 return NS_OK;
1937 mLongTapTimer->Cancel();
1938 mLongTapTimer = nullptr;
1939 SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
1940 mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1941 mLongTapTouchPoint = nullptr;
1942 return NS_OK;
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) {
1965 if (index >= 0) {
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;
1972 } else {
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;
1980 } else {
1981 MOZ_ASSERT(aPointerState == TOUCH_REMOVE || aPointerState == TOUCH_CANCEL);
1982 // a touch point is being lifted, so remove it from the stored list
1983 if (index >= 0) {
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) {
2007 return;
2009 nsCOMPtr<nsIAppWindow> appWindow = mWidgetListener->GetAppWindow();
2010 if (!appWindow) {
2011 return;
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,
2040 bool aStartSwipe) {
2041 if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == aInputBlockId) {
2042 if (aStartSwipe) {
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;
2069 mSwipeTracker =
2070 new SwipeTracker(*this, aSwipeStartEvent, aAllowedDirections, direction);
2072 if (!mAPZC) {
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};
2100 return result;
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
2117 // the swipe now.
2118 TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections);
2119 } else {
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);
2137 return event;
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 "
2152 "a SwipeTracker, "
2153 "and it should have consumed this event.");
2154 return true;
2157 if (aCanTriggerSwipe == CanTriggerSwipe::No) {
2158 return false;
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.
2177 mSwipeEventQueue =
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);
2188 return true;
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());
2203 return true;
2206 already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboard() {
2207 if (XRE_IsContentProcess()) {
2208 return CreateBidiKeyboardContentProcess();
2210 return CreateBidiKeyboardInner();
2213 #ifdef ANDROID
2214 already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboardInner() {
2215 // no bidi keyboard implementation
2216 return nullptr;
2218 #endif
2220 namespace mozilla::widget {
2222 const char* ToChar(InputContext::Origin aOrigin) {
2223 switch (aOrigin) {
2224 case InputContext::ORIGIN_MAIN:
2225 return "ORIGIN_MAIN";
2226 case InputContext::ORIGIN_CONTENT:
2227 return "ORIGIN_CONTENT";
2228 default:
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";
2255 default:
2256 return "Unexpected value";
2260 void NativeIMEContext::Init(nsIWidget* aWidget) {
2261 if (!aWidget) {
2262 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2263 mOriginProcessID = static_cast<uint64_t>(-1);
2264 return;
2266 if (!XRE_IsContentProcess()) {
2267 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(
2268 aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT));
2269 mOriginProcessID = 0;
2270 return;
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);
2283 return;
2285 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext);
2286 mOriginProcessID =
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");
2298 if (!IsValid()) {
2299 *this = aOther;
2300 return;
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
2335 // by composition.
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
2354 // composition.
2355 mIncludingChangesDuringComposition = false;
2356 } else {
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) {
2365 // Case 1:
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
2375 // text.
2376 uint32_t newRemovedEndOffsetInOldText =
2377 newData.mRemovedEndOffset - oldData.Difference();
2378 mRemovedEndOffset =
2379 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2380 // The new end offset of added text is always the larger offset.
2381 mAddedEndOffset = newData.mAddedEndOffset;
2382 return;
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) {
2390 // Case 2:
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
2399 // different text.
2400 uint32_t newRemovedEndOffsetInOldText =
2401 newData.mRemovedEndOffset - oldData.Difference();
2402 mRemovedEndOffset =
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;
2409 return;
2412 // Case 3:
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();
2425 mAddedEndOffset =
2426 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2427 return;
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) {
2438 // Case 4:
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
2441 // modified. Like:
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
2447 // different text.
2448 uint32_t newRemovedEndOffsetInOldText =
2449 newData.mRemovedEndOffset - oldData.Difference();
2450 mRemovedEndOffset =
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;
2458 return;
2461 // Case 5:
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
2469 // text.
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();
2476 mAddedEndOffset =
2477 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2478 return;
2481 // Case 6:
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();
2495 mAddedEndOffset =
2496 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2499 #ifdef DEBUG
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) {
2507 return;
2509 gTestTextChangeEvent = false;
2511 /****************************************************************************
2512 * Case 1
2513 ****************************************************************************/
2515 // Appending text
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");
2520 MOZ_ASSERT(
2521 mRemovedEndOffset == 10, // 20 - (20 - 10)
2522 "Test 1-1-2: mRemovedEndOffset should be the first end of removed text");
2523 MOZ_ASSERT(
2524 mAddedEndOffset == 35,
2525 "Test 1-1-3: mAddedEndOffset should be the last end of added text");
2526 Clear();
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 "
2535 "removed text "
2536 "with already removed length");
2537 MOZ_ASSERT(
2538 mAddedEndOffset == 10,
2539 "Test 1-2-3: mAddedEndOffset should be the last end of added text");
2540 Clear();
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 "
2549 "removed text "
2550 "with already removed length");
2551 MOZ_ASSERT(
2552 mAddedEndOffset == 10,
2553 "Test 1-3-3: mAddedEndOffset should be the last end of added text");
2554 Clear();
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");
2561 MOZ_ASSERT(
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");
2565 MOZ_ASSERT(
2566 mAddedEndOffset == 60,
2567 "Test 1-4-3: mAddedEndOffset should be the last end of added text");
2568 Clear();
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");
2575 MOZ_ASSERT(
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");
2579 MOZ_ASSERT(
2580 mAddedEndOffset == 55,
2581 "Test 1-5-3: mAddedEndOffset should be the largest end of added text");
2582 Clear();
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");
2589 MOZ_ASSERT(
2590 mRemovedEndOffset == 35, // 32 - (32 - 35)
2591 "Test 1-6-2: mRemovedEndOffset should be the the first end of removed "
2592 "text");
2593 MOZ_ASSERT(
2594 mAddedEndOffset == 40,
2595 "Test 1-6-3: mAddedEndOffset should be the last end of added text");
2596 Clear();
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");
2603 MOZ_ASSERT(
2604 mRemovedEndOffset == 35, // 32 - (32 - 35)
2605 "Test 1-7-2: mRemovedEndOffset should be the the first end of removed "
2606 "text");
2607 MOZ_ASSERT(
2608 mAddedEndOffset == 33,
2609 "Test 1-7-3: mAddedEndOffset should be the last end of added text");
2610 Clear();
2612 // Removing text and replacing text after first range (not sure if actually
2613 // occurs)
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 "
2620 "removed text "
2621 "without already removed text");
2622 MOZ_ASSERT(
2623 mAddedEndOffset == 48,
2624 "Test 1-8-3: mAddedEndOffset should be the last end of added text");
2625 Clear();
2627 // Removing text and replacing text after first range (not sure if actually
2628 // occurs)
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 "
2635 "removed text "
2636 "without already removed text");
2637 MOZ_ASSERT(
2638 mAddedEndOffset == 36,
2639 "Test 1-9-3: mAddedEndOffset should be the last end of added text");
2640 Clear();
2642 /****************************************************************************
2643 * Case 2
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 "
2654 "removed text "
2655 "without already added text length");
2656 MOZ_ASSERT(
2657 mAddedEndOffset == 54,
2658 "Test 2-1-3: mAddedEndOffset should be the last end of added text");
2659 Clear();
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 "
2669 "removed text "
2670 "without already added text length");
2671 MOZ_ASSERT(
2672 mAddedEndOffset == 68,
2673 "Test 2-2-3: mAddedEndOffset should be the last end of added text");
2674 Clear();
2676 // Replacing text around end of replaced text (became shorter) (not sure if
2677 // actually occurs)
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 "
2684 "removed text "
2685 "without already removed text length");
2686 MOZ_ASSERT(
2687 mAddedEndOffset == 49,
2688 "Test 2-3-3: mAddedEndOffset should be the last end of added text");
2689 Clear();
2691 // Replacing text around end of replaced text (became longer) (not sure if
2692 // actually occurs)
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 "
2699 "removed text "
2700 "without already added text length");
2701 MOZ_ASSERT(
2702 mAddedEndOffset == 61,
2703 "Test 2-4-3: mAddedEndOffset should be the last end of added text");
2704 Clear();
2706 /****************************************************************************
2707 * Case 3
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 "
2717 "removed text");
2718 MOZ_ASSERT(
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");
2722 Clear();
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 "
2731 "removed text");
2732 MOZ_ASSERT(
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");
2736 Clear();
2738 // Replacing text in replaced text (became shorter) (not sure if actually
2739 // occurs)
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 "
2746 "removed text");
2747 MOZ_ASSERT(
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");
2751 Clear();
2753 // Replacing text in replaced text (became longer) (not sure if actually
2754 // occurs)
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 "
2761 "removed text "
2762 "without already added text length");
2763 MOZ_ASSERT(
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");
2767 Clear();
2769 // Replacing text in replaced text (became shorter) (not sure if actually
2770 // occurs)
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");
2775 MOZ_ASSERT(
2776 mRemovedEndOffset == 48,
2777 "Test 3-5-2: mRemovedEndOffset should be the the first end of removed "
2778 "text");
2779 MOZ_ASSERT(mAddedEndOffset == 48, // 50 + (47 - 49)
2780 "Test 3-5-3: mAddedEndOffset should be the first end of added "
2781 "text without "
2782 "removed text length by the new change");
2783 Clear();
2785 // Replacing text in replaced text (became longer) (not sure if actually
2786 // occurs)
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 "
2793 "removed text "
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 "
2797 "text without "
2798 "removed text length by the new change");
2799 Clear();
2801 /****************************************************************************
2802 * Case 4
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 "
2812 "removed text "
2813 "without already added text length");
2814 MOZ_ASSERT(
2815 mAddedEndOffset == 68,
2816 "Test 4-1-3: mAddedEndOffset should be the last end of added text");
2817 Clear();
2819 // Replacing text around a point in which text was removed (not sure if
2820 // actually occurs)
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 "
2827 "removed text "
2828 "without already removed text length");
2829 MOZ_ASSERT(
2830 mAddedEndOffset == 68,
2831 "Test 4-2-3: mAddedEndOffset should be the last end of added text");
2832 Clear();
2834 // Replacing text all replaced text (became shorter) (not sure if actually
2835 // occurs)
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 "
2842 "removed text "
2843 "without already removed text length");
2844 MOZ_ASSERT(
2845 mAddedEndOffset == 130,
2846 "Test 4-3-3: mAddedEndOffset should be the last end of added text");
2847 Clear();
2849 // Replacing text all replaced text (became longer) (not sure if actually
2850 // occurs)
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 "
2857 "removed text "
2858 "with already added text length");
2859 MOZ_ASSERT(
2860 mAddedEndOffset == 50,
2861 "Test 4-4-3: mAddedEndOffset should be the last end of added text");
2862 Clear();
2864 /****************************************************************************
2865 * Case 5
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");
2873 MOZ_ASSERT(
2874 mRemovedEndOffset == 50,
2875 "Test 5-1-2: mRemovedEndOffset should be the the first end of removed "
2876 "text");
2877 MOZ_ASSERT(
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");
2881 Clear();
2883 // Replacing text around start of replaced text (became shorter) (not sure if
2884 // actually occurs)
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");
2889 MOZ_ASSERT(
2890 mRemovedEndOffset == 60,
2891 "Test 5-2-2: mRemovedEndOffset should be the the first end of removed "
2892 "text");
2893 MOZ_ASSERT(mAddedEndOffset == 56, // 58 + (48 - 50)
2894 "Test 5-2-3: mAddedEndOffset should be the first end of added "
2895 "text without "
2896 "removed text length by the new change");
2897 Clear();
2899 // Replacing text around start of replaced text (became longer) (not sure if
2900 // actually occurs)
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");
2905 MOZ_ASSERT(
2906 mRemovedEndOffset == 60,
2907 "Test 5-3-2: mRemovedEndOffset should be the the first end of removed "
2908 "text");
2909 MOZ_ASSERT(mAddedEndOffset == 66, // 68 + (53 - 55)
2910 "Test 5-3-3: mAddedEndOffset should be the first end of added "
2911 "text without "
2912 "removed text length by the new change");
2913 Clear();
2915 // Replacing text around start of replaced text (became shorter) (not sure if
2916 // actually occurs)
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");
2921 MOZ_ASSERT(
2922 mRemovedEndOffset == 60,
2923 "Test 5-4-2: mRemovedEndOffset should be the the first end of removed "
2924 "text");
2925 MOZ_ASSERT(
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");
2929 Clear();
2931 // Replacing text around start of replaced text (became longer) (not sure if
2932 // actually occurs)
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");
2937 MOZ_ASSERT(
2938 mRemovedEndOffset == 60,
2939 "Test 5-5-2: mRemovedEndOffset should be the the first end of removed "
2940 "text");
2941 MOZ_ASSERT(
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");
2945 Clear();
2947 /****************************************************************************
2948 * Case 6
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");
2956 MOZ_ASSERT(
2957 mRemovedEndOffset == 30,
2958 "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed "
2959 "text");
2960 MOZ_ASSERT(
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");
2964 Clear();
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");
2971 MOZ_ASSERT(
2972 mRemovedEndOffset == 35,
2973 "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed "
2974 "text");
2975 MOZ_ASSERT(
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");
2979 Clear();
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");
2986 MOZ_ASSERT(
2987 mRemovedEndOffset == 65,
2988 "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed "
2989 "text");
2990 MOZ_ASSERT(mAddedEndOffset == 61, // 70 + (15 - 24)
2991 "Test 6-3-3: mAddedEndOffset should be the first end of added "
2992 "text without "
2993 "removed text length by the new change");
2994 Clear();
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");
3001 MOZ_ASSERT(
3002 mRemovedEndOffset == 65,
3003 "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed "
3004 "text");
3005 MOZ_ASSERT(mAddedEndOffset == 82, // 70 + (36 - 24)
3006 "Test 6-4-3: mAddedEndOffset should be the first end of added "
3007 "text without "
3008 "removed text length by the new change");
3009 Clear();
3012 #endif // #ifdef DEBUG
3014 } // namespace mozilla::widget
3016 #ifdef DEBUG
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
3024 // this.
3026 //////////////////////////////////////////////////////////////
3027 /* static */
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) \
3034 case _value: \
3035 eventName.AssignLiteral(_name); \
3036 break
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
3077 default: {
3078 eventName.AssignLiteral("UNKNOWN: ");
3079 eventName.AppendInt(aGuiEvent->mMessage);
3080 } break;
3083 return nsAutoString(eventName);
3085 //////////////////////////////////////////////////////////////
3087 // Code to deal with paint and event debug prefs.
3089 //////////////////////////////////////////////////////////////
3090 struct PrefPair {
3091 const char* name;
3092 bool value;
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;
3112 return false;
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;
3122 return;
3126 NS_ASSERTION(false, "cmon, this code is not reached dude.");
3129 //////////////////////////////////////////////////////////////
3130 class Debug_PrefObserver final : public nsIObserver {
3131 ~Debug_PrefObserver() = default;
3133 public:
3134 NS_DECL_ISUPPORTS
3135 NS_DECL_NSIOBSERVER
3138 NS_IMPL_ISUPPORTS(Debug_PrefObserver, nsIObserver)
3140 NS_IMETHODIMP
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);
3147 return NS_OK;
3150 //////////////////////////////////////////////////////////////
3151 /* static */ void debug_RegisterPrefCallbacks() {
3152 static bool once = true;
3154 if (!once) {
3155 return;
3158 once = false;
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);
3166 if (obs) {
3167 // Register callbacks for when these change
3168 nsCString name;
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;
3179 return ++sCount;
3181 //////////////////////////////////////////////////////////////
3182 /* static */
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"))
3193 return;
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 //////////////////////////////////////////////////////////////
3206 /* static */
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();
3217 fprintf(aFileOut,
3218 "%4d PAINT widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d "
3219 "%3d,%-3d",
3220 _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID, rect.X(),
3221 rect.Y(), rect.Width(), rect.Height());
3223 fprintf(aFileOut, "\n");
3225 //////////////////////////////////////////////////////////////
3226 /* static */
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);
3239 if (aRect) {
3240 fprintf(aFileOut, " rect=%3d,%-3d %3d,%-3d", aRect->X(), aRect->Y(),
3241 aRect->Width(), aRect->Height());
3242 } else {
3243 fprintf(aFileOut, " rect=%-15s", "none");
3246 fprintf(aFileOut, "\n");
3248 //////////////////////////////////////////////////////////////
3250 #endif // DEBUG