Bug 1527719 [wpt PR 15359] - KV storage: make backingStore return the same frozen...
[gecko.git] / widget / nsBaseWidget.cpp
blobf8a2d1c3be5093654b07246ff9997ff730f7657f
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 "mozilla/ArrayUtils.h"
9 #include "mozilla/UniquePtr.h"
10 #include "mozilla/TextEventDispatcher.h"
11 #include "mozilla/TextEventDispatcherListener.h"
13 #include "mozilla/layers/CompositorBridgeChild.h"
14 #include "mozilla/layers/CompositorBridgeParent.h"
15 #include "mozilla/layers/PLayerTransactionChild.h"
16 #include "mozilla/layers/ImageBridgeChild.h"
17 #include "LiveResizeListener.h"
18 #include "nsBaseWidget.h"
19 #include "nsDeviceContext.h"
20 #include "nsCOMPtr.h"
21 #include "nsGfxCIID.h"
22 #include "nsWidgetsCID.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsIKeyEventInPluginCallback.h"
25 #include "nsIScreenManager.h"
26 #include "nsAppDirectoryServiceDefs.h"
27 #include "nsISimpleEnumerator.h"
28 #include "nsIContent.h"
29 #include "mozilla/dom/Document.h"
30 #include "nsIPresShell.h"
31 #include "nsIServiceManager.h"
32 #include "mozilla/Preferences.h"
33 #include "BasicLayers.h"
34 #include "ClientLayerManager.h"
35 #include "mozilla/layers/Compositor.h"
36 #include "nsIXULRuntime.h"
37 #include "nsIXULWindow.h"
38 #include "nsIBaseWindow.h"
39 #include "nsXULPopupManager.h"
40 #include "nsXBLWindowKeyHandler.h"
41 #include "nsIWidgetListener.h"
42 #include "nsIGfxInfo.h"
43 #include "npapi.h"
44 #include "X11UndefineNone.h"
45 #include "base/thread.h"
46 #include "prdtoa.h"
47 #include "prenv.h"
48 #include "mozilla/Attributes.h"
49 #include "mozilla/Unused.h"
50 #include "nsContentUtils.h"
51 #include "gfxPrefs.h"
52 #include "mozilla/gfx/2D.h"
53 #include "mozilla/MouseEvents.h"
54 #include "GLConsts.h"
55 #include "mozilla/Unused.h"
56 #include "mozilla/IMEStateManager.h"
57 #include "mozilla/VsyncDispatcher.h"
58 #include "mozilla/layers/IAPZCTreeManager.h"
59 #include "mozilla/layers/APZEventState.h"
60 #include "mozilla/layers/APZInputBridge.h"
61 #include "mozilla/layers/APZThreadUtils.h"
62 #include "mozilla/layers/ChromeProcessController.h"
63 #include "mozilla/layers/CompositorOptions.h"
64 #include "mozilla/layers/InputAPZContext.h"
65 #include "mozilla/layers/APZCCallbackHelper.h"
66 #include "mozilla/layers/WebRenderLayerManager.h"
67 #include "mozilla/dom/ContentChild.h"
68 #include "mozilla/dom/TabParent.h"
69 #include "mozilla/gfx/GPUProcessManager.h"
70 #include "mozilla/gfx/gfxVars.h"
71 #include "mozilla/Move.h"
72 #include "mozilla/Sprintf.h"
73 #include "mozilla/webrender/WebRenderTypes.h"
74 #include "nsRefPtrHashtable.h"
75 #include "TouchEvents.h"
76 #include "WritingModes.h"
77 #include "InputData.h"
78 #include "FrameLayerBuilder.h"
79 #ifdef ACCESSIBILITY
80 # include "nsAccessibilityService.h"
81 #endif
82 #include "gfxConfig.h"
83 #include "mozilla/layers/CompositorSession.h"
84 #include "VRManagerChild.h"
85 #include "gfxConfig.h"
86 #include "nsView.h"
87 #include "nsViewManager.h"
89 #ifdef DEBUG
90 # include "nsIObserver.h"
92 static void debug_RegisterPrefCallbacks();
94 #endif
96 #ifdef NOISY_WIDGET_LEAKS
97 static int32_t gNumWidgets;
98 #endif
100 #ifdef XP_MACOSX
101 # include "nsCocoaFeatures.h"
102 #endif
104 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
105 static nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>* sPluginWidgetList;
106 #endif
108 nsIRollupListener* nsBaseWidget::gRollupListener = nullptr;
110 using namespace mozilla::dom;
111 using namespace mozilla::layers;
112 using namespace mozilla::ipc;
113 using namespace mozilla::widget;
114 using namespace mozilla;
115 using base::Thread;
117 // Global user preference for disabling native theme. Used
118 // in NativeWindowTheme.
119 bool gDisableNativeTheme = false;
121 // Async pump timer during injected long touch taps
122 #define TOUCH_INJECT_PUMP_TIMER_MSEC 50
123 #define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500
124 int32_t nsIWidget::sPointerIdCounter = 0;
126 // Some statics from nsIWidget.h
127 /*static*/
128 uint64_t AutoObserverNotifier::sObserverId = 0;
129 /*static*/ nsDataHashtable<nsUint64HashKey, nsCOMPtr<nsIObserver>>
130 AutoObserverNotifier::sSavedObservers;
132 // The maximum amount of time to let the EnableDragDrop runnable wait in the
133 // idle queue before timing out and moving it to the regular queue. Value is in
134 // milliseconds.
135 const uint32_t kAsyncDragDropTimeout = 1000;
137 namespace mozilla {
138 namespace widget {
140 void IMENotification::SelectionChangeDataBase::SetWritingMode(
141 const WritingMode& aWritingMode) {
142 mWritingMode = aWritingMode.mWritingMode;
145 WritingMode IMENotification::SelectionChangeDataBase::GetWritingMode() const {
146 return WritingMode(mWritingMode);
149 } // namespace widget
150 } // namespace mozilla
152 NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidget, nsISupportsWeakReference)
154 //-------------------------------------------------------------------------
156 // nsBaseWidget constructor
158 //-------------------------------------------------------------------------
160 nsBaseWidget::nsBaseWidget()
161 : mWidgetListener(nullptr),
162 mAttachedWidgetListener(nullptr),
163 mPreviouslyAttachedWidgetListener(nullptr),
164 mLayerManager(nullptr),
165 mCompositorVsyncDispatcher(nullptr),
166 mCursor(eCursor_standard),
167 mBorderStyle(eBorderStyle_none),
168 mBounds(0, 0, 0, 0),
169 mOriginalBounds(nullptr),
170 mClipRectCount(0),
171 mSizeMode(nsSizeMode_Normal),
172 mPopupLevel(ePopupLevelTop),
173 mPopupType(ePopupTypeAny),
174 mHasRemoteContent(false),
175 mUpdateCursor(true),
176 mUseAttachedEvents(false),
177 mIMEHasFocus(false),
178 mIsFullyOccluded(false) {
179 #ifdef NOISY_WIDGET_LEAKS
180 gNumWidgets++;
181 printf("WIDGETS+ = %d\n", gNumWidgets);
182 #endif
184 #ifdef DEBUG
185 debug_RegisterPrefCallbacks();
186 #endif
188 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
189 if (!sPluginWidgetList) {
190 sPluginWidgetList = new nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>();
192 #endif
193 mShutdownObserver = new WidgetShutdownObserver(this);
196 NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver)
198 WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget)
199 : mWidget(aWidget), mRegistered(false) {
200 Register();
203 WidgetShutdownObserver::~WidgetShutdownObserver() {
204 // No need to call Unregister(), we can't be destroyed until nsBaseWidget
205 // gets torn down. The observer service and nsBaseWidget have a ref on us
206 // so nsBaseWidget has to call Unregister and then clear its ref.
209 NS_IMETHODIMP
210 WidgetShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
211 const char16_t* aData) {
212 if (mWidget && !strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
213 RefPtr<nsBaseWidget> widget(mWidget);
214 widget->Shutdown();
216 return NS_OK;
219 void WidgetShutdownObserver::Register() {
220 if (!mRegistered) {
221 mRegistered = true;
222 nsContentUtils::RegisterShutdownObserver(this);
226 void WidgetShutdownObserver::Unregister() {
227 if (mRegistered) {
228 mWidget = nullptr;
229 nsContentUtils::UnregisterShutdownObserver(this);
230 mRegistered = false;
234 void nsBaseWidget::Shutdown() {
235 NotifyLiveResizeStopped();
236 RevokeTransactionIdAllocator();
237 DestroyCompositor();
238 FreeShutdownObserver();
239 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
240 if (sPluginWidgetList) {
241 delete sPluginWidgetList;
242 sPluginWidgetList = nullptr;
244 #endif
247 void nsBaseWidget::DestroyCompositor() {
248 // We release this before releasing the compositor, since it may hold the
249 // last reference to our ClientLayerManager. ClientLayerManager's dtor can
250 // trigger a paint, creating a new compositor, and we don't want to re-use
251 // the old vsync dispatcher.
252 if (mCompositorVsyncDispatcher) {
253 MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
255 MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
256 mCompositorVsyncDispatcher->Shutdown();
257 mCompositorVsyncDispatcher = nullptr;
260 // The compositor shutdown sequence looks like this:
261 // 1. CompositorSession calls CompositorBridgeChild::Destroy.
262 // 2. CompositorBridgeChild synchronously sends WillClose.
263 // 3. CompositorBridgeParent releases some resources (such as the layer
264 // manager, compositor, and widget).
265 // 4. CompositorBridgeChild::Destroy returns.
266 // 5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the
267 // compositor thread when the I/O thread closes the IPC channel.
268 // 6. Step 5 will schedule DeferredDestroy on the compositor thread, which
269 // releases the reference CompositorBridgeParent holds to itself.
271 // When CompositorSession::Shutdown returns, we assume the compositor is gone
272 // or will be gone very soon.
273 if (mCompositorSession) {
274 ReleaseContentController();
275 mAPZC = nullptr;
276 SetCompositorWidgetDelegate(nullptr);
277 mCompositorBridgeChild = nullptr;
279 // XXX CompositorBridgeChild and CompositorBridgeParent might be re-created
280 // in ClientLayerManager destructor. See bug 1133426.
281 RefPtr<CompositorSession> session = mCompositorSession.forget();
282 session->Shutdown();
286 // This prevents the layer manager from starting a new transaction during
287 // shutdown.
288 void nsBaseWidget::RevokeTransactionIdAllocator() {
289 if (!mLayerManager) {
290 return;
292 mLayerManager->SetTransactionIdAllocator(nullptr);
295 void nsBaseWidget::ReleaseContentController() {
296 if (mRootContentController) {
297 mRootContentController->Destroy();
298 mRootContentController = nullptr;
302 void nsBaseWidget::DestroyLayerManager() {
303 if (mLayerManager) {
304 mLayerManager->Destroy();
305 mLayerManager = nullptr;
307 DestroyCompositor();
310 void nsBaseWidget::OnRenderingDeviceReset() { DestroyLayerManager(); }
312 void nsBaseWidget::FreeShutdownObserver() {
313 if (mShutdownObserver) {
314 mShutdownObserver->Unregister();
316 mShutdownObserver = nullptr;
319 //-------------------------------------------------------------------------
321 // nsBaseWidget destructor
323 //-------------------------------------------------------------------------
325 nsBaseWidget::~nsBaseWidget() {
326 IMEStateManager::WidgetDestroyed(this);
328 if (mLayerManager) {
329 if (BasicLayerManager* mgr = mLayerManager->AsBasicLayerManager()) {
330 mgr->ClearRetainerWidget();
334 FreeShutdownObserver();
335 RevokeTransactionIdAllocator();
336 DestroyLayerManager();
338 #ifdef NOISY_WIDGET_LEAKS
339 gNumWidgets--;
340 printf("WIDGETS- = %d\n", gNumWidgets);
341 #endif
343 delete mOriginalBounds;
346 //-------------------------------------------------------------------------
348 // Basic create.
350 //-------------------------------------------------------------------------
351 void nsBaseWidget::BaseCreate(nsIWidget* aParent, nsWidgetInitData* aInitData) {
352 static bool gDisableNativeThemeCached = false;
353 if (!gDisableNativeThemeCached) {
354 Preferences::AddBoolVarCache(&gDisableNativeTheme,
355 "mozilla.widget.disable-native-theme",
356 gDisableNativeTheme);
357 gDisableNativeThemeCached = true;
360 // keep a reference to the device context
361 if (nullptr != aInitData) {
362 mWindowType = aInitData->mWindowType;
363 mBorderStyle = aInitData->mBorderStyle;
364 mPopupLevel = aInitData->mPopupLevel;
365 mPopupType = aInitData->mPopupHint;
366 mHasRemoteContent = aInitData->mHasRemoteContent;
369 if (aParent) {
370 aParent->AddChild(this);
374 //-------------------------------------------------------------------------
376 // Accessor functions to get/set the client data
378 //-------------------------------------------------------------------------
380 nsIWidgetListener* nsBaseWidget::GetWidgetListener() { return mWidgetListener; }
382 void nsBaseWidget::SetWidgetListener(nsIWidgetListener* aWidgetListener) {
383 mWidgetListener = aWidgetListener;
386 already_AddRefed<nsIWidget> nsBaseWidget::CreateChild(
387 const LayoutDeviceIntRect& aRect, nsWidgetInitData* aInitData,
388 bool aForceUseIWidgetParent) {
389 nsIWidget* parent = this;
390 nsNativeWidget nativeParent = nullptr;
392 if (!aForceUseIWidgetParent) {
393 // Use only either parent or nativeParent, not both, to match
394 // existing code. Eventually Create() should be divested of its
395 // nativeWidget parameter.
396 nativeParent = parent ? parent->GetNativeData(NS_NATIVE_WIDGET) : nullptr;
397 parent = nativeParent ? nullptr : parent;
398 MOZ_ASSERT(!parent || !nativeParent, "messed up logic");
401 nsCOMPtr<nsIWidget> widget;
402 if (aInitData && aInitData->mWindowType == eWindowType_popup) {
403 widget = AllocateChildPopupWidget();
404 } else {
405 widget = nsIWidget::CreateChildWindow();
408 if (widget &&
409 NS_SUCCEEDED(widget->Create(parent, nativeParent, aRect, aInitData))) {
410 return widget.forget();
413 return nullptr;
416 // Attach a view to our widget which we'll send events to.
417 void nsBaseWidget::AttachViewToTopLevel(bool aUseAttachedEvents) {
418 NS_ASSERTION((mWindowType == eWindowType_toplevel ||
419 mWindowType == eWindowType_dialog ||
420 mWindowType == eWindowType_invisible ||
421 mWindowType == eWindowType_child),
422 "Can't attach to window of that type");
424 mUseAttachedEvents = aUseAttachedEvents;
427 nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener() {
428 return mAttachedWidgetListener;
431 nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener() {
432 return mPreviouslyAttachedWidgetListener;
435 void nsBaseWidget::SetPreviouslyAttachedWidgetListener(
436 nsIWidgetListener* aListener) {
437 mPreviouslyAttachedWidgetListener = aListener;
440 void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener) {
441 mAttachedWidgetListener = aListener;
444 //-------------------------------------------------------------------------
446 // Close this nsBaseWidget
448 //-------------------------------------------------------------------------
449 void nsBaseWidget::Destroy() {
450 // Just in case our parent is the only ref to us
451 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
452 // disconnect from the parent
453 nsIWidget* parent = GetParent();
454 if (parent) {
455 parent->RemoveChild(this);
458 #if defined(XP_WIN)
459 // Allow our scroll capture container to be cleaned up, if we have one.
460 mScrollCaptureContainer = nullptr;
461 #endif
464 //-------------------------------------------------------------------------
466 // Get this nsBaseWidget parent
468 //-------------------------------------------------------------------------
469 nsIWidget* nsBaseWidget::GetParent(void) { return nullptr; }
471 //-------------------------------------------------------------------------
473 // Get this nsBaseWidget top level widget
475 //-------------------------------------------------------------------------
476 nsIWidget* nsBaseWidget::GetTopLevelWidget() {
477 nsIWidget *topLevelWidget = nullptr, *widget = this;
478 while (widget) {
479 topLevelWidget = widget;
480 widget = widget->GetParent();
482 return topLevelWidget;
485 //-------------------------------------------------------------------------
487 // Get this nsBaseWidget's top (non-sheet) parent (if it's a sheet)
489 //-------------------------------------------------------------------------
490 nsIWidget* nsBaseWidget::GetSheetWindowParent(void) { return nullptr; }
492 float nsBaseWidget::GetDPI() { return 96.0f; }
494 CSSToLayoutDeviceScale nsIWidget::GetDefaultScale() {
495 double devPixelsPerCSSPixel = DefaultScaleOverride();
497 if (devPixelsPerCSSPixel <= 0.0) {
498 devPixelsPerCSSPixel = GetDefaultScaleInternal();
501 return CSSToLayoutDeviceScale(devPixelsPerCSSPixel);
504 /* static */
505 double nsIWidget::DefaultScaleOverride() {
506 // The number of device pixels per CSS pixel. A value <= 0 means choose
507 // automatically based on the DPI. A positive value is used as-is. This
508 // effectively controls the size of a CSS "px".
509 static float devPixelsPerCSSPixel = -1.0f;
511 static bool valueCached = false;
512 if (!valueCached) {
513 Preferences::AddFloatVarCache(&devPixelsPerCSSPixel,
514 "layout.css.devPixelsPerPx", -1.0f);
515 valueCached = true;
518 return devPixelsPerCSSPixel;
521 //-------------------------------------------------------------------------
523 // Add a child to the list of children
525 //-------------------------------------------------------------------------
526 void nsBaseWidget::AddChild(nsIWidget* aChild) {
527 MOZ_ASSERT(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
528 "aChild not properly removed from its old child list");
530 if (!mFirstChild) {
531 mFirstChild = mLastChild = aChild;
532 } else {
533 // append to the list
534 MOZ_ASSERT(mLastChild);
535 MOZ_ASSERT(!mLastChild->GetNextSibling());
536 mLastChild->SetNextSibling(aChild);
537 aChild->SetPrevSibling(mLastChild);
538 mLastChild = aChild;
542 //-------------------------------------------------------------------------
544 // Remove a child from the list of children
546 //-------------------------------------------------------------------------
547 void nsBaseWidget::RemoveChild(nsIWidget* aChild) {
548 #ifdef DEBUG
549 # ifdef XP_MACOSX
550 // nsCocoaWindow doesn't implement GetParent, so in that case parent will be
551 // null and we'll just have to do without this assertion.
552 nsIWidget* parent = aChild->GetParent();
553 NS_ASSERTION(!parent || parent == this, "Not one of our kids!");
554 # else
555 MOZ_RELEASE_ASSERT(aChild->GetParent() == this, "Not one of our kids!");
556 # endif
557 #endif
559 if (mLastChild == aChild) {
560 mLastChild = mLastChild->GetPrevSibling();
562 if (mFirstChild == aChild) {
563 mFirstChild = mFirstChild->GetNextSibling();
566 // Now remove from the list. Make sure that we pass ownership of the tail
567 // of the list correctly before we have aChild let go of it.
568 nsIWidget* prev = aChild->GetPrevSibling();
569 nsIWidget* next = aChild->GetNextSibling();
570 if (prev) {
571 prev->SetNextSibling(next);
573 if (next) {
574 next->SetPrevSibling(prev);
577 aChild->SetNextSibling(nullptr);
578 aChild->SetPrevSibling(nullptr);
581 //-------------------------------------------------------------------------
583 // Sets widget's position within its parent's child list.
585 //-------------------------------------------------------------------------
586 void nsBaseWidget::SetZIndex(int32_t aZIndex) {
587 // Hold a ref to ourselves just in case, since we're going to remove
588 // from our parent.
589 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
591 mZIndex = aZIndex;
593 // reorder this child in its parent's list.
594 auto* parent = static_cast<nsBaseWidget*>(GetParent());
595 if (parent) {
596 parent->RemoveChild(this);
597 // Scope sib outside the for loop so we can check it afterward
598 nsIWidget* sib = parent->GetFirstChild();
599 for (; sib; sib = sib->GetNextSibling()) {
600 int32_t childZIndex = GetZIndex();
601 if (aZIndex < childZIndex) {
602 // Insert ourselves before sib
603 nsIWidget* prev = sib->GetPrevSibling();
604 mNextSibling = sib;
605 mPrevSibling = prev;
606 sib->SetPrevSibling(this);
607 if (prev) {
608 prev->SetNextSibling(this);
609 } else {
610 NS_ASSERTION(sib == parent->mFirstChild, "Broken child list");
611 // We've taken ownership of sib, so it's safe to have parent let
612 // go of it
613 parent->mFirstChild = this;
615 PlaceBehind(eZPlacementBelow, sib, false);
616 break;
619 // were we added to the list?
620 if (!sib) {
621 parent->AddChild(this);
626 //-------------------------------------------------------------------------
628 // Maximize, minimize or restore the window. The BaseWidget implementation
629 // merely stores the state.
631 //-------------------------------------------------------------------------
632 void nsBaseWidget::SetSizeMode(nsSizeMode aMode) {
633 MOZ_ASSERT(aMode == nsSizeMode_Normal || aMode == nsSizeMode_Minimized ||
634 aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen);
635 mSizeMode = aMode;
638 //-------------------------------------------------------------------------
640 // Get this component cursor
642 //-------------------------------------------------------------------------
644 void nsBaseWidget::SetCursor(nsCursor aCursor, imgIContainer*, uint32_t,
645 uint32_t) {
646 // We don't support the cursor image.
647 mCursor = aCursor;
650 //-------------------------------------------------------------------------
652 // Window transparency methods
654 //-------------------------------------------------------------------------
656 void nsBaseWidget::SetTransparencyMode(nsTransparencyMode aMode) {}
658 nsTransparencyMode nsBaseWidget::GetTransparencyMode() {
659 return eTransparencyOpaque;
662 bool nsBaseWidget::IsWindowClipRegionEqual(
663 const nsTArray<LayoutDeviceIntRect>& aRects) {
664 return mClipRects && mClipRectCount == aRects.Length() &&
665 memcmp(mClipRects.get(), aRects.Elements(),
666 sizeof(LayoutDeviceIntRect) * mClipRectCount) == 0;
669 void nsBaseWidget::StoreWindowClipRegion(
670 const nsTArray<LayoutDeviceIntRect>& aRects) {
671 mClipRectCount = aRects.Length();
672 mClipRects = MakeUnique<LayoutDeviceIntRect[]>(mClipRectCount);
673 if (mClipRects) {
674 memcpy(mClipRects.get(), aRects.Elements(),
675 sizeof(LayoutDeviceIntRect) * mClipRectCount);
679 void nsBaseWidget::GetWindowClipRegion(nsTArray<LayoutDeviceIntRect>* aRects) {
680 if (mClipRects) {
681 aRects->AppendElements(mClipRects.get(), mClipRectCount);
682 } else {
683 aRects->AppendElement(
684 LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height()));
688 const LayoutDeviceIntRegion nsBaseWidget::RegionFromArray(
689 const nsTArray<LayoutDeviceIntRect>& aRects) {
690 LayoutDeviceIntRegion region;
691 for (uint32_t i = 0; i < aRects.Length(); ++i) {
692 region.Or(region, aRects[i]);
694 return region;
697 void nsBaseWidget::ArrayFromRegion(const LayoutDeviceIntRegion& aRegion,
698 nsTArray<LayoutDeviceIntRect>& aRects) {
699 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
700 aRects.AppendElement(iter.Get());
704 nsresult nsBaseWidget::SetWindowClipRegion(
705 const nsTArray<LayoutDeviceIntRect>& aRects, bool aIntersectWithExisting) {
706 if (!aIntersectWithExisting) {
707 StoreWindowClipRegion(aRects);
708 } else {
709 // get current rects
710 nsTArray<LayoutDeviceIntRect> currentRects;
711 GetWindowClipRegion(&currentRects);
712 // create region from them
713 LayoutDeviceIntRegion currentRegion = RegionFromArray(currentRects);
714 // create region from new rects
715 LayoutDeviceIntRegion newRegion = RegionFromArray(aRects);
716 // intersect regions
717 LayoutDeviceIntRegion intersection;
718 intersection.And(currentRegion, newRegion);
719 // create int rect array from intersection
720 nsTArray<LayoutDeviceIntRect> rects;
721 ArrayFromRegion(intersection, rects);
722 // store
723 StoreWindowClipRegion(rects);
725 return NS_OK;
728 /* virtual */
729 void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage,
730 uint16_t aDuration,
731 nsISupports* aData,
732 nsIRunnable* aCallback) {
733 MOZ_ASSERT_UNREACHABLE(
734 "Should never call PerformFullscreenTransition on nsBaseWidget");
737 //-------------------------------------------------------------------------
739 // Put the window into full-screen mode
741 //-------------------------------------------------------------------------
742 void nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen,
743 nsIScreen* aScreen) {
744 HideWindowChrome(aFullScreen);
746 if (aFullScreen) {
747 if (!mOriginalBounds) {
748 mOriginalBounds = new LayoutDeviceIntRect();
750 *mOriginalBounds = GetScreenBounds();
752 // Move to top-left corner of screen and size to the screen dimensions
753 nsCOMPtr<nsIScreen> screen = aScreen;
754 if (!screen) {
755 screen = GetWidgetScreen();
757 if (screen) {
758 int32_t left, top, width, height;
759 if (NS_SUCCEEDED(
760 screen->GetRectDisplayPix(&left, &top, &width, &height))) {
761 Resize(left, top, width, height, true);
764 } else if (mOriginalBounds) {
765 if (BoundsUseDesktopPixels()) {
766 DesktopRect deskRect = *mOriginalBounds / GetDesktopToDeviceScale();
767 Resize(deskRect.X(), deskRect.Y(), deskRect.Width(), deskRect.Height(),
768 true);
769 } else {
770 Resize(mOriginalBounds->X(), mOriginalBounds->Y(),
771 mOriginalBounds->Width(), mOriginalBounds->Height(), true);
776 nsresult nsBaseWidget::MakeFullScreen(bool aFullScreen, nsIScreen* aScreen) {
777 InfallibleMakeFullScreen(aFullScreen, aScreen);
778 return NS_OK;
781 nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
782 nsBaseWidget* aWidget, gfxContext* aTarget, BufferMode aDoubleBuffering,
783 ScreenRotation aRotation)
784 : mWidget(aWidget) {
785 LayerManager* lm = mWidget->GetLayerManager();
786 NS_ASSERTION(
787 !lm || lm->GetBackendType() == LayersBackend::LAYERS_BASIC,
788 "AutoLayerManagerSetup instantiated for non-basic layer backend!");
789 if (lm) {
790 mLayerManager = lm->AsBasicLayerManager();
791 if (mLayerManager) {
792 mLayerManager->SetDefaultTarget(aTarget);
793 mLayerManager->SetDefaultTargetConfiguration(aDoubleBuffering, aRotation);
798 nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() {
799 if (mLayerManager) {
800 mLayerManager->SetDefaultTarget(nullptr);
801 mLayerManager->SetDefaultTargetConfiguration(
802 mozilla::layers::BufferMode::BUFFER_NONE, ROTATION_0);
806 bool nsBaseWidget::IsSmallPopup() const {
807 return mWindowType == eWindowType_popup && mPopupType != ePopupTypePanel;
810 bool nsBaseWidget::ComputeShouldAccelerate() {
811 if (gfx::gfxVars::UseWebRender() && !AllowWebRenderForThisWindow()) {
812 // If WebRender is enabled, non-WebRender widgets use the basic compositor
813 // (at least for now), even though they would get an accelerated compositor
814 // if WebRender wasn't enabled.
815 return false;
817 return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
818 WidgetTypeSupportsAcceleration();
821 bool nsBaseWidget::UseAPZ() {
822 return (gfxPlatform::AsyncPanZoomEnabled() &&
823 (WindowType() == eWindowType_toplevel ||
824 WindowType() == eWindowType_child ||
825 (WindowType() == eWindowType_popup && HasRemoteContent() &&
826 gfxPrefs::APZPopupsEnabled())));
829 bool nsBaseWidget::AllowWebRenderForThisWindow() {
830 return WindowType() == eWindowType_toplevel ||
831 WindowType() == eWindowType_child ||
832 WindowType() == eWindowType_dialog ||
833 (WindowType() == eWindowType_popup && HasRemoteContent());
836 void nsBaseWidget::CreateCompositor() {
837 LayoutDeviceIntRect rect = GetBounds();
838 CreateCompositor(rect.Width(), rect.Height());
841 already_AddRefed<GeckoContentController>
842 nsBaseWidget::CreateRootContentController() {
843 RefPtr<GeckoContentController> controller =
844 new ChromeProcessController(this, mAPZEventState, mAPZC);
845 return controller.forget();
848 void nsBaseWidget::ConfigureAPZCTreeManager() {
849 MOZ_ASSERT(NS_IsMainThread());
850 MOZ_ASSERT(mAPZC);
852 ConfigureAPZControllerThread();
854 float dpi = GetDPI();
855 // On Android the main thread is not the controller thread
856 APZThreadUtils::RunOnControllerThread(
857 NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mAPZC,
858 &IAPZCTreeManager::SetDPI, dpi));
860 if (gfxPrefs::APZKeyboardEnabled()) {
861 KeyboardMap map = nsXBLWindowKeyHandler::CollectKeyboardShortcuts();
862 // On Android the main thread is not the controller thread
863 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
864 "layers::IAPZCTreeManager::SetKeyboardMap", mAPZC,
865 &IAPZCTreeManager::SetKeyboardMap, map));
868 RefPtr<IAPZCTreeManager> treeManager = mAPZC; // for capture by the lambdas
870 ContentReceivedInputBlockCallback callback(
871 [treeManager](const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId,
872 bool aPreventDefault) {
873 MOZ_ASSERT(NS_IsMainThread());
874 APZThreadUtils::RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
875 "layers::IAPZCTreeManager::ContentReceivedInputBlock", treeManager,
876 &IAPZCTreeManager::ContentReceivedInputBlock, aInputBlockId,
877 aPreventDefault));
879 mAPZEventState = new APZEventState(this, std::move(callback));
881 mSetAllowedTouchBehaviorCallback =
882 [treeManager](uint64_t aInputBlockId,
883 const nsTArray<TouchBehaviorFlags>& aFlags) {
884 MOZ_ASSERT(NS_IsMainThread());
885 APZThreadUtils::RunOnControllerThread(
886 NewRunnableMethod<
887 uint64_t, StoreCopyPassByLRef<nsTArray<TouchBehaviorFlags>>>(
888 "layers::IAPZCTreeManager::SetAllowedTouchBehavior",
889 treeManager, &IAPZCTreeManager::SetAllowedTouchBehavior,
890 aInputBlockId, aFlags));
893 mRootContentController = CreateRootContentController();
894 if (mRootContentController) {
895 mCompositorSession->SetContentController(mRootContentController);
898 // When APZ is enabled, we can actually enable raw touch events because we
899 // have code that can deal with them properly. If APZ is not enabled, this
900 // function doesn't get called.
901 if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0) ||
902 Preferences::GetBool("dom.w3c_pointer_events.enabled", false)) {
903 RegisterTouchWindow();
907 void nsBaseWidget::ConfigureAPZControllerThread() {
908 // By default the controller thread is the main thread.
909 APZThreadUtils::SetControllerThread(MessageLoop::current());
912 void nsBaseWidget::SetConfirmedTargetAPZC(
913 uint64_t aInputBlockId,
914 const nsTArray<ScrollableLayerGuid>& aTargets) const {
915 APZThreadUtils::RunOnControllerThread(
916 NewRunnableMethod<uint64_t,
917 StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
918 "layers::IAPZCTreeManager::SetTargetAPZC", mAPZC,
919 &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets));
922 void nsBaseWidget::UpdateZoomConstraints(
923 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
924 const Maybe<ZoomConstraints>& aConstraints) {
925 if (!mCompositorSession || !mAPZC) {
926 if (mInitialZoomConstraints) {
927 MOZ_ASSERT(mInitialZoomConstraints->mPresShellID == aPresShellId);
928 MOZ_ASSERT(mInitialZoomConstraints->mViewID == aViewId);
929 if (!aConstraints) {
930 mInitialZoomConstraints.reset();
934 if (aConstraints) {
935 // We have some constraints, but the compositor and APZC aren't created
936 // yet. Save these so we can use them later.
937 mInitialZoomConstraints = Some(
938 InitialZoomConstraints(aPresShellId, aViewId, aConstraints.ref()));
940 return;
942 LayersId layersId = mCompositorSession->RootLayerTreeId();
943 mAPZC->UpdateZoomConstraints(
944 ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints);
947 bool nsBaseWidget::AsyncPanZoomEnabled() const { return !!mAPZC; }
949 nsEventStatus nsBaseWidget::ProcessUntransformedAPZEvent(
950 WidgetInputEvent* aEvent, const ScrollableLayerGuid& aGuid,
951 uint64_t aInputBlockId, nsEventStatus aApzResponse) {
952 MOZ_ASSERT(NS_IsMainThread());
953 InputAPZContext context(aGuid, aInputBlockId, aApzResponse);
955 // If this is an event that the APZ has targeted to an APZC in the root
956 // process, apply that APZC's callback-transform before dispatching the
957 // event. If the event is instead targeted to an APZC in the child process,
958 // the transform will be applied in the child process before dispatching
959 // the event there (see e.g. TabChild::RecvRealTouchEvent()).
960 if (aGuid.mLayersId == mCompositorSession->RootLayerTreeId()) {
961 APZCCallbackHelper::ApplyCallbackTransform(*aEvent, aGuid,
962 GetDefaultScale());
965 // Make a copy of the original event for the APZCCallbackHelper helpers that
966 // we call later, because the event passed to DispatchEvent can get mutated in
967 // ways that we don't want (i.e. touch points can get stripped out).
968 nsEventStatus status;
969 UniquePtr<WidgetEvent> original(aEvent->Duplicate());
970 DispatchEvent(aEvent, status);
972 if (mAPZC && !InputAPZContext::WasRoutedToChildProcess() && aInputBlockId) {
973 // EventStateManager did not route the event into the child process.
974 // It's safe to communicate to APZ that the event has been processed.
975 UniquePtr<DisplayportSetListener> postLayerization;
976 if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
977 if (touchEvent->mMessage == eTouchStart) {
978 if (gfxPrefs::TouchActionEnabled()) {
979 APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
980 this, GetDocument(), *(original->AsTouchEvent()), aInputBlockId,
981 mSetAllowedTouchBehaviorCallback);
983 postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
984 this, GetDocument(), *(original->AsTouchEvent()), aGuid,
985 aInputBlockId);
987 mAPZEventState->ProcessTouchEvent(*touchEvent, aGuid, aInputBlockId,
988 aApzResponse, status);
989 } else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
990 MOZ_ASSERT(wheelEvent->mFlags.mHandledByAPZ);
991 postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
992 this, GetDocument(), *(original->AsWheelEvent()), aGuid,
993 aInputBlockId);
994 if (wheelEvent->mCanTriggerSwipe) {
995 ReportSwipeStarted(aInputBlockId, wheelEvent->TriggersSwipe());
997 mAPZEventState->ProcessWheelEvent(*wheelEvent, aGuid, aInputBlockId);
998 } else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
999 MOZ_ASSERT(mouseEvent->mFlags.mHandledByAPZ);
1000 postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
1001 this, GetDocument(), *(original->AsMouseEvent()), aGuid,
1002 aInputBlockId);
1003 mAPZEventState->ProcessMouseEvent(*mouseEvent, aGuid, aInputBlockId);
1005 if (postLayerization && postLayerization->Register()) {
1006 Unused << postLayerization.release();
1010 return status;
1013 class DispatchWheelEventOnMainThread : public Runnable {
1014 public:
1015 DispatchWheelEventOnMainThread(const ScrollWheelInput& aWheelInput,
1016 nsBaseWidget* aWidget,
1017 nsEventStatus aAPZResult,
1018 uint64_t aInputBlockId,
1019 ScrollableLayerGuid aGuid)
1020 : mozilla::Runnable("DispatchWheelEventOnMainThread"),
1021 mWheelInput(aWheelInput),
1022 mWidget(aWidget),
1023 mAPZResult(aAPZResult),
1024 mInputBlockId(aInputBlockId),
1025 mGuid(aGuid) {}
1027 NS_IMETHOD Run() override {
1028 WidgetWheelEvent wheelEvent = mWheelInput.ToWidgetWheelEvent(mWidget);
1029 mWidget->ProcessUntransformedAPZEvent(&wheelEvent, mGuid, mInputBlockId,
1030 mAPZResult);
1031 return NS_OK;
1034 private:
1035 ScrollWheelInput mWheelInput;
1036 nsBaseWidget* mWidget;
1037 nsEventStatus mAPZResult;
1038 uint64_t mInputBlockId;
1039 ScrollableLayerGuid mGuid;
1042 class DispatchWheelInputOnControllerThread : public Runnable {
1043 public:
1044 DispatchWheelInputOnControllerThread(const WidgetWheelEvent& aWheelEvent,
1045 IAPZCTreeManager* aAPZC,
1046 nsBaseWidget* aWidget)
1047 : mozilla::Runnable("DispatchWheelInputOnControllerThread"),
1048 mMainMessageLoop(MessageLoop::current()),
1049 mWheelInput(aWheelEvent),
1050 mAPZC(aAPZC),
1051 mWidget(aWidget),
1052 mInputBlockId(0) {}
1054 NS_IMETHOD Run() override {
1055 nsEventStatus result = mAPZC->InputBridge()->ReceiveInputEvent(
1056 mWheelInput, &mGuid, &mInputBlockId);
1057 if (result == nsEventStatus_eConsumeNoDefault) {
1058 return NS_OK;
1060 RefPtr<Runnable> r = new DispatchWheelEventOnMainThread(
1061 mWheelInput, mWidget, result, mInputBlockId, mGuid);
1062 mMainMessageLoop->PostTask(r.forget());
1063 return NS_OK;
1066 private:
1067 MessageLoop* mMainMessageLoop;
1068 ScrollWheelInput mWheelInput;
1069 RefPtr<IAPZCTreeManager> mAPZC;
1070 nsBaseWidget* mWidget;
1071 uint64_t mInputBlockId;
1072 ScrollableLayerGuid mGuid;
1075 void nsBaseWidget::DispatchTouchInput(MultiTouchInput& aInput) {
1076 MOZ_ASSERT(NS_IsMainThread());
1077 if (mAPZC) {
1078 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1079 uint64_t inputBlockId = 0;
1080 ScrollableLayerGuid guid;
1082 nsEventStatus result =
1083 mAPZC->InputBridge()->ReceiveInputEvent(aInput, &guid, &inputBlockId);
1084 if (result == nsEventStatus_eConsumeNoDefault) {
1085 return;
1088 WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
1089 ProcessUntransformedAPZEvent(&event, guid, inputBlockId, result);
1090 } else {
1091 WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
1093 nsEventStatus status;
1094 DispatchEvent(&event, status);
1098 nsEventStatus nsBaseWidget::DispatchInputEvent(WidgetInputEvent* aEvent) {
1099 MOZ_ASSERT(NS_IsMainThread());
1100 if (mAPZC) {
1101 if (APZThreadUtils::IsControllerThread()) {
1102 uint64_t inputBlockId = 0;
1103 ScrollableLayerGuid guid;
1105 nsEventStatus result = mAPZC->InputBridge()->ReceiveInputEvent(
1106 *aEvent, &guid, &inputBlockId);
1107 if (result == nsEventStatus_eConsumeNoDefault) {
1108 return result;
1110 return ProcessUntransformedAPZEvent(aEvent, guid, inputBlockId, result);
1112 WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
1113 if (wheelEvent) {
1114 RefPtr<Runnable> r =
1115 new DispatchWheelInputOnControllerThread(*wheelEvent, mAPZC, this);
1116 APZThreadUtils::RunOnControllerThread(r.forget());
1117 return nsEventStatus_eConsumeDoDefault;
1119 // Allow dispatching keyboard events on Gecko thread.
1120 MOZ_ASSERT(aEvent->AsKeyboardEvent());
1123 nsEventStatus status;
1124 DispatchEvent(aEvent, status);
1125 return status;
1128 void nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) {
1129 MOZ_ASSERT(NS_IsMainThread());
1130 if (mAPZC) {
1131 MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1132 uint64_t inputBlockId = 0;
1133 ScrollableLayerGuid guid;
1134 mAPZC->InputBridge()->ReceiveInputEvent(*aEvent, &guid, &inputBlockId);
1138 // static
1139 bool nsBaseWidget::ShowContextMenuAfterMouseUp() {
1140 static bool gContextMenuAfterMouseUp = false;
1141 static bool gContextMenuAfterMouseUpCached = false;
1142 if (!gContextMenuAfterMouseUpCached) {
1143 Preferences::AddBoolVarCache(&gContextMenuAfterMouseUp,
1144 "ui.context_menus.after_mouseup", false);
1146 gContextMenuAfterMouseUpCached = true;
1148 return gContextMenuAfterMouseUp;
1151 Document* nsBaseWidget::GetDocument() const {
1152 if (mWidgetListener) {
1153 if (nsIPresShell* presShell = mWidgetListener->GetPresShell()) {
1154 return presShell->GetDocument();
1157 return nullptr;
1160 void nsBaseWidget::CreateCompositorVsyncDispatcher() {
1161 // Parent directly listens to the vsync source whereas
1162 // child process communicate via IPC
1163 // Should be called AFTER gfxPlatform is initialized
1164 if (XRE_IsParentProcess()) {
1165 if (!mCompositorVsyncDispatcherLock) {
1166 mCompositorVsyncDispatcherLock =
1167 MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
1169 MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
1170 mCompositorVsyncDispatcher = new CompositorVsyncDispatcher();
1174 already_AddRefed<CompositorVsyncDispatcher>
1175 nsBaseWidget::GetCompositorVsyncDispatcher() {
1176 MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
1178 MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
1179 RefPtr<CompositorVsyncDispatcher> dispatcher = mCompositorVsyncDispatcher;
1180 return dispatcher.forget();
1183 already_AddRefed<LayerManager> nsBaseWidget::CreateCompositorSession(
1184 int aWidth, int aHeight, CompositorOptions* aOptionsOut) {
1185 MOZ_ASSERT(aOptionsOut);
1187 do {
1188 CreateCompositorVsyncDispatcher();
1190 gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
1191 // Make sure GPU process is ready for use.
1192 // If it failed to connect to GPU process, GPU process usage is disabled in
1193 // EnsureGPUReady(). It could update gfxVars and gfxConfigs.
1194 gpu->EnsureGPUReady();
1196 // If widget type does not supports acceleration, we use ClientLayerManager
1197 // even when gfxVars::UseWebRender() is true. WebRender could coexist only
1198 // with BasicCompositor.
1199 bool enableWR = gfx::gfxVars::UseWebRender() &&
1200 WidgetTypeSupportsAcceleration() &&
1201 AllowWebRenderForThisWindow();
1202 bool enableAPZ = UseAPZ();
1203 CompositorOptions options(enableAPZ, enableWR);
1205 bool enableAL = gfx::gfxConfig::IsEnabled(gfx::Feature::ADVANCED_LAYERS);
1206 options.SetUseAdvancedLayers(enableAL);
1208 #ifdef MOZ_WIDGET_ANDROID
1209 if (!GetNativeData(NS_JAVA_SURFACE)) {
1210 options.SetInitiallyPaused(true);
1212 #endif
1214 RefPtr<LayerManager> lm;
1215 if (options.UseWebRender()) {
1216 lm = new WebRenderLayerManager(this);
1217 } else {
1218 lm = new ClientLayerManager(this);
1221 bool retry = false;
1222 mCompositorSession = gpu->CreateTopLevelCompositor(
1223 this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(),
1224 gfx::IntSize(aWidth, aHeight), &retry);
1226 if (lm->AsWebRenderLayerManager() && mCompositorSession) {
1227 TextureFactoryIdentifier textureFactoryIdentifier;
1228 lm->AsWebRenderLayerManager()->Initialize(
1229 mCompositorSession->GetCompositorBridgeChild(),
1230 wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
1231 &textureFactoryIdentifier);
1232 if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) {
1233 retry = true;
1234 DestroyCompositor();
1235 gfx::GPUProcessManager::Get()->DisableWebRender(
1236 wr::WebRenderError::INITIALIZE);
1240 // We need to retry in a loop because the act of failing to create the
1241 // compositor can change our state (e.g. disable WebRender).
1242 if (mCompositorSession || !retry) {
1243 *aOptionsOut = options;
1244 return lm.forget();
1246 } while (true);
1249 void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) {
1250 // This makes sure that gfxPlatforms gets initialized if it hasn't by now.
1251 gfxPlatform::GetPlatform();
1253 MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
1254 "This function assumes OMTC");
1256 MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild,
1257 "Should have properly cleaned up the previous PCompositor pair "
1258 "beforehand");
1260 if (mCompositorBridgeChild) {
1261 mCompositorBridgeChild->Destroy();
1264 // Recreating this is tricky, as we may still have an old and we need
1265 // to make sure it's properly destroyed by calling DestroyCompositor!
1267 // If we've already received a shutdown notification, don't try
1268 // create a new compositor.
1269 if (!mShutdownObserver) {
1270 return;
1273 CompositorOptions options;
1274 RefPtr<LayerManager> lm = CreateCompositorSession(aWidth, aHeight, &options);
1275 if (!lm) {
1276 return;
1279 MOZ_ASSERT(mCompositorSession);
1280 mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild();
1281 SetCompositorWidgetDelegate(
1282 mCompositorSession->GetCompositorWidgetDelegate());
1284 if (options.UseAPZ()) {
1285 mAPZC = mCompositorSession->GetAPZCTreeManager();
1286 ConfigureAPZCTreeManager();
1287 } else {
1288 mAPZC = nullptr;
1291 if (mInitialZoomConstraints) {
1292 UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID,
1293 mInitialZoomConstraints->mViewID,
1294 Some(mInitialZoomConstraints->mConstraints));
1295 mInitialZoomConstraints.reset();
1298 if (lm->AsWebRenderLayerManager()) {
1299 TextureFactoryIdentifier textureFactoryIdentifier =
1300 lm->GetTextureFactoryIdentifier();
1301 MOZ_ASSERT(textureFactoryIdentifier.mParentBackend ==
1302 LayersBackend::LAYERS_WR);
1303 ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
1304 gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
1307 ShadowLayerForwarder* lf = lm->AsShadowForwarder();
1308 if (lf) {
1309 // lf is non-null if we are creating a ClientLayerManager above
1310 TextureFactoryIdentifier textureFactoryIdentifier;
1311 PLayerTransactionChild* shadowManager = nullptr;
1313 nsTArray<LayersBackend> backendHints;
1314 gfxPlatform::GetPlatform()->GetCompositorBackends(ComputeShouldAccelerate(),
1315 backendHints);
1317 bool success = false;
1318 if (!backendHints.IsEmpty()) {
1319 shadowManager = mCompositorBridgeChild->SendPLayerTransactionConstructor(
1320 backendHints, LayersId{0});
1321 if (shadowManager->SendGetTextureFactoryIdentifier(
1322 &textureFactoryIdentifier) &&
1323 textureFactoryIdentifier.mParentBackend !=
1324 LayersBackend::LAYERS_NONE) {
1325 success = true;
1329 if (!success) {
1330 NS_WARNING("Failed to create an OMT compositor.");
1331 DestroyCompositor();
1332 mLayerManager = nullptr;
1333 return;
1336 lf->SetShadowManager(shadowManager);
1337 lm->UpdateTextureFactoryIdentifier(textureFactoryIdentifier);
1338 // Some popup or transparent widgets may use a different backend than the
1339 // compositors used with ImageBridge and VR (and more generally web
1340 // content).
1341 if (WidgetTypeSupportsAcceleration()) {
1342 ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
1343 gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
1347 WindowUsesOMTC();
1349 mLayerManager = lm.forget();
1351 // Only track compositors for top-level windows, since other window types
1352 // may use the basic compositor. Except on the OS X - see bug 1306383
1353 #if defined(XP_MACOSX)
1354 bool getCompositorFromThisWindow = true;
1355 #else
1356 bool getCompositorFromThisWindow = (mWindowType == eWindowType_toplevel);
1357 #endif
1359 if (getCompositorFromThisWindow) {
1360 gfxPlatform::GetPlatform()->NotifyCompositorCreated(
1361 mLayerManager->GetCompositorBackendType());
1365 void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession* aSession) {
1366 MOZ_ASSERT(aSession == mCompositorSession);
1367 DestroyLayerManager();
1370 bool nsBaseWidget::ShouldUseOffMainThreadCompositing() {
1371 return gfxPlatform::UsesOffMainThreadCompositing();
1374 LayerManager* nsBaseWidget::GetLayerManager(
1375 PLayerTransactionChild* aShadowManager, LayersBackend aBackendHint,
1376 LayerManagerPersistence aPersistence) {
1377 if (!mLayerManager) {
1378 if (!mShutdownObserver) {
1379 // We are shutting down, do not try to re-create a LayerManager
1380 return nullptr;
1382 // Try to use an async compositor first, if possible
1383 if (ShouldUseOffMainThreadCompositing()) {
1384 // e10s uses the parameter to pass in the shadow manager from the TabChild
1385 // so we don't expect to see it there since this doesn't support e10s.
1386 NS_ASSERTION(aShadowManager == nullptr,
1387 "Async Compositor not supported with e10s");
1388 CreateCompositor();
1391 if (!mLayerManager) {
1392 mLayerManager = CreateBasicLayerManager();
1395 return mLayerManager;
1398 LayerManager* nsBaseWidget::CreateBasicLayerManager() {
1399 return new BasicLayerManager(this);
1402 CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer() {
1403 return mCompositorBridgeChild;
1406 already_AddRefed<gfx::DrawTarget> nsBaseWidget::StartRemoteDrawing() {
1407 if (recordreplay::IsRecordingOrReplaying()) {
1408 return recordreplay::child::DrawTargetForRemoteDrawing(mBounds.Size());
1410 return nullptr;
1413 uint32_t nsBaseWidget::GetGLFrameBufferFormat() { return LOCAL_GL_RGBA; }
1415 //-------------------------------------------------------------------------
1417 // Destroy the window
1419 //-------------------------------------------------------------------------
1420 void nsBaseWidget::OnDestroy() {
1421 if (mTextEventDispatcher) {
1422 mTextEventDispatcher->OnDestroyWidget();
1423 // Don't release it until this widget actually released because after this
1424 // is called, TextEventDispatcher() may create it again.
1427 // If this widget is being destroyed, let the APZ code know to drop references
1428 // to this widget. Callers of this function all should be holding a deathgrip
1429 // on this widget already.
1430 ReleaseContentController();
1433 void nsBaseWidget::MoveClient(double aX, double aY) {
1434 LayoutDeviceIntPoint clientOffset(GetClientOffset());
1436 // GetClientOffset returns device pixels; scale back to desktop pixels
1437 // if that's what this widget uses for the Move/Resize APIs
1438 if (BoundsUseDesktopPixels()) {
1439 DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale();
1440 Move(aX - desktopOffset.x, aY - desktopOffset.y);
1441 } else {
1442 Move(aX - clientOffset.x, aY - clientOffset.y);
1446 void nsBaseWidget::ResizeClient(double aWidth, double aHeight, bool aRepaint) {
1447 NS_ASSERTION((aWidth >= 0), "Negative width passed to ResizeClient");
1448 NS_ASSERTION((aHeight >= 0), "Negative height passed to ResizeClient");
1450 LayoutDeviceIntRect clientBounds = GetClientBounds();
1452 // GetClientBounds and mBounds are device pixels; scale back to desktop pixels
1453 // if that's what this widget uses for the Move/Resize APIs
1454 if (BoundsUseDesktopPixels()) {
1455 DesktopSize desktopDelta =
1456 (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
1457 clientBounds.Size()) /
1458 GetDesktopToDeviceScale();
1459 Resize(aWidth + desktopDelta.width, aHeight + desktopDelta.height,
1460 aRepaint);
1461 } else {
1462 Resize(mBounds.Width() + (aWidth - clientBounds.Width()),
1463 mBounds.Height() + (aHeight - clientBounds.Height()), aRepaint);
1467 void nsBaseWidget::ResizeClient(double aX, double aY, double aWidth,
1468 double aHeight, bool aRepaint) {
1469 NS_ASSERTION((aWidth >= 0), "Negative width passed to ResizeClient");
1470 NS_ASSERTION((aHeight >= 0), "Negative height passed to ResizeClient");
1472 LayoutDeviceIntRect clientBounds = GetClientBounds();
1473 LayoutDeviceIntPoint clientOffset = GetClientOffset();
1475 if (BoundsUseDesktopPixels()) {
1476 DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale();
1477 DesktopPoint desktopOffset = clientOffset / scale;
1478 DesktopSize desktopDelta =
1479 (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
1480 clientBounds.Size()) /
1481 scale;
1482 Resize(aX - desktopOffset.x, aY - desktopOffset.y,
1483 aWidth + desktopDelta.width, aHeight + desktopDelta.height,
1484 aRepaint);
1485 } else {
1486 Resize(aX - clientOffset.x, aY - clientOffset.y,
1487 aWidth + mBounds.Width() - clientBounds.Width(),
1488 aHeight + mBounds.Height() - clientBounds.Height(), aRepaint);
1492 //-------------------------------------------------------------------------
1494 // Bounds
1496 //-------------------------------------------------------------------------
1499 * If the implementation of nsWindow supports borders this method MUST be
1500 * overridden
1503 LayoutDeviceIntRect nsBaseWidget::GetClientBounds() { return GetBounds(); }
1506 * If the implementation of nsWindow supports borders this method MUST be
1507 * overridden
1510 LayoutDeviceIntRect nsBaseWidget::GetBounds() { return mBounds; }
1513 * If the implementation of nsWindow uses a local coordinate system within the
1514 *window, this method must be overridden
1517 LayoutDeviceIntRect nsBaseWidget::GetScreenBounds() { return GetBounds(); }
1519 nsresult nsBaseWidget::GetRestoredBounds(LayoutDeviceIntRect& aRect) {
1520 if (SizeMode() != nsSizeMode_Normal) {
1521 return NS_ERROR_FAILURE;
1523 aRect = GetScreenBounds();
1524 return NS_OK;
1527 LayoutDeviceIntPoint nsBaseWidget::GetClientOffset() {
1528 return LayoutDeviceIntPoint(0, 0);
1531 nsresult nsBaseWidget::SetNonClientMargins(LayoutDeviceIntMargin& margins) {
1532 return NS_ERROR_NOT_IMPLEMENTED;
1535 uint32_t nsBaseWidget::GetMaxTouchPoints() const { return 0; }
1537 bool nsBaseWidget::HasPendingInputEvent() { return false; }
1539 bool nsBaseWidget::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) {
1540 return false;
1544 * Modifies aFile to point at an icon file with the given name and suffix. The
1545 * suffix may correspond to a file extension with leading '.' if appropriate.
1546 * Returns true if the icon file exists and can be read.
1548 static bool ResolveIconNameHelper(nsIFile* aFile, const nsAString& aIconName,
1549 const nsAString& aIconSuffix) {
1550 aFile->Append(NS_LITERAL_STRING("icons"));
1551 aFile->Append(NS_LITERAL_STRING("default"));
1552 aFile->Append(aIconName + aIconSuffix);
1554 bool readable;
1555 return NS_SUCCEEDED(aFile->IsReadable(&readable)) && readable;
1559 * Resolve the given icon name into a local file object. This method is
1560 * intended to be called by subclasses of nsBaseWidget. aIconSuffix is a
1561 * platform specific icon file suffix (e.g., ".ico" under Win32).
1563 * If no file is found matching the given parameters, then null is returned.
1565 void nsBaseWidget::ResolveIconName(const nsAString& aIconName,
1566 const nsAString& aIconSuffix,
1567 nsIFile** aResult) {
1568 *aResult = nullptr;
1570 nsCOMPtr<nsIProperties> dirSvc =
1571 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1572 if (!dirSvc) return;
1574 // first check auxilary chrome directories
1576 nsCOMPtr<nsISimpleEnumerator> dirs;
1577 dirSvc->Get(NS_APP_CHROME_DIR_LIST, NS_GET_IID(nsISimpleEnumerator),
1578 getter_AddRefs(dirs));
1579 if (dirs) {
1580 bool hasMore;
1581 while (NS_SUCCEEDED(dirs->HasMoreElements(&hasMore)) && hasMore) {
1582 nsCOMPtr<nsISupports> element;
1583 dirs->GetNext(getter_AddRefs(element));
1584 if (!element) continue;
1585 nsCOMPtr<nsIFile> file = do_QueryInterface(element);
1586 if (!file) continue;
1587 if (ResolveIconNameHelper(file, aIconName, aIconSuffix)) {
1588 NS_ADDREF(*aResult = file);
1589 return;
1594 // then check the main app chrome directory
1596 nsCOMPtr<nsIFile> file;
1597 dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(file));
1598 if (file && ResolveIconNameHelper(file, aIconName, aIconSuffix))
1599 NS_ADDREF(*aResult = file);
1602 void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints) {
1603 mSizeConstraints = aConstraints;
1605 // Popups are constrained during layout, and we don't want to synchronously
1606 // paint from reflow, so bail out... This is not great, but it's no worse than
1607 // what we used to do.
1609 // The right fix here is probably making constraint changes go through the
1610 // view manager and such.
1611 if (mWindowType == eWindowType_popup) {
1612 return;
1615 // If the current size doesn't meet the new constraints, trigger a
1616 // resize to apply it. Note that, we don't want to invoke Resize if
1617 // the new constraints don't affect the current size, because Resize
1618 // implementation on some platforms may touch other geometry even if
1619 // the size don't need to change.
1620 LayoutDeviceIntSize curSize = mBounds.Size();
1621 LayoutDeviceIntSize clampedSize =
1622 Max(aConstraints.mMinSize, Min(aConstraints.mMaxSize, curSize));
1623 if (clampedSize != curSize) {
1624 gfx::Size size;
1625 if (BoundsUseDesktopPixels()) {
1626 DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale();
1627 size = desktopSize.ToUnknownSize();
1628 } else {
1629 size = gfx::Size(clampedSize.ToUnknownSize());
1631 Resize(size.width, size.height, true);
1635 const widget::SizeConstraints nsBaseWidget::GetSizeConstraints() {
1636 return mSizeConstraints;
1639 // static
1640 nsIRollupListener* nsBaseWidget::GetActiveRollupListener() {
1641 // If set, then this is likely an <html:select> dropdown.
1642 if (gRollupListener) return gRollupListener;
1644 return nsXULPopupManager::GetInstance();
1647 void nsBaseWidget::NotifyWindowDestroyed() {
1648 if (!mWidgetListener) return;
1650 nsCOMPtr<nsIXULWindow> window = mWidgetListener->GetXULWindow();
1651 nsCOMPtr<nsIBaseWindow> xulWindow(do_QueryInterface(window));
1652 if (xulWindow) {
1653 xulWindow->Destroy();
1657 void nsBaseWidget::NotifyWindowMoved(int32_t aX, int32_t aY) {
1658 if (mWidgetListener) {
1659 mWidgetListener->WindowMoved(this, aX, aY);
1662 if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) {
1663 NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE));
1667 void nsBaseWidget::NotifyPresShell(NotificationFunc aNotificationFunc) {
1668 if (!mWidgetListener) {
1669 return;
1672 nsIPresShell* presShell = mWidgetListener->GetPresShell();
1673 if (presShell) {
1674 (presShell->*aNotificationFunc)();
1678 void nsBaseWidget::NotifySizeMoveDone() {
1679 NotifyPresShell(&nsIPresShell::WindowSizeMoveDone);
1682 void nsBaseWidget::NotifySysColorChanged() {
1683 NotifyPresShell(&nsIPresShell::SysColorChanged);
1686 void nsBaseWidget::NotifyThemeChanged() {
1687 NotifyPresShell(&nsIPresShell::ThemeChanged);
1690 void nsBaseWidget::NotifyUIStateChanged(UIStateChangeType aShowAccelerators,
1691 UIStateChangeType aShowFocusRings) {
1692 if (Document* doc = GetDocument()) {
1693 nsPIDOMWindowOuter* win = doc->GetWindow();
1694 if (win) {
1695 win->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
1700 nsresult nsBaseWidget::NotifyIME(const IMENotification& aIMENotification) {
1701 switch (aIMENotification.mMessage) {
1702 case REQUEST_TO_COMMIT_COMPOSITION:
1703 case REQUEST_TO_CANCEL_COMPOSITION:
1704 // We should send request to IME only when there is a TextEventDispatcher
1705 // instance (this means that this widget has dispatched at least one
1706 // composition event or keyboard event) and the it has composition.
1707 // Otherwise, there is nothing to do.
1708 // Note that if current input transaction is for native input events,
1709 // TextEventDispatcher::NotifyIME() will call
1710 // TextEventDispatcherListener::NotifyIME().
1711 if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) {
1712 return mTextEventDispatcher->NotifyIME(aIMENotification);
1714 return NS_OK;
1715 default: {
1716 if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
1717 mIMEHasFocus = true;
1719 EnsureTextEventDispatcher();
1720 // TextEventDispatcher::NotifyIME() will always call
1721 // TextEventDispatcherListener::NotifyIME(). I.e., even if current
1722 // input transaction is for synthesized events for automated tests,
1723 // notifications will be sent to native IME.
1724 nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification);
1725 if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
1726 mIMEHasFocus = false;
1728 return rv;
1733 void nsBaseWidget::EnsureTextEventDispatcher() {
1734 if (mTextEventDispatcher) {
1735 return;
1737 mTextEventDispatcher = new TextEventDispatcher(this);
1740 nsIWidget::NativeIMEContext nsBaseWidget::GetNativeIMEContext() {
1741 if (mTextEventDispatcher && mTextEventDispatcher->GetPseudoIMEContext()) {
1742 // If we already have a TextEventDispatcher and it's working with
1743 // a TextInputProcessor, we need to return pseudo IME context since
1744 // TextCompositionArray::IndexOf(nsIWidget*) should return a composition
1745 // on the pseudo IME context in such case.
1746 NativeIMEContext pseudoIMEContext;
1747 pseudoIMEContext.InitWithRawNativeIMEContext(
1748 mTextEventDispatcher->GetPseudoIMEContext());
1749 return pseudoIMEContext;
1751 return NativeIMEContext(this);
1754 nsIWidget::TextEventDispatcher* nsBaseWidget::GetTextEventDispatcher() {
1755 EnsureTextEventDispatcher();
1756 return mTextEventDispatcher;
1759 void* nsBaseWidget::GetPseudoIMEContext() {
1760 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
1761 if (!dispatcher) {
1762 return nullptr;
1764 return dispatcher->GetPseudoIMEContext();
1767 TextEventDispatcherListener*
1768 nsBaseWidget::GetNativeTextEventDispatcherListener() {
1769 // TODO: If all platforms supported use of TextEventDispatcher for handling
1770 // native IME and keyboard events, this method should be removed since
1771 // in such case, this is overridden by all the subclasses.
1772 return nullptr;
1775 void nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId,
1776 const ScrollableLayerGuid::ViewID& aViewId,
1777 const CSSRect& aRect, const uint32_t& aFlags) {
1778 if (!mCompositorSession || !mAPZC) {
1779 return;
1781 LayersId layerId = mCompositorSession->RootLayerTreeId();
1782 APZThreadUtils::RunOnControllerThread(
1783 NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
1784 "layers::IAPZCTreeManager::ZoomToRect", mAPZC,
1785 &IAPZCTreeManager::ZoomToRect,
1786 ScrollableLayerGuid(layerId, aPresShellId, aViewId), aRect, aFlags));
1789 #ifdef ACCESSIBILITY
1791 a11y::Accessible* nsBaseWidget::GetRootAccessible() {
1792 NS_ENSURE_TRUE(mWidgetListener, nullptr);
1794 nsIPresShell* presShell = mWidgetListener->GetPresShell();
1795 NS_ENSURE_TRUE(presShell, nullptr);
1797 // If container is null then the presshell is not active. This often happens
1798 // when a preshell is being held onto for fastback.
1799 nsPresContext* presContext = presShell->GetPresContext();
1800 NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr);
1802 // Accessible creation might be not safe so use IsSafeToRunScript to
1803 // make sure it's not created at unsafe times.
1804 nsAccessibilityService* accService = GetOrCreateAccService();
1805 if (accService) {
1806 return accService->GetRootDocumentAccessible(
1807 presShell, nsContentUtils::IsSafeToRunScript());
1810 return nullptr;
1813 #endif // ACCESSIBILITY
1815 void nsBaseWidget::StartAsyncScrollbarDrag(
1816 const AsyncDragMetrics& aDragMetrics) {
1817 if (!AsyncPanZoomEnabled()) {
1818 return;
1821 MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession);
1823 LayersId layersId = mCompositorSession->RootLayerTreeId();
1824 ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId,
1825 aDragMetrics.mViewId);
1827 APZThreadUtils::RunOnControllerThread(
1828 NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
1829 "layers::IAPZCTreeManager::StartScrollbarDrag", mAPZC,
1830 &IAPZCTreeManager::StartScrollbarDrag, guid, aDragMetrics));
1833 bool nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
1834 const ScrollableLayerGuid& aGuid) {
1835 MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
1837 return mAPZC->StartAutoscroll(aGuid, aAnchorLocation);
1840 void nsBaseWidget::StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) {
1841 MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
1843 mAPZC->StopAutoscroll(aGuid);
1846 already_AddRefed<nsIScreen> nsBaseWidget::GetWidgetScreen() {
1847 nsCOMPtr<nsIScreenManager> screenManager;
1848 screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
1849 if (!screenManager) {
1850 return nullptr;
1853 LayoutDeviceIntRect bounds = GetScreenBounds();
1854 DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale());
1855 nsCOMPtr<nsIScreen> screen;
1856 screenManager->ScreenForRect(deskBounds.X(), deskBounds.Y(),
1857 deskBounds.Width(), deskBounds.Height(),
1858 getter_AddRefs(screen));
1859 return screen.forget();
1862 mozilla::DesktopToLayoutDeviceScale
1863 nsBaseWidget::GetDesktopToDeviceScaleByScreen() {
1864 return (nsView::GetViewFor(this)->GetViewManager()->GetDeviceContext())
1865 ->GetDesktopToDeviceScale();
1868 nsresult nsIWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
1869 bool aLongTap,
1870 nsIObserver* aObserver) {
1871 AutoObserverNotifier notifier(aObserver, "touchtap");
1873 if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) {
1874 sPointerIdCounter = 0;
1876 int pointerId = sPointerIdCounter;
1877 sPointerIdCounter++;
1878 nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT, aPoint,
1879 1.0, 90, nullptr);
1880 if (NS_FAILED(rv)) {
1881 return rv;
1884 if (!aLongTap) {
1885 return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, aPoint, 0, 0,
1886 nullptr);
1889 // initiate a long tap
1890 int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay",
1891 TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC);
1892 if (!mLongTapTimer) {
1893 mLongTapTimer = NS_NewTimer();
1894 if (!mLongTapTimer) {
1895 SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL, aPoint, 0, 0,
1896 nullptr);
1897 return NS_ERROR_UNEXPECTED;
1899 // Windows requires recuring events, so we set this to a smaller window
1900 // than the pref value.
1901 int timeout = elapse;
1902 if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) {
1903 timeout = TOUCH_INJECT_PUMP_TIMER_MSEC;
1905 mLongTapTimer->InitWithNamedFuncCallback(
1906 OnLongTapTimerCallback, this, timeout, nsITimer::TYPE_REPEATING_SLACK,
1907 "nsIWidget::SynthesizeNativeTouchTap");
1910 // If we already have a long tap pending, cancel it. We only allow one long
1911 // tap to be active at a time.
1912 if (mLongTapTouchPoint) {
1913 SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
1914 mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1917 mLongTapTouchPoint = MakeUnique<LongTapInfo>(
1918 pointerId, aPoint, TimeDuration::FromMilliseconds(elapse), aObserver);
1919 notifier.SkipNotification(); // we'll do it in the long-tap callback
1920 return NS_OK;
1923 // static
1924 void nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) {
1925 auto* self = static_cast<nsIWidget*>(aClosure);
1927 if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) >
1928 TimeStamp::Now()) {
1929 #ifdef XP_WIN
1930 // Windows needs us to keep pumping feedback to the digitizer, so update
1931 // the pointer id with the same position.
1932 self->SynthesizeNativeTouchPoint(
1933 self->mLongTapTouchPoint->mPointerId, TOUCH_CONTACT,
1934 self->mLongTapTouchPoint->mPosition, 1.0, 90, nullptr);
1935 #endif
1936 return;
1939 AutoObserverNotifier notifier(self->mLongTapTouchPoint->mObserver,
1940 "touchtap");
1942 // finished, remove the touch point
1943 self->mLongTapTimer->Cancel();
1944 self->mLongTapTimer = nullptr;
1945 self->SynthesizeNativeTouchPoint(
1946 self->mLongTapTouchPoint->mPointerId, TOUCH_REMOVE,
1947 self->mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1948 self->mLongTapTouchPoint = nullptr;
1951 nsresult nsIWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
1952 AutoObserverNotifier notifier(aObserver, "cleartouch");
1954 if (!mLongTapTimer) {
1955 return NS_OK;
1957 mLongTapTimer->Cancel();
1958 mLongTapTimer = nullptr;
1959 SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
1960 mLongTapTouchPoint->mPosition, 0, 0, nullptr);
1961 mLongTapTouchPoint = nullptr;
1962 return NS_OK;
1965 MultiTouchInput nsBaseWidget::UpdateSynthesizedTouchState(
1966 MultiTouchInput* aState, uint32_t aTime, mozilla::TimeStamp aTimeStamp,
1967 uint32_t aPointerId, TouchPointerState aPointerState,
1968 LayoutDeviceIntPoint aPoint, double aPointerPressure,
1969 uint32_t aPointerOrientation) {
1970 ScreenIntPoint pointerScreenPoint = ViewAs<ScreenPixel>(
1971 aPoint, PixelCastJustification::LayoutDeviceIsScreenForBounds);
1973 // We can't dispatch *aState directly because (a) dispatching
1974 // it might inadvertently modify it and (b) in the case of touchend or
1975 // touchcancel events aState will hold the touches that are
1976 // still down whereas the input dispatched needs to hold the removed
1977 // touch(es). We use |inputToDispatch| for this purpose.
1978 MultiTouchInput inputToDispatch;
1979 inputToDispatch.mInputType = MULTITOUCH_INPUT;
1980 inputToDispatch.mTime = aTime;
1981 inputToDispatch.mTimeStamp = aTimeStamp;
1983 int32_t index = aState->IndexOfTouch((int32_t)aPointerId);
1984 if (aPointerState == TOUCH_CONTACT) {
1985 if (index >= 0) {
1986 // found an existing touch point, update it
1987 SingleTouchData& point = aState->mTouches[index];
1988 point.mScreenPoint = pointerScreenPoint;
1989 point.mRotationAngle = (float)aPointerOrientation;
1990 point.mForce = (float)aPointerPressure;
1991 inputToDispatch.mType = MultiTouchInput::MULTITOUCH_MOVE;
1992 } else {
1993 // new touch point, add it
1994 aState->mTouches.AppendElement(SingleTouchData(
1995 (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0),
1996 (float)aPointerOrientation, (float)aPointerPressure));
1997 inputToDispatch.mType = MultiTouchInput::MULTITOUCH_START;
1999 inputToDispatch.mTouches = aState->mTouches;
2000 } else {
2001 MOZ_ASSERT(aPointerState == TOUCH_REMOVE || aPointerState == TOUCH_CANCEL);
2002 // a touch point is being lifted, so remove it from the stored list
2003 if (index >= 0) {
2004 aState->mTouches.RemoveElementAt(index);
2006 inputToDispatch.mType =
2007 (aPointerState == TOUCH_REMOVE ? MultiTouchInput::MULTITOUCH_END
2008 : MultiTouchInput::MULTITOUCH_CANCEL);
2009 inputToDispatch.mTouches.AppendElement(SingleTouchData(
2010 (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0),
2011 (float)aPointerOrientation, (float)aPointerPressure));
2014 return inputToDispatch;
2017 void nsBaseWidget::NotifyLiveResizeStarted() {
2018 // If we have mLiveResizeListeners already non-empty, we should notify those
2019 // listeners that the resize stopped before starting anew. In theory this
2020 // should never happen because we shouldn't get nested live resize actions.
2021 NotifyLiveResizeStopped();
2022 MOZ_ASSERT(mLiveResizeListeners.IsEmpty());
2024 // If we can get the active tab parent for the current widget, suppress
2025 // the displayport on it during the live resize.
2026 if (!mWidgetListener) {
2027 return;
2029 nsCOMPtr<nsIXULWindow> xulWindow = mWidgetListener->GetXULWindow();
2030 if (!xulWindow) {
2031 return;
2033 mLiveResizeListeners = xulWindow->GetLiveResizeListeners();
2034 for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) {
2035 mLiveResizeListeners[i]->LiveResizeStarted();
2039 void nsBaseWidget::NotifyLiveResizeStopped() {
2040 if (!mLiveResizeListeners.IsEmpty()) {
2041 for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) {
2042 mLiveResizeListeners[i]->LiveResizeStopped();
2044 mLiveResizeListeners.Clear();
2048 void nsBaseWidget::RegisterPluginWindowForRemoteUpdates() {
2049 #if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2050 MOZ_ASSERT_UNREACHABLE(
2051 "nsBaseWidget::RegisterPluginWindowForRemoteUpdates "
2052 "not implemented!");
2053 return;
2054 #else
2055 MOZ_ASSERT(NS_IsMainThread());
2056 void* id = GetNativeData(NS_NATIVE_PLUGIN_ID);
2057 if (!id) {
2058 NS_WARNING("This is not a valid native widget!");
2059 return;
2061 MOZ_ASSERT(sPluginWidgetList);
2062 sPluginWidgetList->Put(id, this);
2063 #endif
2066 void nsBaseWidget::UnregisterPluginWindowForRemoteUpdates() {
2067 #if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2068 MOZ_ASSERT_UNREACHABLE(
2069 "nsBaseWidget::UnregisterPluginWindowForRemoteUpdates "
2070 "not implemented!");
2071 return;
2072 #else
2073 MOZ_ASSERT(NS_IsMainThread());
2074 void* id = GetNativeData(NS_NATIVE_PLUGIN_ID);
2075 if (!id) {
2076 NS_WARNING("This is not a valid native widget!");
2077 return;
2079 MOZ_ASSERT(sPluginWidgetList);
2080 sPluginWidgetList->Remove(id);
2081 #endif
2084 nsresult nsBaseWidget::AsyncEnableDragDrop(bool aEnable) {
2085 RefPtr<nsBaseWidget> kungFuDeathGrip = this;
2086 return NS_DispatchToCurrentThreadQueue(
2087 NS_NewRunnableFunction(
2088 "AsyncEnableDragDropFn",
2089 [this, aEnable, kungFuDeathGrip]() { EnableDragDrop(aEnable); }),
2090 kAsyncDragDropTimeout, EventQueuePriority::Idle);
2093 // static
2094 nsIWidget* nsIWidget::LookupRegisteredPluginWindow(uintptr_t aWindowID) {
2095 #if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2096 MOZ_ASSERT_UNREACHABLE(
2097 "nsBaseWidget::LookupRegisteredPluginWindow "
2098 "not implemented!");
2099 return nullptr;
2100 #else
2101 MOZ_ASSERT(NS_IsMainThread());
2102 MOZ_ASSERT(sPluginWidgetList);
2103 return sPluginWidgetList->GetWeak((void*)aWindowID);
2104 #endif
2107 // static
2108 void nsIWidget::UpdateRegisteredPluginWindowVisibility(
2109 uintptr_t aOwnerWidget, nsTArray<uintptr_t>& aPluginIds) {
2110 #if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2111 MOZ_ASSERT_UNREACHABLE(
2112 "nsBaseWidget::UpdateRegisteredPluginWindowVisibility"
2113 " not implemented!");
2114 return;
2115 #else
2116 MOZ_ASSERT(NS_IsMainThread());
2117 MOZ_ASSERT(sPluginWidgetList);
2119 // Our visible list is associated with a compositor which is associated with
2120 // a specific top level window. We use the parent widget during iteration
2121 // to skip the plugin widgets owned by other top level windows.
2122 for (auto iter = sPluginWidgetList->Iter(); !iter.Done(); iter.Next()) {
2123 const void* windowId = iter.Key();
2124 nsIWidget* widget = iter.UserData();
2126 MOZ_ASSERT(windowId);
2127 MOZ_ASSERT(widget);
2129 if (!widget->Destroyed()) {
2130 if ((uintptr_t)widget->GetParent() == aOwnerWidget) {
2131 widget->Show(aPluginIds.Contains((uintptr_t)windowId));
2135 #endif
2138 #if defined(XP_WIN)
2139 // static
2140 void nsIWidget::CaptureRegisteredPlugins(uintptr_t aOwnerWidget) {
2141 MOZ_ASSERT(NS_IsMainThread());
2142 MOZ_ASSERT(sPluginWidgetList);
2144 // Our visible list is associated with a compositor which is associated with
2145 // a specific top level window. We use the parent widget during iteration
2146 // to skip the plugin widgets owned by other top level windows.
2147 for (auto iter = sPluginWidgetList->Iter(); !iter.Done(); iter.Next()) {
2148 DebugOnly<const void*> windowId = iter.Key();
2149 nsIWidget* widget = iter.UserData();
2151 MOZ_ASSERT(windowId);
2152 MOZ_ASSERT(widget);
2154 if (!widget->Destroyed() && widget->IsVisible()) {
2155 if ((uintptr_t)widget->GetParent() == aOwnerWidget) {
2156 widget->UpdateScrollCapture();
2162 uint64_t nsBaseWidget::CreateScrollCaptureContainer() {
2163 mScrollCaptureContainer =
2164 LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
2165 if (!mScrollCaptureContainer) {
2166 NS_WARNING("Failed to create ImageContainer for widget image capture.");
2167 return ImageContainer::sInvalidAsyncContainerId;
2170 return mScrollCaptureContainer->GetAsyncContainerHandle().Value();
2173 void nsBaseWidget::UpdateScrollCapture() {
2174 // Don't capture if no container or no size.
2175 if (!mScrollCaptureContainer || mBounds.IsEmpty()) {
2176 return;
2179 // If the derived class cannot take a snapshot, for example due to clipping,
2180 // then it is responsible for creating a fallback. If null is returned, this
2181 // means that we want to keep the existing snapshot.
2182 RefPtr<gfx::SourceSurface> snapshot = CreateScrollSnapshot();
2183 if (!snapshot) {
2184 return;
2187 ImageContainer::NonOwningImage holder(new SourceSurfaceImage(snapshot));
2189 AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
2190 imageList.AppendElement(holder);
2192 mScrollCaptureContainer->SetCurrentImages(imageList);
2195 void nsBaseWidget::DefaultFillScrollCapture(DrawTarget* aSnapshotDrawTarget) {
2196 gfx::IntSize dtSize = aSnapshotDrawTarget->GetSize();
2197 aSnapshotDrawTarget->FillRect(
2198 gfx::Rect(0, 0, dtSize.width, dtSize.height),
2199 gfx::ColorPattern(gfx::Color::FromABGR(kScrollCaptureFillColor)),
2200 gfx::DrawOptions(1.f, gfx::CompositionOp::OP_SOURCE));
2201 aSnapshotDrawTarget->Flush();
2203 #endif
2205 const IMENotificationRequests& nsIWidget::IMENotificationRequestsRef() {
2206 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
2207 return dispatcher->IMENotificationRequestsRef();
2210 nsresult nsIWidget::OnWindowedPluginKeyEvent(
2211 const NativeEventData& aKeyEventData,
2212 nsIKeyEventInPluginCallback* aCallback) {
2213 return NS_ERROR_NOT_IMPLEMENTED;
2216 void nsIWidget::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {}
2218 void nsIWidget::GetEditCommands(nsIWidget::NativeKeyBindingsType aType,
2219 const WidgetKeyboardEvent& aEvent,
2220 nsTArray<CommandInt>& aCommands) {
2221 MOZ_ASSERT(aEvent.IsTrusted());
2222 MOZ_ASSERT(aCommands.IsEmpty());
2225 already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboard() {
2226 if (XRE_IsContentProcess()) {
2227 return CreateBidiKeyboardContentProcess();
2229 return CreateBidiKeyboardInner();
2232 #ifdef ANDROID
2233 already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboardInner() {
2234 // no bidi keyboard implementation
2235 return nullptr;
2237 #endif
2239 namespace mozilla {
2240 namespace widget {
2242 const char* ToChar(InputContext::Origin aOrigin) {
2243 switch (aOrigin) {
2244 case InputContext::ORIGIN_MAIN:
2245 return "ORIGIN_MAIN";
2246 case InputContext::ORIGIN_CONTENT:
2247 return "ORIGIN_CONTENT";
2248 default:
2249 return "Unexpected value";
2253 const char* ToChar(IMEMessage aIMEMessage) {
2254 switch (aIMEMessage) {
2255 case NOTIFY_IME_OF_NOTHING:
2256 return "NOTIFY_IME_OF_NOTHING";
2257 case NOTIFY_IME_OF_FOCUS:
2258 return "NOTIFY_IME_OF_FOCUS";
2259 case NOTIFY_IME_OF_BLUR:
2260 return "NOTIFY_IME_OF_BLUR";
2261 case NOTIFY_IME_OF_SELECTION_CHANGE:
2262 return "NOTIFY_IME_OF_SELECTION_CHANGE";
2263 case NOTIFY_IME_OF_TEXT_CHANGE:
2264 return "NOTIFY_IME_OF_TEXT_CHANGE";
2265 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
2266 return "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED";
2267 case NOTIFY_IME_OF_POSITION_CHANGE:
2268 return "NOTIFY_IME_OF_POSITION_CHANGE";
2269 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
2270 return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT";
2271 case REQUEST_TO_COMMIT_COMPOSITION:
2272 return "REQUEST_TO_COMMIT_COMPOSITION";
2273 case REQUEST_TO_CANCEL_COMPOSITION:
2274 return "REQUEST_TO_CANCEL_COMPOSITION";
2275 default:
2276 return "Unexpected value";
2280 void NativeIMEContext::Init(nsIWidget* aWidget) {
2281 if (!aWidget) {
2282 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2283 mOriginProcessID = static_cast<uint64_t>(-1);
2284 return;
2286 if (!XRE_IsContentProcess()) {
2287 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(
2288 aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT));
2289 mOriginProcessID = 0;
2290 return;
2292 // If this is created in a child process, aWidget is an instance of
2293 // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT.
2294 // Instead of that PuppetWidget::GetNativeIMEContext() returns cached
2295 // native IME context of the parent process.
2296 *this = aWidget->GetNativeIMEContext();
2299 void NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext) {
2300 if (NS_WARN_IF(!aRawNativeIMEContext)) {
2301 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2302 mOriginProcessID = static_cast<uint64_t>(-1);
2303 return;
2305 mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext);
2306 mOriginProcessID =
2307 XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0;
2310 void IMENotification::TextChangeDataBase::MergeWith(
2311 const IMENotification::TextChangeDataBase& aOther) {
2312 MOZ_ASSERT(aOther.IsValid(), "Merging data must store valid data");
2313 MOZ_ASSERT(aOther.mStartOffset <= aOther.mRemovedEndOffset,
2314 "end of removed text must be same or larger than start");
2315 MOZ_ASSERT(aOther.mStartOffset <= aOther.mAddedEndOffset,
2316 "end of added text must be same or larger than start");
2318 if (!IsValid()) {
2319 *this = aOther;
2320 return;
2323 // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed
2324 // text ranges. I.e., mStartOffset should be the smallest offset of all
2325 // modified text ranges in old text. |mRemovedEndOffset| should be the
2326 // largest end offset in old text of all modified text ranges.
2327 // |mAddedEndOffset| represents the end offset of all inserted text ranges.
2328 // I.e., only this is an offset in new text.
2329 // In other words, between mStartOffset and |mRemovedEndOffset| of the
2330 // premodified text was already removed. And some text whose length is
2331 // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|. I.e.,
2332 // this allows IME to mark dirty the modified text range with |mStartOffset|
2333 // and |mRemovedEndOffset| if IME stores all text of the focused editor and
2334 // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|.
2335 // Additionally, IME can retrieve only the text between |mStartOffset| and
2336 // |mAddedEndOffset| for updating stored text.
2338 // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they
2339 // should be adjusted to be in same text. The |newData.mStartOffset| and
2340 // |newData.mRemovedEndOffset| should be computed as in old text because
2341 // |mStartOffset| and |mRemovedEndOffset| represent the modified text range
2342 // in the old text but even if some text before the values of the newData
2343 // has already been modified, the values don't include the changes.
2345 // For comparing new and old |mAddedEndOffset| values, they should be
2346 // adjusted to be in same text. The |oldData.mAddedEndOffset| should be
2347 // computed as in the new text because |mAddedEndOffset| indicates the end
2348 // offset of inserted text in the new text but |oldData.mAddedEndOffset|
2349 // doesn't include any changes of the text before |newData.mAddedEndOffset|.
2351 const TextChangeDataBase& newData = aOther;
2352 const TextChangeDataBase oldData = *this;
2354 // mCausedOnlyByComposition should be true only when all changes are caused
2355 // by composition.
2356 mCausedOnlyByComposition =
2357 newData.mCausedOnlyByComposition && oldData.mCausedOnlyByComposition;
2359 // mIncludingChangesWithoutComposition should be true if at least one of
2360 // merged changes occurred without composition.
2361 mIncludingChangesWithoutComposition =
2362 newData.mIncludingChangesWithoutComposition ||
2363 oldData.mIncludingChangesWithoutComposition;
2365 // mIncludingChangesDuringComposition should be true when at least one of
2366 // the merged non-composition changes occurred during the latest composition.
2367 if (!newData.mCausedOnlyByComposition &&
2368 !newData.mIncludingChangesDuringComposition) {
2369 MOZ_ASSERT(newData.mIncludingChangesWithoutComposition);
2370 MOZ_ASSERT(mIncludingChangesWithoutComposition);
2371 // If new change is neither caused by composition nor occurred during
2372 // composition, set mIncludingChangesDuringComposition to false because
2373 // IME doesn't want outdated text changes as text change during current
2374 // composition.
2375 mIncludingChangesDuringComposition = false;
2376 } else {
2377 // Otherwise, set mIncludingChangesDuringComposition to true if either
2378 // oldData or newData includes changes during composition.
2379 mIncludingChangesDuringComposition =
2380 newData.mIncludingChangesDuringComposition ||
2381 oldData.mIncludingChangesDuringComposition;
2384 if (newData.mStartOffset >= oldData.mAddedEndOffset) {
2385 // Case 1:
2386 // If new start is after old end offset of added text, it means that text
2387 // after the modified range is modified. Like:
2388 // added range of old change: +----------+
2389 // removed range of new change: +----------+
2390 // So, the old start offset is always the smaller offset.
2391 mStartOffset = oldData.mStartOffset;
2392 // The new end offset of removed text is moved by the old change and we
2393 // need to cancel the move of the old change for comparing the offsets in
2394 // same text because it doesn't make sensce to compare offsets in different
2395 // text.
2396 uint32_t newRemovedEndOffsetInOldText =
2397 newData.mRemovedEndOffset - oldData.Difference();
2398 mRemovedEndOffset =
2399 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2400 // The new end offset of added text is always the larger offset.
2401 mAddedEndOffset = newData.mAddedEndOffset;
2402 return;
2405 if (newData.mStartOffset >= oldData.mStartOffset) {
2406 // If new start is in the modified range, it means that new data changes
2407 // a part or all of the range.
2408 mStartOffset = oldData.mStartOffset;
2409 if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
2410 // Case 2:
2411 // If new end of removed text is greater than old end of added text, it
2412 // means that all or a part of modified range modified again and text
2413 // after the modified range is also modified. Like:
2414 // added range of old change: +----------+
2415 // removed range of new change: +----------+
2416 // So, the new removed end offset is moved by the old change and we need
2417 // to cancel the move of the old change for comparing the offsets in the
2418 // same text because it doesn't make sense to compare the offsets in
2419 // different text.
2420 uint32_t newRemovedEndOffsetInOldText =
2421 newData.mRemovedEndOffset - oldData.Difference();
2422 mRemovedEndOffset =
2423 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2424 // The old end of added text is replaced by new change. So, it should be
2425 // same as the new start. On the other hand, the new added end offset is
2426 // always same or larger. Therefore, the merged end offset of added
2427 // text should be the new end offset of added text.
2428 mAddedEndOffset = newData.mAddedEndOffset;
2429 return;
2432 // Case 3:
2433 // If new end of removed text is less than old end of added text, it means
2434 // that only a part of the modified range is modified again. Like:
2435 // added range of old change: +------------+
2436 // removed range of new change: +-----+
2437 // So, the new end offset of removed text should be same as the old end
2438 // offset of removed text. Therefore, the merged end offset of removed
2439 // text should be the old text change's |mRemovedEndOffset|.
2440 mRemovedEndOffset = oldData.mRemovedEndOffset;
2441 // The old end of added text is moved by new change. So, we need to cancel
2442 // the move of the new change for comparing the offsets in same text.
2443 uint32_t oldAddedEndOffsetInNewText =
2444 oldData.mAddedEndOffset + newData.Difference();
2445 mAddedEndOffset =
2446 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2447 return;
2450 if (newData.mRemovedEndOffset >= oldData.mStartOffset) {
2451 // If new end of removed text is greater than old start (and new start is
2452 // less than old start), it means that a part of modified range is modified
2453 // again and some new text before the modified range is also modified.
2454 MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
2455 "new start offset should be less than old one here");
2456 mStartOffset = newData.mStartOffset;
2457 if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
2458 // Case 4:
2459 // If new end of removed text is greater than old end of added text, it
2460 // means that all modified text and text after the modified range is
2461 // modified. Like:
2462 // added range of old change: +----------+
2463 // removed range of new change: +------------------+
2464 // So, the new end of removed text is moved by the old change. Therefore,
2465 // we need to cancel the move of the old change for comparing the offsets
2466 // in same text because it doesn't make sense to compare the offsets in
2467 // different text.
2468 uint32_t newRemovedEndOffsetInOldText =
2469 newData.mRemovedEndOffset - oldData.Difference();
2470 mRemovedEndOffset =
2471 std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2472 // The old end of added text is replaced by new change. So, the old end
2473 // offset of added text is same as new text change's start offset. Then,
2474 // new change's end offset of added text is always same or larger than
2475 // it. Therefore, merged end offset of added text is always the new end
2476 // offset of added text.
2477 mAddedEndOffset = newData.mAddedEndOffset;
2478 return;
2481 // Case 5:
2482 // If new end of removed text is less than old end of added text, it
2483 // means that only a part of the modified range is modified again. Like:
2484 // added range of old change: +----------+
2485 // removed range of new change: +----------+
2486 // So, the new end of removed text should be same as old end of removed
2487 // text for preventing end of removed text to be modified. Therefore,
2488 // merged end offset of removed text is always the old end offset of removed
2489 // text.
2490 mRemovedEndOffset = oldData.mRemovedEndOffset;
2491 // The old end of added text is moved by this change. So, we need to
2492 // cancel the move of the new change for comparing the offsets in same text
2493 // because it doesn't make sense to compare the offsets in different text.
2494 uint32_t oldAddedEndOffsetInNewText =
2495 oldData.mAddedEndOffset + newData.Difference();
2496 mAddedEndOffset =
2497 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2498 return;
2501 // Case 6:
2502 // Otherwise, i.e., both new end of added text and new start are less than
2503 // old start, text before the modified range is modified. Like:
2504 // added range of old change: +----------+
2505 // removed range of new change: +----------+
2506 MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
2507 "new start offset should be less than old one here");
2508 mStartOffset = newData.mStartOffset;
2509 MOZ_ASSERT(newData.mRemovedEndOffset < oldData.mRemovedEndOffset,
2510 "new removed end offset should be less than old one here");
2511 mRemovedEndOffset = oldData.mRemovedEndOffset;
2512 // The end of added text should be adjusted with the new difference.
2513 uint32_t oldAddedEndOffsetInNewText =
2514 oldData.mAddedEndOffset + newData.Difference();
2515 mAddedEndOffset =
2516 std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2519 #ifdef DEBUG
2521 // Let's test the code of merging multiple text change data in debug build
2522 // and crash if one of them fails because this feature is very complex but
2523 // cannot be tested with mochitest.
2524 void IMENotification::TextChangeDataBase::Test() {
2525 static bool gTestTextChangeEvent = true;
2526 if (!gTestTextChangeEvent) {
2527 return;
2529 gTestTextChangeEvent = false;
2531 /****************************************************************************
2532 * Case 1
2533 ****************************************************************************/
2535 // Appending text
2536 MergeWith(TextChangeData(10, 10, 20, false, false));
2537 MergeWith(TextChangeData(20, 20, 35, false, false));
2538 MOZ_ASSERT(mStartOffset == 10,
2539 "Test 1-1-1: mStartOffset should be the first offset");
2540 MOZ_ASSERT(
2541 mRemovedEndOffset == 10, // 20 - (20 - 10)
2542 "Test 1-1-2: mRemovedEndOffset should be the first end of removed text");
2543 MOZ_ASSERT(
2544 mAddedEndOffset == 35,
2545 "Test 1-1-3: mAddedEndOffset should be the last end of added text");
2546 Clear();
2548 // Removing text (longer line -> shorter line)
2549 MergeWith(TextChangeData(10, 20, 10, false, false));
2550 MergeWith(TextChangeData(10, 30, 10, false, false));
2551 MOZ_ASSERT(mStartOffset == 10,
2552 "Test 1-2-1: mStartOffset should be the first offset");
2553 MOZ_ASSERT(mRemovedEndOffset == 40, // 30 + (10 - 20)
2554 "Test 1-2-2: mRemovedEndOffset should be the the last end of "
2555 "removed text "
2556 "with already removed length");
2557 MOZ_ASSERT(
2558 mAddedEndOffset == 10,
2559 "Test 1-2-3: mAddedEndOffset should be the last end of added text");
2560 Clear();
2562 // Removing text (shorter line -> longer line)
2563 MergeWith(TextChangeData(10, 20, 10, false, false));
2564 MergeWith(TextChangeData(10, 15, 10, false, false));
2565 MOZ_ASSERT(mStartOffset == 10,
2566 "Test 1-3-1: mStartOffset should be the first offset");
2567 MOZ_ASSERT(mRemovedEndOffset == 25, // 15 + (10 - 20)
2568 "Test 1-3-2: mRemovedEndOffset should be the the last end of "
2569 "removed text "
2570 "with already removed length");
2571 MOZ_ASSERT(
2572 mAddedEndOffset == 10,
2573 "Test 1-3-3: mAddedEndOffset should be the last end of added text");
2574 Clear();
2576 // Appending text at different point (not sure if actually occurs)
2577 MergeWith(TextChangeData(10, 10, 20, false, false));
2578 MergeWith(TextChangeData(55, 55, 60, false, false));
2579 MOZ_ASSERT(mStartOffset == 10,
2580 "Test 1-4-1: mStartOffset should be the smallest offset");
2581 MOZ_ASSERT(
2582 mRemovedEndOffset == 45, // 55 - (10 - 20)
2583 "Test 1-4-2: mRemovedEndOffset should be the the largest end of removed "
2584 "text without already added length");
2585 MOZ_ASSERT(
2586 mAddedEndOffset == 60,
2587 "Test 1-4-3: mAddedEndOffset should be the last end of added text");
2588 Clear();
2590 // Removing text at different point (not sure if actually occurs)
2591 MergeWith(TextChangeData(10, 20, 10, false, false));
2592 MergeWith(TextChangeData(55, 68, 55, false, false));
2593 MOZ_ASSERT(mStartOffset == 10,
2594 "Test 1-5-1: mStartOffset should be the smallest offset");
2595 MOZ_ASSERT(
2596 mRemovedEndOffset == 78, // 68 - (10 - 20)
2597 "Test 1-5-2: mRemovedEndOffset should be the the largest end of removed "
2598 "text with already removed length");
2599 MOZ_ASSERT(
2600 mAddedEndOffset == 55,
2601 "Test 1-5-3: mAddedEndOffset should be the largest end of added text");
2602 Clear();
2604 // Replacing text and append text (becomes longer)
2605 MergeWith(TextChangeData(30, 35, 32, false, false));
2606 MergeWith(TextChangeData(32, 32, 40, false, false));
2607 MOZ_ASSERT(mStartOffset == 30,
2608 "Test 1-6-1: mStartOffset should be the smallest offset");
2609 MOZ_ASSERT(
2610 mRemovedEndOffset == 35, // 32 - (32 - 35)
2611 "Test 1-6-2: mRemovedEndOffset should be the the first end of removed "
2612 "text");
2613 MOZ_ASSERT(
2614 mAddedEndOffset == 40,
2615 "Test 1-6-3: mAddedEndOffset should be the last end of added text");
2616 Clear();
2618 // Replacing text and append text (becomes shorter)
2619 MergeWith(TextChangeData(30, 35, 32, false, false));
2620 MergeWith(TextChangeData(32, 32, 33, false, false));
2621 MOZ_ASSERT(mStartOffset == 30,
2622 "Test 1-7-1: mStartOffset should be the smallest offset");
2623 MOZ_ASSERT(
2624 mRemovedEndOffset == 35, // 32 - (32 - 35)
2625 "Test 1-7-2: mRemovedEndOffset should be the the first end of removed "
2626 "text");
2627 MOZ_ASSERT(
2628 mAddedEndOffset == 33,
2629 "Test 1-7-3: mAddedEndOffset should be the last end of added text");
2630 Clear();
2632 // Removing text and replacing text after first range (not sure if actually
2633 // occurs)
2634 MergeWith(TextChangeData(30, 35, 30, false, false));
2635 MergeWith(TextChangeData(32, 34, 48, false, false));
2636 MOZ_ASSERT(mStartOffset == 30,
2637 "Test 1-8-1: mStartOffset should be the smallest offset");
2638 MOZ_ASSERT(mRemovedEndOffset == 39, // 34 - (30 - 35)
2639 "Test 1-8-2: mRemovedEndOffset should be the the first end of "
2640 "removed text "
2641 "without already removed text");
2642 MOZ_ASSERT(
2643 mAddedEndOffset == 48,
2644 "Test 1-8-3: mAddedEndOffset should be the last end of added text");
2645 Clear();
2647 // Removing text and replacing text after first range (not sure if actually
2648 // occurs)
2649 MergeWith(TextChangeData(30, 35, 30, false, false));
2650 MergeWith(TextChangeData(32, 38, 36, false, false));
2651 MOZ_ASSERT(mStartOffset == 30,
2652 "Test 1-9-1: mStartOffset should be the smallest offset");
2653 MOZ_ASSERT(mRemovedEndOffset == 43, // 38 - (30 - 35)
2654 "Test 1-9-2: mRemovedEndOffset should be the the first end of "
2655 "removed text "
2656 "without already removed text");
2657 MOZ_ASSERT(
2658 mAddedEndOffset == 36,
2659 "Test 1-9-3: mAddedEndOffset should be the last end of added text");
2660 Clear();
2662 /****************************************************************************
2663 * Case 2
2664 ****************************************************************************/
2666 // Replacing text in around end of added text (becomes shorter) (not sure
2667 // if actually occurs)
2668 MergeWith(TextChangeData(50, 50, 55, false, false));
2669 MergeWith(TextChangeData(53, 60, 54, false, false));
2670 MOZ_ASSERT(mStartOffset == 50,
2671 "Test 2-1-1: mStartOffset should be the smallest offset");
2672 MOZ_ASSERT(mRemovedEndOffset == 55, // 60 - (55 - 50)
2673 "Test 2-1-2: mRemovedEndOffset should be the the last end of "
2674 "removed text "
2675 "without already added text length");
2676 MOZ_ASSERT(
2677 mAddedEndOffset == 54,
2678 "Test 2-1-3: mAddedEndOffset should be the last end of added text");
2679 Clear();
2681 // Replacing text around end of added text (becomes longer) (not sure
2682 // if actually occurs)
2683 MergeWith(TextChangeData(50, 50, 55, false, false));
2684 MergeWith(TextChangeData(54, 62, 68, false, false));
2685 MOZ_ASSERT(mStartOffset == 50,
2686 "Test 2-2-1: mStartOffset should be the smallest offset");
2687 MOZ_ASSERT(mRemovedEndOffset == 57, // 62 - (55 - 50)
2688 "Test 2-2-2: mRemovedEndOffset should be the the last end of "
2689 "removed text "
2690 "without already added text length");
2691 MOZ_ASSERT(
2692 mAddedEndOffset == 68,
2693 "Test 2-2-3: mAddedEndOffset should be the last end of added text");
2694 Clear();
2696 // Replacing text around end of replaced text (became shorter) (not sure if
2697 // actually occurs)
2698 MergeWith(TextChangeData(36, 48, 45, false, false));
2699 MergeWith(TextChangeData(43, 50, 49, false, false));
2700 MOZ_ASSERT(mStartOffset == 36,
2701 "Test 2-3-1: mStartOffset should be the smallest offset");
2702 MOZ_ASSERT(mRemovedEndOffset == 53, // 50 - (45 - 48)
2703 "Test 2-3-2: mRemovedEndOffset should be the the last end of "
2704 "removed text "
2705 "without already removed text length");
2706 MOZ_ASSERT(
2707 mAddedEndOffset == 49,
2708 "Test 2-3-3: mAddedEndOffset should be the last end of added text");
2709 Clear();
2711 // Replacing text around end of replaced text (became longer) (not sure if
2712 // actually occurs)
2713 MergeWith(TextChangeData(36, 52, 53, false, false));
2714 MergeWith(TextChangeData(43, 68, 61, false, false));
2715 MOZ_ASSERT(mStartOffset == 36,
2716 "Test 2-4-1: mStartOffset should be the smallest offset");
2717 MOZ_ASSERT(mRemovedEndOffset == 67, // 68 - (53 - 52)
2718 "Test 2-4-2: mRemovedEndOffset should be the the last end of "
2719 "removed text "
2720 "without already added text length");
2721 MOZ_ASSERT(
2722 mAddedEndOffset == 61,
2723 "Test 2-4-3: mAddedEndOffset should be the last end of added text");
2724 Clear();
2726 /****************************************************************************
2727 * Case 3
2728 ****************************************************************************/
2730 // Appending text in already added text (not sure if actually occurs)
2731 MergeWith(TextChangeData(10, 10, 20, false, false));
2732 MergeWith(TextChangeData(15, 15, 30, false, false));
2733 MOZ_ASSERT(mStartOffset == 10,
2734 "Test 3-1-1: mStartOffset should be the smallest offset");
2735 MOZ_ASSERT(mRemovedEndOffset == 10,
2736 "Test 3-1-2: mRemovedEndOffset should be the the first end of "
2737 "removed text");
2738 MOZ_ASSERT(
2739 mAddedEndOffset == 35, // 20 + (30 - 15)
2740 "Test 3-1-3: mAddedEndOffset should be the first end of added text with "
2741 "added text length by the new change");
2742 Clear();
2744 // Replacing text in added text (not sure if actually occurs)
2745 MergeWith(TextChangeData(50, 50, 55, false, false));
2746 MergeWith(TextChangeData(52, 53, 56, false, false));
2747 MOZ_ASSERT(mStartOffset == 50,
2748 "Test 3-2-1: mStartOffset should be the smallest offset");
2749 MOZ_ASSERT(mRemovedEndOffset == 50,
2750 "Test 3-2-2: mRemovedEndOffset should be the the first end of "
2751 "removed text");
2752 MOZ_ASSERT(
2753 mAddedEndOffset == 58, // 55 + (56 - 53)
2754 "Test 3-2-3: mAddedEndOffset should be the first end of added text with "
2755 "added text length by the new change");
2756 Clear();
2758 // Replacing text in replaced text (became shorter) (not sure if actually
2759 // occurs)
2760 MergeWith(TextChangeData(36, 48, 45, false, false));
2761 MergeWith(TextChangeData(37, 38, 50, false, false));
2762 MOZ_ASSERT(mStartOffset == 36,
2763 "Test 3-3-1: mStartOffset should be the smallest offset");
2764 MOZ_ASSERT(mRemovedEndOffset == 48,
2765 "Test 3-3-2: mRemovedEndOffset should be the the first end of "
2766 "removed text");
2767 MOZ_ASSERT(
2768 mAddedEndOffset == 57, // 45 + (50 - 38)
2769 "Test 3-3-3: mAddedEndOffset should be the first end of added text with "
2770 "added text length by the new change");
2771 Clear();
2773 // Replacing text in replaced text (became longer) (not sure if actually
2774 // occurs)
2775 MergeWith(TextChangeData(32, 48, 53, false, false));
2776 MergeWith(TextChangeData(43, 50, 52, false, false));
2777 MOZ_ASSERT(mStartOffset == 32,
2778 "Test 3-4-1: mStartOffset should be the smallest offset");
2779 MOZ_ASSERT(mRemovedEndOffset == 48,
2780 "Test 3-4-2: mRemovedEndOffset should be the the last end of "
2781 "removed text "
2782 "without already added text length");
2783 MOZ_ASSERT(
2784 mAddedEndOffset == 55, // 53 + (52 - 50)
2785 "Test 3-4-3: mAddedEndOffset should be the first end of added text with "
2786 "added text length by the new change");
2787 Clear();
2789 // Replacing text in replaced text (became shorter) (not sure if actually
2790 // occurs)
2791 MergeWith(TextChangeData(36, 48, 50, false, false));
2792 MergeWith(TextChangeData(37, 49, 47, false, false));
2793 MOZ_ASSERT(mStartOffset == 36,
2794 "Test 3-5-1: mStartOffset should be the smallest offset");
2795 MOZ_ASSERT(
2796 mRemovedEndOffset == 48,
2797 "Test 3-5-2: mRemovedEndOffset should be the the first end of removed "
2798 "text");
2799 MOZ_ASSERT(mAddedEndOffset == 48, // 50 + (47 - 49)
2800 "Test 3-5-3: mAddedEndOffset should be the first end of added "
2801 "text without "
2802 "removed text length by the new change");
2803 Clear();
2805 // Replacing text in replaced text (became longer) (not sure if actually
2806 // occurs)
2807 MergeWith(TextChangeData(32, 48, 53, false, false));
2808 MergeWith(TextChangeData(43, 50, 47, false, false));
2809 MOZ_ASSERT(mStartOffset == 32,
2810 "Test 3-6-1: mStartOffset should be the smallest offset");
2811 MOZ_ASSERT(mRemovedEndOffset == 48,
2812 "Test 3-6-2: mRemovedEndOffset should be the the last end of "
2813 "removed text "
2814 "without already added text length");
2815 MOZ_ASSERT(mAddedEndOffset == 50, // 53 + (47 - 50)
2816 "Test 3-6-3: mAddedEndOffset should be the first end of added "
2817 "text without "
2818 "removed text length by the new change");
2819 Clear();
2821 /****************************************************************************
2822 * Case 4
2823 ****************************************************************************/
2825 // Replacing text all of already append text (not sure if actually occurs)
2826 MergeWith(TextChangeData(50, 50, 55, false, false));
2827 MergeWith(TextChangeData(44, 66, 68, false, false));
2828 MOZ_ASSERT(mStartOffset == 44,
2829 "Test 4-1-1: mStartOffset should be the smallest offset");
2830 MOZ_ASSERT(mRemovedEndOffset == 61, // 66 - (55 - 50)
2831 "Test 4-1-2: mRemovedEndOffset should be the the last end of "
2832 "removed text "
2833 "without already added text length");
2834 MOZ_ASSERT(
2835 mAddedEndOffset == 68,
2836 "Test 4-1-3: mAddedEndOffset should be the last end of added text");
2837 Clear();
2839 // Replacing text around a point in which text was removed (not sure if
2840 // actually occurs)
2841 MergeWith(TextChangeData(50, 62, 50, false, false));
2842 MergeWith(TextChangeData(44, 66, 68, false, false));
2843 MOZ_ASSERT(mStartOffset == 44,
2844 "Test 4-2-1: mStartOffset should be the smallest offset");
2845 MOZ_ASSERT(mRemovedEndOffset == 78, // 66 - (50 - 62)
2846 "Test 4-2-2: mRemovedEndOffset should be the the last end of "
2847 "removed text "
2848 "without already removed text length");
2849 MOZ_ASSERT(
2850 mAddedEndOffset == 68,
2851 "Test 4-2-3: mAddedEndOffset should be the last end of added text");
2852 Clear();
2854 // Replacing text all replaced text (became shorter) (not sure if actually
2855 // occurs)
2856 MergeWith(TextChangeData(50, 62, 60, false, false));
2857 MergeWith(TextChangeData(49, 128, 130, false, false));
2858 MOZ_ASSERT(mStartOffset == 49,
2859 "Test 4-3-1: mStartOffset should be the smallest offset");
2860 MOZ_ASSERT(mRemovedEndOffset == 130, // 128 - (60 - 62)
2861 "Test 4-3-2: mRemovedEndOffset should be the the last end of "
2862 "removed text "
2863 "without already removed text length");
2864 MOZ_ASSERT(
2865 mAddedEndOffset == 130,
2866 "Test 4-3-3: mAddedEndOffset should be the last end of added text");
2867 Clear();
2869 // Replacing text all replaced text (became longer) (not sure if actually
2870 // occurs)
2871 MergeWith(TextChangeData(50, 61, 73, false, false));
2872 MergeWith(TextChangeData(44, 100, 50, false, false));
2873 MOZ_ASSERT(mStartOffset == 44,
2874 "Test 4-4-1: mStartOffset should be the smallest offset");
2875 MOZ_ASSERT(mRemovedEndOffset == 88, // 100 - (73 - 61)
2876 "Test 4-4-2: mRemovedEndOffset should be the the last end of "
2877 "removed text "
2878 "with already added text length");
2879 MOZ_ASSERT(
2880 mAddedEndOffset == 50,
2881 "Test 4-4-3: mAddedEndOffset should be the last end of added text");
2882 Clear();
2884 /****************************************************************************
2885 * Case 5
2886 ****************************************************************************/
2888 // Replacing text around start of added text (not sure if actually occurs)
2889 MergeWith(TextChangeData(50, 50, 55, false, false));
2890 MergeWith(TextChangeData(48, 52, 49, false, false));
2891 MOZ_ASSERT(mStartOffset == 48,
2892 "Test 5-1-1: mStartOffset should be the smallest offset");
2893 MOZ_ASSERT(
2894 mRemovedEndOffset == 50,
2895 "Test 5-1-2: mRemovedEndOffset should be the the first end of removed "
2896 "text");
2897 MOZ_ASSERT(
2898 mAddedEndOffset == 52, // 55 + (52 - 49)
2899 "Test 5-1-3: mAddedEndOffset should be the first end of added text with "
2900 "added text length by the new change");
2901 Clear();
2903 // Replacing text around start of replaced text (became shorter) (not sure if
2904 // actually occurs)
2905 MergeWith(TextChangeData(50, 60, 58, false, false));
2906 MergeWith(TextChangeData(43, 50, 48, false, false));
2907 MOZ_ASSERT(mStartOffset == 43,
2908 "Test 5-2-1: mStartOffset should be the smallest offset");
2909 MOZ_ASSERT(
2910 mRemovedEndOffset == 60,
2911 "Test 5-2-2: mRemovedEndOffset should be the the first end of removed "
2912 "text");
2913 MOZ_ASSERT(mAddedEndOffset == 56, // 58 + (48 - 50)
2914 "Test 5-2-3: mAddedEndOffset should be the first end of added "
2915 "text without "
2916 "removed text length by the new change");
2917 Clear();
2919 // Replacing text around start of replaced text (became longer) (not sure if
2920 // actually occurs)
2921 MergeWith(TextChangeData(50, 60, 68, false, false));
2922 MergeWith(TextChangeData(43, 55, 53, false, false));
2923 MOZ_ASSERT(mStartOffset == 43,
2924 "Test 5-3-1: mStartOffset should be the smallest offset");
2925 MOZ_ASSERT(
2926 mRemovedEndOffset == 60,
2927 "Test 5-3-2: mRemovedEndOffset should be the the first end of removed "
2928 "text");
2929 MOZ_ASSERT(mAddedEndOffset == 66, // 68 + (53 - 55)
2930 "Test 5-3-3: mAddedEndOffset should be the first end of added "
2931 "text without "
2932 "removed text length by the new change");
2933 Clear();
2935 // Replacing text around start of replaced text (became shorter) (not sure if
2936 // actually occurs)
2937 MergeWith(TextChangeData(50, 60, 58, false, false));
2938 MergeWith(TextChangeData(43, 50, 128, false, false));
2939 MOZ_ASSERT(mStartOffset == 43,
2940 "Test 5-4-1: mStartOffset should be the smallest offset");
2941 MOZ_ASSERT(
2942 mRemovedEndOffset == 60,
2943 "Test 5-4-2: mRemovedEndOffset should be the the first end of removed "
2944 "text");
2945 MOZ_ASSERT(
2946 mAddedEndOffset == 136, // 58 + (128 - 50)
2947 "Test 5-4-3: mAddedEndOffset should be the first end of added text with "
2948 "added text length by the new change");
2949 Clear();
2951 // Replacing text around start of replaced text (became longer) (not sure if
2952 // actually occurs)
2953 MergeWith(TextChangeData(50, 60, 68, false, false));
2954 MergeWith(TextChangeData(43, 55, 65, false, false));
2955 MOZ_ASSERT(mStartOffset == 43,
2956 "Test 5-5-1: mStartOffset should be the smallest offset");
2957 MOZ_ASSERT(
2958 mRemovedEndOffset == 60,
2959 "Test 5-5-2: mRemovedEndOffset should be the the first end of removed "
2960 "text");
2961 MOZ_ASSERT(
2962 mAddedEndOffset == 78, // 68 + (65 - 55)
2963 "Test 5-5-3: mAddedEndOffset should be the first end of added text with "
2964 "added text length by the new change");
2965 Clear();
2967 /****************************************************************************
2968 * Case 6
2969 ****************************************************************************/
2971 // Appending text before already added text (not sure if actually occurs)
2972 MergeWith(TextChangeData(30, 30, 45, false, false));
2973 MergeWith(TextChangeData(10, 10, 20, false, false));
2974 MOZ_ASSERT(mStartOffset == 10,
2975 "Test 6-1-1: mStartOffset should be the smallest offset");
2976 MOZ_ASSERT(
2977 mRemovedEndOffset == 30,
2978 "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed "
2979 "text");
2980 MOZ_ASSERT(
2981 mAddedEndOffset == 55, // 45 + (20 - 10)
2982 "Test 6-1-3: mAddedEndOffset should be the first end of added text with "
2983 "added text length by the new change");
2984 Clear();
2986 // Removing text before already removed text (not sure if actually occurs)
2987 MergeWith(TextChangeData(30, 35, 30, false, false));
2988 MergeWith(TextChangeData(10, 25, 10, false, false));
2989 MOZ_ASSERT(mStartOffset == 10,
2990 "Test 6-2-1: mStartOffset should be the smallest offset");
2991 MOZ_ASSERT(
2992 mRemovedEndOffset == 35,
2993 "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed "
2994 "text");
2995 MOZ_ASSERT(
2996 mAddedEndOffset == 15, // 30 - (25 - 10)
2997 "Test 6-2-3: mAddedEndOffset should be the first end of added text with "
2998 "removed text length by the new change");
2999 Clear();
3001 // Replacing text before already replaced text (not sure if actually occurs)
3002 MergeWith(TextChangeData(50, 65, 70, false, false));
3003 MergeWith(TextChangeData(13, 24, 15, false, false));
3004 MOZ_ASSERT(mStartOffset == 13,
3005 "Test 6-3-1: mStartOffset should be the smallest offset");
3006 MOZ_ASSERT(
3007 mRemovedEndOffset == 65,
3008 "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed "
3009 "text");
3010 MOZ_ASSERT(mAddedEndOffset == 61, // 70 + (15 - 24)
3011 "Test 6-3-3: mAddedEndOffset should be the first end of added "
3012 "text without "
3013 "removed text length by the new change");
3014 Clear();
3016 // Replacing text before already replaced text (not sure if actually occurs)
3017 MergeWith(TextChangeData(50, 65, 70, false, false));
3018 MergeWith(TextChangeData(13, 24, 36, false, false));
3019 MOZ_ASSERT(mStartOffset == 13,
3020 "Test 6-4-1: mStartOffset should be the smallest offset");
3021 MOZ_ASSERT(
3022 mRemovedEndOffset == 65,
3023 "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed "
3024 "text");
3025 MOZ_ASSERT(mAddedEndOffset == 82, // 70 + (36 - 24)
3026 "Test 6-4-3: mAddedEndOffset should be the first end of added "
3027 "text without "
3028 "removed text length by the new change");
3029 Clear();
3032 #endif // #ifdef DEBUG
3034 } // namespace widget
3035 } // namespace mozilla
3037 #ifdef DEBUG
3038 //////////////////////////////////////////////////////////////
3040 // Convert a GUI event message code to a string.
3041 // Makes it a lot easier to debug events.
3043 // See gtk/nsWidget.cpp and windows/nsWindow.cpp
3044 // for a DebugPrintEvent() function that uses
3045 // this.
3047 //////////////////////////////////////////////////////////////
3048 /* static */
3049 nsAutoString nsBaseWidget::debug_GuiEventToString(WidgetGUIEvent* aGuiEvent) {
3050 NS_ASSERTION(nullptr != aGuiEvent, "cmon, null gui event.");
3052 nsAutoString eventName(NS_LITERAL_STRING("UNKNOWN"));
3054 # define _ASSIGN_eventName(_value, _name) \
3055 case _value: \
3056 eventName.AssignLiteral(_name); \
3057 break
3059 switch (aGuiEvent->mMessage) {
3060 _ASSIGN_eventName(eBlur, "eBlur");
3061 _ASSIGN_eventName(eDrop, "eDrop");
3062 _ASSIGN_eventName(eDragEnter, "eDragEnter");
3063 _ASSIGN_eventName(eDragExit, "eDragExit");
3064 _ASSIGN_eventName(eDragOver, "eDragOver");
3065 _ASSIGN_eventName(eEditorInput, "eEditorInput");
3066 _ASSIGN_eventName(eFocus, "eFocus");
3067 _ASSIGN_eventName(eFocusIn, "eFocusIn");
3068 _ASSIGN_eventName(eFocusOut, "eFocusOut");
3069 _ASSIGN_eventName(eFormSelect, "eFormSelect");
3070 _ASSIGN_eventName(eFormChange, "eFormChange");
3071 _ASSIGN_eventName(eFormReset, "eFormReset");
3072 _ASSIGN_eventName(eFormSubmit, "eFormSubmit");
3073 _ASSIGN_eventName(eImageAbort, "eImageAbort");
3074 _ASSIGN_eventName(eLoadError, "eLoadError");
3075 _ASSIGN_eventName(eKeyDown, "eKeyDown");
3076 _ASSIGN_eventName(eKeyPress, "eKeyPress");
3077 _ASSIGN_eventName(eKeyUp, "eKeyUp");
3078 _ASSIGN_eventName(eMouseEnterIntoWidget, "eMouseEnterIntoWidget");
3079 _ASSIGN_eventName(eMouseExitFromWidget, "eMouseExitFromWidget");
3080 _ASSIGN_eventName(eMouseDown, "eMouseDown");
3081 _ASSIGN_eventName(eMouseUp, "eMouseUp");
3082 _ASSIGN_eventName(eMouseClick, "eMouseClick");
3083 _ASSIGN_eventName(eMouseAuxClick, "eMouseAuxClick");
3084 _ASSIGN_eventName(eMouseDoubleClick, "eMouseDoubleClick");
3085 _ASSIGN_eventName(eMouseMove, "eMouseMove");
3086 _ASSIGN_eventName(eLoad, "eLoad");
3087 _ASSIGN_eventName(ePopState, "ePopState");
3088 _ASSIGN_eventName(eBeforeScriptExecute, "eBeforeScriptExecute");
3089 _ASSIGN_eventName(eAfterScriptExecute, "eAfterScriptExecute");
3090 _ASSIGN_eventName(eUnload, "eUnload");
3091 _ASSIGN_eventName(eHashChange, "eHashChange");
3092 _ASSIGN_eventName(eReadyStateChange, "eReadyStateChange");
3093 _ASSIGN_eventName(eXULBroadcast, "eXULBroadcast");
3094 _ASSIGN_eventName(eXULCommandUpdate, "eXULCommandUpdate");
3096 # undef _ASSIGN_eventName
3098 default: {
3099 eventName.AssignLiteral("UNKNOWN: ");
3100 eventName.AppendInt(aGuiEvent->mMessage);
3101 } break;
3104 return nsAutoString(eventName);
3106 //////////////////////////////////////////////////////////////
3108 // Code to deal with paint and event debug prefs.
3110 //////////////////////////////////////////////////////////////
3111 struct PrefPair {
3112 const char* name;
3113 bool value;
3116 static PrefPair debug_PrefValues[] = {
3117 {"nglayout.debug.crossing_event_dumping", false},
3118 {"nglayout.debug.event_dumping", false},
3119 {"nglayout.debug.invalidate_dumping", false},
3120 {"nglayout.debug.motion_event_dumping", false},
3121 {"nglayout.debug.paint_dumping", false},
3122 {"nglayout.debug.paint_flashing", false},
3123 {"nglayout.debug.paint_flashing_chrome", false}};
3125 //////////////////////////////////////////////////////////////
3126 bool nsBaseWidget::debug_GetCachedBoolPref(const char* aPrefName) {
3127 NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null.");
3129 for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3130 if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) {
3131 return debug_PrefValues[i].value;
3135 return false;
3137 //////////////////////////////////////////////////////////////
3138 static void debug_SetCachedBoolPref(const char* aPrefName, bool aValue) {
3139 NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null.");
3141 for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3142 if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) {
3143 debug_PrefValues[i].value = aValue;
3145 return;
3149 NS_ASSERTION(false, "cmon, this code is not reached dude.");
3152 //////////////////////////////////////////////////////////////
3153 class Debug_PrefObserver final : public nsIObserver {
3154 ~Debug_PrefObserver() {}
3156 public:
3157 NS_DECL_ISUPPORTS
3158 NS_DECL_NSIOBSERVER
3161 NS_IMPL_ISUPPORTS(Debug_PrefObserver, nsIObserver)
3163 NS_IMETHODIMP
3164 Debug_PrefObserver::Observe(nsISupports* subject, const char* topic,
3165 const char16_t* data) {
3166 NS_ConvertUTF16toUTF8 prefName(data);
3168 bool value = Preferences::GetBool(prefName.get(), false);
3169 debug_SetCachedBoolPref(prefName.get(), value);
3170 return NS_OK;
3173 //////////////////////////////////////////////////////////////
3174 /* static */ void debug_RegisterPrefCallbacks() {
3175 static bool once = true;
3177 if (!once) {
3178 return;
3181 once = false;
3183 nsCOMPtr<nsIObserver> obs(new Debug_PrefObserver());
3184 for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3185 // Initialize the pref values
3186 debug_PrefValues[i].value =
3187 Preferences::GetBool(debug_PrefValues[i].name, false);
3189 if (obs) {
3190 // Register callbacks for when these change
3191 nsCString name;
3192 name.AssignLiteral(debug_PrefValues[i].name,
3193 strlen(debug_PrefValues[i].name));
3194 Preferences::AddStrongObserver(obs, name);
3198 //////////////////////////////////////////////////////////////
3199 static int32_t _GetPrintCount() {
3200 static int32_t sCount = 0;
3202 return ++sCount;
3204 //////////////////////////////////////////////////////////////
3205 /* static */
3206 bool nsBaseWidget::debug_WantPaintFlashing() {
3207 return debug_GetCachedBoolPref("nglayout.debug.paint_flashing");
3209 //////////////////////////////////////////////////////////////
3210 /* static */
3211 void nsBaseWidget::debug_DumpEvent(FILE* aFileOut, nsIWidget* aWidget,
3212 WidgetGUIEvent* aGuiEvent,
3213 const char* aWidgetName, int32_t aWindowID) {
3214 if (aGuiEvent->mMessage == eMouseMove) {
3215 if (!debug_GetCachedBoolPref("nglayout.debug.motion_event_dumping")) return;
3218 if (aGuiEvent->mMessage == eMouseEnterIntoWidget ||
3219 aGuiEvent->mMessage == eMouseExitFromWidget) {
3220 if (!debug_GetCachedBoolPref("nglayout.debug.crossing_event_dumping"))
3221 return;
3224 if (!debug_GetCachedBoolPref("nglayout.debug.event_dumping")) return;
3226 NS_LossyConvertUTF16toASCII tempString(
3227 debug_GuiEventToString(aGuiEvent).get());
3229 fprintf(aFileOut, "%4d %-26s widget=%-8p name=%-12s id=0x%-6x refpt=%d,%d\n",
3230 _GetPrintCount(), tempString.get(), (void*)aWidget, aWidgetName,
3231 aWindowID, aGuiEvent->mRefPoint.x, aGuiEvent->mRefPoint.y);
3233 //////////////////////////////////////////////////////////////
3234 /* static */
3235 void nsBaseWidget::debug_DumpPaintEvent(FILE* aFileOut, nsIWidget* aWidget,
3236 const nsIntRegion& aRegion,
3237 const char* aWidgetName,
3238 int32_t aWindowID) {
3239 NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE");
3240 NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null");
3242 if (!debug_GetCachedBoolPref("nglayout.debug.paint_dumping")) return;
3244 nsIntRect rect = aRegion.GetBounds();
3245 fprintf(aFileOut,
3246 "%4d PAINT widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d "
3247 "%3d,%-3d",
3248 _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID, rect.X(),
3249 rect.Y(), rect.Width(), rect.Height());
3251 fprintf(aFileOut, "\n");
3253 //////////////////////////////////////////////////////////////
3254 /* static */
3255 void nsBaseWidget::debug_DumpInvalidate(FILE* aFileOut, nsIWidget* aWidget,
3256 const LayoutDeviceIntRect* aRect,
3257 const char* aWidgetName,
3258 int32_t aWindowID) {
3259 if (!debug_GetCachedBoolPref("nglayout.debug.invalidate_dumping")) return;
3261 NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE");
3262 NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null");
3264 fprintf(aFileOut, "%4d Invalidate widget=%p name=%-12s id=0x%-6x",
3265 _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID);
3267 if (aRect) {
3268 fprintf(aFileOut, " rect=%3d,%-3d %3d,%-3d", aRect->X(), aRect->Y(),
3269 aRect->Width(), aRect->Height());
3270 } else {
3271 fprintf(aFileOut, " rect=%-15s", "none");
3274 fprintf(aFileOut, "\n");
3276 //////////////////////////////////////////////////////////////
3278 #endif // DEBUG