Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / widget / nsBaseWidget.cpp
blobb6fd99bee1c89393fd6015d02cc330125b58c486
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 mFissionWindow(false),
150 mUpdateCursor(true),
151 mUseAttachedEvents(false),
152 mIMEHasFocus(false),
153 mIMEHasQuit(false),
154 mIsFullyOccluded(false),
155 mNeedFastSnaphot(false),
156 mCurrentPanGestureBelongsToSwipe(false) {
157 #ifdef NOISY_WIDGET_LEAKS
158 gNumWidgets++;
159 printf("WIDGETS+ = %d\n", gNumWidgets);
160 #endif
162 #ifdef DEBUG
163 debug_RegisterPrefCallbacks();
164 #endif
166 mShutdownObserver = new WidgetShutdownObserver(this);
169 NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver)
171 WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget)
172 : mWidget(aWidget), mRegistered(false) {
173 Register();
176 WidgetShutdownObserver::~WidgetShutdownObserver() {
177 // No need to call Unregister(), we can't be destroyed until nsBaseWidget
178 // gets torn down. The observer service and nsBaseWidget have a ref on us
179 // so nsBaseWidget has to call Unregister and then clear its ref.
182 NS_IMETHODIMP
183 WidgetShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
184 const char16_t* aData) {
185 if (!mWidget) {
186 return NS_OK;
188 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
189 RefPtr<nsBaseWidget> widget(mWidget);
190 widget->Shutdown();
191 } else if (!strcmp(aTopic, "quit-application")) {
192 RefPtr<nsBaseWidget> widget(mWidget);
193 widget->QuitIME();
195 return NS_OK;
198 void WidgetShutdownObserver::Register() {
199 if (!mRegistered) {
200 mRegistered = true;
201 nsContentUtils::RegisterShutdownObserver(this);
203 #ifndef MOZ_WIDGET_ANDROID
204 // The primary purpose of observing quit-application is
205 // to avoid leaking a widget on Windows when nothing else
206 // breaks the circular reference between the widget and
207 // TSFTextStore. However, our Android IME code crashes if
208 // doing this on Android, so let's not do this on Android.
209 // Doing this on Gtk and Mac just in case.
210 nsCOMPtr<nsIObserverService> observerService =
211 mozilla::services::GetObserverService();
212 if (observerService) {
213 observerService->AddObserver(this, "quit-application", false);
215 #endif
219 void WidgetShutdownObserver::Unregister() {
220 if (mRegistered) {
221 mWidget = nullptr;
223 #ifndef MOZ_WIDGET_ANDROID
224 nsCOMPtr<nsIObserverService> observerService =
225 mozilla::services::GetObserverService();
226 if (observerService) {
227 observerService->RemoveObserver(this, "quit-application");
229 #endif
231 nsContentUtils::UnregisterShutdownObserver(this);
232 mRegistered = false;
236 #define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed"
238 NS_IMPL_ISUPPORTS(LocalesChangedObserver, nsIObserver)
240 LocalesChangedObserver::LocalesChangedObserver(nsBaseWidget* aWidget)
241 : mWidget(aWidget), mRegistered(false) {
242 Register();
245 LocalesChangedObserver::~LocalesChangedObserver() {
246 // No need to call Unregister(), we can't be destroyed until nsBaseWidget
247 // gets torn down. The observer service and nsBaseWidget have a ref on us
248 // so nsBaseWidget has to call Unregister and then clear its ref.
251 NS_IMETHODIMP
252 LocalesChangedObserver::Observe(nsISupports* aSubject, const char* aTopic,
253 const char16_t* aData) {
254 if (!mWidget) {
255 return NS_OK;
257 if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) {
258 RefPtr<nsBaseWidget> widget(mWidget);
259 widget->LocalesChanged();
261 return NS_OK;
264 void LocalesChangedObserver::Register() {
265 if (mRegistered) {
266 return;
269 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
270 if (obs) {
271 obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true);
274 // Locale might be update before registering
275 RefPtr<nsBaseWidget> widget(mWidget);
276 widget->LocalesChanged();
278 mRegistered = true;
281 void LocalesChangedObserver::Unregister() {
282 if (!mRegistered) {
283 return;
286 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
287 if (obs) {
288 obs->RemoveObserver(this, INTL_APP_LOCALES_CHANGED);
291 mWidget = nullptr;
292 mRegistered = false;
295 void nsBaseWidget::Shutdown() {
296 NotifyLiveResizeStopped();
297 RevokeTransactionIdAllocator();
298 DestroyCompositor();
299 FreeLocalesChangedObserver();
300 FreeShutdownObserver();
303 void nsBaseWidget::QuitIME() {
304 IMEStateManager::WidgetOnQuit(this);
305 this->mIMEHasQuit = true;
308 void nsBaseWidget::DestroyCompositor() {
309 // We release this before releasing the compositor, since it may hold the
310 // last reference to our ClientLayerManager. ClientLayerManager's dtor can
311 // trigger a paint, creating a new compositor, and we don't want to re-use
312 // the old vsync dispatcher.
313 if (mCompositorVsyncDispatcher) {
314 MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
316 MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
317 mCompositorVsyncDispatcher->Shutdown();
318 mCompositorVsyncDispatcher = nullptr;
321 // The compositor shutdown sequence looks like this:
322 // 1. CompositorSession calls CompositorBridgeChild::Destroy.
323 // 2. CompositorBridgeChild synchronously sends WillClose.
324 // 3. CompositorBridgeParent releases some resources (such as the layer
325 // manager, compositor, and widget).
326 // 4. CompositorBridgeChild::Destroy returns.
327 // 5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the
328 // compositor thread when the I/O thread closes the IPC channel.
329 // 6. Step 5 will schedule DeferredDestroy on the compositor thread, which
330 // releases the reference CompositorBridgeParent holds to itself.
332 // When CompositorSession::Shutdown returns, we assume the compositor is gone
333 // or will be gone very soon.
334 if (mCompositorSession) {
335 ReleaseContentController();
336 mAPZC = nullptr;
337 SetCompositorWidgetDelegate(nullptr);
338 mCompositorBridgeChild = nullptr;
339 mCompositorSession->Shutdown();
340 mCompositorSession = nullptr;
344 // This prevents the layer manager from starting a new transaction during
345 // shutdown.
346 void nsBaseWidget::RevokeTransactionIdAllocator() {
347 if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) {
348 return;
350 mWindowRenderer->AsWebRender()->SetTransactionIdAllocator(nullptr);
353 void nsBaseWidget::ReleaseContentController() {
354 if (mRootContentController) {
355 mRootContentController->Destroy();
356 mRootContentController = nullptr;
360 void nsBaseWidget::DestroyLayerManager() {
361 if (mWindowRenderer) {
362 mWindowRenderer->Destroy();
363 mWindowRenderer = nullptr;
365 DestroyCompositor();
368 void nsBaseWidget::OnRenderingDeviceReset() { DestroyLayerManager(); }
370 void nsBaseWidget::FreeShutdownObserver() {
371 if (mShutdownObserver) {
372 mShutdownObserver->Unregister();
374 mShutdownObserver = nullptr;
377 void nsBaseWidget::FreeLocalesChangedObserver() {
378 if (mLocalesChangedObserver) {
379 mLocalesChangedObserver->Unregister();
381 mLocalesChangedObserver = nullptr;
384 //-------------------------------------------------------------------------
386 // nsBaseWidget destructor
388 //-------------------------------------------------------------------------
390 nsBaseWidget::~nsBaseWidget() {
391 if (mSwipeTracker) {
392 mSwipeTracker->Destroy();
393 mSwipeTracker = nullptr;
396 IMEStateManager::WidgetDestroyed(this);
398 FreeLocalesChangedObserver();
399 FreeShutdownObserver();
400 RevokeTransactionIdAllocator();
401 DestroyLayerManager();
403 #ifdef NOISY_WIDGET_LEAKS
404 gNumWidgets--;
405 printf("WIDGETS- = %d\n", gNumWidgets);
406 #endif
408 delete mOriginalBounds;
411 //-------------------------------------------------------------------------
413 // Basic create.
415 //-------------------------------------------------------------------------
416 void nsBaseWidget::BaseCreate(nsIWidget* aParent, nsWidgetInitData* aInitData) {
417 // keep a reference to the device context
418 if (nullptr != aInitData) {
419 mWindowType = aInitData->mWindowType;
420 mBorderStyle = aInitData->mBorderStyle;
421 mPopupLevel = aInitData->mPopupLevel;
422 mPopupType = aInitData->mPopupHint;
423 mHasRemoteContent = aInitData->mHasRemoteContent;
424 mFissionWindow = aInitData->mFissionWindow;
427 if (aParent) {
428 aParent->AddChild(this);
432 //-------------------------------------------------------------------------
434 // Accessor functions to get/set the client data
436 //-------------------------------------------------------------------------
438 nsIWidgetListener* nsBaseWidget::GetWidgetListener() const {
439 return mWidgetListener;
442 void nsBaseWidget::SetWidgetListener(nsIWidgetListener* aWidgetListener) {
443 mWidgetListener = aWidgetListener;
446 already_AddRefed<nsIWidget> nsBaseWidget::CreateChild(
447 const LayoutDeviceIntRect& aRect, nsWidgetInitData* aInitData,
448 bool aForceUseIWidgetParent) {
449 nsIWidget* parent = this;
450 nsNativeWidget nativeParent = nullptr;
452 if (!aForceUseIWidgetParent) {
453 // Use only either parent or nativeParent, not both, to match
454 // existing code. Eventually Create() should be divested of its
455 // nativeWidget parameter.
456 nativeParent = parent ? parent->GetNativeData(NS_NATIVE_WIDGET) : nullptr;
457 parent = nativeParent ? nullptr : parent;
458 MOZ_ASSERT(!parent || !nativeParent, "messed up logic");
461 nsCOMPtr<nsIWidget> widget;
462 if (aInitData && aInitData->mWindowType == eWindowType_popup) {
463 widget = AllocateChildPopupWidget();
464 } else {
465 widget = nsIWidget::CreateChildWindow();
468 if (widget && mNeedFastSnaphot) {
469 widget->SetNeedFastSnaphot();
472 if (widget &&
473 NS_SUCCEEDED(widget->Create(parent, nativeParent, aRect, aInitData))) {
474 return widget.forget();
477 return nullptr;
480 // Attach a view to our widget which we'll send events to.
481 void nsBaseWidget::AttachViewToTopLevel(bool aUseAttachedEvents) {
482 NS_ASSERTION((mWindowType == eWindowType_toplevel ||
483 mWindowType == eWindowType_dialog ||
484 mWindowType == eWindowType_invisible ||
485 mWindowType == eWindowType_child),
486 "Can't attach to window of that type");
488 mUseAttachedEvents = aUseAttachedEvents;
491 nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener() const {
492 return mAttachedWidgetListener;
495 nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener() {
496 return mPreviouslyAttachedWidgetListener;
499 void nsBaseWidget::SetPreviouslyAttachedWidgetListener(
500 nsIWidgetListener* aListener) {
501 mPreviouslyAttachedWidgetListener = aListener;
504 void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener) {
505 mAttachedWidgetListener = aListener;
508 //-------------------------------------------------------------------------
510 // Close this nsBaseWidget
512 //-------------------------------------------------------------------------
513 void nsBaseWidget::Destroy() {
514 // Just in case our parent is the only ref to us
515 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
516 // disconnect from the parent
517 nsIWidget* parent = GetParent();
518 if (parent) {
519 parent->RemoveChild(this);
523 //-------------------------------------------------------------------------
525 // Get this nsBaseWidget parent
527 //-------------------------------------------------------------------------
528 nsIWidget* nsBaseWidget::GetParent(void) { return nullptr; }
530 //-------------------------------------------------------------------------
532 // Get this nsBaseWidget top level widget
534 //-------------------------------------------------------------------------
535 nsIWidget* nsBaseWidget::GetTopLevelWidget() {
536 nsIWidget *topLevelWidget = nullptr, *widget = this;
537 while (widget) {
538 topLevelWidget = widget;
539 widget = widget->GetParent();
541 return topLevelWidget;
544 //-------------------------------------------------------------------------
546 // Get this nsBaseWidget's top (non-sheet) parent (if it's a sheet)
548 //-------------------------------------------------------------------------
549 nsIWidget* nsBaseWidget::GetSheetWindowParent(void) { return nullptr; }
551 float nsBaseWidget::GetDPI() { return 96.0f; }
553 CSSToLayoutDeviceScale nsIWidget::GetDefaultScale() {
554 double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx();
556 if (devPixelsPerCSSPixel <= 0.0) {
557 devPixelsPerCSSPixel = GetDefaultScaleInternal();
560 return CSSToLayoutDeviceScale(devPixelsPerCSSPixel);
563 nsIntSize nsIWidget::CustomCursorSize(const Cursor& aCursor) {
564 MOZ_ASSERT(aCursor.IsCustom());
565 int32_t width = 0;
566 int32_t height = 0;
567 aCursor.mContainer->GetWidth(&width);
568 aCursor.mContainer->GetHeight(&height);
569 aCursor.mResolution.ApplyTo(width, height);
570 return {width, height};
573 RefPtr<mozilla::VsyncDispatcher> nsIWidget::GetVsyncDispatcher() {
574 return nullptr;
577 //-------------------------------------------------------------------------
579 // Add a child to the list of children
581 //-------------------------------------------------------------------------
582 void nsBaseWidget::AddChild(nsIWidget* aChild) {
583 MOZ_ASSERT(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
584 "aChild not properly removed from its old child list");
586 if (!mFirstChild) {
587 mFirstChild = mLastChild = aChild;
588 } else {
589 // append to the list
590 MOZ_ASSERT(mLastChild);
591 MOZ_ASSERT(!mLastChild->GetNextSibling());
592 mLastChild->SetNextSibling(aChild);
593 aChild->SetPrevSibling(mLastChild);
594 mLastChild = aChild;
598 //-------------------------------------------------------------------------
600 // Remove a child from the list of children
602 //-------------------------------------------------------------------------
603 void nsBaseWidget::RemoveChild(nsIWidget* aChild) {
604 #ifdef DEBUG
605 # ifdef XP_MACOSX
606 // nsCocoaWindow doesn't implement GetParent, so in that case parent will be
607 // null and we'll just have to do without this assertion.
608 nsIWidget* parent = aChild->GetParent();
609 NS_ASSERTION(!parent || parent == this, "Not one of our kids!");
610 # else
611 MOZ_RELEASE_ASSERT(aChild->GetParent() == this, "Not one of our kids!");
612 # endif
613 #endif
615 if (mLastChild == aChild) {
616 mLastChild = mLastChild->GetPrevSibling();
618 if (mFirstChild == aChild) {
619 mFirstChild = mFirstChild->GetNextSibling();
622 // Now remove from the list. Make sure that we pass ownership of the tail
623 // of the list correctly before we have aChild let go of it.
624 nsIWidget* prev = aChild->GetPrevSibling();
625 nsIWidget* next = aChild->GetNextSibling();
626 if (prev) {
627 prev->SetNextSibling(next);
629 if (next) {
630 next->SetPrevSibling(prev);
633 aChild->SetNextSibling(nullptr);
634 aChild->SetPrevSibling(nullptr);
637 //-------------------------------------------------------------------------
639 // Sets widget's position within its parent's child list.
641 //-------------------------------------------------------------------------
642 void nsBaseWidget::SetZIndex(int32_t aZIndex) {
643 // Hold a ref to ourselves just in case, since we're going to remove
644 // from our parent.
645 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
647 mZIndex = aZIndex;
649 // reorder this child in its parent's list.
650 auto* parent = static_cast<nsBaseWidget*>(GetParent());
651 if (parent) {
652 parent->RemoveChild(this);
653 // Scope sib outside the for loop so we can check it afterward
654 nsIWidget* sib = parent->GetFirstChild();
655 for (; sib; sib = sib->GetNextSibling()) {
656 int32_t childZIndex = GetZIndex();
657 if (aZIndex < childZIndex) {
658 // Insert ourselves before sib
659 nsIWidget* prev = sib->GetPrevSibling();
660 mNextSibling = sib;
661 mPrevSibling = prev;
662 sib->SetPrevSibling(this);
663 if (prev) {
664 prev->SetNextSibling(this);
665 } else {
666 NS_ASSERTION(sib == parent->mFirstChild, "Broken child list");
667 // We've taken ownership of sib, so it's safe to have parent let
668 // go of it
669 parent->mFirstChild = this;
671 PlaceBehind(eZPlacementBelow, sib, false);
672 break;
675 // were we added to the list?
676 if (!sib) {
677 parent->AddChild(this);
682 void nsBaseWidget::GetWorkspaceID(nsAString& workspaceID) {
683 workspaceID.Truncate();
686 void nsBaseWidget::MoveToWorkspace(const nsAString& workspaceID) {
687 // Noop.
690 //-------------------------------------------------------------------------
692 // Get this component cursor
694 //-------------------------------------------------------------------------
696 void nsBaseWidget::SetCursor(const Cursor& aCursor) { mCursor = aCursor; }
698 //-------------------------------------------------------------------------
700 // Window transparency methods
702 //-------------------------------------------------------------------------
704 void nsBaseWidget::SetTransparencyMode(nsTransparencyMode aMode) {}
706 nsTransparencyMode nsBaseWidget::GetTransparencyMode() {
707 return eTransparencyOpaque;
710 /* virtual */
711 void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage,
712 uint16_t aDuration,
713 nsISupports* aData,
714 nsIRunnable* aCallback) {
715 MOZ_ASSERT_UNREACHABLE(
716 "Should never call PerformFullscreenTransition on nsBaseWidget");
719 //-------------------------------------------------------------------------
721 // Put the window into full-screen mode
723 //-------------------------------------------------------------------------
724 void nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen) {
725 HideWindowChrome(aFullScreen);
727 if (aFullScreen) {
728 if (!mOriginalBounds) {
729 mOriginalBounds = new LayoutDeviceIntRect();
731 *mOriginalBounds = GetScreenBounds();
733 // Move to top-left corner of screen and size to the screen dimensions
734 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
735 if (screen) {
736 int32_t left, top, width, height;
737 if (NS_SUCCEEDED(
738 screen->GetRectDisplayPix(&left, &top, &width, &height))) {
739 Resize(left, top, width, height, true);
742 } else if (mOriginalBounds) {
743 if (BoundsUseDesktopPixels()) {
744 DesktopRect deskRect = *mOriginalBounds / GetDesktopToDeviceScale();
745 Resize(deskRect.X(), deskRect.Y(), deskRect.Width(), deskRect.Height(),
746 true);
747 } else {
748 Resize(mOriginalBounds->X(), mOriginalBounds->Y(),
749 mOriginalBounds->Width(), mOriginalBounds->Height(), true);
754 nsresult nsBaseWidget::MakeFullScreen(bool aFullScreen) {
755 InfallibleMakeFullScreen(aFullScreen);
756 return NS_OK;
759 nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
760 nsBaseWidget* aWidget, gfxContext* aTarget, BufferMode aDoubleBuffering)
761 : mWidget(aWidget) {
762 WindowRenderer* renderer = mWidget->GetWindowRenderer();
763 if (renderer->AsFallback()) {
764 mRenderer = renderer->AsFallback();
765 mRenderer->SetTarget(aTarget, aDoubleBuffering);
769 nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() {
770 if (mRenderer) {
771 mRenderer->SetTarget(nullptr, mozilla::layers::BufferMode::BUFFER_NONE);
775 bool nsBaseWidget::IsSmallPopup() const {
776 return mWindowType == eWindowType_popup && mPopupType != ePopupTypePanel;
779 bool nsBaseWidget::ComputeShouldAccelerate() {
780 return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
781 (WidgetTypeSupportsAcceleration() ||
782 StaticPrefs::gfx_webrender_unaccelerated_widget_force());
785 bool nsBaseWidget::UseAPZ() {
786 return (gfxPlatform::AsyncPanZoomEnabled() &&
787 (WindowType() == eWindowType_toplevel ||
788 WindowType() == eWindowType_child ||
789 ((WindowType() == eWindowType_popup ||
790 WindowType() == eWindowType_dialog) &&
791 HasRemoteContent() && StaticPrefs::apz_popups_enabled())));
794 void nsBaseWidget::CreateCompositor() {
795 LayoutDeviceIntRect rect = GetBounds();
796 CreateCompositor(rect.Width(), rect.Height());
799 already_AddRefed<GeckoContentController>
800 nsBaseWidget::CreateRootContentController() {
801 RefPtr<GeckoContentController> controller =
802 new ChromeProcessController(this, mAPZEventState, mAPZC);
803 return controller.forget();
806 void nsBaseWidget::ConfigureAPZCTreeManager() {
807 MOZ_ASSERT(NS_IsMainThread());
808 MOZ_ASSERT(mAPZC);
810 mAPZC->SetDPI(GetDPI());
812 if (StaticPrefs::apz_keyboard_enabled_AtStartup()) {
813 KeyboardMap map = RootWindowGlobalKeyListener::CollectKeyboardShortcuts();
814 mAPZC->SetKeyboardMap(map);
817 ContentReceivedInputBlockCallback callback(
818 [treeManager = RefPtr{mAPZC.get()}](uint64_t aInputBlockId,
819 bool aPreventDefault) {
820 MOZ_ASSERT(NS_IsMainThread());
821 treeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
823 mAPZEventState = new APZEventState(this, std::move(callback));
825 mRootContentController = CreateRootContentController();
826 if (mRootContentController) {
827 mCompositorSession->SetContentController(mRootContentController);
830 // When APZ is enabled, we can actually enable raw touch events because we
831 // have code that can deal with them properly. If APZ is not enabled, this
832 // function doesn't get called.
833 if (StaticPrefs::dom_w3c_touch_events_enabled()) {
834 RegisterTouchWindow();
838 void nsBaseWidget::ConfigureAPZControllerThread() {
839 // By default the controller thread is the main thread.
840 APZThreadUtils::SetControllerThread(NS_GetCurrentThread());
843 void nsBaseWidget::SetConfirmedTargetAPZC(
844 uint64_t aInputBlockId,
845 const nsTArray<ScrollableLayerGuid>& aTargets) const {
846 mAPZC->SetTargetAPZC(aInputBlockId, aTargets);
849 void nsBaseWidget::UpdateZoomConstraints(
850 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
851 const Maybe<ZoomConstraints>& aConstraints) {
852 if (!mCompositorSession || !mAPZC) {
853 if (mInitialZoomConstraints) {
854 MOZ_ASSERT(mInitialZoomConstraints->mPresShellID == aPresShellId);
855 MOZ_ASSERT(mInitialZoomConstraints->mViewID == aViewId);
856 if (!aConstraints) {
857 mInitialZoomConstraints.reset();
861 if (aConstraints) {
862 // We have some constraints, but the compositor and APZC aren't created
863 // yet. Save these so we can use them later.
864 mInitialZoomConstraints = Some(
865 InitialZoomConstraints(aPresShellId, aViewId, aConstraints.ref()));
867 return;
869 LayersId layersId = mCompositorSession->RootLayerTreeId();
870 mAPZC->UpdateZoomConstraints(
871 ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints);
874 bool nsBaseWidget::AsyncPanZoomEnabled() const { return !!mAPZC; }
876 nsEventStatus nsBaseWidget::ProcessUntransformedAPZEvent(
877 WidgetInputEvent* aEvent, const APZEventResult& aApzResult) {
878 MOZ_ASSERT(NS_IsMainThread());
879 ScrollableLayerGuid targetGuid = aApzResult.mTargetGuid;
880 uint64_t inputBlockId = aApzResult.mInputBlockId;
881 InputAPZContext context(aApzResult.mTargetGuid, inputBlockId,
882 aApzResult.GetStatus());
884 // Make a copy of the original event for the APZCCallbackHelper helpers that
885 // we call later, because the event passed to DispatchEvent can get mutated in
886 // ways that we don't want (i.e. touch points can get stripped out).
887 nsEventStatus status;
888 UniquePtr<WidgetEvent> original(aEvent->Duplicate());
889 DispatchEvent(aEvent, status);
891 if (mAPZC && !InputAPZContext::WasRoutedToChildProcess() && inputBlockId) {
892 // EventStateManager did not route the event into the child process.
893 // It's safe to communicate to APZ that the event has been processed.
894 // Note that here aGuid.mLayersId might be different from
895 // mCompositorSession->RootLayerTreeId() because the event might have gotten
896 // hit-tested by APZ to be targeted at a child process, but a parent process
897 // event listener called preventDefault on it. In that case aGuid.mLayersId
898 // would still be the layers id for the child process, but the event would
899 // not have actually gotten routed to the child process. The main-thread
900 // hit-test result therefore needs to use the parent process layers id.
901 LayersId rootLayersId = mCompositorSession->RootLayerTreeId();
903 RefPtr<DisplayportSetListener> postLayerization;
904 if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
905 nsTArray<TouchBehaviorFlags> allowedTouchBehaviors;
906 if (touchEvent->mMessage == eTouchStart) {
907 auto& originalEvent = *original->AsTouchEvent();
908 MOZ_ASSERT(NS_IsMainThread());
909 allowedTouchBehaviors = TouchActionHelper::GetAllowedTouchBehavior(
910 this, GetDocument(), originalEvent);
911 if (!allowedTouchBehaviors.IsEmpty()) {
912 mAPZC->SetAllowedTouchBehavior(inputBlockId, allowedTouchBehaviors);
914 postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
915 this, GetDocument(), originalEvent, rootLayersId, inputBlockId);
917 mAPZEventState->ProcessTouchEvent(*touchEvent, targetGuid, inputBlockId,
918 aApzResult.GetStatus(), status,
919 std::move(allowedTouchBehaviors));
920 } else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
921 MOZ_ASSERT(wheelEvent->mFlags.mHandledByAPZ);
922 postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
923 this, GetDocument(), *original->AsWheelEvent(), rootLayersId,
924 inputBlockId);
925 if (wheelEvent->mCanTriggerSwipe) {
926 ReportSwipeStarted(inputBlockId, wheelEvent->TriggersSwipe());
928 mAPZEventState->ProcessWheelEvent(*wheelEvent, inputBlockId);
929 } else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
930 MOZ_ASSERT(mouseEvent->mFlags.mHandledByAPZ);
931 postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
932 this, GetDocument(), *original->AsMouseEvent(), rootLayersId,
933 inputBlockId);
934 mAPZEventState->ProcessMouseEvent(*mouseEvent, inputBlockId);
936 if (postLayerization) {
937 postLayerization->Register();
941 return status;
944 template <class InputType, class EventType>
945 class DispatchEventOnMainThread : public Runnable {
946 public:
947 DispatchEventOnMainThread(const InputType& aInput, nsBaseWidget* aWidget,
948 const APZEventResult& aAPZResult)
949 : mozilla::Runnable("DispatchEventOnMainThread"),
950 mInput(aInput),
951 mWidget(aWidget),
952 mAPZResult(aAPZResult) {}
954 NS_IMETHOD Run() override {
955 EventType event = mInput.ToWidgetEvent(mWidget);
956 mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult);
957 return NS_OK;
960 private:
961 InputType mInput;
962 nsBaseWidget* mWidget;
963 APZEventResult mAPZResult;
966 template <class InputType, class EventType>
967 class DispatchInputOnControllerThread : public Runnable {
968 public:
969 DispatchInputOnControllerThread(const EventType& aEvent,
970 IAPZCTreeManager* aAPZC,
971 nsBaseWidget* aWidget)
972 : mozilla::Runnable("DispatchInputOnControllerThread"),
973 mMainMessageLoop(MessageLoop::current()),
974 mInput(aEvent),
975 mAPZC(aAPZC),
976 mWidget(aWidget) {}
978 NS_IMETHOD Run() override {
979 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(mInput);
980 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
981 return NS_OK;
983 RefPtr<Runnable> r = new DispatchEventOnMainThread<InputType, EventType>(
984 mInput, mWidget, result);
985 mMainMessageLoop->PostTask(r.forget());
986 return NS_OK;
989 private:
990 MessageLoop* mMainMessageLoop;
991 InputType mInput;
992 RefPtr<IAPZCTreeManager> mAPZC;
993 nsBaseWidget* mWidget;
996 void nsBaseWidget::DispatchTouchInput(MultiTouchInput& aInput,
997 uint16_t aInputSource) {
998 MOZ_ASSERT(NS_IsMainThread());
999 MOZ_ASSERT(aInputSource ==
1000 mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH ||
1001 aInputSource == mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN);
1002 if (mAPZC) {
1003 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1005 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
1006 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1007 return;
1010 WidgetTouchEvent event = aInput.ToWidgetEvent(this, aInputSource);
1011 ProcessUntransformedAPZEvent(&event, result);
1012 } else {
1013 WidgetTouchEvent event = aInput.ToWidgetEvent(this, aInputSource);
1015 nsEventStatus status;
1016 DispatchEvent(&event, status);
1020 void nsBaseWidget::DispatchPanGestureInput(PanGestureInput& aInput) {
1021 MOZ_ASSERT(NS_IsMainThread());
1022 if (mAPZC) {
1023 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1025 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
1026 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1027 return;
1030 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1031 ProcessUntransformedAPZEvent(&event, result);
1032 } else {
1033 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1034 nsEventStatus status;
1035 DispatchEvent(&event, status);
1039 void nsBaseWidget::DispatchPinchGestureInput(PinchGestureInput& aInput) {
1040 MOZ_ASSERT(NS_IsMainThread());
1041 if (mAPZC) {
1042 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1043 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput);
1045 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1046 return;
1048 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1049 ProcessUntransformedAPZEvent(&event, result);
1050 } else {
1051 WidgetWheelEvent event = aInput.ToWidgetEvent(this);
1052 nsEventStatus status;
1053 DispatchEvent(&event, status);
1057 nsIWidget::ContentAndAPZEventStatus nsBaseWidget::DispatchInputEvent(
1058 WidgetInputEvent* aEvent) {
1059 nsIWidget::ContentAndAPZEventStatus status;
1060 MOZ_ASSERT(NS_IsMainThread());
1061 if (mAPZC) {
1062 if (APZThreadUtils::IsControllerThread()) {
1063 APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(*aEvent);
1064 status.mApzStatus = result.GetStatus();
1065 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
1066 return status;
1068 status.mContentStatus = ProcessUntransformedAPZEvent(aEvent, result);
1069 return status;
1071 if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
1072 RefPtr<Runnable> r =
1073 new DispatchInputOnControllerThread<ScrollWheelInput,
1074 WidgetWheelEvent>(*wheelEvent,
1075 mAPZC, this);
1076 APZThreadUtils::RunOnControllerThread(std::move(r));
1077 status.mContentStatus = nsEventStatus_eConsumeDoDefault;
1078 return status;
1080 if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
1081 RefPtr<Runnable> r =
1082 new DispatchInputOnControllerThread<MouseInput, WidgetMouseEvent>(
1083 *mouseEvent, mAPZC, this);
1084 APZThreadUtils::RunOnControllerThread(std::move(r));
1085 status.mContentStatus = nsEventStatus_eConsumeDoDefault;
1086 return status;
1088 if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
1089 RefPtr<Runnable> r =
1090 new DispatchInputOnControllerThread<MultiTouchInput,
1091 WidgetTouchEvent>(*touchEvent,
1092 mAPZC, this);
1093 APZThreadUtils::RunOnControllerThread(std::move(r));
1094 status.mContentStatus = nsEventStatus_eConsumeDoDefault;
1095 return status;
1097 // Allow dispatching keyboard events on Gecko thread.
1098 MOZ_ASSERT(aEvent->AsKeyboardEvent());
1101 DispatchEvent(aEvent, status.mContentStatus);
1102 return status;
1105 void nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) {
1106 MOZ_ASSERT(NS_IsMainThread());
1107 if (mAPZC) {
1108 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1109 mAPZC->InputBridge()->ReceiveInputEvent(*aEvent);
1113 bool nsBaseWidget::DispatchWindowEvent(WidgetGUIEvent& event) {
1114 nsEventStatus status;
1115 DispatchEvent(&event, status);
1116 return ConvertStatus(status);
1119 Document* nsBaseWidget::GetDocument() const {
1120 if (mWidgetListener) {
1121 if (PresShell* presShell = mWidgetListener->GetPresShell()) {
1122 return presShell->GetDocument();
1125 return nullptr;
1128 void nsBaseWidget::CreateCompositorVsyncDispatcher() {
1129 // Parent directly listens to the vsync source whereas
1130 // child process communicate via IPC
1131 // Should be called AFTER gfxPlatform is initialized
1132 if (XRE_IsParentProcess()) {
1133 if (!mCompositorVsyncDispatcherLock) {
1134 mCompositorVsyncDispatcherLock =
1135 MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
1137 MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
1138 if (!mCompositorVsyncDispatcher) {
1139 RefPtr<VsyncDispatcher> vsyncDispatcher =
1140 gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher();
1141 mCompositorVsyncDispatcher =
1142 new CompositorVsyncDispatcher(std::move(vsyncDispatcher));
1147 already_AddRefed<CompositorVsyncDispatcher>
1148 nsBaseWidget::GetCompositorVsyncDispatcher() {
1149 MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
1151 MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
1152 RefPtr<CompositorVsyncDispatcher> dispatcher = mCompositorVsyncDispatcher;
1153 return dispatcher.forget();
1156 already_AddRefed<WebRenderLayerManager> nsBaseWidget::CreateCompositorSession(
1157 int aWidth, int aHeight, CompositorOptions* aOptionsOut) {
1158 MOZ_ASSERT(aOptionsOut);
1160 do {
1161 CreateCompositorVsyncDispatcher();
1163 gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
1164 // Make sure GPU process is ready for use.
1165 // If it failed to connect to GPU process, GPU process usage is disabled in
1166 // EnsureGPUReady(). It could update gfxVars and gfxConfigs.
1167 gpu->EnsureGPUReady();
1169 // If widget type does not supports acceleration, we may be allowed to use
1170 // software WebRender instead. If not, then we use ClientLayerManager even
1171 // when gfxVars::UseWebRender() is true. WebRender could coexist only with
1172 // BasicCompositor.
1173 bool supportsAcceleration = WidgetTypeSupportsAcceleration();
1174 bool enableWR;
1175 bool enableSWWR;
1176 if (supportsAcceleration ||
1177 StaticPrefs::gfx_webrender_unaccelerated_widget_force()) {
1178 enableWR = gfx::gfxVars::UseWebRender();
1179 enableSWWR = gfx::gfxVars::UseSoftwareWebRender();
1180 } else {
1181 enableWR = enableSWWR = gfx::gfxVars::UseWebRender();
1183 MOZ_RELEASE_ASSERT(enableWR);
1184 bool enableAPZ = UseAPZ();
1185 CompositorOptions options(enableAPZ, enableSWWR);
1187 #ifdef XP_WIN
1188 if (supportsAcceleration) {
1189 options.SetAllowSoftwareWebRenderD3D11(
1190 gfx::gfxVars::AllowSoftwareWebRenderD3D11());
1192 if (mNeedFastSnaphot) {
1193 options.SetNeedFastSnaphot(true);
1195 #elif defined(MOZ_WIDGET_ANDROID)
1196 MOZ_ASSERT(supportsAcceleration);
1197 options.SetAllowSoftwareWebRenderOGL(
1198 StaticPrefs::gfx_webrender_software_opengl_AtStartup());
1199 #elif defined(MOZ_WIDGET_GTK)
1200 if (supportsAcceleration) {
1201 options.SetAllowSoftwareWebRenderOGL(
1202 StaticPrefs::gfx_webrender_software_opengl_AtStartup());
1204 #endif
1206 #ifdef MOZ_WIDGET_ANDROID
1207 // Unconditionally set the compositor as initially paused, as we have not
1208 // yet had a chance to send the compositor surface to the GPU process. We
1209 // will do so shortly once we have returned to nsWindow::CreateLayerManager,
1210 // where we will also resume the compositor if required.
1211 options.SetInitiallyPaused(true);
1212 #else
1213 options.SetInitiallyPaused(CompositorInitiallyPaused());
1214 #endif
1216 RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this);
1218 uint64_t innerWindowId = 0;
1219 if (Document* doc = GetDocument()) {
1220 innerWindowId = doc->InnerWindowID();
1223 bool retry = false;
1224 mCompositorSession = gpu->CreateTopLevelCompositor(
1225 this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(),
1226 gfx::IntSize(aWidth, aHeight), innerWindowId, &retry);
1228 if (mCompositorSession) {
1229 TextureFactoryIdentifier textureFactoryIdentifier;
1230 nsCString error;
1231 lm->Initialize(mCompositorSession->GetCompositorBridgeChild(),
1232 wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
1233 &textureFactoryIdentifier, error);
1234 if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) {
1235 retry = true;
1236 DestroyCompositor();
1237 // gfxVars::UseDoubleBufferingWithCompositor() is also disabled.
1238 gfx::GPUProcessManager::Get()->DisableWebRender(
1239 wr::WebRenderError::INITIALIZE, error);
1243 // We need to retry in a loop because the act of failing to create the
1244 // compositor can change our state (e.g. disable WebRender).
1245 if (mCompositorSession || !retry) {
1246 *aOptionsOut = options;
1247 return lm.forget();
1249 } while (true);
1252 void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) {
1253 // This makes sure that gfxPlatforms gets initialized if it hasn't by now.
1254 gfxPlatform::GetPlatform();
1256 MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
1257 "This function assumes OMTC");
1259 MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild,
1260 "Should have properly cleaned up the previous PCompositor pair "
1261 "beforehand");
1263 if (mCompositorBridgeChild) {
1264 mCompositorBridgeChild->Destroy();
1267 // Recreating this is tricky, as we may still have an old and we need
1268 // to make sure it's properly destroyed by calling DestroyCompositor!
1270 // If we've already received a shutdown notification, don't try
1271 // create a new compositor.
1272 if (!mShutdownObserver) {
1273 return;
1276 // The controller thread must be configured before the compositor
1277 // session is created, so that the input bridge runs on the right
1278 // thread.
1279 ConfigureAPZControllerThread();
1281 CompositorOptions options;
1282 RefPtr<WebRenderLayerManager> lm =
1283 CreateCompositorSession(aWidth, aHeight, &options);
1284 if (!lm) {
1285 return;
1288 MOZ_ASSERT(mCompositorSession);
1289 mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild();
1290 SetCompositorWidgetDelegate(
1291 mCompositorSession->GetCompositorWidgetDelegate());
1293 if (options.UseAPZ()) {
1294 mAPZC = mCompositorSession->GetAPZCTreeManager();
1295 ConfigureAPZCTreeManager();
1296 } else {
1297 mAPZC = nullptr;
1300 if (mInitialZoomConstraints) {
1301 UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID,
1302 mInitialZoomConstraints->mViewID,
1303 Some(mInitialZoomConstraints->mConstraints));
1304 mInitialZoomConstraints.reset();
1307 TextureFactoryIdentifier textureFactoryIdentifier =
1308 lm->GetTextureFactoryIdentifier();
1309 MOZ_ASSERT(textureFactoryIdentifier.mParentBackend ==
1310 LayersBackend::LAYERS_WR);
1311 ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
1312 gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
1314 WindowUsesOMTC();
1316 mWindowRenderer = std::move(lm);
1318 // Only track compositors for top-level windows, since other window types
1319 // may use the basic compositor. Except on the OS X - see bug 1306383
1320 #if defined(XP_MACOSX)
1321 bool getCompositorFromThisWindow = true;
1322 #else
1323 bool getCompositorFromThisWindow = (mWindowType == eWindowType_toplevel);
1324 #endif
1326 if (getCompositorFromThisWindow) {
1327 gfxPlatform::GetPlatform()->NotifyCompositorCreated(
1328 mWindowRenderer->GetCompositorBackendType());
1332 void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession* aSession) {
1333 MOZ_ASSERT(aSession == mCompositorSession);
1334 DestroyLayerManager();
1337 bool nsBaseWidget::ShouldUseOffMainThreadCompositing() {
1338 return gfxPlatform::UsesOffMainThreadCompositing();
1341 WindowRenderer* nsBaseWidget::GetWindowRenderer() {
1342 if (!mWindowRenderer) {
1343 if (!mShutdownObserver) {
1344 // We are shutting down, do not try to re-create a LayerManager
1345 return nullptr;
1347 // Try to use an async compositor first, if possible
1348 if (ShouldUseOffMainThreadCompositing()) {
1349 CreateCompositor();
1352 if (!mWindowRenderer) {
1353 mWindowRenderer = CreateFallbackRenderer();
1356 return mWindowRenderer;
1359 WindowRenderer* nsBaseWidget::CreateFallbackRenderer() {
1360 return new FallbackRenderer;
1363 CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer() {
1364 return mCompositorBridgeChild;
1367 void nsBaseWidget::ClearCachedWebrenderResources() {
1368 if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) {
1369 return;
1371 mWindowRenderer->AsWebRender()->ClearCachedResources();
1374 bool nsBaseWidget::SetNeedFastSnaphot() {
1375 MOZ_ASSERT(XRE_IsParentProcess());
1376 MOZ_ASSERT(!mCompositorSession);
1378 if (!XRE_IsParentProcess() || mCompositorSession) {
1379 return false;
1382 mNeedFastSnaphot = true;
1383 return true;
1386 already_AddRefed<gfx::DrawTarget> nsBaseWidget::StartRemoteDrawing() {
1387 return nullptr;
1390 uint32_t nsBaseWidget::GetGLFrameBufferFormat() { return LOCAL_GL_RGBA; }
1392 //-------------------------------------------------------------------------
1394 // Destroy the window
1396 //-------------------------------------------------------------------------
1397 void nsBaseWidget::OnDestroy() {
1398 if (mTextEventDispatcher) {
1399 mTextEventDispatcher->OnDestroyWidget();
1400 // Don't release it until this widget actually released because after this
1401 // is called, TextEventDispatcher() may create it again.
1404 // If this widget is being destroyed, let the APZ code know to drop references
1405 // to this widget. Callers of this function all should be holding a deathgrip
1406 // on this widget already.
1407 ReleaseContentController();
1410 void nsBaseWidget::MoveClient(const DesktopPoint& aOffset) {
1411 LayoutDeviceIntPoint clientOffset(GetClientOffset());
1413 // GetClientOffset returns device pixels; scale back to desktop pixels
1414 // if that's what this widget uses for the Move/Resize APIs
1415 if (BoundsUseDesktopPixels()) {
1416 DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale();
1417 Move(aOffset.x - desktopOffset.x, aOffset.y - desktopOffset.y);
1418 } else {
1419 LayoutDevicePoint layoutOffset = aOffset * GetDesktopToDeviceScale();
1420 Move(layoutOffset.x - clientOffset.x, layoutOffset.y - clientOffset.y);
1424 void nsBaseWidget::ResizeClient(const DesktopSize& aSize, bool aRepaint) {
1425 NS_ASSERTION((aSize.width >= 0), "Negative width passed to ResizeClient");
1426 NS_ASSERTION((aSize.height >= 0), "Negative height passed to ResizeClient");
1428 LayoutDeviceIntRect clientBounds = GetClientBounds();
1430 // GetClientBounds and mBounds are device pixels; scale back to desktop pixels
1431 // if that's what this widget uses for the Move/Resize APIs
1432 if (BoundsUseDesktopPixels()) {
1433 DesktopSize desktopDelta =
1434 (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
1435 clientBounds.Size()) /
1436 GetDesktopToDeviceScale();
1437 Resize(aSize.width + desktopDelta.width, aSize.height + desktopDelta.height,
1438 aRepaint);
1439 } else {
1440 LayoutDeviceSize layoutSize = aSize * GetDesktopToDeviceScale();
1441 Resize(mBounds.Width() + (layoutSize.width - clientBounds.Width()),
1442 mBounds.Height() + (layoutSize.height - clientBounds.Height()),
1443 aRepaint);
1447 void nsBaseWidget::ResizeClient(const DesktopRect& aRect, bool aRepaint) {
1448 NS_ASSERTION((aRect.Width() >= 0), "Negative width passed to ResizeClient");
1449 NS_ASSERTION((aRect.Height() >= 0), "Negative height passed to ResizeClient");
1451 LayoutDeviceIntRect clientBounds = GetClientBounds();
1452 LayoutDeviceIntPoint clientOffset = GetClientOffset();
1453 DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale();
1455 if (BoundsUseDesktopPixels()) {
1456 DesktopPoint desktopOffset = clientOffset / scale;
1457 DesktopSize desktopDelta =
1458 (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
1459 clientBounds.Size()) /
1460 scale;
1461 Resize(aRect.X() - desktopOffset.x, aRect.Y() - desktopOffset.y,
1462 aRect.Width() + desktopDelta.width,
1463 aRect.Height() + desktopDelta.height, aRepaint);
1464 } else {
1465 LayoutDeviceRect layoutRect = aRect * scale;
1466 Resize(layoutRect.X() - clientOffset.x, layoutRect.Y() - clientOffset.y,
1467 layoutRect.Width() + mBounds.Width() - clientBounds.Width(),
1468 layoutRect.Height() + mBounds.Height() - clientBounds.Height(),
1469 aRepaint);
1473 //-------------------------------------------------------------------------
1475 // Bounds
1477 //-------------------------------------------------------------------------
1480 * If the implementation of nsWindow supports borders this method MUST be
1481 * overridden
1484 LayoutDeviceIntRect nsBaseWidget::GetClientBounds() { return GetBounds(); }
1487 * If the implementation of nsWindow supports borders this method MUST be
1488 * overridden
1491 LayoutDeviceIntRect nsBaseWidget::GetBounds() { return mBounds; }
1494 * If the implementation of nsWindow uses a local coordinate system within the
1495 *window, this method must be overridden
1498 LayoutDeviceIntRect nsBaseWidget::GetScreenBounds() { return GetBounds(); }
1500 nsresult nsBaseWidget::GetRestoredBounds(LayoutDeviceIntRect& aRect) {
1501 if (SizeMode() != nsSizeMode_Normal) {
1502 return NS_ERROR_FAILURE;
1504 aRect = GetScreenBounds();
1505 return NS_OK;
1508 LayoutDeviceIntPoint nsBaseWidget::GetClientOffset() {
1509 return LayoutDeviceIntPoint(0, 0);
1512 nsresult nsBaseWidget::SetNonClientMargins(LayoutDeviceIntMargin& margins) {
1513 return NS_ERROR_NOT_IMPLEMENTED;
1516 void nsBaseWidget::SetResizeMargin(LayoutDeviceIntCoord aResizeMargin) {}
1518 uint32_t nsBaseWidget::GetMaxTouchPoints() const { return 0; }
1520 bool nsBaseWidget::HasPendingInputEvent() { return false; }
1522 bool nsBaseWidget::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) {
1523 return false;
1527 * Modifies aFile to point at an icon file with the given name and suffix. The
1528 * suffix may correspond to a file extension with leading '.' if appropriate.
1529 * Returns true if the icon file exists and can be read.
1531 static bool ResolveIconNameHelper(nsIFile* aFile, const nsAString& aIconName,
1532 const nsAString& aIconSuffix) {
1533 aFile->Append(u"icons"_ns);
1534 aFile->Append(u"default"_ns);
1535 aFile->Append(aIconName + aIconSuffix);
1537 bool readable;
1538 return NS_SUCCEEDED(aFile->IsReadable(&readable)) && readable;
1542 * Resolve the given icon name into a local file object. This method is
1543 * intended to be called by subclasses of nsBaseWidget. aIconSuffix is a
1544 * platform specific icon file suffix (e.g., ".ico" under Win32).
1546 * If no file is found matching the given parameters, then null is returned.
1548 void nsBaseWidget::ResolveIconName(const nsAString& aIconName,
1549 const nsAString& aIconSuffix,
1550 nsIFile** aResult) {
1551 *aResult = nullptr;
1553 nsCOMPtr<nsIProperties> dirSvc =
1554 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1555 if (!dirSvc) return;
1557 // first check auxilary chrome directories
1559 nsCOMPtr<nsISimpleEnumerator> dirs;
1560 dirSvc->Get(NS_APP_CHROME_DIR_LIST, NS_GET_IID(nsISimpleEnumerator),
1561 getter_AddRefs(dirs));
1562 if (dirs) {
1563 bool hasMore;
1564 while (NS_SUCCEEDED(dirs->HasMoreElements(&hasMore)) && hasMore) {
1565 nsCOMPtr<nsISupports> element;
1566 dirs->GetNext(getter_AddRefs(element));
1567 if (!element) continue;
1568 nsCOMPtr<nsIFile> file = do_QueryInterface(element);
1569 if (!file) continue;
1570 if (ResolveIconNameHelper(file, aIconName, aIconSuffix)) {
1571 NS_ADDREF(*aResult = file);
1572 return;
1577 // then check the main app chrome directory
1579 nsCOMPtr<nsIFile> file;
1580 dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(file));
1581 if (file && ResolveIconNameHelper(file, aIconName, aIconSuffix))
1582 NS_ADDREF(*aResult = file);
1585 void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints) {
1586 mSizeConstraints = aConstraints;
1588 // Popups are constrained during layout, and we don't want to synchronously
1589 // paint from reflow, so bail out... This is not great, but it's no worse than
1590 // what we used to do.
1592 // The right fix here is probably making constraint changes go through the
1593 // view manager and such.
1594 if (mWindowType == eWindowType_popup) {
1595 return;
1598 // If the current size doesn't meet the new constraints, trigger a
1599 // resize to apply it. Note that, we don't want to invoke Resize if
1600 // the new constraints don't affect the current size, because Resize
1601 // implementation on some platforms may touch other geometry even if
1602 // the size don't need to change.
1603 LayoutDeviceIntSize curSize = mBounds.Size();
1604 LayoutDeviceIntSize clampedSize =
1605 Max(aConstraints.mMinSize, Min(aConstraints.mMaxSize, curSize));
1606 if (clampedSize != curSize) {
1607 gfx::Size size;
1608 if (BoundsUseDesktopPixels()) {
1609 DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale();
1610 size = desktopSize.ToUnknownSize();
1611 } else {
1612 size = gfx::Size(clampedSize.ToUnknownSize());
1614 Resize(size.width, size.height, true);
1618 const widget::SizeConstraints nsBaseWidget::GetSizeConstraints() {
1619 return mSizeConstraints;
1622 // static
1623 nsIRollupListener* nsBaseWidget::GetActiveRollupListener() {
1624 // If set, then this is likely an <html:select> dropdown.
1625 if (gRollupListener) return gRollupListener;
1627 return nsXULPopupManager::GetInstance();
1630 void nsBaseWidget::NotifyWindowDestroyed() {
1631 if (!mWidgetListener) return;
1633 nsCOMPtr<nsIAppWindow> window = mWidgetListener->GetAppWindow();
1634 nsCOMPtr<nsIBaseWindow> appWindow(do_QueryInterface(window));
1635 if (appWindow) {
1636 appWindow->Destroy();
1640 void nsBaseWidget::NotifyWindowMoved(int32_t aX, int32_t aY) {
1641 if (mWidgetListener) {
1642 mWidgetListener->WindowMoved(this, aX, aY);
1645 if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) {
1646 NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE));
1650 void nsBaseWidget::NotifySizeMoveDone() {
1651 if (!mWidgetListener) {
1652 return;
1654 if (PresShell* presShell = mWidgetListener->GetPresShell()) {
1655 presShell->WindowSizeMoveDone();
1659 void nsBaseWidget::NotifyThemeChanged(ThemeChangeKind aKind) {
1660 LookAndFeel::NotifyChangedAllWindows(aKind);
1663 void nsBaseWidget::NotifyUIStateChanged(UIStateChangeType aShowFocusRings) {
1664 if (Document* doc = GetDocument()) {
1665 if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
1666 win->SetKeyboardIndicators(aShowFocusRings);
1671 nsresult nsBaseWidget::NotifyIME(const IMENotification& aIMENotification) {
1672 if (mIMEHasQuit) {
1673 return NS_OK;
1675 switch (aIMENotification.mMessage) {
1676 case REQUEST_TO_COMMIT_COMPOSITION:
1677 case REQUEST_TO_CANCEL_COMPOSITION:
1678 // We should send request to IME only when there is a TextEventDispatcher
1679 // instance (this means that this widget has dispatched at least one
1680 // composition event or keyboard event) and the it has composition.
1681 // Otherwise, there is nothing to do.
1682 // Note that if current input transaction is for native input events,
1683 // TextEventDispatcher::NotifyIME() will call
1684 // TextEventDispatcherListener::NotifyIME().
1685 if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) {
1686 return mTextEventDispatcher->NotifyIME(aIMENotification);
1688 return NS_OK;
1689 default: {
1690 if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
1691 mIMEHasFocus = true;
1693 EnsureTextEventDispatcher();
1694 // TextEventDispatcher::NotifyIME() will always call
1695 // TextEventDispatcherListener::NotifyIME(). I.e., even if current
1696 // input transaction is for synthesized events for automated tests,
1697 // notifications will be sent to native IME.
1698 nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification);
1699 if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
1700 mIMEHasFocus = false;
1702 return rv;
1707 void nsBaseWidget::EnsureTextEventDispatcher() {
1708 if (mTextEventDispatcher) {
1709 return;
1711 mTextEventDispatcher = new TextEventDispatcher(this);
1714 nsIWidget::NativeIMEContext nsBaseWidget::GetNativeIMEContext() {
1715 if (mTextEventDispatcher && mTextEventDispatcher->GetPseudoIMEContext()) {
1716 // If we already have a TextEventDispatcher and it's working with
1717 // a TextInputProcessor, we need to return pseudo IME context since
1718 // TextCompositionArray::IndexOf(nsIWidget*) should return a composition
1719 // on the pseudo IME context in such case.
1720 NativeIMEContext pseudoIMEContext;
1721 pseudoIMEContext.InitWithRawNativeIMEContext(
1722 mTextEventDispatcher->GetPseudoIMEContext());
1723 return pseudoIMEContext;
1725 return NativeIMEContext(this);
1728 nsIWidget::TextEventDispatcher* nsBaseWidget::GetTextEventDispatcher() {
1729 EnsureTextEventDispatcher();
1730 return mTextEventDispatcher;
1733 void* nsBaseWidget::GetPseudoIMEContext() {
1734 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
1735 if (!dispatcher) {
1736 return nullptr;
1738 return dispatcher->GetPseudoIMEContext();
1741 TextEventDispatcherListener*
1742 nsBaseWidget::GetNativeTextEventDispatcherListener() {
1743 // TODO: If all platforms supported use of TextEventDispatcher for handling
1744 // native IME and keyboard events, this method should be removed since
1745 // in such case, this is overridden by all the subclasses.
1746 return nullptr;
1749 void nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId,
1750 const ScrollableLayerGuid::ViewID& aViewId,
1751 const CSSRect& aRect, const uint32_t& aFlags) {
1752 if (!mCompositorSession || !mAPZC) {
1753 return;
1755 LayersId layerId = mCompositorSession->RootLayerTreeId();
1756 mAPZC->ZoomToRect(ScrollableLayerGuid(layerId, aPresShellId, aViewId),
1757 ZoomTarget{aRect}, aFlags);
1760 #ifdef ACCESSIBILITY
1762 a11y::LocalAccessible* nsBaseWidget::GetRootAccessible() {
1763 NS_ENSURE_TRUE(mWidgetListener, nullptr);
1765 PresShell* presShell = mWidgetListener->GetPresShell();
1766 NS_ENSURE_TRUE(presShell, nullptr);
1768 // If container is null then the presshell is not active. This often happens
1769 // when a preshell is being held onto for fastback.
1770 nsPresContext* presContext = presShell->GetPresContext();
1771 NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr);
1773 // LocalAccessible creation might be not safe so use IsSafeToRunScript to
1774 // make sure it's not created at unsafe times.
1775 nsAccessibilityService* accService = GetOrCreateAccService();
1776 if (accService) {
1777 return accService->GetRootDocumentAccessible(
1778 presShell, nsContentUtils::IsSafeToRunScript());
1781 return nullptr;
1784 #endif // ACCESSIBILITY
1786 void nsBaseWidget::StartAsyncScrollbarDrag(
1787 const AsyncDragMetrics& aDragMetrics) {
1788 if (!AsyncPanZoomEnabled()) {
1789 return;
1792 MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession);
1794 LayersId layersId = mCompositorSession->RootLayerTreeId();
1795 ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId,
1796 aDragMetrics.mViewId);
1798 mAPZC->StartScrollbarDrag(guid, aDragMetrics);
1801 bool nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
1802 const ScrollableLayerGuid& aGuid) {
1803 MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
1805 return mAPZC->StartAutoscroll(aGuid, aAnchorLocation);
1808 void nsBaseWidget::StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) {
1809 MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
1811 mAPZC->StopAutoscroll(aGuid);
1814 LayersId nsBaseWidget::GetRootLayerTreeId() {
1815 return mCompositorSession ? mCompositorSession->RootLayerTreeId()
1816 : LayersId{0};
1819 already_AddRefed<nsIScreen> nsBaseWidget::GetWidgetScreen() {
1820 nsCOMPtr<nsIScreenManager> screenManager;
1821 screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
1822 if (!screenManager) {
1823 return nullptr;
1826 LayoutDeviceIntRect bounds = GetScreenBounds();
1827 DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale());
1828 nsCOMPtr<nsIScreen> screen;
1829 screenManager->ScreenForRect(deskBounds.X(), deskBounds.Y(),
1830 deskBounds.Width(), deskBounds.Height(),
1831 getter_AddRefs(screen));
1832 return screen.forget();
1835 mozilla::DesktopToLayoutDeviceScale
1836 nsBaseWidget::GetDesktopToDeviceScaleByScreen() {
1837 return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext())
1838 ->GetDesktopToDeviceScale();
1841 nsresult nsIWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
1842 bool aLongTap,
1843 nsIObserver* aObserver) {
1844 AutoObserverNotifier notifier(aObserver, "touchtap");
1846 if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) {
1847 sPointerIdCounter = 0;
1849 int pointerId = sPointerIdCounter;
1850 sPointerIdCounter++;
1851 nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT, aPoint,
1852 1.0, 90, nullptr);
1853 if (NS_FAILED(rv)) {
1854 return rv;
1857 if (!aLongTap) {
1858 return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, aPoint, 0, 0,
1859 nullptr);
1862 // initiate a long tap
1863 int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay",
1864 TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC);
1865 if (!mLongTapTimer) {
1866 mLongTapTimer = NS_NewTimer();
1867 if (!mLongTapTimer) {
1868 SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL, aPoint, 0, 0,
1869 nullptr);
1870 return NS_ERROR_UNEXPECTED;
1872 // Windows requires recuring events, so we set this to a smaller window
1873 // than the pref value.
1874 int timeout = elapse;
1875 if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) {
1876 timeout = TOUCH_INJECT_PUMP_TIMER_MSEC;
1878 mLongTapTimer->InitWithNamedFuncCallback(
1879 OnLongTapTimerCallback, this, timeout, nsITimer::TYPE_REPEATING_SLACK,
1880 "nsIWidget::SynthesizeNativeTouchTap");
1883 // If we already have a long tap pending, cancel it. We only allow one long
1884 // tap to be active at a time.
1885 if (mLongTapTouchPoint) {
1886 SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
1887 mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1890 mLongTapTouchPoint = MakeUnique<LongTapInfo>(
1891 pointerId, aPoint, TimeDuration::FromMilliseconds(elapse), aObserver);
1892 notifier.SkipNotification(); // we'll do it in the long-tap callback
1893 return NS_OK;
1896 // static
1897 void nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) {
1898 auto* self = static_cast<nsIWidget*>(aClosure);
1900 if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) >
1901 TimeStamp::Now()) {
1902 #ifdef XP_WIN
1903 // Windows needs us to keep pumping feedback to the digitizer, so update
1904 // the pointer id with the same position.
1905 self->SynthesizeNativeTouchPoint(
1906 self->mLongTapTouchPoint->mPointerId, TOUCH_CONTACT,
1907 self->mLongTapTouchPoint->mPosition, 1.0, 90, nullptr);
1908 #endif
1909 return;
1912 AutoObserverNotifier notifier(self->mLongTapTouchPoint->mObserver,
1913 "touchtap");
1915 // finished, remove the touch point
1916 self->mLongTapTimer->Cancel();
1917 self->mLongTapTimer = nullptr;
1918 self->SynthesizeNativeTouchPoint(
1919 self->mLongTapTouchPoint->mPointerId, TOUCH_REMOVE,
1920 self->mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1921 self->mLongTapTouchPoint = nullptr;
1924 nsresult nsIWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
1925 AutoObserverNotifier notifier(aObserver, "cleartouch");
1927 if (!mLongTapTimer) {
1928 return NS_OK;
1930 mLongTapTimer->Cancel();
1931 mLongTapTimer = nullptr;
1932 SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
1933 mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1934 mLongTapTouchPoint = nullptr;
1935 return NS_OK;
1938 MultiTouchInput nsBaseWidget::UpdateSynthesizedTouchState(
1939 MultiTouchInput* aState, uint32_t aTime, mozilla::TimeStamp aTimeStamp,
1940 uint32_t aPointerId, TouchPointerState aPointerState,
1941 LayoutDeviceIntPoint aPoint, double aPointerPressure,
1942 uint32_t aPointerOrientation) {
1943 ScreenIntPoint pointerScreenPoint = ViewAs<ScreenPixel>(
1944 aPoint, PixelCastJustification::LayoutDeviceIsScreenForBounds);
1946 // We can't dispatch *aState directly because (a) dispatching
1947 // it might inadvertently modify it and (b) in the case of touchend or
1948 // touchcancel events aState will hold the touches that are
1949 // still down whereas the input dispatched needs to hold the removed
1950 // touch(es). We use |inputToDispatch| for this purpose.
1951 MultiTouchInput inputToDispatch;
1952 inputToDispatch.mInputType = MULTITOUCH_INPUT;
1953 inputToDispatch.mTime = aTime;
1954 inputToDispatch.mTimeStamp = aTimeStamp;
1956 int32_t index = aState->IndexOfTouch((int32_t)aPointerId);
1957 if (aPointerState == TOUCH_CONTACT) {
1958 if (index >= 0) {
1959 // found an existing touch point, update it
1960 SingleTouchData& point = aState->mTouches[index];
1961 point.mScreenPoint = pointerScreenPoint;
1962 point.mRotationAngle = (float)aPointerOrientation;
1963 point.mForce = (float)aPointerPressure;
1964 inputToDispatch.mType = MultiTouchInput::MULTITOUCH_MOVE;
1965 } else {
1966 // new touch point, add it
1967 aState->mTouches.AppendElement(SingleTouchData(
1968 (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0),
1969 (float)aPointerOrientation, (float)aPointerPressure));
1970 inputToDispatch.mType = MultiTouchInput::MULTITOUCH_START;
1972 inputToDispatch.mTouches = aState->mTouches;
1973 } else {
1974 MOZ_ASSERT(aPointerState == TOUCH_REMOVE || aPointerState == TOUCH_CANCEL);
1975 // a touch point is being lifted, so remove it from the stored list
1976 if (index >= 0) {
1977 aState->mTouches.RemoveElementAt(index);
1979 inputToDispatch.mType =
1980 (aPointerState == TOUCH_REMOVE ? MultiTouchInput::MULTITOUCH_END
1981 : MultiTouchInput::MULTITOUCH_CANCEL);
1982 inputToDispatch.mTouches.AppendElement(SingleTouchData(
1983 (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0),
1984 (float)aPointerOrientation, (float)aPointerPressure));
1987 return inputToDispatch;
1990 void nsBaseWidget::NotifyLiveResizeStarted() {
1991 // If we have mLiveResizeListeners already non-empty, we should notify those
1992 // listeners that the resize stopped before starting anew. In theory this
1993 // should never happen because we shouldn't get nested live resize actions.
1994 NotifyLiveResizeStopped();
1995 MOZ_ASSERT(mLiveResizeListeners.IsEmpty());
1997 // If we can get the active remote tab for the current widget, suppress
1998 // the displayport on it during the live resize.
1999 if (!mWidgetListener) {
2000 return;
2002 nsCOMPtr<nsIAppWindow> appWindow = mWidgetListener->GetAppWindow();
2003 if (!appWindow) {
2004 return;
2006 mLiveResizeListeners = appWindow->GetLiveResizeListeners();
2007 for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) {
2008 mLiveResizeListeners[i]->LiveResizeStarted();
2012 void nsBaseWidget::NotifyLiveResizeStopped() {
2013 if (!mLiveResizeListeners.IsEmpty()) {
2014 for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) {
2015 mLiveResizeListeners[i]->LiveResizeStopped();
2017 mLiveResizeListeners.Clear();
2021 nsresult nsBaseWidget::AsyncEnableDragDrop(bool aEnable) {
2022 RefPtr<nsBaseWidget> kungFuDeathGrip = this;
2023 return NS_DispatchToCurrentThreadQueue(
2024 NS_NewRunnableFunction(
2025 "AsyncEnableDragDropFn",
2026 [this, aEnable, kungFuDeathGrip]() { EnableDragDrop(aEnable); }),
2027 kAsyncDragDropTimeout, EventQueuePriority::Idle);
2030 void nsBaseWidget::SwipeFinished() { mSwipeTracker = nullptr; }
2032 void nsBaseWidget::ReportSwipeStarted(uint64_t aInputBlockId,
2033 bool aStartSwipe) {
2034 if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == aInputBlockId) {
2035 if (aStartSwipe) {
2036 PanGestureInput& startEvent = mSwipeEventQueue->queuedEvents[0];
2037 TrackScrollEventAsSwipe(startEvent, mSwipeEventQueue->allowedDirections);
2038 for (size_t i = 1; i < mSwipeEventQueue->queuedEvents.Length(); i++) {
2039 mSwipeTracker->ProcessEvent(mSwipeEventQueue->queuedEvents[i]);
2042 mSwipeEventQueue = nullptr;
2046 void nsBaseWidget::TrackScrollEventAsSwipe(
2047 const mozilla::PanGestureInput& aSwipeStartEvent,
2048 uint32_t aAllowedDirections) {
2049 // If a swipe is currently being tracked kill it -- it's been interrupted
2050 // by another gesture event.
2051 if (mSwipeTracker) {
2052 mSwipeTracker->CancelSwipe(aSwipeStartEvent.mTimeStamp);
2053 mSwipeTracker->Destroy();
2054 mSwipeTracker = nullptr;
2057 uint32_t direction =
2058 (aSwipeStartEvent.mPanDisplacement.x > 0.0)
2059 ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT
2060 : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT;
2062 mSwipeTracker =
2063 new SwipeTracker(*this, aSwipeStartEvent, aAllowedDirections, direction);
2065 if (!mAPZC) {
2066 mCurrentPanGestureBelongsToSwipe = true;
2070 nsBaseWidget::SwipeInfo nsBaseWidget::SendMayStartSwipe(
2071 const mozilla::PanGestureInput& aSwipeStartEvent) {
2072 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
2074 uint32_t direction =
2075 (aSwipeStartEvent.mPanDisplacement.x > 0.0)
2076 ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT
2077 : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT;
2079 // We're ready to start the animation. Tell Gecko about it, and at the same
2080 // time ask it if it really wants to start an animation for this event.
2081 // This event also reports back the directions that we can swipe in.
2082 LayoutDeviceIntPoint position = RoundedToInt(aSwipeStartEvent.mPanStartPoint *
2083 ScreenToLayoutDeviceScale(1));
2084 WidgetSimpleGestureEvent geckoEvent = SwipeTracker::CreateSwipeGestureEvent(
2085 eSwipeGestureMayStart, this, position, aSwipeStartEvent.mTimeStamp);
2086 geckoEvent.mDirection = direction;
2087 geckoEvent.mDelta = 0.0;
2088 geckoEvent.mAllowedDirections = 0;
2089 bool shouldStartSwipe =
2090 DispatchWindowEvent(geckoEvent); // event cancelled == swipe should start
2092 SwipeInfo result = {shouldStartSwipe, geckoEvent.mAllowedDirections};
2093 return result;
2096 WidgetWheelEvent nsBaseWidget::MayStartSwipeForAPZ(
2097 const PanGestureInput& aPanInput, const APZEventResult& aApzResult,
2098 CanTriggerSwipe aCanTriggerSwipe) {
2099 WidgetWheelEvent event = aPanInput.ToWidgetEvent(this);
2100 if (aCanTriggerSwipe == CanTriggerSwipe::Yes &&
2101 aPanInput.mOverscrollBehaviorAllowsSwipe) {
2102 SwipeInfo swipeInfo = SendMayStartSwipe(aPanInput);
2103 event.mCanTriggerSwipe = swipeInfo.wantsSwipe;
2104 if (swipeInfo.wantsSwipe) {
2105 if (aApzResult.GetStatus() == nsEventStatus_eIgnore) {
2106 // APZ has determined and that scrolling horizontally in the
2107 // requested direction is impossible, so it didn't do any
2108 // scrolling for the event.
2109 // We know now that MayStartSwipe wants a swipe, so we can start
2110 // the swipe now.
2111 TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections);
2112 } else {
2113 // We don't know whether this event can start a swipe, so we need
2114 // to queue up events and wait for a call to ReportSwipeStarted.
2115 // APZ might already have started scrolling in response to the
2116 // event if it knew that it's the right thing to do. In that case
2117 // we'll still get a call to ReportSwipeStarted, and we will
2118 // discard the queued events at that point.
2119 mSwipeEventQueue = MakeUnique<SwipeEventQueue>(
2120 swipeInfo.allowedDirections, aApzResult.mInputBlockId);
2125 if (mSwipeEventQueue &&
2126 mSwipeEventQueue->inputBlockId == aApzResult.mInputBlockId) {
2127 mSwipeEventQueue->queuedEvents.AppendElement(aPanInput);
2130 return event;
2133 bool nsBaseWidget::MayStartSwipeForNonAPZ(const PanGestureInput& aPanInput,
2134 CanTriggerSwipe aCanTriggerSwipe) {
2135 if (aPanInput.mType == PanGestureInput::PANGESTURE_MAYSTART ||
2136 aPanInput.mType == PanGestureInput::PANGESTURE_START) {
2137 mCurrentPanGestureBelongsToSwipe = false;
2139 if (mCurrentPanGestureBelongsToSwipe) {
2140 // Ignore this event. It's a momentum event from a scroll gesture
2141 // that was processed as a swipe, and the swipe animation has
2142 // already finished (so mSwipeTracker is already null).
2143 MOZ_ASSERT(aPanInput.IsMomentum(),
2144 "If the fingers are still on the touchpad, we should still have "
2145 "a SwipeTracker, "
2146 "and it should have consumed this event.");
2147 return true;
2150 if (aCanTriggerSwipe == CanTriggerSwipe::No) {
2151 return false;
2154 SwipeInfo swipeInfo = SendMayStartSwipe(aPanInput);
2156 // We're in the non-APZ case here, but we still want to know whether
2157 // the event was routed to a child process, so we use InputAPZContext
2158 // to get that piece of information.
2159 ScrollableLayerGuid guid;
2160 InputAPZContext context(guid, 0, nsEventStatus_eIgnore);
2162 WidgetWheelEvent event = aPanInput.ToWidgetEvent(this);
2163 event.mCanTriggerSwipe = swipeInfo.wantsSwipe;
2164 nsEventStatus status;
2165 DispatchEvent(&event, status);
2166 if (swipeInfo.wantsSwipe) {
2167 if (context.WasRoutedToChildProcess()) {
2168 // We don't know whether this event can start a swipe, so we need
2169 // to queue up events and wait for a call to ReportSwipeStarted.
2170 mSwipeEventQueue =
2171 MakeUnique<SwipeEventQueue>(swipeInfo.allowedDirections, 0);
2172 } else if (event.TriggersSwipe()) {
2173 TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections);
2177 if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == 0) {
2178 mSwipeEventQueue->queuedEvents.AppendElement(aPanInput);
2181 return true;
2184 const IMENotificationRequests& nsIWidget::IMENotificationRequestsRef() {
2185 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
2186 return dispatcher->IMENotificationRequestsRef();
2189 void nsIWidget::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {}
2191 bool nsIWidget::GetEditCommands(NativeKeyBindingsType aType,
2192 const WidgetKeyboardEvent& aEvent,
2193 nsTArray<CommandInt>& aCommands) {
2194 MOZ_ASSERT(aEvent.IsTrusted());
2195 MOZ_ASSERT(aCommands.IsEmpty());
2196 return true;
2199 already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboard() {
2200 if (XRE_IsContentProcess()) {
2201 return CreateBidiKeyboardContentProcess();
2203 return CreateBidiKeyboardInner();
2206 #ifdef ANDROID
2207 already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboardInner() {
2208 // no bidi keyboard implementation
2209 return nullptr;
2211 #endif
2213 namespace mozilla::widget {
2215 const char* ToChar(InputContext::Origin aOrigin) {
2216 switch (aOrigin) {
2217 case InputContext::ORIGIN_MAIN:
2218 return "ORIGIN_MAIN";
2219 case InputContext::ORIGIN_CONTENT:
2220 return "ORIGIN_CONTENT";
2221 default:
2222 return "Unexpected value";
2226 const char* ToChar(IMEMessage aIMEMessage) {
2227 switch (aIMEMessage) {
2228 case NOTIFY_IME_OF_NOTHING:
2229 return "NOTIFY_IME_OF_NOTHING";
2230 case NOTIFY_IME_OF_FOCUS:
2231 return "NOTIFY_IME_OF_FOCUS";
2232 case NOTIFY_IME_OF_BLUR:
2233 return "NOTIFY_IME_OF_BLUR";
2234 case NOTIFY_IME_OF_SELECTION_CHANGE:
2235 return "NOTIFY_IME_OF_SELECTION_CHANGE";
2236 case NOTIFY_IME_OF_TEXT_CHANGE:
2237 return "NOTIFY_IME_OF_TEXT_CHANGE";
2238 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
2239 return "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED";
2240 case NOTIFY_IME_OF_POSITION_CHANGE:
2241 return "NOTIFY_IME_OF_POSITION_CHANGE";
2242 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
2243 return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT";
2244 case REQUEST_TO_COMMIT_COMPOSITION:
2245 return "REQUEST_TO_COMMIT_COMPOSITION";
2246 case REQUEST_TO_CANCEL_COMPOSITION:
2247 return "REQUEST_TO_CANCEL_COMPOSITION";
2248 default:
2249 return "Unexpected value";
2253 void NativeIMEContext::Init(nsIWidget* aWidget) {
2254 if (!aWidget) {
2255 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2256 mOriginProcessID = static_cast<uint64_t>(-1);
2257 return;
2259 if (!XRE_IsContentProcess()) {
2260 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(
2261 aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT));
2262 mOriginProcessID = 0;
2263 return;
2265 // If this is created in a child process, aWidget is an instance of
2266 // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT.
2267 // Instead of that PuppetWidget::GetNativeIMEContext() returns cached
2268 // native IME context of the parent process.
2269 *this = aWidget->GetNativeIMEContext();
2272 void NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext) {
2273 if (NS_WARN_IF(!aRawNativeIMEContext)) {
2274 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2275 mOriginProcessID = static_cast<uint64_t>(-1);
2276 return;
2278 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext);
2279 mOriginProcessID =
2280 XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0;
2283 void IMENotification::TextChangeDataBase::MergeWith(
2284 const IMENotification::TextChangeDataBase& aOther) {
2285 MOZ_ASSERT(aOther.IsValid(), "Merging data must store valid data");
2286 MOZ_ASSERT(aOther.mStartOffset <= aOther.mRemovedEndOffset,
2287 "end of removed text must be same or larger than start");
2288 MOZ_ASSERT(aOther.mStartOffset <= aOther.mAddedEndOffset,
2289 "end of added text must be same or larger than start");
2291 if (!IsValid()) {
2292 *this = aOther;
2293 return;
2296 // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed
2297 // text ranges. I.e., mStartOffset should be the smallest offset of all
2298 // modified text ranges in old text. |mRemovedEndOffset| should be the
2299 // largest end offset in old text of all modified text ranges.
2300 // |mAddedEndOffset| represents the end offset of all inserted text ranges.
2301 // I.e., only this is an offset in new text.
2302 // In other words, between mStartOffset and |mRemovedEndOffset| of the
2303 // premodified text was already removed. And some text whose length is
2304 // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|. I.e.,
2305 // this allows IME to mark dirty the modified text range with |mStartOffset|
2306 // and |mRemovedEndOffset| if IME stores all text of the focused editor and
2307 // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|.
2308 // Additionally, IME can retrieve only the text between |mStartOffset| and
2309 // |mAddedEndOffset| for updating stored text.
2311 // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they
2312 // should be adjusted to be in same text. The |newData.mStartOffset| and
2313 // |newData.mRemovedEndOffset| should be computed as in old text because
2314 // |mStartOffset| and |mRemovedEndOffset| represent the modified text range
2315 // in the old text but even if some text before the values of the newData
2316 // has already been modified, the values don't include the changes.
2318 // For comparing new and old |mAddedEndOffset| values, they should be
2319 // adjusted to be in same text. The |oldData.mAddedEndOffset| should be
2320 // computed as in the new text because |mAddedEndOffset| indicates the end
2321 // offset of inserted text in the new text but |oldData.mAddedEndOffset|
2322 // doesn't include any changes of the text before |newData.mAddedEndOffset|.
2324 const TextChangeDataBase& newData = aOther;
2325 const TextChangeDataBase oldData = *this;
2327 // mCausedOnlyByComposition should be true only when all changes are caused
2328 // by composition.
2329 mCausedOnlyByComposition =
2330 newData.mCausedOnlyByComposition && oldData.mCausedOnlyByComposition;
2332 // mIncludingChangesWithoutComposition should be true if at least one of
2333 // merged changes occurred without composition.
2334 mIncludingChangesWithoutComposition =
2335 newData.mIncludingChangesWithoutComposition ||
2336 oldData.mIncludingChangesWithoutComposition;
2338 // mIncludingChangesDuringComposition should be true when at least one of
2339 // the merged non-composition changes occurred during the latest composition.
2340 if (!newData.mCausedOnlyByComposition &&
2341 !newData.mIncludingChangesDuringComposition) {
2342 MOZ_ASSERT(newData.mIncludingChangesWithoutComposition);
2343 MOZ_ASSERT(mIncludingChangesWithoutComposition);
2344 // If new change is neither caused by composition nor occurred during
2345 // composition, set mIncludingChangesDuringComposition to false because
2346 // IME doesn't want outdated text changes as text change during current
2347 // composition.
2348 mIncludingChangesDuringComposition = false;
2349 } else {
2350 // Otherwise, set mIncludingChangesDuringComposition to true if either
2351 // oldData or newData includes changes during composition.
2352 mIncludingChangesDuringComposition =
2353 newData.mIncludingChangesDuringComposition ||
2354 oldData.mIncludingChangesDuringComposition;
2357 if (newData.mStartOffset >= oldData.mAddedEndOffset) {
2358 // Case 1:
2359 // If new start is after old end offset of added text, it means that text
2360 // after the modified range is modified. Like:
2361 // added range of old change: +----------+
2362 // removed range of new change: +----------+
2363 // So, the old start offset is always the smaller offset.
2364 mStartOffset = oldData.mStartOffset;
2365 // The new end offset of removed text is moved by the old change and we
2366 // need to cancel the move of the old change for comparing the offsets in
2367 // same text because it doesn't make sensce to compare offsets in different
2368 // text.
2369 uint32_t newRemovedEndOffsetInOldText =
2370 newData.mRemovedEndOffset - oldData.Difference();
2371 mRemovedEndOffset =
2372 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2373 // The new end offset of added text is always the larger offset.
2374 mAddedEndOffset = newData.mAddedEndOffset;
2375 return;
2378 if (newData.mStartOffset >= oldData.mStartOffset) {
2379 // If new start is in the modified range, it means that new data changes
2380 // a part or all of the range.
2381 mStartOffset = oldData.mStartOffset;
2382 if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
2383 // Case 2:
2384 // If new end of removed text is greater than old end of added text, it
2385 // means that all or a part of modified range modified again and text
2386 // after the modified range is also modified. Like:
2387 // added range of old change: +----------+
2388 // removed range of new change: +----------+
2389 // So, the new removed end offset is moved by the old change and we need
2390 // to cancel the move of the old change for comparing the offsets in the
2391 // same text because it doesn't make sense to compare the offsets in
2392 // different text.
2393 uint32_t newRemovedEndOffsetInOldText =
2394 newData.mRemovedEndOffset - oldData.Difference();
2395 mRemovedEndOffset =
2396 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2397 // The old end of added text is replaced by new change. So, it should be
2398 // same as the new start. On the other hand, the new added end offset is
2399 // always same or larger. Therefore, the merged end offset of added
2400 // text should be the new end offset of added text.
2401 mAddedEndOffset = newData.mAddedEndOffset;
2402 return;
2405 // Case 3:
2406 // If new end of removed text is less than old end of added text, it means
2407 // that only a part of the modified range is modified again. Like:
2408 // added range of old change: +------------+
2409 // removed range of new change: +-----+
2410 // So, the new end offset of removed text should be same as the old end
2411 // offset of removed text. Therefore, the merged end offset of removed
2412 // text should be the old text change's |mRemovedEndOffset|.
2413 mRemovedEndOffset = oldData.mRemovedEndOffset;
2414 // The old end of added text is moved by new change. So, we need to cancel
2415 // the move of the new change for comparing the offsets in same text.
2416 uint32_t oldAddedEndOffsetInNewText =
2417 oldData.mAddedEndOffset + newData.Difference();
2418 mAddedEndOffset =
2419 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2420 return;
2423 if (newData.mRemovedEndOffset >= oldData.mStartOffset) {
2424 // If new end of removed text is greater than old start (and new start is
2425 // less than old start), it means that a part of modified range is modified
2426 // again and some new text before the modified range is also modified.
2427 MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
2428 "new start offset should be less than old one here");
2429 mStartOffset = newData.mStartOffset;
2430 if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
2431 // Case 4:
2432 // If new end of removed text is greater than old end of added text, it
2433 // means that all modified text and text after the modified range is
2434 // modified. Like:
2435 // added range of old change: +----------+
2436 // removed range of new change: +------------------+
2437 // So, the new end of removed text is moved by the old change. Therefore,
2438 // we need to cancel the move of the old change for comparing the offsets
2439 // in same text because it doesn't make sense to compare the offsets in
2440 // different text.
2441 uint32_t newRemovedEndOffsetInOldText =
2442 newData.mRemovedEndOffset - oldData.Difference();
2443 mRemovedEndOffset =
2444 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2445 // The old end of added text is replaced by new change. So, the old end
2446 // offset of added text is same as new text change's start offset. Then,
2447 // new change's end offset of added text is always same or larger than
2448 // it. Therefore, merged end offset of added text is always the new end
2449 // offset of added text.
2450 mAddedEndOffset = newData.mAddedEndOffset;
2451 return;
2454 // Case 5:
2455 // If new end of removed text is less than old end of added text, it
2456 // means that only a part of the modified range is modified again. Like:
2457 // added range of old change: +----------+
2458 // removed range of new change: +----------+
2459 // So, the new end of removed text should be same as old end of removed
2460 // text for preventing end of removed text to be modified. Therefore,
2461 // merged end offset of removed text is always the old end offset of removed
2462 // text.
2463 mRemovedEndOffset = oldData.mRemovedEndOffset;
2464 // The old end of added text is moved by this change. So, we need to
2465 // cancel the move of the new change for comparing the offsets in same text
2466 // because it doesn't make sense to compare the offsets in different text.
2467 uint32_t oldAddedEndOffsetInNewText =
2468 oldData.mAddedEndOffset + newData.Difference();
2469 mAddedEndOffset =
2470 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2471 return;
2474 // Case 6:
2475 // Otherwise, i.e., both new end of added text and new start are less than
2476 // old start, text before the modified range is modified. Like:
2477 // added range of old change: +----------+
2478 // removed range of new change: +----------+
2479 MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
2480 "new start offset should be less than old one here");
2481 mStartOffset = newData.mStartOffset;
2482 MOZ_ASSERT(newData.mRemovedEndOffset < oldData.mRemovedEndOffset,
2483 "new removed end offset should be less than old one here");
2484 mRemovedEndOffset = oldData.mRemovedEndOffset;
2485 // The end of added text should be adjusted with the new difference.
2486 uint32_t oldAddedEndOffsetInNewText =
2487 oldData.mAddedEndOffset + newData.Difference();
2488 mAddedEndOffset =
2489 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2492 #ifdef DEBUG
2494 // Let's test the code of merging multiple text change data in debug build
2495 // and crash if one of them fails because this feature is very complex but
2496 // cannot be tested with mochitest.
2497 void IMENotification::TextChangeDataBase::Test() {
2498 static bool gTestTextChangeEvent = true;
2499 if (!gTestTextChangeEvent) {
2500 return;
2502 gTestTextChangeEvent = false;
2504 /****************************************************************************
2505 * Case 1
2506 ****************************************************************************/
2508 // Appending text
2509 MergeWith(TextChangeData(10, 10, 20, false, false));
2510 MergeWith(TextChangeData(20, 20, 35, false, false));
2511 MOZ_ASSERT(mStartOffset == 10,
2512 "Test 1-1-1: mStartOffset should be the first offset");
2513 MOZ_ASSERT(
2514 mRemovedEndOffset == 10, // 20 - (20 - 10)
2515 "Test 1-1-2: mRemovedEndOffset should be the first end of removed text");
2516 MOZ_ASSERT(
2517 mAddedEndOffset == 35,
2518 "Test 1-1-3: mAddedEndOffset should be the last end of added text");
2519 Clear();
2521 // Removing text (longer line -> shorter line)
2522 MergeWith(TextChangeData(10, 20, 10, false, false));
2523 MergeWith(TextChangeData(10, 30, 10, false, false));
2524 MOZ_ASSERT(mStartOffset == 10,
2525 "Test 1-2-1: mStartOffset should be the first offset");
2526 MOZ_ASSERT(mRemovedEndOffset == 40, // 30 + (10 - 20)
2527 "Test 1-2-2: mRemovedEndOffset should be the the last end of "
2528 "removed text "
2529 "with already removed length");
2530 MOZ_ASSERT(
2531 mAddedEndOffset == 10,
2532 "Test 1-2-3: mAddedEndOffset should be the last end of added text");
2533 Clear();
2535 // Removing text (shorter line -> longer line)
2536 MergeWith(TextChangeData(10, 20, 10, false, false));
2537 MergeWith(TextChangeData(10, 15, 10, false, false));
2538 MOZ_ASSERT(mStartOffset == 10,
2539 "Test 1-3-1: mStartOffset should be the first offset");
2540 MOZ_ASSERT(mRemovedEndOffset == 25, // 15 + (10 - 20)
2541 "Test 1-3-2: mRemovedEndOffset should be the the last end of "
2542 "removed text "
2543 "with already removed length");
2544 MOZ_ASSERT(
2545 mAddedEndOffset == 10,
2546 "Test 1-3-3: mAddedEndOffset should be the last end of added text");
2547 Clear();
2549 // Appending text at different point (not sure if actually occurs)
2550 MergeWith(TextChangeData(10, 10, 20, false, false));
2551 MergeWith(TextChangeData(55, 55, 60, false, false));
2552 MOZ_ASSERT(mStartOffset == 10,
2553 "Test 1-4-1: mStartOffset should be the smallest offset");
2554 MOZ_ASSERT(
2555 mRemovedEndOffset == 45, // 55 - (10 - 20)
2556 "Test 1-4-2: mRemovedEndOffset should be the the largest end of removed "
2557 "text without already added length");
2558 MOZ_ASSERT(
2559 mAddedEndOffset == 60,
2560 "Test 1-4-3: mAddedEndOffset should be the last end of added text");
2561 Clear();
2563 // Removing text at different point (not sure if actually occurs)
2564 MergeWith(TextChangeData(10, 20, 10, false, false));
2565 MergeWith(TextChangeData(55, 68, 55, false, false));
2566 MOZ_ASSERT(mStartOffset == 10,
2567 "Test 1-5-1: mStartOffset should be the smallest offset");
2568 MOZ_ASSERT(
2569 mRemovedEndOffset == 78, // 68 - (10 - 20)
2570 "Test 1-5-2: mRemovedEndOffset should be the the largest end of removed "
2571 "text with already removed length");
2572 MOZ_ASSERT(
2573 mAddedEndOffset == 55,
2574 "Test 1-5-3: mAddedEndOffset should be the largest end of added text");
2575 Clear();
2577 // Replacing text and append text (becomes longer)
2578 MergeWith(TextChangeData(30, 35, 32, false, false));
2579 MergeWith(TextChangeData(32, 32, 40, false, false));
2580 MOZ_ASSERT(mStartOffset == 30,
2581 "Test 1-6-1: mStartOffset should be the smallest offset");
2582 MOZ_ASSERT(
2583 mRemovedEndOffset == 35, // 32 - (32 - 35)
2584 "Test 1-6-2: mRemovedEndOffset should be the the first end of removed "
2585 "text");
2586 MOZ_ASSERT(
2587 mAddedEndOffset == 40,
2588 "Test 1-6-3: mAddedEndOffset should be the last end of added text");
2589 Clear();
2591 // Replacing text and append text (becomes shorter)
2592 MergeWith(TextChangeData(30, 35, 32, false, false));
2593 MergeWith(TextChangeData(32, 32, 33, false, false));
2594 MOZ_ASSERT(mStartOffset == 30,
2595 "Test 1-7-1: mStartOffset should be the smallest offset");
2596 MOZ_ASSERT(
2597 mRemovedEndOffset == 35, // 32 - (32 - 35)
2598 "Test 1-7-2: mRemovedEndOffset should be the the first end of removed "
2599 "text");
2600 MOZ_ASSERT(
2601 mAddedEndOffset == 33,
2602 "Test 1-7-3: mAddedEndOffset should be the last end of added text");
2603 Clear();
2605 // Removing text and replacing text after first range (not sure if actually
2606 // occurs)
2607 MergeWith(TextChangeData(30, 35, 30, false, false));
2608 MergeWith(TextChangeData(32, 34, 48, false, false));
2609 MOZ_ASSERT(mStartOffset == 30,
2610 "Test 1-8-1: mStartOffset should be the smallest offset");
2611 MOZ_ASSERT(mRemovedEndOffset == 39, // 34 - (30 - 35)
2612 "Test 1-8-2: mRemovedEndOffset should be the the first end of "
2613 "removed text "
2614 "without already removed text");
2615 MOZ_ASSERT(
2616 mAddedEndOffset == 48,
2617 "Test 1-8-3: mAddedEndOffset should be the last end of added text");
2618 Clear();
2620 // Removing text and replacing text after first range (not sure if actually
2621 // occurs)
2622 MergeWith(TextChangeData(30, 35, 30, false, false));
2623 MergeWith(TextChangeData(32, 38, 36, false, false));
2624 MOZ_ASSERT(mStartOffset == 30,
2625 "Test 1-9-1: mStartOffset should be the smallest offset");
2626 MOZ_ASSERT(mRemovedEndOffset == 43, // 38 - (30 - 35)
2627 "Test 1-9-2: mRemovedEndOffset should be the the first end of "
2628 "removed text "
2629 "without already removed text");
2630 MOZ_ASSERT(
2631 mAddedEndOffset == 36,
2632 "Test 1-9-3: mAddedEndOffset should be the last end of added text");
2633 Clear();
2635 /****************************************************************************
2636 * Case 2
2637 ****************************************************************************/
2639 // Replacing text in around end of added text (becomes shorter) (not sure
2640 // if actually occurs)
2641 MergeWith(TextChangeData(50, 50, 55, false, false));
2642 MergeWith(TextChangeData(53, 60, 54, false, false));
2643 MOZ_ASSERT(mStartOffset == 50,
2644 "Test 2-1-1: mStartOffset should be the smallest offset");
2645 MOZ_ASSERT(mRemovedEndOffset == 55, // 60 - (55 - 50)
2646 "Test 2-1-2: mRemovedEndOffset should be the the last end of "
2647 "removed text "
2648 "without already added text length");
2649 MOZ_ASSERT(
2650 mAddedEndOffset == 54,
2651 "Test 2-1-3: mAddedEndOffset should be the last end of added text");
2652 Clear();
2654 // Replacing text around end of added text (becomes longer) (not sure
2655 // if actually occurs)
2656 MergeWith(TextChangeData(50, 50, 55, false, false));
2657 MergeWith(TextChangeData(54, 62, 68, false, false));
2658 MOZ_ASSERT(mStartOffset == 50,
2659 "Test 2-2-1: mStartOffset should be the smallest offset");
2660 MOZ_ASSERT(mRemovedEndOffset == 57, // 62 - (55 - 50)
2661 "Test 2-2-2: mRemovedEndOffset should be the the last end of "
2662 "removed text "
2663 "without already added text length");
2664 MOZ_ASSERT(
2665 mAddedEndOffset == 68,
2666 "Test 2-2-3: mAddedEndOffset should be the last end of added text");
2667 Clear();
2669 // Replacing text around end of replaced text (became shorter) (not sure if
2670 // actually occurs)
2671 MergeWith(TextChangeData(36, 48, 45, false, false));
2672 MergeWith(TextChangeData(43, 50, 49, false, false));
2673 MOZ_ASSERT(mStartOffset == 36,
2674 "Test 2-3-1: mStartOffset should be the smallest offset");
2675 MOZ_ASSERT(mRemovedEndOffset == 53, // 50 - (45 - 48)
2676 "Test 2-3-2: mRemovedEndOffset should be the the last end of "
2677 "removed text "
2678 "without already removed text length");
2679 MOZ_ASSERT(
2680 mAddedEndOffset == 49,
2681 "Test 2-3-3: mAddedEndOffset should be the last end of added text");
2682 Clear();
2684 // Replacing text around end of replaced text (became longer) (not sure if
2685 // actually occurs)
2686 MergeWith(TextChangeData(36, 52, 53, false, false));
2687 MergeWith(TextChangeData(43, 68, 61, false, false));
2688 MOZ_ASSERT(mStartOffset == 36,
2689 "Test 2-4-1: mStartOffset should be the smallest offset");
2690 MOZ_ASSERT(mRemovedEndOffset == 67, // 68 - (53 - 52)
2691 "Test 2-4-2: mRemovedEndOffset should be the the last end of "
2692 "removed text "
2693 "without already added text length");
2694 MOZ_ASSERT(
2695 mAddedEndOffset == 61,
2696 "Test 2-4-3: mAddedEndOffset should be the last end of added text");
2697 Clear();
2699 /****************************************************************************
2700 * Case 3
2701 ****************************************************************************/
2703 // Appending text in already added text (not sure if actually occurs)
2704 MergeWith(TextChangeData(10, 10, 20, false, false));
2705 MergeWith(TextChangeData(15, 15, 30, false, false));
2706 MOZ_ASSERT(mStartOffset == 10,
2707 "Test 3-1-1: mStartOffset should be the smallest offset");
2708 MOZ_ASSERT(mRemovedEndOffset == 10,
2709 "Test 3-1-2: mRemovedEndOffset should be the the first end of "
2710 "removed text");
2711 MOZ_ASSERT(
2712 mAddedEndOffset == 35, // 20 + (30 - 15)
2713 "Test 3-1-3: mAddedEndOffset should be the first end of added text with "
2714 "added text length by the new change");
2715 Clear();
2717 // Replacing text in added text (not sure if actually occurs)
2718 MergeWith(TextChangeData(50, 50, 55, false, false));
2719 MergeWith(TextChangeData(52, 53, 56, false, false));
2720 MOZ_ASSERT(mStartOffset == 50,
2721 "Test 3-2-1: mStartOffset should be the smallest offset");
2722 MOZ_ASSERT(mRemovedEndOffset == 50,
2723 "Test 3-2-2: mRemovedEndOffset should be the the first end of "
2724 "removed text");
2725 MOZ_ASSERT(
2726 mAddedEndOffset == 58, // 55 + (56 - 53)
2727 "Test 3-2-3: mAddedEndOffset should be the first end of added text with "
2728 "added text length by the new change");
2729 Clear();
2731 // Replacing text in replaced text (became shorter) (not sure if actually
2732 // occurs)
2733 MergeWith(TextChangeData(36, 48, 45, false, false));
2734 MergeWith(TextChangeData(37, 38, 50, false, false));
2735 MOZ_ASSERT(mStartOffset == 36,
2736 "Test 3-3-1: mStartOffset should be the smallest offset");
2737 MOZ_ASSERT(mRemovedEndOffset == 48,
2738 "Test 3-3-2: mRemovedEndOffset should be the the first end of "
2739 "removed text");
2740 MOZ_ASSERT(
2741 mAddedEndOffset == 57, // 45 + (50 - 38)
2742 "Test 3-3-3: mAddedEndOffset should be the first end of added text with "
2743 "added text length by the new change");
2744 Clear();
2746 // Replacing text in replaced text (became longer) (not sure if actually
2747 // occurs)
2748 MergeWith(TextChangeData(32, 48, 53, false, false));
2749 MergeWith(TextChangeData(43, 50, 52, false, false));
2750 MOZ_ASSERT(mStartOffset == 32,
2751 "Test 3-4-1: mStartOffset should be the smallest offset");
2752 MOZ_ASSERT(mRemovedEndOffset == 48,
2753 "Test 3-4-2: mRemovedEndOffset should be the the last end of "
2754 "removed text "
2755 "without already added text length");
2756 MOZ_ASSERT(
2757 mAddedEndOffset == 55, // 53 + (52 - 50)
2758 "Test 3-4-3: mAddedEndOffset should be the first end of added text with "
2759 "added text length by the new change");
2760 Clear();
2762 // Replacing text in replaced text (became shorter) (not sure if actually
2763 // occurs)
2764 MergeWith(TextChangeData(36, 48, 50, false, false));
2765 MergeWith(TextChangeData(37, 49, 47, false, false));
2766 MOZ_ASSERT(mStartOffset == 36,
2767 "Test 3-5-1: mStartOffset should be the smallest offset");
2768 MOZ_ASSERT(
2769 mRemovedEndOffset == 48,
2770 "Test 3-5-2: mRemovedEndOffset should be the the first end of removed "
2771 "text");
2772 MOZ_ASSERT(mAddedEndOffset == 48, // 50 + (47 - 49)
2773 "Test 3-5-3: mAddedEndOffset should be the first end of added "
2774 "text without "
2775 "removed text length by the new change");
2776 Clear();
2778 // Replacing text in replaced text (became longer) (not sure if actually
2779 // occurs)
2780 MergeWith(TextChangeData(32, 48, 53, false, false));
2781 MergeWith(TextChangeData(43, 50, 47, false, false));
2782 MOZ_ASSERT(mStartOffset == 32,
2783 "Test 3-6-1: mStartOffset should be the smallest offset");
2784 MOZ_ASSERT(mRemovedEndOffset == 48,
2785 "Test 3-6-2: mRemovedEndOffset should be the the last end of "
2786 "removed text "
2787 "without already added text length");
2788 MOZ_ASSERT(mAddedEndOffset == 50, // 53 + (47 - 50)
2789 "Test 3-6-3: mAddedEndOffset should be the first end of added "
2790 "text without "
2791 "removed text length by the new change");
2792 Clear();
2794 /****************************************************************************
2795 * Case 4
2796 ****************************************************************************/
2798 // Replacing text all of already append text (not sure if actually occurs)
2799 MergeWith(TextChangeData(50, 50, 55, false, false));
2800 MergeWith(TextChangeData(44, 66, 68, false, false));
2801 MOZ_ASSERT(mStartOffset == 44,
2802 "Test 4-1-1: mStartOffset should be the smallest offset");
2803 MOZ_ASSERT(mRemovedEndOffset == 61, // 66 - (55 - 50)
2804 "Test 4-1-2: mRemovedEndOffset should be the the last end of "
2805 "removed text "
2806 "without already added text length");
2807 MOZ_ASSERT(
2808 mAddedEndOffset == 68,
2809 "Test 4-1-3: mAddedEndOffset should be the last end of added text");
2810 Clear();
2812 // Replacing text around a point in which text was removed (not sure if
2813 // actually occurs)
2814 MergeWith(TextChangeData(50, 62, 50, false, false));
2815 MergeWith(TextChangeData(44, 66, 68, false, false));
2816 MOZ_ASSERT(mStartOffset == 44,
2817 "Test 4-2-1: mStartOffset should be the smallest offset");
2818 MOZ_ASSERT(mRemovedEndOffset == 78, // 66 - (50 - 62)
2819 "Test 4-2-2: mRemovedEndOffset should be the the last end of "
2820 "removed text "
2821 "without already removed text length");
2822 MOZ_ASSERT(
2823 mAddedEndOffset == 68,
2824 "Test 4-2-3: mAddedEndOffset should be the last end of added text");
2825 Clear();
2827 // Replacing text all replaced text (became shorter) (not sure if actually
2828 // occurs)
2829 MergeWith(TextChangeData(50, 62, 60, false, false));
2830 MergeWith(TextChangeData(49, 128, 130, false, false));
2831 MOZ_ASSERT(mStartOffset == 49,
2832 "Test 4-3-1: mStartOffset should be the smallest offset");
2833 MOZ_ASSERT(mRemovedEndOffset == 130, // 128 - (60 - 62)
2834 "Test 4-3-2: mRemovedEndOffset should be the the last end of "
2835 "removed text "
2836 "without already removed text length");
2837 MOZ_ASSERT(
2838 mAddedEndOffset == 130,
2839 "Test 4-3-3: mAddedEndOffset should be the last end of added text");
2840 Clear();
2842 // Replacing text all replaced text (became longer) (not sure if actually
2843 // occurs)
2844 MergeWith(TextChangeData(50, 61, 73, false, false));
2845 MergeWith(TextChangeData(44, 100, 50, false, false));
2846 MOZ_ASSERT(mStartOffset == 44,
2847 "Test 4-4-1: mStartOffset should be the smallest offset");
2848 MOZ_ASSERT(mRemovedEndOffset == 88, // 100 - (73 - 61)
2849 "Test 4-4-2: mRemovedEndOffset should be the the last end of "
2850 "removed text "
2851 "with already added text length");
2852 MOZ_ASSERT(
2853 mAddedEndOffset == 50,
2854 "Test 4-4-3: mAddedEndOffset should be the last end of added text");
2855 Clear();
2857 /****************************************************************************
2858 * Case 5
2859 ****************************************************************************/
2861 // Replacing text around start of added text (not sure if actually occurs)
2862 MergeWith(TextChangeData(50, 50, 55, false, false));
2863 MergeWith(TextChangeData(48, 52, 49, false, false));
2864 MOZ_ASSERT(mStartOffset == 48,
2865 "Test 5-1-1: mStartOffset should be the smallest offset");
2866 MOZ_ASSERT(
2867 mRemovedEndOffset == 50,
2868 "Test 5-1-2: mRemovedEndOffset should be the the first end of removed "
2869 "text");
2870 MOZ_ASSERT(
2871 mAddedEndOffset == 52, // 55 + (52 - 49)
2872 "Test 5-1-3: mAddedEndOffset should be the first end of added text with "
2873 "added text length by the new change");
2874 Clear();
2876 // Replacing text around start of replaced text (became shorter) (not sure if
2877 // actually occurs)
2878 MergeWith(TextChangeData(50, 60, 58, false, false));
2879 MergeWith(TextChangeData(43, 50, 48, false, false));
2880 MOZ_ASSERT(mStartOffset == 43,
2881 "Test 5-2-1: mStartOffset should be the smallest offset");
2882 MOZ_ASSERT(
2883 mRemovedEndOffset == 60,
2884 "Test 5-2-2: mRemovedEndOffset should be the the first end of removed "
2885 "text");
2886 MOZ_ASSERT(mAddedEndOffset == 56, // 58 + (48 - 50)
2887 "Test 5-2-3: mAddedEndOffset should be the first end of added "
2888 "text without "
2889 "removed text length by the new change");
2890 Clear();
2892 // Replacing text around start of replaced text (became longer) (not sure if
2893 // actually occurs)
2894 MergeWith(TextChangeData(50, 60, 68, false, false));
2895 MergeWith(TextChangeData(43, 55, 53, false, false));
2896 MOZ_ASSERT(mStartOffset == 43,
2897 "Test 5-3-1: mStartOffset should be the smallest offset");
2898 MOZ_ASSERT(
2899 mRemovedEndOffset == 60,
2900 "Test 5-3-2: mRemovedEndOffset should be the the first end of removed "
2901 "text");
2902 MOZ_ASSERT(mAddedEndOffset == 66, // 68 + (53 - 55)
2903 "Test 5-3-3: mAddedEndOffset should be the first end of added "
2904 "text without "
2905 "removed text length by the new change");
2906 Clear();
2908 // Replacing text around start of replaced text (became shorter) (not sure if
2909 // actually occurs)
2910 MergeWith(TextChangeData(50, 60, 58, false, false));
2911 MergeWith(TextChangeData(43, 50, 128, false, false));
2912 MOZ_ASSERT(mStartOffset == 43,
2913 "Test 5-4-1: mStartOffset should be the smallest offset");
2914 MOZ_ASSERT(
2915 mRemovedEndOffset == 60,
2916 "Test 5-4-2: mRemovedEndOffset should be the the first end of removed "
2917 "text");
2918 MOZ_ASSERT(
2919 mAddedEndOffset == 136, // 58 + (128 - 50)
2920 "Test 5-4-3: mAddedEndOffset should be the first end of added text with "
2921 "added text length by the new change");
2922 Clear();
2924 // Replacing text around start of replaced text (became longer) (not sure if
2925 // actually occurs)
2926 MergeWith(TextChangeData(50, 60, 68, false, false));
2927 MergeWith(TextChangeData(43, 55, 65, false, false));
2928 MOZ_ASSERT(mStartOffset == 43,
2929 "Test 5-5-1: mStartOffset should be the smallest offset");
2930 MOZ_ASSERT(
2931 mRemovedEndOffset == 60,
2932 "Test 5-5-2: mRemovedEndOffset should be the the first end of removed "
2933 "text");
2934 MOZ_ASSERT(
2935 mAddedEndOffset == 78, // 68 + (65 - 55)
2936 "Test 5-5-3: mAddedEndOffset should be the first end of added text with "
2937 "added text length by the new change");
2938 Clear();
2940 /****************************************************************************
2941 * Case 6
2942 ****************************************************************************/
2944 // Appending text before already added text (not sure if actually occurs)
2945 MergeWith(TextChangeData(30, 30, 45, false, false));
2946 MergeWith(TextChangeData(10, 10, 20, false, false));
2947 MOZ_ASSERT(mStartOffset == 10,
2948 "Test 6-1-1: mStartOffset should be the smallest offset");
2949 MOZ_ASSERT(
2950 mRemovedEndOffset == 30,
2951 "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed "
2952 "text");
2953 MOZ_ASSERT(
2954 mAddedEndOffset == 55, // 45 + (20 - 10)
2955 "Test 6-1-3: mAddedEndOffset should be the first end of added text with "
2956 "added text length by the new change");
2957 Clear();
2959 // Removing text before already removed text (not sure if actually occurs)
2960 MergeWith(TextChangeData(30, 35, 30, false, false));
2961 MergeWith(TextChangeData(10, 25, 10, false, false));
2962 MOZ_ASSERT(mStartOffset == 10,
2963 "Test 6-2-1: mStartOffset should be the smallest offset");
2964 MOZ_ASSERT(
2965 mRemovedEndOffset == 35,
2966 "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed "
2967 "text");
2968 MOZ_ASSERT(
2969 mAddedEndOffset == 15, // 30 - (25 - 10)
2970 "Test 6-2-3: mAddedEndOffset should be the first end of added text with "
2971 "removed text length by the new change");
2972 Clear();
2974 // Replacing text before already replaced text (not sure if actually occurs)
2975 MergeWith(TextChangeData(50, 65, 70, false, false));
2976 MergeWith(TextChangeData(13, 24, 15, false, false));
2977 MOZ_ASSERT(mStartOffset == 13,
2978 "Test 6-3-1: mStartOffset should be the smallest offset");
2979 MOZ_ASSERT(
2980 mRemovedEndOffset == 65,
2981 "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed "
2982 "text");
2983 MOZ_ASSERT(mAddedEndOffset == 61, // 70 + (15 - 24)
2984 "Test 6-3-3: mAddedEndOffset should be the first end of added "
2985 "text without "
2986 "removed text length by the new change");
2987 Clear();
2989 // Replacing text before already replaced text (not sure if actually occurs)
2990 MergeWith(TextChangeData(50, 65, 70, false, false));
2991 MergeWith(TextChangeData(13, 24, 36, false, false));
2992 MOZ_ASSERT(mStartOffset == 13,
2993 "Test 6-4-1: mStartOffset should be the smallest offset");
2994 MOZ_ASSERT(
2995 mRemovedEndOffset == 65,
2996 "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed "
2997 "text");
2998 MOZ_ASSERT(mAddedEndOffset == 82, // 70 + (36 - 24)
2999 "Test 6-4-3: mAddedEndOffset should be the first end of added "
3000 "text without "
3001 "removed text length by the new change");
3002 Clear();
3005 #endif // #ifdef DEBUG
3007 } // namespace mozilla::widget
3009 #ifdef DEBUG
3010 //////////////////////////////////////////////////////////////
3012 // Convert a GUI event message code to a string.
3013 // Makes it a lot easier to debug events.
3015 // See gtk/nsWidget.cpp and windows/nsWindow.cpp
3016 // for a DebugPrintEvent() function that uses
3017 // this.
3019 //////////////////////////////////////////////////////////////
3020 /* static */
3021 nsAutoString nsBaseWidget::debug_GuiEventToString(WidgetGUIEvent* aGuiEvent) {
3022 NS_ASSERTION(nullptr != aGuiEvent, "cmon, null gui event.");
3024 nsAutoString eventName(u"UNKNOWN"_ns);
3026 # define _ASSIGN_eventName(_value, _name) \
3027 case _value: \
3028 eventName.AssignLiteral(_name); \
3029 break
3031 switch (aGuiEvent->mMessage) {
3032 _ASSIGN_eventName(eBlur, "eBlur");
3033 _ASSIGN_eventName(eDrop, "eDrop");
3034 _ASSIGN_eventName(eDragEnter, "eDragEnter");
3035 _ASSIGN_eventName(eDragExit, "eDragExit");
3036 _ASSIGN_eventName(eDragOver, "eDragOver");
3037 _ASSIGN_eventName(eEditorInput, "eEditorInput");
3038 _ASSIGN_eventName(eFocus, "eFocus");
3039 _ASSIGN_eventName(eFocusIn, "eFocusIn");
3040 _ASSIGN_eventName(eFocusOut, "eFocusOut");
3041 _ASSIGN_eventName(eFormSelect, "eFormSelect");
3042 _ASSIGN_eventName(eFormChange, "eFormChange");
3043 _ASSIGN_eventName(eFormReset, "eFormReset");
3044 _ASSIGN_eventName(eFormSubmit, "eFormSubmit");
3045 _ASSIGN_eventName(eImageAbort, "eImageAbort");
3046 _ASSIGN_eventName(eLoadError, "eLoadError");
3047 _ASSIGN_eventName(eKeyDown, "eKeyDown");
3048 _ASSIGN_eventName(eKeyPress, "eKeyPress");
3049 _ASSIGN_eventName(eKeyUp, "eKeyUp");
3050 _ASSIGN_eventName(eMouseEnterIntoWidget, "eMouseEnterIntoWidget");
3051 _ASSIGN_eventName(eMouseExitFromWidget, "eMouseExitFromWidget");
3052 _ASSIGN_eventName(eMouseDown, "eMouseDown");
3053 _ASSIGN_eventName(eMouseUp, "eMouseUp");
3054 _ASSIGN_eventName(eMouseClick, "eMouseClick");
3055 _ASSIGN_eventName(eMouseAuxClick, "eMouseAuxClick");
3056 _ASSIGN_eventName(eMouseDoubleClick, "eMouseDoubleClick");
3057 _ASSIGN_eventName(eMouseMove, "eMouseMove");
3058 _ASSIGN_eventName(eLoad, "eLoad");
3059 _ASSIGN_eventName(ePopState, "ePopState");
3060 _ASSIGN_eventName(eBeforeScriptExecute, "eBeforeScriptExecute");
3061 _ASSIGN_eventName(eAfterScriptExecute, "eAfterScriptExecute");
3062 _ASSIGN_eventName(eUnload, "eUnload");
3063 _ASSIGN_eventName(eHashChange, "eHashChange");
3064 _ASSIGN_eventName(eReadyStateChange, "eReadyStateChange");
3065 _ASSIGN_eventName(eXULBroadcast, "eXULBroadcast");
3066 _ASSIGN_eventName(eXULCommandUpdate, "eXULCommandUpdate");
3068 # undef _ASSIGN_eventName
3070 default: {
3071 eventName.AssignLiteral("UNKNOWN: ");
3072 eventName.AppendInt(aGuiEvent->mMessage);
3073 } break;
3076 return nsAutoString(eventName);
3078 //////////////////////////////////////////////////////////////
3080 // Code to deal with paint and event debug prefs.
3082 //////////////////////////////////////////////////////////////
3083 struct PrefPair {
3084 const char* name;
3085 bool value;
3088 static PrefPair debug_PrefValues[] = {
3089 {"nglayout.debug.crossing_event_dumping", false},
3090 {"nglayout.debug.event_dumping", false},
3091 {"nglayout.debug.invalidate_dumping", false},
3092 {"nglayout.debug.motion_event_dumping", false},
3093 {"nglayout.debug.paint_dumping", false}};
3095 //////////////////////////////////////////////////////////////
3096 bool nsBaseWidget::debug_GetCachedBoolPref(const char* aPrefName) {
3097 NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null.");
3099 for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3100 if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) {
3101 return debug_PrefValues[i].value;
3105 return false;
3107 //////////////////////////////////////////////////////////////
3108 static void debug_SetCachedBoolPref(const char* aPrefName, bool aValue) {
3109 NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null.");
3111 for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3112 if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) {
3113 debug_PrefValues[i].value = aValue;
3115 return;
3119 NS_ASSERTION(false, "cmon, this code is not reached dude.");
3122 //////////////////////////////////////////////////////////////
3123 class Debug_PrefObserver final : public nsIObserver {
3124 ~Debug_PrefObserver() = default;
3126 public:
3127 NS_DECL_ISUPPORTS
3128 NS_DECL_NSIOBSERVER
3131 NS_IMPL_ISUPPORTS(Debug_PrefObserver, nsIObserver)
3133 NS_IMETHODIMP
3134 Debug_PrefObserver::Observe(nsISupports* subject, const char* topic,
3135 const char16_t* data) {
3136 NS_ConvertUTF16toUTF8 prefName(data);
3138 bool value = Preferences::GetBool(prefName.get(), false);
3139 debug_SetCachedBoolPref(prefName.get(), value);
3140 return NS_OK;
3143 //////////////////////////////////////////////////////////////
3144 /* static */ void debug_RegisterPrefCallbacks() {
3145 static bool once = true;
3147 if (!once) {
3148 return;
3151 once = false;
3153 nsCOMPtr<nsIObserver> obs(new Debug_PrefObserver());
3154 for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3155 // Initialize the pref values
3156 debug_PrefValues[i].value =
3157 Preferences::GetBool(debug_PrefValues[i].name, false);
3159 if (obs) {
3160 // Register callbacks for when these change
3161 nsCString name;
3162 name.AssignLiteral(debug_PrefValues[i].name,
3163 strlen(debug_PrefValues[i].name));
3164 Preferences::AddStrongObserver(obs, name);
3168 //////////////////////////////////////////////////////////////
3169 static int32_t _GetPrintCount() {
3170 static int32_t sCount = 0;
3172 return ++sCount;
3174 //////////////////////////////////////////////////////////////
3175 /* static */
3176 void nsBaseWidget::debug_DumpEvent(FILE* aFileOut, nsIWidget* aWidget,
3177 WidgetGUIEvent* aGuiEvent,
3178 const char* aWidgetName, int32_t aWindowID) {
3179 if (aGuiEvent->mMessage == eMouseMove) {
3180 if (!debug_GetCachedBoolPref("nglayout.debug.motion_event_dumping")) return;
3183 if (aGuiEvent->mMessage == eMouseEnterIntoWidget ||
3184 aGuiEvent->mMessage == eMouseExitFromWidget) {
3185 if (!debug_GetCachedBoolPref("nglayout.debug.crossing_event_dumping"))
3186 return;
3189 if (!debug_GetCachedBoolPref("nglayout.debug.event_dumping")) return;
3191 NS_LossyConvertUTF16toASCII tempString(
3192 debug_GuiEventToString(aGuiEvent).get());
3194 fprintf(aFileOut, "%4d %-26s widget=%-8p name=%-12s id=0x%-6x refpt=%d,%d\n",
3195 _GetPrintCount(), tempString.get(), (void*)aWidget, aWidgetName,
3196 aWindowID, aGuiEvent->mRefPoint.x, aGuiEvent->mRefPoint.y);
3198 //////////////////////////////////////////////////////////////
3199 /* static */
3200 void nsBaseWidget::debug_DumpPaintEvent(FILE* aFileOut, nsIWidget* aWidget,
3201 const nsIntRegion& aRegion,
3202 const char* aWidgetName,
3203 int32_t aWindowID) {
3204 NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE");
3205 NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null");
3207 if (!debug_GetCachedBoolPref("nglayout.debug.paint_dumping")) return;
3209 nsIntRect rect = aRegion.GetBounds();
3210 fprintf(aFileOut,
3211 "%4d PAINT widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d "
3212 "%3d,%-3d",
3213 _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID, rect.X(),
3214 rect.Y(), rect.Width(), rect.Height());
3216 fprintf(aFileOut, "\n");
3218 //////////////////////////////////////////////////////////////
3219 /* static */
3220 void nsBaseWidget::debug_DumpInvalidate(FILE* aFileOut, nsIWidget* aWidget,
3221 const LayoutDeviceIntRect* aRect,
3222 const char* aWidgetName,
3223 int32_t aWindowID) {
3224 if (!debug_GetCachedBoolPref("nglayout.debug.invalidate_dumping")) return;
3226 NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE");
3227 NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null");
3229 fprintf(aFileOut, "%4d Invalidate widget=%p name=%-12s id=0x%-6x",
3230 _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID);
3232 if (aRect) {
3233 fprintf(aFileOut, " rect=%3d,%-3d %3d,%-3d", aRect->X(), aRect->Y(),
3234 aRect->Width(), aRect->Height());
3235 } else {
3236 fprintf(aFileOut, " rect=%-15s", "none");
3239 fprintf(aFileOut, "\n");
3241 //////////////////////////////////////////////////////////////
3243 #endif // DEBUG