Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/nsBaseWidget.cpp
Line
Count
Source (jump to first uncovered line)
1
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/. */
7
8
#include "mozilla/ArrayUtils.h"
9
#include "mozilla/UniquePtr.h"
10
#include "mozilla/TextEventDispatcher.h"
11
#include "mozilla/TextEventDispatcherListener.h"
12
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 "nsIDocument.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/Services.h"
73
#include "mozilla/Sprintf.h"
74
#include "mozilla/webrender/WebRenderTypes.h"
75
#include "nsRefPtrHashtable.h"
76
#include "TouchEvents.h"
77
#include "WritingModes.h"
78
#include "InputData.h"
79
#include "FrameLayerBuilder.h"
80
#ifdef ACCESSIBILITY
81
#include "nsAccessibilityService.h"
82
#endif
83
#include "gfxConfig.h"
84
#include "mozilla/layers/CompositorSession.h"
85
#include "VRManagerChild.h"
86
#include "gfxConfig.h"
87
88
#ifdef DEBUG
89
#include "nsIObserver.h"
90
91
static void debug_RegisterPrefCallbacks();
92
93
#endif
94
95
#ifdef NOISY_WIDGET_LEAKS
96
static int32_t gNumWidgets;
97
#endif
98
99
#ifdef XP_MACOSX
100
#include "nsCocoaFeatures.h"
101
#endif
102
103
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
104
static nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>* sPluginWidgetList;
105
#endif
106
107
nsIRollupListener* nsBaseWidget::gRollupListener = nullptr;
108
109
using namespace mozilla::dom;
110
using namespace mozilla::layers;
111
using namespace mozilla::ipc;
112
using namespace mozilla::widget;
113
using namespace mozilla;
114
using base::Thread;
115
116
// Global user preference for disabling native theme. Used
117
// in NativeWindowTheme.
118
bool            gDisableNativeTheme               = false;
119
120
// Async pump timer during injected long touch taps
121
0
#define TOUCH_INJECT_PUMP_TIMER_MSEC 50
122
0
#define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500
123
int32_t nsIWidget::sPointerIdCounter = 0;
124
125
// Some statics from nsIWidget.h
126
/*static*/ uint64_t AutoObserverNotifier::sObserverId = 0;
127
/*static*/ nsDataHashtable<nsUint64HashKey, nsCOMPtr<nsIObserver>> AutoObserverNotifier::sSavedObservers;
128
129
// The maximum amount of time to let the EnableDragDrop runnable wait in the
130
// idle queue before timing out and moving it to the regular queue. Value is in
131
// milliseconds.
132
const uint32_t kAsyncDragDropTimeout = 1000;
133
134
namespace mozilla {
135
namespace widget {
136
137
void
138
IMENotification::SelectionChangeDataBase::SetWritingMode(
139
                                        const WritingMode& aWritingMode)
140
0
{
141
0
  mWritingMode = aWritingMode.mWritingMode;
142
0
}
143
144
WritingMode
145
IMENotification::SelectionChangeDataBase::GetWritingMode() const
146
0
{
147
0
  return WritingMode(mWritingMode);
148
0
}
149
150
} // namespace widget
151
} // namespace mozilla
152
153
NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidget, nsISupportsWeakReference)
154
155
//-------------------------------------------------------------------------
156
//
157
// nsBaseWidget constructor
158
//
159
//-------------------------------------------------------------------------
160
161
nsBaseWidget::nsBaseWidget()
162
: mWidgetListener(nullptr)
163
, mAttachedWidgetListener(nullptr)
164
, mPreviouslyAttachedWidgetListener(nullptr)
165
, mLayerManager(nullptr)
166
, mCompositorVsyncDispatcher(nullptr)
167
, mCursor(eCursor_standard)
168
, mBorderStyle(eBorderStyle_none)
169
, mBounds(0,0,0,0)
170
, mOriginalBounds(nullptr)
171
, mClipRectCount(0)
172
, mSizeMode(nsSizeMode_Normal)
173
, mPopupLevel(ePopupLevelTop)
174
, mPopupType(ePopupTypeAny)
175
, mHasRemoteContent(false)
176
, mUpdateCursor(true)
177
, mUseAttachedEvents(false)
178
, mIMEHasFocus(false)
179
, mIsFullyOccluded(false)
180
0
{
181
#ifdef NOISY_WIDGET_LEAKS
182
  gNumWidgets++;
183
  printf("WIDGETS+ = %d\n", gNumWidgets);
184
#endif
185
186
#ifdef DEBUG
187
  debug_RegisterPrefCallbacks();
188
#endif
189
190
0
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
191
0
  if (!sPluginWidgetList) {
192
0
    sPluginWidgetList = new nsRefPtrHashtable<nsVoidPtrHashKey, nsIWidget>();
193
0
  }
194
0
#endif
195
0
  mShutdownObserver = new WidgetShutdownObserver(this);
196
0
}
197
198
NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver)
199
200
WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget) :
201
  mWidget(aWidget),
202
  mRegistered(false)
203
0
{
204
0
  Register();
205
0
}
206
207
WidgetShutdownObserver::~WidgetShutdownObserver()
208
0
{
209
0
  // No need to call Unregister(), we can't be destroyed until nsBaseWidget
210
0
  // gets torn down. The observer service and nsBaseWidget have a ref on us
211
0
  // so nsBaseWidget has to call Unregister and then clear its ref.
212
0
}
213
214
NS_IMETHODIMP
215
WidgetShutdownObserver::Observe(nsISupports *aSubject,
216
                                const char *aTopic,
217
                                const char16_t *aData)
218
0
{
219
0
  if (mWidget && !strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
220
0
    RefPtr<nsBaseWidget> widget(mWidget);
221
0
    widget->Shutdown();
222
0
  }
223
0
  return NS_OK;
224
0
}
225
226
void
227
WidgetShutdownObserver::Register()
228
0
{
229
0
  if (!mRegistered) {
230
0
    mRegistered = true;
231
0
    nsContentUtils::RegisterShutdownObserver(this);
232
0
  }
233
0
}
234
235
void
236
WidgetShutdownObserver::Unregister()
237
0
{
238
0
  if (mRegistered) {
239
0
    mWidget = nullptr;
240
0
    nsContentUtils::UnregisterShutdownObserver(this);
241
0
    mRegistered = false;
242
0
  }
243
0
}
244
245
void
246
nsBaseWidget::Shutdown()
247
0
{
248
0
  NotifyLiveResizeStopped();
249
0
  RevokeTransactionIdAllocator();
250
0
  DestroyCompositor();
251
0
  FreeShutdownObserver();
252
0
#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
253
0
  if (sPluginWidgetList) {
254
0
    delete sPluginWidgetList;
255
0
    sPluginWidgetList = nullptr;
256
0
  }
257
0
#endif
258
0
}
259
260
void nsBaseWidget::DestroyCompositor()
261
0
{
262
0
  // We release this before releasing the compositor, since it may hold the
263
0
  // last reference to our ClientLayerManager. ClientLayerManager's dtor can
264
0
  // trigger a paint, creating a new compositor, and we don't want to re-use
265
0
  // the old vsync dispatcher.
266
0
  if (mCompositorVsyncDispatcher) {
267
0
    MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
268
0
269
0
    MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
270
0
    mCompositorVsyncDispatcher->Shutdown();
271
0
    mCompositorVsyncDispatcher = nullptr;
272
0
  }
273
0
274
0
  // The compositor shutdown sequence looks like this:
275
0
  //  1. CompositorSession calls CompositorBridgeChild::Destroy.
276
0
  //  2. CompositorBridgeChild synchronously sends WillClose.
277
0
  //  3. CompositorBridgeParent releases some resources (such as the layer
278
0
  //     manager, compositor, and widget).
279
0
  //  4. CompositorBridgeChild::Destroy returns.
280
0
  //  5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the
281
0
  //     compositor thread when the I/O thread closes the IPC channel.
282
0
  //  6. Step 5 will schedule DeferredDestroy on the compositor thread, which
283
0
  //     releases the reference CompositorBridgeParent holds to itself.
284
0
  //
285
0
  // When CompositorSession::Shutdown returns, we assume the compositor is gone
286
0
  // or will be gone very soon.
287
0
  if (mCompositorSession) {
288
0
    ReleaseContentController();
289
0
    mAPZC = nullptr;
290
0
    SetCompositorWidgetDelegate(nullptr);
291
0
    mCompositorBridgeChild = nullptr;
292
0
293
0
    // XXX CompositorBridgeChild and CompositorBridgeParent might be re-created in
294
0
    // ClientLayerManager destructor. See bug 1133426.
295
0
    RefPtr<CompositorSession> session = mCompositorSession.forget();
296
0
    session->Shutdown();
297
0
  }
298
0
}
299
300
// This prevents the layer manager from starting a new transaction during
301
// shutdown.
302
void
303
nsBaseWidget::RevokeTransactionIdAllocator()
304
0
{
305
0
  if (!mLayerManager) {
306
0
    return;
307
0
  }
308
0
  mLayerManager->SetTransactionIdAllocator(nullptr);
309
0
}
310
311
void nsBaseWidget::ReleaseContentController()
312
0
{
313
0
  if (mRootContentController) {
314
0
    mRootContentController->Destroy();
315
0
    mRootContentController = nullptr;
316
0
  }
317
0
}
318
319
void nsBaseWidget::DestroyLayerManager()
320
0
{
321
0
  if (mLayerManager) {
322
0
    mLayerManager->Destroy();
323
0
    mLayerManager = nullptr;
324
0
  }
325
0
  DestroyCompositor();
326
0
}
327
328
void
329
nsBaseWidget::OnRenderingDeviceReset()
330
0
{
331
0
  DestroyLayerManager();
332
0
}
333
334
void
335
nsBaseWidget::FreeShutdownObserver()
336
0
{
337
0
  if (mShutdownObserver) {
338
0
    mShutdownObserver->Unregister();
339
0
  }
340
0
  mShutdownObserver = nullptr;
341
0
}
342
343
//-------------------------------------------------------------------------
344
//
345
// nsBaseWidget destructor
346
//
347
//-------------------------------------------------------------------------
348
349
nsBaseWidget::~nsBaseWidget()
350
0
{
351
0
  IMEStateManager::WidgetDestroyed(this);
352
0
353
0
  if (mLayerManager) {
354
0
    if (BasicLayerManager* mgr = mLayerManager->AsBasicLayerManager()) {
355
0
      mgr->ClearRetainerWidget();
356
0
    }
357
0
  }
358
0
359
0
  FreeShutdownObserver();
360
0
  RevokeTransactionIdAllocator();
361
0
  DestroyLayerManager();
362
0
363
#ifdef NOISY_WIDGET_LEAKS
364
  gNumWidgets--;
365
  printf("WIDGETS- = %d\n", gNumWidgets);
366
#endif
367
368
0
  delete mOriginalBounds;
369
0
}
370
371
//-------------------------------------------------------------------------
372
//
373
// Basic create.
374
//
375
//-------------------------------------------------------------------------
376
void nsBaseWidget::BaseCreate(nsIWidget* aParent,
377
                              nsWidgetInitData* aInitData)
378
0
{
379
0
  static bool gDisableNativeThemeCached = false;
380
0
  if (!gDisableNativeThemeCached) {
381
0
    Preferences::AddBoolVarCache(&gDisableNativeTheme,
382
0
                                 "mozilla.widget.disable-native-theme",
383
0
                                 gDisableNativeTheme);
384
0
    gDisableNativeThemeCached = true;
385
0
  }
386
0
387
0
  // keep a reference to the device context
388
0
  if (nullptr != aInitData) {
389
0
    mWindowType = aInitData->mWindowType;
390
0
    mBorderStyle = aInitData->mBorderStyle;
391
0
    mPopupLevel = aInitData->mPopupLevel;
392
0
    mPopupType = aInitData->mPopupHint;
393
0
    mHasRemoteContent = aInitData->mHasRemoteContent;
394
0
  }
395
0
396
0
  if (aParent) {
397
0
    aParent->AddChild(this);
398
0
  }
399
0
}
400
401
//-------------------------------------------------------------------------
402
//
403
// Accessor functions to get/set the client data
404
//
405
//-------------------------------------------------------------------------
406
407
nsIWidgetListener* nsBaseWidget::GetWidgetListener()
408
0
{
409
0
  return mWidgetListener;
410
0
}
411
412
void nsBaseWidget::SetWidgetListener(nsIWidgetListener* aWidgetListener)
413
0
{
414
0
  mWidgetListener = aWidgetListener;
415
0
}
416
417
already_AddRefed<nsIWidget>
418
nsBaseWidget::CreateChild(const LayoutDeviceIntRect& aRect,
419
                          nsWidgetInitData* aInitData,
420
                          bool aForceUseIWidgetParent)
421
0
{
422
0
  nsIWidget* parent = this;
423
0
  nsNativeWidget nativeParent = nullptr;
424
0
425
0
  if (!aForceUseIWidgetParent) {
426
0
    // Use only either parent or nativeParent, not both, to match
427
0
    // existing code.  Eventually Create() should be divested of its
428
0
    // nativeWidget parameter.
429
0
    nativeParent = parent ? parent->GetNativeData(NS_NATIVE_WIDGET) : nullptr;
430
0
    parent = nativeParent ? nullptr : parent;
431
0
    MOZ_ASSERT(!parent || !nativeParent, "messed up logic");
432
0
  }
433
0
434
0
  nsCOMPtr<nsIWidget> widget;
435
0
  if (aInitData && aInitData->mWindowType == eWindowType_popup) {
436
0
    widget = AllocateChildPopupWidget();
437
0
  } else {
438
0
    widget = nsIWidget::CreateChildWindow();
439
0
  }
440
0
441
0
  if (widget &&
442
0
      NS_SUCCEEDED(widget->Create(parent, nativeParent, aRect, aInitData))) {
443
0
    return widget.forget();
444
0
  }
445
0
446
0
  return nullptr;
447
0
}
448
449
// Attach a view to our widget which we'll send events to.
450
void
451
nsBaseWidget::AttachViewToTopLevel(bool aUseAttachedEvents)
452
0
{
453
0
  NS_ASSERTION((mWindowType == eWindowType_toplevel ||
454
0
                mWindowType == eWindowType_dialog ||
455
0
                mWindowType == eWindowType_invisible ||
456
0
                mWindowType == eWindowType_child),
457
0
               "Can't attach to window of that type");
458
0
459
0
  mUseAttachedEvents = aUseAttachedEvents;
460
0
}
461
462
nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener()
463
0
 {
464
0
   return mAttachedWidgetListener;
465
0
 }
466
467
nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener()
468
0
 {
469
0
   return mPreviouslyAttachedWidgetListener;
470
0
 }
471
472
void nsBaseWidget::SetPreviouslyAttachedWidgetListener(nsIWidgetListener* aListener)
473
0
 {
474
0
   mPreviouslyAttachedWidgetListener = aListener;
475
0
 }
476
477
void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener)
478
0
 {
479
0
   mAttachedWidgetListener = aListener;
480
0
 }
481
482
//-------------------------------------------------------------------------
483
//
484
// Close this nsBaseWidget
485
//
486
//-------------------------------------------------------------------------
487
void nsBaseWidget::Destroy()
488
0
{
489
0
  // Just in case our parent is the only ref to us
490
0
  nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
491
0
  // disconnect from the parent
492
0
  nsIWidget *parent = GetParent();
493
0
  if (parent) {
494
0
    parent->RemoveChild(this);
495
0
  }
496
0
497
#if defined(XP_WIN)
498
  // Allow our scroll capture container to be cleaned up, if we have one.
499
  mScrollCaptureContainer = nullptr;
500
#endif
501
}
502
503
//-------------------------------------------------------------------------
504
//
505
// Get this nsBaseWidget parent
506
//
507
//-------------------------------------------------------------------------
508
nsIWidget* nsBaseWidget::GetParent(void)
509
0
{
510
0
  return nullptr;
511
0
}
512
513
//-------------------------------------------------------------------------
514
//
515
// Get this nsBaseWidget top level widget
516
//
517
//-------------------------------------------------------------------------
518
nsIWidget* nsBaseWidget::GetTopLevelWidget()
519
0
{
520
0
  nsIWidget *topLevelWidget = nullptr, *widget = this;
521
0
  while (widget) {
522
0
    topLevelWidget = widget;
523
0
    widget = widget->GetParent();
524
0
  }
525
0
  return topLevelWidget;
526
0
}
527
528
//-------------------------------------------------------------------------
529
//
530
// Get this nsBaseWidget's top (non-sheet) parent (if it's a sheet)
531
//
532
//-------------------------------------------------------------------------
533
nsIWidget* nsBaseWidget::GetSheetWindowParent(void)
534
0
{
535
0
  return nullptr;
536
0
}
537
538
float nsBaseWidget::GetDPI()
539
0
{
540
0
  return 96.0f;
541
0
}
542
543
CSSToLayoutDeviceScale nsIWidget::GetDefaultScale()
544
0
{
545
0
  double devPixelsPerCSSPixel = DefaultScaleOverride();
546
0
547
0
  if (devPixelsPerCSSPixel <= 0.0) {
548
0
    devPixelsPerCSSPixel = GetDefaultScaleInternal();
549
0
  }
550
0
551
0
  return CSSToLayoutDeviceScale(devPixelsPerCSSPixel);
552
0
}
553
554
/* static */
555
double nsIWidget::DefaultScaleOverride()
556
0
{
557
0
  // The number of device pixels per CSS pixel. A value <= 0 means choose
558
0
  // automatically based on the DPI. A positive value is used as-is. This effectively
559
0
  // controls the size of a CSS "px".
560
0
  static float devPixelsPerCSSPixel = -1.0f;
561
0
562
0
  static bool valueCached = false;
563
0
  if (!valueCached) {
564
0
    Preferences::AddFloatVarCache(&devPixelsPerCSSPixel,
565
0
                                  "layout.css.devPixelsPerPx", -1.0f);
566
0
    valueCached = true;
567
0
  }
568
0
569
0
  return devPixelsPerCSSPixel;
570
0
}
571
572
//-------------------------------------------------------------------------
573
//
574
// Add a child to the list of children
575
//
576
//-------------------------------------------------------------------------
577
void nsBaseWidget::AddChild(nsIWidget* aChild)
578
0
{
579
0
  MOZ_ASSERT(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
580
0
             "aChild not properly removed from its old child list");
581
0
582
0
  if (!mFirstChild) {
583
0
    mFirstChild = mLastChild = aChild;
584
0
  } else {
585
0
    // append to the list
586
0
    MOZ_ASSERT(mLastChild);
587
0
    MOZ_ASSERT(!mLastChild->GetNextSibling());
588
0
    mLastChild->SetNextSibling(aChild);
589
0
    aChild->SetPrevSibling(mLastChild);
590
0
    mLastChild = aChild;
591
0
  }
592
0
}
593
594
595
//-------------------------------------------------------------------------
596
//
597
// Remove a child from the list of children
598
//
599
//-------------------------------------------------------------------------
600
void nsBaseWidget::RemoveChild(nsIWidget* aChild)
601
0
{
602
#ifdef DEBUG
603
#ifdef XP_MACOSX
604
  // nsCocoaWindow doesn't implement GetParent, so in that case parent will be
605
  // null and we'll just have to do without this assertion.
606
  nsIWidget* parent = aChild->GetParent();
607
  NS_ASSERTION(!parent || parent == this, "Not one of our kids!");
608
#else
609
  MOZ_RELEASE_ASSERT(aChild->GetParent() == this, "Not one of our kids!");
610
#endif
611
#endif
612
613
0
  if (mLastChild == aChild) {
614
0
    mLastChild = mLastChild->GetPrevSibling();
615
0
  }
616
0
  if (mFirstChild == aChild) {
617
0
    mFirstChild = mFirstChild->GetNextSibling();
618
0
  }
619
0
620
0
  // Now remove from the list.  Make sure that we pass ownership of the tail
621
0
  // of the list correctly before we have aChild let go of it.
622
0
  nsIWidget* prev = aChild->GetPrevSibling();
623
0
  nsIWidget* next = aChild->GetNextSibling();
624
0
  if (prev) {
625
0
    prev->SetNextSibling(next);
626
0
  }
627
0
  if (next) {
628
0
    next->SetPrevSibling(prev);
629
0
  }
630
0
631
0
  aChild->SetNextSibling(nullptr);
632
0
  aChild->SetPrevSibling(nullptr);
633
0
}
634
635
636
//-------------------------------------------------------------------------
637
//
638
// Sets widget's position within its parent's child list.
639
//
640
//-------------------------------------------------------------------------
641
void nsBaseWidget::SetZIndex(int32_t aZIndex)
642
0
{
643
0
  // Hold a ref to ourselves just in case, since we're going to remove
644
0
  // from our parent.
645
0
  nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
646
0
647
0
  mZIndex = aZIndex;
648
0
649
0
  // reorder this child in its parent's list.
650
0
  auto* parent = static_cast<nsBaseWidget*>(GetParent());
651
0
  if (parent) {
652
0
    parent->RemoveChild(this);
653
0
    // Scope sib outside the for loop so we can check it afterward
654
0
    nsIWidget* sib = parent->GetFirstChild();
655
0
    for ( ; sib; sib = sib->GetNextSibling()) {
656
0
      int32_t childZIndex = GetZIndex();
657
0
      if (aZIndex < childZIndex) {
658
0
        // Insert ourselves before sib
659
0
        nsIWidget* prev = sib->GetPrevSibling();
660
0
        mNextSibling = sib;
661
0
        mPrevSibling = prev;
662
0
        sib->SetPrevSibling(this);
663
0
        if (prev) {
664
0
          prev->SetNextSibling(this);
665
0
        } else {
666
0
          NS_ASSERTION(sib == parent->mFirstChild, "Broken child list");
667
0
          // We've taken ownership of sib, so it's safe to have parent let
668
0
          // go of it
669
0
          parent->mFirstChild = this;
670
0
        }
671
0
        PlaceBehind(eZPlacementBelow, sib, false);
672
0
        break;
673
0
      }
674
0
    }
675
0
    // were we added to the list?
676
0
    if (!sib) {
677
0
      parent->AddChild(this);
678
0
    }
679
0
  }
680
0
}
681
682
//-------------------------------------------------------------------------
683
//
684
// Maximize, minimize or restore the window. The BaseWidget implementation
685
// merely stores the state.
686
//
687
//-------------------------------------------------------------------------
688
void
689
nsBaseWidget::SetSizeMode(nsSizeMode aMode)
690
0
{
691
0
  MOZ_ASSERT(aMode == nsSizeMode_Normal ||
692
0
             aMode == nsSizeMode_Minimized ||
693
0
             aMode == nsSizeMode_Maximized ||
694
0
             aMode == nsSizeMode_Fullscreen);
695
0
  mSizeMode = aMode;
696
0
}
697
698
//-------------------------------------------------------------------------
699
//
700
// Get this component cursor
701
//
702
//-------------------------------------------------------------------------
703
704
void
705
nsBaseWidget::SetCursor(nsCursor aCursor)
706
0
{
707
0
  mCursor = aCursor;
708
0
}
709
710
nsresult
711
nsBaseWidget::SetCursor(imgIContainer* aCursor,
712
                        uint32_t aHotspotX, uint32_t aHotspotY)
713
0
{
714
0
  return NS_ERROR_NOT_IMPLEMENTED;
715
0
}
716
717
//-------------------------------------------------------------------------
718
//
719
// Window transparency methods
720
//
721
//-------------------------------------------------------------------------
722
723
0
void nsBaseWidget::SetTransparencyMode(nsTransparencyMode aMode) {
724
0
}
725
726
0
nsTransparencyMode nsBaseWidget::GetTransparencyMode() {
727
0
  return eTransparencyOpaque;
728
0
}
729
730
bool
731
nsBaseWidget::IsWindowClipRegionEqual(const nsTArray<LayoutDeviceIntRect>& aRects)
732
0
{
733
0
  return mClipRects &&
734
0
         mClipRectCount == aRects.Length() &&
735
0
         memcmp(mClipRects.get(), aRects.Elements(), sizeof(LayoutDeviceIntRect)*mClipRectCount) == 0;
736
0
}
737
738
void
739
nsBaseWidget::StoreWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects)
740
0
{
741
0
  mClipRectCount = aRects.Length();
742
0
  mClipRects = MakeUnique<LayoutDeviceIntRect[]>(mClipRectCount);
743
0
  if (mClipRects) {
744
0
    memcpy(mClipRects.get(), aRects.Elements(), sizeof(LayoutDeviceIntRect)*mClipRectCount);
745
0
  }
746
0
}
747
748
void
749
nsBaseWidget::GetWindowClipRegion(nsTArray<LayoutDeviceIntRect>* aRects)
750
0
{
751
0
  if (mClipRects) {
752
0
    aRects->AppendElements(mClipRects.get(), mClipRectCount);
753
0
  } else {
754
0
    aRects->AppendElement(LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height()));
755
0
  }
756
0
}
757
758
const LayoutDeviceIntRegion
759
nsBaseWidget::RegionFromArray(const nsTArray<LayoutDeviceIntRect>& aRects)
760
0
{
761
0
  LayoutDeviceIntRegion region;
762
0
  for (uint32_t i = 0; i < aRects.Length(); ++i) {
763
0
    region.Or(region, aRects[i]);
764
0
  }
765
0
  return region;
766
0
}
767
768
void
769
nsBaseWidget::ArrayFromRegion(const LayoutDeviceIntRegion& aRegion,
770
                              nsTArray<LayoutDeviceIntRect>& aRects)
771
0
{
772
0
  for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
773
0
    aRects.AppendElement(iter.Get());
774
0
  }
775
0
}
776
777
nsresult
778
nsBaseWidget::SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects,
779
                                  bool aIntersectWithExisting)
780
0
{
781
0
  if (!aIntersectWithExisting) {
782
0
    StoreWindowClipRegion(aRects);
783
0
  } else {
784
0
    // get current rects
785
0
    nsTArray<LayoutDeviceIntRect> currentRects;
786
0
    GetWindowClipRegion(&currentRects);
787
0
    // create region from them
788
0
    LayoutDeviceIntRegion currentRegion = RegionFromArray(currentRects);
789
0
    // create region from new rects
790
0
    LayoutDeviceIntRegion newRegion = RegionFromArray(aRects);
791
0
    // intersect regions
792
0
    LayoutDeviceIntRegion intersection;
793
0
    intersection.And(currentRegion, newRegion);
794
0
    // create int rect array from intersection
795
0
    nsTArray<LayoutDeviceIntRect> rects;
796
0
    ArrayFromRegion(intersection, rects);
797
0
    // store
798
0
    StoreWindowClipRegion(rects);
799
0
  }
800
0
  return NS_OK;
801
0
}
802
803
/* virtual */ void
804
nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage,
805
                                          uint16_t aDuration,
806
                                          nsISupports* aData,
807
                                          nsIRunnable* aCallback)
808
0
{
809
0
  MOZ_ASSERT_UNREACHABLE(
810
0
    "Should never call PerformFullscreenTransition on nsBaseWidget");
811
0
}
812
813
//-------------------------------------------------------------------------
814
//
815
// Put the window into full-screen mode
816
//
817
//-------------------------------------------------------------------------
818
void
819
nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen, nsIScreen* aScreen)
820
0
{
821
0
  HideWindowChrome(aFullScreen);
822
0
823
0
  if (aFullScreen) {
824
0
    if (!mOriginalBounds) {
825
0
      mOriginalBounds = new LayoutDeviceIntRect();
826
0
    }
827
0
    *mOriginalBounds = GetScreenBounds();
828
0
829
0
    // Move to top-left corner of screen and size to the screen dimensions
830
0
    nsCOMPtr<nsIScreen> screen = aScreen;
831
0
    if (!screen) {
832
0
      screen = GetWidgetScreen();
833
0
    }
834
0
    if (screen) {
835
0
      int32_t left, top, width, height;
836
0
      if (NS_SUCCEEDED(screen->GetRectDisplayPix(&left, &top, &width, &height))) {
837
0
        Resize(left, top, width, height, true);
838
0
      }
839
0
    }
840
0
  } else if (mOriginalBounds) {
841
0
    if (BoundsUseDesktopPixels()) {
842
0
      DesktopRect deskRect = *mOriginalBounds / GetDesktopToDeviceScale();
843
0
      Resize(deskRect.X(), deskRect.Y(),
844
0
       deskRect.Width(), deskRect.Height(), true);
845
0
    } else {
846
0
      Resize(mOriginalBounds->X(), mOriginalBounds->Y(),
847
0
       mOriginalBounds->Width(), mOriginalBounds->Height(), true);
848
0
    }
849
0
  }
850
0
}
851
852
nsresult
853
nsBaseWidget::MakeFullScreen(bool aFullScreen, nsIScreen* aScreen)
854
0
{
855
0
  InfallibleMakeFullScreen(aFullScreen, aScreen);
856
0
  return NS_OK;
857
0
}
858
859
nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
860
    nsBaseWidget* aWidget, gfxContext* aTarget,
861
    BufferMode aDoubleBuffering, ScreenRotation aRotation)
862
  : mWidget(aWidget)
863
0
{
864
0
  LayerManager* lm = mWidget->GetLayerManager();
865
0
  NS_ASSERTION(!lm || lm->GetBackendType() == LayersBackend::LAYERS_BASIC,
866
0
    "AutoLayerManagerSetup instantiated for non-basic layer backend!");
867
0
  if (lm) {
868
0
    mLayerManager = lm->AsBasicLayerManager();
869
0
    if (mLayerManager) {
870
0
      mLayerManager->SetDefaultTarget(aTarget);
871
0
      mLayerManager->SetDefaultTargetConfiguration(aDoubleBuffering, aRotation);
872
0
    }
873
0
  }
874
0
}
875
876
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup()
877
0
{
878
0
  if (mLayerManager) {
879
0
    mLayerManager->SetDefaultTarget(nullptr);
880
0
    mLayerManager->SetDefaultTargetConfiguration(mozilla::layers::BufferMode::BUFFER_NONE, ROTATION_0);
881
0
  }
882
0
}
883
884
bool nsBaseWidget::IsSmallPopup() const
885
0
{
886
0
  return mWindowType == eWindowType_popup && mPopupType != ePopupTypePanel;
887
0
}
888
889
bool
890
nsBaseWidget::ComputeShouldAccelerate()
891
0
{
892
0
  if (gfx::gfxVars::UseWebRender() && !AllowWebRenderForThisWindow()) {
893
0
    // If WebRender is enabled, non-WebRender widgets use the basic compositor
894
0
    // (at least for now), even though they would get an accelerated compositor
895
0
    // if WebRender wasn't enabled.
896
0
    return false;
897
0
  }
898
0
  return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
899
0
         WidgetTypeSupportsAcceleration();
900
0
}
901
902
bool
903
nsBaseWidget::UseAPZ()
904
0
{
905
0
  return (gfxPlatform::AsyncPanZoomEnabled() &&
906
0
          (WindowType() == eWindowType_toplevel ||
907
0
           WindowType() == eWindowType_child ||
908
0
           (WindowType() == eWindowType_popup && HasRemoteContent() &&
909
0
            gfxPrefs::APZPopupsEnabled())));
910
0
}
911
912
bool
913
nsBaseWidget::AllowWebRenderForThisWindow()
914
0
{
915
0
  return WindowType() == eWindowType_toplevel ||
916
0
         WindowType() == eWindowType_child ||
917
0
         WindowType() == eWindowType_dialog ||
918
0
         (WindowType() == eWindowType_popup && HasRemoteContent());
919
0
}
920
921
void nsBaseWidget::CreateCompositor()
922
0
{
923
0
  LayoutDeviceIntRect rect = GetBounds();
924
0
  CreateCompositor(rect.Width(), rect.Height());
925
0
}
926
927
already_AddRefed<GeckoContentController>
928
nsBaseWidget::CreateRootContentController()
929
0
{
930
0
  RefPtr<GeckoContentController> controller = new ChromeProcessController(this, mAPZEventState, mAPZC);
931
0
  return controller.forget();
932
0
}
933
934
void nsBaseWidget::ConfigureAPZCTreeManager()
935
0
{
936
0
  MOZ_ASSERT(NS_IsMainThread());
937
0
  MOZ_ASSERT(mAPZC);
938
0
939
0
  ConfigureAPZControllerThread();
940
0
941
0
  float dpi = GetDPI();
942
0
  // On Android the main thread is not the controller thread
943
0
  APZThreadUtils::RunOnControllerThread(NewRunnableMethod<float>(
944
0
      "layers::IAPZCTreeManager::SetDPI",
945
0
      mAPZC,
946
0
      &IAPZCTreeManager::SetDPI,
947
0
      dpi));
948
0
949
0
  if (gfxPrefs::APZKeyboardEnabled()) {
950
0
    KeyboardMap map = nsXBLWindowKeyHandler::CollectKeyboardShortcuts();
951
0
    // On Android the main thread is not the controller thread
952
0
    APZThreadUtils::RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
953
0
        "layers::IAPZCTreeManager::SetKeyboardMap",
954
0
        mAPZC,
955
0
        &IAPZCTreeManager::SetKeyboardMap,
956
0
        map));
957
0
  }
958
0
959
0
  RefPtr<IAPZCTreeManager> treeManager = mAPZC;  // for capture by the lambdas
960
0
961
0
  ContentReceivedInputBlockCallback callback(
962
0
      [treeManager](const ScrollableLayerGuid& aGuid,
963
0
                    uint64_t aInputBlockId,
964
0
                    bool aPreventDefault)
965
0
      {
966
0
        MOZ_ASSERT(NS_IsMainThread());
967
0
        APZThreadUtils::RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
968
0
          "layers::IAPZCTreeManager::ContentReceivedInputBlock",
969
0
          treeManager,
970
0
          &IAPZCTreeManager::ContentReceivedInputBlock,
971
0
          aInputBlockId,
972
0
          aPreventDefault));
973
0
      });
974
0
  mAPZEventState = new APZEventState(this, std::move(callback));
975
0
976
0
  mSetAllowedTouchBehaviorCallback = [treeManager](uint64_t aInputBlockId,
977
0
                                                   const nsTArray<TouchBehaviorFlags>& aFlags)
978
0
  {
979
0
    MOZ_ASSERT(NS_IsMainThread());
980
0
    APZThreadUtils::RunOnControllerThread(
981
0
      NewRunnableMethod<uint64_t,
982
0
                        StoreCopyPassByLRef<nsTArray<TouchBehaviorFlags>>>(
983
0
        "layers::IAPZCTreeManager::SetAllowedTouchBehavior",
984
0
        treeManager,
985
0
        &IAPZCTreeManager::SetAllowedTouchBehavior,
986
0
        aInputBlockId,
987
0
        aFlags));
988
0
  };
989
0
990
0
  mRootContentController = CreateRootContentController();
991
0
  if (mRootContentController) {
992
0
    mCompositorSession->SetContentController(mRootContentController);
993
0
  }
994
0
995
0
  // When APZ is enabled, we can actually enable raw touch events because we
996
0
  // have code that can deal with them properly. If APZ is not enabled, this
997
0
  // function doesn't get called.
998
0
  if (Preferences::GetInt("dom.w3c_touch_events.enabled", 0) ||
999
0
      Preferences::GetBool("dom.w3c_pointer_events.enabled", false)) {
1000
0
    RegisterTouchWindow();
1001
0
  }
1002
0
}
1003
1004
void nsBaseWidget::ConfigureAPZControllerThread()
1005
0
{
1006
0
  // By default the controller thread is the main thread.
1007
0
  APZThreadUtils::SetControllerThread(MessageLoop::current());
1008
0
}
1009
1010
void
1011
nsBaseWidget::SetConfirmedTargetAPZC(uint64_t aInputBlockId,
1012
                                     const nsTArray<ScrollableLayerGuid>& aTargets) const
1013
0
{
1014
0
  APZThreadUtils::RunOnControllerThread(
1015
0
    NewRunnableMethod<uint64_t,
1016
0
                      StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
1017
0
      "layers::IAPZCTreeManager::SetTargetAPZC",
1018
0
      mAPZC,
1019
0
      &IAPZCTreeManager::SetTargetAPZC,
1020
0
      aInputBlockId,
1021
0
      aTargets));
1022
0
}
1023
1024
void
1025
nsBaseWidget::UpdateZoomConstraints(const uint32_t& aPresShellId,
1026
                                    const FrameMetrics::ViewID& aViewId,
1027
                                    const Maybe<ZoomConstraints>& aConstraints)
1028
0
{
1029
0
  if (!mCompositorSession || !mAPZC) {
1030
0
    if (mInitialZoomConstraints) {
1031
0
      MOZ_ASSERT(mInitialZoomConstraints->mPresShellID == aPresShellId);
1032
0
      MOZ_ASSERT(mInitialZoomConstraints->mViewID == aViewId);
1033
0
      if (!aConstraints) {
1034
0
        mInitialZoomConstraints.reset();
1035
0
      }
1036
0
    }
1037
0
1038
0
    if (aConstraints) {
1039
0
      // We have some constraints, but the compositor and APZC aren't created yet.
1040
0
      // Save these so we can use them later.
1041
0
      mInitialZoomConstraints = Some(InitialZoomConstraints(aPresShellId, aViewId, aConstraints.ref()));
1042
0
    }
1043
0
    return;
1044
0
  }
1045
0
  LayersId layersId = mCompositorSession->RootLayerTreeId();
1046
0
  mAPZC->UpdateZoomConstraints(ScrollableLayerGuid(layersId, aPresShellId, aViewId),
1047
0
                               aConstraints);
1048
0
}
1049
1050
bool
1051
nsBaseWidget::AsyncPanZoomEnabled() const
1052
0
{
1053
0
  return !!mAPZC;
1054
0
}
1055
1056
nsEventStatus
1057
nsBaseWidget::ProcessUntransformedAPZEvent(WidgetInputEvent* aEvent,
1058
                                           const ScrollableLayerGuid& aGuid,
1059
                                           uint64_t aInputBlockId,
1060
                                           nsEventStatus aApzResponse)
1061
0
{
1062
0
  MOZ_ASSERT(NS_IsMainThread());
1063
0
  InputAPZContext context(aGuid, aInputBlockId, aApzResponse);
1064
0
1065
0
  // If this is an event that the APZ has targeted to an APZC in the root
1066
0
  // process, apply that APZC's callback-transform before dispatching the
1067
0
  // event. If the event is instead targeted to an APZC in the child process,
1068
0
  // the transform will be applied in the child process before dispatching
1069
0
  // the event there (see e.g. TabChild::RecvRealTouchEvent()).
1070
0
  if (aGuid.mLayersId == mCompositorSession->RootLayerTreeId()) {
1071
0
    APZCCallbackHelper::ApplyCallbackTransform(*aEvent, aGuid,
1072
0
        GetDefaultScale());
1073
0
  }
1074
0
1075
0
  // Make a copy of the original event for the APZCCallbackHelper helpers that
1076
0
  // we call later, because the event passed to DispatchEvent can get mutated in
1077
0
  // ways that we don't want (i.e. touch points can get stripped out).
1078
0
  nsEventStatus status;
1079
0
  UniquePtr<WidgetEvent> original(aEvent->Duplicate());
1080
0
  DispatchEvent(aEvent, status);
1081
0
1082
0
  if (mAPZC && !InputAPZContext::WasRoutedToChildProcess() && aInputBlockId) {
1083
0
    // EventStateManager did not route the event into the child process.
1084
0
    // It's safe to communicate to APZ that the event has been processed.
1085
0
    UniquePtr<DisplayportSetListener> postLayerization;
1086
0
    if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) {
1087
0
      if (touchEvent->mMessage == eTouchStart) {
1088
0
        if (gfxPrefs::TouchActionEnabled()) {
1089
0
          APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(this,
1090
0
              GetDocument(), *(original->AsTouchEvent()), aInputBlockId,
1091
0
              mSetAllowedTouchBehaviorCallback);
1092
0
        }
1093
0
        postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(),
1094
0
            *(original->AsTouchEvent()), aGuid, aInputBlockId);
1095
0
      }
1096
0
      mAPZEventState->ProcessTouchEvent(*touchEvent, aGuid, aInputBlockId,
1097
0
          aApzResponse, status);
1098
0
    } else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) {
1099
0
      MOZ_ASSERT(wheelEvent->mFlags.mHandledByAPZ);
1100
0
      postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(),
1101
0
          *(original->AsWheelEvent()), aGuid, aInputBlockId);
1102
0
      if (wheelEvent->mCanTriggerSwipe) {
1103
0
        ReportSwipeStarted(aInputBlockId, wheelEvent->TriggersSwipe());
1104
0
      }
1105
0
      mAPZEventState->ProcessWheelEvent(*wheelEvent, aGuid, aInputBlockId);
1106
0
    } else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
1107
0
      MOZ_ASSERT(mouseEvent->mFlags.mHandledByAPZ);
1108
0
      postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(this, GetDocument(),
1109
0
          *(original->AsMouseEvent()), aGuid, aInputBlockId);
1110
0
      mAPZEventState->ProcessMouseEvent(*mouseEvent, aGuid, aInputBlockId);
1111
0
    }
1112
0
    if (postLayerization && postLayerization->Register()) {
1113
0
      Unused << postLayerization.release();
1114
0
    }
1115
0
  }
1116
0
1117
0
  return status;
1118
0
}
1119
1120
class DispatchWheelEventOnMainThread : public Runnable
1121
{
1122
public:
1123
  DispatchWheelEventOnMainThread(const ScrollWheelInput& aWheelInput,
1124
                                 nsBaseWidget* aWidget,
1125
                                 nsEventStatus aAPZResult,
1126
                                 uint64_t aInputBlockId,
1127
                                 ScrollableLayerGuid aGuid)
1128
    : mozilla::Runnable("DispatchWheelEventOnMainThread")
1129
    , mWheelInput(aWheelInput)
1130
    , mWidget(aWidget)
1131
    , mAPZResult(aAPZResult)
1132
    , mInputBlockId(aInputBlockId)
1133
    , mGuid(aGuid)
1134
0
  {
1135
0
  }
1136
1137
  NS_IMETHOD Run() override
1138
0
  {
1139
0
    WidgetWheelEvent wheelEvent = mWheelInput.ToWidgetWheelEvent(mWidget);
1140
0
    mWidget->ProcessUntransformedAPZEvent(&wheelEvent, mGuid, mInputBlockId, mAPZResult);
1141
0
    return NS_OK;
1142
0
  }
1143
1144
private:
1145
  ScrollWheelInput mWheelInput;
1146
  nsBaseWidget* mWidget;
1147
  nsEventStatus mAPZResult;
1148
  uint64_t mInputBlockId;
1149
  ScrollableLayerGuid mGuid;
1150
};
1151
1152
class DispatchWheelInputOnControllerThread : public Runnable
1153
{
1154
public:
1155
  DispatchWheelInputOnControllerThread(const WidgetWheelEvent& aWheelEvent,
1156
                                       IAPZCTreeManager* aAPZC,
1157
                                       nsBaseWidget* aWidget)
1158
    : mozilla::Runnable("DispatchWheelInputOnControllerThread")
1159
    , mMainMessageLoop(MessageLoop::current())
1160
    , mWheelInput(aWheelEvent)
1161
    , mAPZC(aAPZC)
1162
    , mWidget(aWidget)
1163
    , mInputBlockId(0)
1164
0
  {
1165
0
  }
1166
1167
  NS_IMETHOD Run() override
1168
0
  {
1169
0
    nsEventStatus result = mAPZC->InputBridge()->ReceiveInputEvent(mWheelInput, &mGuid, &mInputBlockId);
1170
0
    if (result == nsEventStatus_eConsumeNoDefault) {
1171
0
      return NS_OK;
1172
0
    }
1173
0
    RefPtr<Runnable> r = new DispatchWheelEventOnMainThread(mWheelInput, mWidget, result, mInputBlockId, mGuid);
1174
0
    mMainMessageLoop->PostTask(r.forget());
1175
0
    return NS_OK;
1176
0
  }
1177
1178
private:
1179
  MessageLoop* mMainMessageLoop;
1180
  ScrollWheelInput mWheelInput;
1181
  RefPtr<IAPZCTreeManager> mAPZC;
1182
  nsBaseWidget* mWidget;
1183
  uint64_t mInputBlockId;
1184
  ScrollableLayerGuid mGuid;
1185
};
1186
1187
void
1188
nsBaseWidget::DispatchTouchInput(MultiTouchInput& aInput)
1189
0
{
1190
0
  MOZ_ASSERT(NS_IsMainThread());
1191
0
  if (mAPZC) {
1192
0
    MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1193
0
    uint64_t inputBlockId = 0;
1194
0
    ScrollableLayerGuid guid;
1195
0
1196
0
    nsEventStatus result = mAPZC->InputBridge()->ReceiveInputEvent(aInput, &guid, &inputBlockId);
1197
0
    if (result == nsEventStatus_eConsumeNoDefault) {
1198
0
      return;
1199
0
    }
1200
0
1201
0
    WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
1202
0
    ProcessUntransformedAPZEvent(&event, guid, inputBlockId, result);
1203
0
  } else {
1204
0
    WidgetTouchEvent event = aInput.ToWidgetTouchEvent(this);
1205
0
1206
0
    nsEventStatus status;
1207
0
    DispatchEvent(&event, status);
1208
0
  }
1209
0
}
1210
1211
nsEventStatus
1212
nsBaseWidget::DispatchInputEvent(WidgetInputEvent* aEvent)
1213
0
{
1214
0
  MOZ_ASSERT(NS_IsMainThread());
1215
0
  if (mAPZC) {
1216
0
    if (APZThreadUtils::IsControllerThread()) {
1217
0
      uint64_t inputBlockId = 0;
1218
0
      ScrollableLayerGuid guid;
1219
0
1220
0
      nsEventStatus result =
1221
0
        mAPZC->InputBridge()->ReceiveInputEvent(*aEvent, &guid, &inputBlockId);
1222
0
      if (result == nsEventStatus_eConsumeNoDefault) {
1223
0
        return result;
1224
0
      }
1225
0
      return ProcessUntransformedAPZEvent(aEvent, guid, inputBlockId, result);
1226
0
    }
1227
0
    WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
1228
0
    if (wheelEvent) {
1229
0
      RefPtr<Runnable> r =
1230
0
        new DispatchWheelInputOnControllerThread(*wheelEvent, mAPZC, this);
1231
0
      APZThreadUtils::RunOnControllerThread(r.forget());
1232
0
      return nsEventStatus_eConsumeDoDefault;
1233
0
    }
1234
0
    // Allow dispatching keyboard events on Gecko thread.
1235
0
    MOZ_ASSERT(aEvent->AsKeyboardEvent());
1236
0
  }
1237
0
1238
0
  nsEventStatus status;
1239
0
  DispatchEvent(aEvent, status);
1240
0
  return status;
1241
0
}
1242
1243
void
1244
nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent)
1245
0
{
1246
0
  MOZ_ASSERT(NS_IsMainThread());
1247
0
  if (mAPZC) {
1248
0
    MOZ_ASSERT(APZThreadUtils::IsControllerThread());
1249
0
    uint64_t inputBlockId = 0;
1250
0
    ScrollableLayerGuid guid;
1251
0
    mAPZC->InputBridge()->ReceiveInputEvent(*aEvent, &guid, &inputBlockId);
1252
0
  }
1253
0
}
1254
1255
// static
1256
bool
1257
nsBaseWidget::ShowContextMenuAfterMouseUp()
1258
0
{
1259
0
  static bool gContextMenuAfterMouseUp = false;
1260
0
  static bool gContextMenuAfterMouseUpCached = false;
1261
0
  if (!gContextMenuAfterMouseUpCached) {
1262
0
    Preferences::AddBoolVarCache(&gContextMenuAfterMouseUp,
1263
0
                                 "ui.context_menus.after_mouseup",
1264
0
                                 false);
1265
0
1266
0
    gContextMenuAfterMouseUpCached = true;
1267
0
  }
1268
0
  return gContextMenuAfterMouseUp;
1269
0
}
1270
1271
nsIDocument*
1272
nsBaseWidget::GetDocument() const
1273
0
{
1274
0
  if (mWidgetListener) {
1275
0
    if (nsIPresShell* presShell = mWidgetListener->GetPresShell()) {
1276
0
      return presShell->GetDocument();
1277
0
    }
1278
0
  }
1279
0
  return nullptr;
1280
0
}
1281
1282
void nsBaseWidget::CreateCompositorVsyncDispatcher()
1283
0
{
1284
0
  // Parent directly listens to the vsync source whereas
1285
0
  // child process communicate via IPC
1286
0
  // Should be called AFTER gfxPlatform is initialized
1287
0
  if (XRE_IsParentProcess()) {
1288
0
    if (!mCompositorVsyncDispatcherLock) {
1289
0
      mCompositorVsyncDispatcherLock = MakeUnique<Mutex>("mCompositorVsyncDispatcherLock");
1290
0
    }
1291
0
    MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
1292
0
    mCompositorVsyncDispatcher = new CompositorVsyncDispatcher();
1293
0
  }
1294
0
}
1295
1296
already_AddRefed<CompositorVsyncDispatcher>
1297
nsBaseWidget::GetCompositorVsyncDispatcher()
1298
0
{
1299
0
  MOZ_ASSERT(mCompositorVsyncDispatcherLock.get());
1300
0
1301
0
  MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get());
1302
0
  RefPtr<CompositorVsyncDispatcher> dispatcher = mCompositorVsyncDispatcher;
1303
0
  return dispatcher.forget();
1304
0
}
1305
1306
already_AddRefed<LayerManager>
1307
nsBaseWidget::CreateCompositorSession(int aWidth,
1308
                                      int aHeight,
1309
                                      CompositorOptions* aOptionsOut)
1310
0
{
1311
0
  MOZ_ASSERT(aOptionsOut);
1312
0
1313
0
  do {
1314
0
    CreateCompositorVsyncDispatcher();
1315
0
1316
0
    // If widget type does not supports acceleration, we use ClientLayerManager
1317
0
    // even when gfxVars::UseWebRender() is true. WebRender could coexist only
1318
0
    // with BasicCompositor.
1319
0
    bool enableWR = gfx::gfxVars::UseWebRender() && WidgetTypeSupportsAcceleration()
1320
0
      && AllowWebRenderForThisWindow();
1321
0
    bool enableAPZ = UseAPZ();
1322
0
    CompositorOptions options(enableAPZ, enableWR);
1323
0
1324
0
    bool enableAL = gfx::gfxConfig::IsEnabled(gfx::Feature::ADVANCED_LAYERS);
1325
0
    options.SetUseAdvancedLayers(enableAL);
1326
0
1327
#ifdef MOZ_WIDGET_ANDROID
1328
    if (!GetNativeData(NS_JAVA_SURFACE)) {
1329
      options.SetInitiallyPaused(true);
1330
    }
1331
#endif
1332
1333
0
    RefPtr<LayerManager> lm;
1334
0
    if (options.UseWebRender()) {
1335
0
      lm = new WebRenderLayerManager(this);
1336
0
    } else {
1337
0
      lm = new ClientLayerManager(this);
1338
0
    }
1339
0
1340
0
    bool retry = false;
1341
0
    gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
1342
0
    mCompositorSession = gpu->CreateTopLevelCompositor(
1343
0
      this,
1344
0
      lm,
1345
0
      GetDefaultScale(),
1346
0
      options,
1347
0
      UseExternalCompositingSurface(),
1348
0
      gfx::IntSize(aWidth, aHeight),
1349
0
      &retry);
1350
0
1351
0
    if (lm->AsWebRenderLayerManager() && mCompositorSession) {
1352
0
      TextureFactoryIdentifier textureFactoryIdentifier;
1353
0
      lm->AsWebRenderLayerManager()->Initialize(mCompositorSession->GetCompositorBridgeChild(),
1354
0
                                                wr::AsPipelineId(mCompositorSession->RootLayerTreeId()),
1355
0
                                                &textureFactoryIdentifier);
1356
0
      if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) {
1357
0
        retry = true;
1358
0
        DestroyCompositor();
1359
0
        gfx::GPUProcessManager::Get()->DisableWebRender(wr::WebRenderError::INITIALIZE);
1360
0
      }
1361
0
    }
1362
0
1363
0
    // We need to retry in a loop because the act of failing to create the
1364
0
    // compositor can change our state (e.g. disable WebRender).
1365
0
    if (mCompositorSession || !retry) {
1366
0
      *aOptionsOut = options;
1367
0
      return lm.forget();
1368
0
    }
1369
0
  } while (true);
1370
0
}
1371
1372
void nsBaseWidget::CreateCompositor(int aWidth, int aHeight)
1373
0
{
1374
0
  // This makes sure that gfxPlatforms gets initialized if it hasn't by now.
1375
0
  gfxPlatform::GetPlatform();
1376
0
1377
0
  MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(),
1378
0
             "This function assumes OMTC");
1379
0
1380
0
  MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild,
1381
0
    "Should have properly cleaned up the previous PCompositor pair beforehand");
1382
0
1383
0
  if (mCompositorBridgeChild) {
1384
0
    mCompositorBridgeChild->Destroy();
1385
0
  }
1386
0
1387
0
  // Recreating this is tricky, as we may still have an old and we need
1388
0
  // to make sure it's properly destroyed by calling DestroyCompositor!
1389
0
1390
0
  // If we've already received a shutdown notification, don't try
1391
0
  // create a new compositor.
1392
0
  if (!mShutdownObserver) {
1393
0
    return;
1394
0
  }
1395
0
1396
0
  CompositorOptions options;
1397
0
  RefPtr<LayerManager> lm =
1398
0
    CreateCompositorSession(aWidth, aHeight, &options);
1399
0
  if (!lm) {
1400
0
    return;
1401
0
  }
1402
0
1403
0
  MOZ_ASSERT(mCompositorSession);
1404
0
  mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild();
1405
0
  SetCompositorWidgetDelegate(mCompositorSession->GetCompositorWidgetDelegate());
1406
0
1407
0
  if (options.UseAPZ()) {
1408
0
    mAPZC = mCompositorSession->GetAPZCTreeManager();
1409
0
    ConfigureAPZCTreeManager();
1410
0
  } else {
1411
0
    mAPZC = nullptr;
1412
0
  }
1413
0
1414
0
  if (mInitialZoomConstraints) {
1415
0
    UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID,
1416
0
                          mInitialZoomConstraints->mViewID,
1417
0
                          Some(mInitialZoomConstraints->mConstraints));
1418
0
    mInitialZoomConstraints.reset();
1419
0
  }
1420
0
1421
0
  if (lm->AsWebRenderLayerManager()) {
1422
0
    TextureFactoryIdentifier textureFactoryIdentifier = lm->GetTextureFactoryIdentifier();
1423
0
    MOZ_ASSERT(textureFactoryIdentifier.mParentBackend == LayersBackend::LAYERS_WR);
1424
0
    ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
1425
0
    gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
1426
0
  }
1427
0
1428
0
  ShadowLayerForwarder* lf = lm->AsShadowForwarder();
1429
0
  if (lf) {
1430
0
    // lf is non-null if we are creating a ClientLayerManager above
1431
0
    TextureFactoryIdentifier textureFactoryIdentifier;
1432
0
    PLayerTransactionChild* shadowManager = nullptr;
1433
0
1434
0
    nsTArray<LayersBackend> backendHints;
1435
0
    gfxPlatform::GetPlatform()->GetCompositorBackends(ComputeShouldAccelerate(), backendHints);
1436
0
1437
0
    bool success = false;
1438
0
    if (!backendHints.IsEmpty()) {
1439
0
      shadowManager =
1440
0
        mCompositorBridgeChild->SendPLayerTransactionConstructor(backendHints, LayersId{0});
1441
0
      if (shadowManager->SendGetTextureFactoryIdentifier(&textureFactoryIdentifier) &&
1442
0
          textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_NONE)
1443
0
      {
1444
0
        success = true;
1445
0
      }
1446
0
    }
1447
0
1448
0
    if (!success) {
1449
0
      NS_WARNING("Failed to create an OMT compositor.");
1450
0
      DestroyCompositor();
1451
0
      mLayerManager = nullptr;
1452
0
      return;
1453
0
    }
1454
0
1455
0
    lf->SetShadowManager(shadowManager);
1456
0
    lm->UpdateTextureFactoryIdentifier(textureFactoryIdentifier);
1457
0
    // Some popup or transparent widgets may use a different backend than the
1458
0
    // compositors used with ImageBridge and VR (and more generally web content).
1459
0
    if (WidgetTypeSupportsAcceleration()) {
1460
0
      ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier);
1461
0
      gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier);
1462
0
    }
1463
0
  }
1464
0
1465
0
  WindowUsesOMTC();
1466
0
1467
0
  mLayerManager = lm.forget();
1468
0
1469
0
  // Only track compositors for top-level windows, since other window types
1470
0
  // may use the basic compositor.  Except on the OS X - see bug 1306383
1471
#if defined(XP_MACOSX)
1472
  bool getCompositorFromThisWindow = true;
1473
#else
1474
  bool getCompositorFromThisWindow = (mWindowType == eWindowType_toplevel);
1475
0
#endif
1476
0
1477
0
  if (getCompositorFromThisWindow) {
1478
0
    gfxPlatform::GetPlatform()->NotifyCompositorCreated(mLayerManager->GetCompositorBackendType());
1479
0
  }
1480
0
}
1481
1482
void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession* aSession)
1483
0
{
1484
0
  MOZ_ASSERT(aSession == mCompositorSession);
1485
0
  DestroyLayerManager();
1486
0
}
1487
1488
bool nsBaseWidget::ShouldUseOffMainThreadCompositing()
1489
0
{
1490
0
  return gfxPlatform::UsesOffMainThreadCompositing();
1491
0
}
1492
1493
LayerManager* nsBaseWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
1494
                                            LayersBackend aBackendHint,
1495
                                            LayerManagerPersistence aPersistence)
1496
0
{
1497
0
  if (!mLayerManager) {
1498
0
    if (!mShutdownObserver) {
1499
0
      // We are shutting down, do not try to re-create a LayerManager
1500
0
      return nullptr;
1501
0
    }
1502
0
    // Try to use an async compositor first, if possible
1503
0
    if (ShouldUseOffMainThreadCompositing()) {
1504
0
      // e10s uses the parameter to pass in the shadow manager from the TabChild
1505
0
      // so we don't expect to see it there since this doesn't support e10s.
1506
0
      NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s");
1507
0
      CreateCompositor();
1508
0
    }
1509
0
1510
0
    if (!mLayerManager) {
1511
0
      mLayerManager = CreateBasicLayerManager();
1512
0
    }
1513
0
  }
1514
0
  return mLayerManager;
1515
0
}
1516
1517
LayerManager* nsBaseWidget::CreateBasicLayerManager()
1518
0
{
1519
0
  return new BasicLayerManager(this);
1520
0
}
1521
1522
CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer()
1523
0
{
1524
0
  return mCompositorBridgeChild;
1525
0
}
1526
1527
already_AddRefed<gfx::DrawTarget>
1528
nsBaseWidget::StartRemoteDrawing()
1529
0
{
1530
0
  if (recordreplay::IsRecordingOrReplaying()) {
1531
0
    return recordreplay::child::DrawTargetForRemoteDrawing(mBounds.Size());
1532
0
  }
1533
0
  return nullptr;
1534
0
}
1535
1536
uint32_t
1537
nsBaseWidget::GetGLFrameBufferFormat()
1538
0
{
1539
0
  return LOCAL_GL_RGBA;
1540
0
}
1541
1542
//-------------------------------------------------------------------------
1543
//
1544
// Destroy the window
1545
//
1546
//-------------------------------------------------------------------------
1547
void nsBaseWidget::OnDestroy()
1548
0
{
1549
0
  if (mTextEventDispatcher) {
1550
0
    mTextEventDispatcher->OnDestroyWidget();
1551
0
    // Don't release it until this widget actually released because after this
1552
0
    // is called, TextEventDispatcher() may create it again.
1553
0
  }
1554
0
1555
0
  // If this widget is being destroyed, let the APZ code know to drop references
1556
0
  // to this widget. Callers of this function all should be holding a deathgrip
1557
0
  // on this widget already.
1558
0
  ReleaseContentController();
1559
0
}
1560
1561
void
1562
nsBaseWidget::MoveClient(double aX, double aY)
1563
0
{
1564
0
  LayoutDeviceIntPoint clientOffset(GetClientOffset());
1565
0
1566
0
  // GetClientOffset returns device pixels; scale back to desktop pixels
1567
0
  // if that's what this widget uses for the Move/Resize APIs
1568
0
  if (BoundsUseDesktopPixels()) {
1569
0
    DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale();
1570
0
    Move(aX - desktopOffset.x, aY - desktopOffset.y);
1571
0
  } else {
1572
0
    Move(aX - clientOffset.x, aY - clientOffset.y);
1573
0
  }
1574
0
}
1575
1576
void
1577
nsBaseWidget::ResizeClient(double aWidth, double aHeight, bool aRepaint)
1578
0
{
1579
0
  NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
1580
0
  NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
1581
0
1582
0
  LayoutDeviceIntRect clientBounds = GetClientBounds();
1583
0
1584
0
  // GetClientBounds and mBounds are device pixels; scale back to desktop pixels
1585
0
  // if that's what this widget uses for the Move/Resize APIs
1586
0
  if (BoundsUseDesktopPixels()) {
1587
0
    DesktopSize desktopDelta =
1588
0
      (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
1589
0
       clientBounds.Size()) / GetDesktopToDeviceScale();
1590
0
    Resize(aWidth + desktopDelta.width, aHeight + desktopDelta.height,
1591
0
           aRepaint);
1592
0
  } else {
1593
0
    Resize(mBounds.Width() + (aWidth - clientBounds.Width()),
1594
0
           mBounds.Height() + (aHeight - clientBounds.Height()), aRepaint);
1595
0
  }
1596
0
}
1597
1598
void
1599
nsBaseWidget::ResizeClient(double aX,
1600
                           double aY,
1601
                           double aWidth,
1602
                           double aHeight,
1603
                           bool aRepaint)
1604
0
{
1605
0
  NS_ASSERTION((aWidth >=0) , "Negative width passed to ResizeClient");
1606
0
  NS_ASSERTION((aHeight >=0), "Negative height passed to ResizeClient");
1607
0
1608
0
  LayoutDeviceIntRect clientBounds = GetClientBounds();
1609
0
  LayoutDeviceIntPoint clientOffset = GetClientOffset();
1610
0
1611
0
  if (BoundsUseDesktopPixels()) {
1612
0
    DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale();
1613
0
    DesktopPoint desktopOffset = clientOffset / scale;
1614
0
    DesktopSize desktopDelta =
1615
0
      (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) -
1616
0
       clientBounds.Size()) / scale;
1617
0
    Resize(aX - desktopOffset.x, aY - desktopOffset.y,
1618
0
           aWidth + desktopDelta.width, aHeight + desktopDelta.height,
1619
0
           aRepaint);
1620
0
  } else {
1621
0
    Resize(aX - clientOffset.x, aY - clientOffset.y,
1622
0
           aWidth + mBounds.Width() - clientBounds.Width(),
1623
0
           aHeight + mBounds.Height() - clientBounds.Height(),
1624
0
           aRepaint);
1625
0
  }
1626
0
}
1627
1628
//-------------------------------------------------------------------------
1629
//
1630
// Bounds
1631
//
1632
//-------------------------------------------------------------------------
1633
1634
/**
1635
* If the implementation of nsWindow supports borders this method MUST be overridden
1636
*
1637
**/
1638
LayoutDeviceIntRect
1639
nsBaseWidget::GetClientBounds()
1640
0
{
1641
0
  return GetBounds();
1642
0
}
1643
1644
/**
1645
* If the implementation of nsWindow supports borders this method MUST be overridden
1646
*
1647
**/
1648
LayoutDeviceIntRect
1649
nsBaseWidget::GetBounds()
1650
0
{
1651
0
  return mBounds;
1652
0
}
1653
1654
/**
1655
* If the implementation of nsWindow uses a local coordinate system within the window,
1656
* this method must be overridden
1657
*
1658
**/
1659
LayoutDeviceIntRect
1660
nsBaseWidget::GetScreenBounds()
1661
0
{
1662
0
  return GetBounds();
1663
0
}
1664
1665
nsresult
1666
nsBaseWidget::GetRestoredBounds(LayoutDeviceIntRect& aRect)
1667
0
{
1668
0
  if (SizeMode() != nsSizeMode_Normal) {
1669
0
    return NS_ERROR_FAILURE;
1670
0
  }
1671
0
  aRect = GetScreenBounds();
1672
0
  return NS_OK;
1673
0
}
1674
1675
LayoutDeviceIntPoint
1676
nsBaseWidget::GetClientOffset()
1677
0
{
1678
0
  return LayoutDeviceIntPoint(0, 0);
1679
0
}
1680
1681
nsresult
1682
nsBaseWidget::SetNonClientMargins(LayoutDeviceIntMargin &margins)
1683
0
{
1684
0
  return NS_ERROR_NOT_IMPLEMENTED;
1685
0
}
1686
1687
uint32_t nsBaseWidget::GetMaxTouchPoints() const
1688
0
{
1689
0
  return 0;
1690
0
}
1691
1692
bool
1693
nsBaseWidget::HasPendingInputEvent()
1694
0
{
1695
0
  return false;
1696
0
}
1697
1698
bool
1699
nsBaseWidget::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect)
1700
0
{
1701
0
  return false;
1702
0
}
1703
1704
/**
1705
 * Modifies aFile to point at an icon file with the given name and suffix.  The
1706
 * suffix may correspond to a file extension with leading '.' if appropriate.
1707
 * Returns true if the icon file exists and can be read.
1708
 */
1709
static bool
1710
ResolveIconNameHelper(nsIFile *aFile,
1711
                      const nsAString &aIconName,
1712
                      const nsAString &aIconSuffix)
1713
0
{
1714
0
  aFile->Append(NS_LITERAL_STRING("icons"));
1715
0
  aFile->Append(NS_LITERAL_STRING("default"));
1716
0
  aFile->Append(aIconName + aIconSuffix);
1717
0
1718
0
  bool readable;
1719
0
  return NS_SUCCEEDED(aFile->IsReadable(&readable)) && readable;
1720
0
}
1721
1722
/**
1723
 * Resolve the given icon name into a local file object.  This method is
1724
 * intended to be called by subclasses of nsBaseWidget.  aIconSuffix is a
1725
 * platform specific icon file suffix (e.g., ".ico" under Win32).
1726
 *
1727
 * If no file is found matching the given parameters, then null is returned.
1728
 */
1729
void
1730
nsBaseWidget::ResolveIconName(const nsAString &aIconName,
1731
                              const nsAString &aIconSuffix,
1732
                              nsIFile **aResult)
1733
0
{
1734
0
  *aResult = nullptr;
1735
0
1736
0
  nsCOMPtr<nsIProperties> dirSvc = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
1737
0
  if (!dirSvc)
1738
0
    return;
1739
0
1740
0
  // first check auxilary chrome directories
1741
0
1742
0
  nsCOMPtr<nsISimpleEnumerator> dirs;
1743
0
  dirSvc->Get(NS_APP_CHROME_DIR_LIST, NS_GET_IID(nsISimpleEnumerator),
1744
0
              getter_AddRefs(dirs));
1745
0
  if (dirs) {
1746
0
    bool hasMore;
1747
0
    while (NS_SUCCEEDED(dirs->HasMoreElements(&hasMore)) && hasMore) {
1748
0
      nsCOMPtr<nsISupports> element;
1749
0
      dirs->GetNext(getter_AddRefs(element));
1750
0
      if (!element)
1751
0
        continue;
1752
0
      nsCOMPtr<nsIFile> file = do_QueryInterface(element);
1753
0
      if (!file)
1754
0
        continue;
1755
0
      if (ResolveIconNameHelper(file, aIconName, aIconSuffix)) {
1756
0
        NS_ADDREF(*aResult = file);
1757
0
        return;
1758
0
      }
1759
0
    }
1760
0
  }
1761
0
1762
0
  // then check the main app chrome directory
1763
0
1764
0
  nsCOMPtr<nsIFile> file;
1765
0
  dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile),
1766
0
              getter_AddRefs(file));
1767
0
  if (file && ResolveIconNameHelper(file, aIconName, aIconSuffix))
1768
0
    NS_ADDREF(*aResult = file);
1769
0
}
1770
1771
void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints)
1772
0
{
1773
0
  mSizeConstraints = aConstraints;
1774
0
1775
0
  // Popups are constrained during layout, and we don't want to synchronously
1776
0
  // paint from reflow, so bail out... This is not great, but it's no worse than
1777
0
  // what we used to do.
1778
0
  //
1779
0
  // The right fix here is probably making constraint changes go through the
1780
0
  // view manager and such.
1781
0
  if (mWindowType == eWindowType_popup) {
1782
0
    return;
1783
0
  }
1784
0
1785
0
  // If the current size doesn't meet the new constraints, trigger a
1786
0
  // resize to apply it. Note that, we don't want to invoke Resize if
1787
0
  // the new constraints don't affect the current size, because Resize
1788
0
  // implementation on some platforms may touch other geometry even if
1789
0
  // the size don't need to change.
1790
0
  LayoutDeviceIntSize curSize = mBounds.Size();
1791
0
  LayoutDeviceIntSize clampedSize =
1792
0
    Max(aConstraints.mMinSize, Min(aConstraints.mMaxSize, curSize));
1793
0
  if (clampedSize != curSize) {
1794
0
    gfx::Size size;
1795
0
    if (BoundsUseDesktopPixels()) {
1796
0
      DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale();
1797
0
      size = desktopSize.ToUnknownSize();
1798
0
    } else {
1799
0
      size = gfx::Size(clampedSize.ToUnknownSize());
1800
0
    }
1801
0
    Resize(size.width, size.height, true);
1802
0
  }
1803
0
}
1804
1805
const widget::SizeConstraints nsBaseWidget::GetSizeConstraints()
1806
0
{
1807
0
  return mSizeConstraints;
1808
0
}
1809
1810
// static
1811
nsIRollupListener*
1812
nsBaseWidget::GetActiveRollupListener()
1813
0
{
1814
0
  // If set, then this is likely an <html:select> dropdown.
1815
0
  if (gRollupListener)
1816
0
    return gRollupListener;
1817
0
1818
0
  return nsXULPopupManager::GetInstance();
1819
0
}
1820
1821
void
1822
nsBaseWidget::NotifyWindowDestroyed()
1823
0
{
1824
0
  if (!mWidgetListener)
1825
0
    return;
1826
0
1827
0
  nsCOMPtr<nsIXULWindow> window = mWidgetListener->GetXULWindow();
1828
0
  nsCOMPtr<nsIBaseWindow> xulWindow(do_QueryInterface(window));
1829
0
  if (xulWindow) {
1830
0
    xulWindow->Destroy();
1831
0
  }
1832
0
}
1833
1834
void
1835
nsBaseWidget::NotifyWindowMoved(int32_t aX, int32_t aY)
1836
0
{
1837
0
  if (mWidgetListener) {
1838
0
    mWidgetListener->WindowMoved(this, aX, aY);
1839
0
  }
1840
0
1841
0
  if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) {
1842
0
    NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE));
1843
0
  }
1844
0
}
1845
1846
void
1847
nsBaseWidget::NotifyPresShell(NotificationFunc aNotificationFunc)
1848
0
{
1849
0
  if (!mWidgetListener) {
1850
0
    return;
1851
0
  }
1852
0
1853
0
  nsIPresShell* presShell = mWidgetListener->GetPresShell();
1854
0
  if (presShell) {
1855
0
    (presShell->*aNotificationFunc)();
1856
0
  }
1857
0
}
1858
1859
void
1860
nsBaseWidget::NotifySizeMoveDone()
1861
0
{
1862
0
  NotifyPresShell(&nsIPresShell::WindowSizeMoveDone);
1863
0
}
1864
1865
void
1866
nsBaseWidget::NotifySysColorChanged()
1867
0
{
1868
0
  NotifyPresShell(&nsIPresShell::SysColorChanged);
1869
0
}
1870
1871
void
1872
nsBaseWidget::NotifyThemeChanged()
1873
0
{
1874
0
  NotifyPresShell(&nsIPresShell::ThemeChanged);
1875
0
}
1876
1877
void
1878
nsBaseWidget::NotifyUIStateChanged(UIStateChangeType aShowAccelerators,
1879
                                   UIStateChangeType aShowFocusRings)
1880
0
{
1881
0
  if (nsIDocument* doc = GetDocument()) {
1882
0
    nsPIDOMWindowOuter* win = doc->GetWindow();
1883
0
    if (win) {
1884
0
      win->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
1885
0
    }
1886
0
  }
1887
0
}
1888
1889
nsresult
1890
nsBaseWidget::NotifyIME(const IMENotification& aIMENotification)
1891
0
{
1892
0
  switch (aIMENotification.mMessage) {
1893
0
    case REQUEST_TO_COMMIT_COMPOSITION:
1894
0
    case REQUEST_TO_CANCEL_COMPOSITION:
1895
0
      // We should send request to IME only when there is a TextEventDispatcher
1896
0
      // instance (this means that this widget has dispatched at least one
1897
0
      // composition event or keyboard event) and the it has composition.
1898
0
      // Otherwise, there is nothing to do.
1899
0
      // Note that if current input transaction is for native input events,
1900
0
      // TextEventDispatcher::NotifyIME() will call
1901
0
      // TextEventDispatcherListener::NotifyIME().
1902
0
      if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) {
1903
0
        return mTextEventDispatcher->NotifyIME(aIMENotification);
1904
0
      }
1905
0
      return NS_OK;
1906
0
    default: {
1907
0
      if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
1908
0
        mIMEHasFocus = true;
1909
0
      }
1910
0
      EnsureTextEventDispatcher();
1911
0
      // TextEventDispatcher::NotifyIME() will always call
1912
0
      // TextEventDispatcherListener::NotifyIME().  I.e., even if current
1913
0
      // input transaction is for synthesized events for automated tests,
1914
0
      // notifications will be sent to native IME.
1915
0
      nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification);
1916
0
      if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) {
1917
0
        mIMEHasFocus = false;
1918
0
      }
1919
0
      return rv;
1920
0
    }
1921
0
  }
1922
0
}
1923
1924
void
1925
nsBaseWidget::EnsureTextEventDispatcher()
1926
0
{
1927
0
  if (mTextEventDispatcher) {
1928
0
    return;
1929
0
  }
1930
0
  mTextEventDispatcher = new TextEventDispatcher(this);
1931
0
}
1932
1933
nsIWidget::NativeIMEContext
1934
nsBaseWidget::GetNativeIMEContext()
1935
0
{
1936
0
  if (mTextEventDispatcher && mTextEventDispatcher->GetPseudoIMEContext()) {
1937
0
    // If we already have a TextEventDispatcher and it's working with
1938
0
    // a TextInputProcessor, we need to return pseudo IME context since
1939
0
    // TextCompositionArray::IndexOf(nsIWidget*) should return a composition
1940
0
    // on the pseudo IME context in such case.
1941
0
    NativeIMEContext pseudoIMEContext;
1942
0
    pseudoIMEContext.InitWithRawNativeIMEContext(
1943
0
                       mTextEventDispatcher->GetPseudoIMEContext());
1944
0
    return pseudoIMEContext;
1945
0
  }
1946
0
  return NativeIMEContext(this);
1947
0
}
1948
1949
nsIWidget::TextEventDispatcher*
1950
nsBaseWidget::GetTextEventDispatcher()
1951
0
{
1952
0
  EnsureTextEventDispatcher();
1953
0
  return mTextEventDispatcher;
1954
0
}
1955
1956
void*
1957
nsBaseWidget::GetPseudoIMEContext()
1958
0
{
1959
0
  TextEventDispatcher* dispatcher = GetTextEventDispatcher();
1960
0
  if (!dispatcher) {
1961
0
    return nullptr;
1962
0
  }
1963
0
  return dispatcher->GetPseudoIMEContext();
1964
0
}
1965
1966
TextEventDispatcherListener*
1967
nsBaseWidget::GetNativeTextEventDispatcherListener()
1968
0
{
1969
0
  // TODO: If all platforms supported use of TextEventDispatcher for handling
1970
0
  //       native IME and keyboard events, this method should be removed since
1971
0
  //       in such case, this is overridden by all the subclasses.
1972
0
  return nullptr;
1973
0
}
1974
1975
void
1976
nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId,
1977
                         const FrameMetrics::ViewID& aViewId,
1978
                         const CSSRect& aRect,
1979
                         const uint32_t& aFlags)
1980
0
{
1981
0
  if (!mCompositorSession || !mAPZC) {
1982
0
    return;
1983
0
  }
1984
0
  LayersId layerId = mCompositorSession->RootLayerTreeId();
1985
0
  APZThreadUtils::RunOnControllerThread(
1986
0
    NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
1987
0
      "layers::IAPZCTreeManager::ZoomToRect",
1988
0
      mAPZC,
1989
0
      &IAPZCTreeManager::ZoomToRect,
1990
0
      ScrollableLayerGuid(layerId, aPresShellId, aViewId),
1991
0
      aRect,
1992
0
      aFlags));
1993
0
}
1994
1995
#ifdef ACCESSIBILITY
1996
1997
a11y::Accessible*
1998
nsBaseWidget::GetRootAccessible()
1999
0
{
2000
0
  NS_ENSURE_TRUE(mWidgetListener, nullptr);
2001
0
2002
0
  nsIPresShell* presShell = mWidgetListener->GetPresShell();
2003
0
  NS_ENSURE_TRUE(presShell, nullptr);
2004
0
2005
0
  // If container is null then the presshell is not active. This often happens
2006
0
  // when a preshell is being held onto for fastback.
2007
0
  nsPresContext* presContext = presShell->GetPresContext();
2008
0
  NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr);
2009
0
2010
0
  // Accessible creation might be not safe so use IsSafeToRunScript to
2011
0
  // make sure it's not created at unsafe times.
2012
0
  nsAccessibilityService* accService = GetOrCreateAccService();
2013
0
  if (accService) {
2014
0
    return accService->GetRootDocumentAccessible(presShell, nsContentUtils::IsSafeToRunScript());
2015
0
  }
2016
0
2017
0
  return nullptr;
2018
0
}
2019
2020
#endif // ACCESSIBILITY
2021
2022
void
2023
nsBaseWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
2024
0
{
2025
0
  if (!AsyncPanZoomEnabled()) {
2026
0
    return;
2027
0
  }
2028
0
2029
0
  MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession);
2030
0
2031
0
  LayersId layersId = mCompositorSession->RootLayerTreeId();
2032
0
  ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, aDragMetrics.mViewId);
2033
0
2034
0
  APZThreadUtils::RunOnControllerThread(
2035
0
    NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
2036
0
      "layers::IAPZCTreeManager::StartScrollbarDrag",
2037
0
      mAPZC,
2038
0
      &IAPZCTreeManager::StartScrollbarDrag,
2039
0
      guid,
2040
0
      aDragMetrics));
2041
0
}
2042
2043
bool
2044
nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation,
2045
                                   const ScrollableLayerGuid& aGuid)
2046
0
{
2047
0
  MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
2048
0
2049
0
  return mAPZC->StartAutoscroll(aGuid, aAnchorLocation);
2050
0
}
2051
2052
void
2053
nsBaseWidget::StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid)
2054
0
{
2055
0
  MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled());
2056
0
2057
0
  mAPZC->StopAutoscroll(aGuid);
2058
0
}
2059
2060
2061
already_AddRefed<nsIScreen>
2062
nsBaseWidget::GetWidgetScreen()
2063
0
{
2064
0
  nsCOMPtr<nsIScreenManager> screenManager;
2065
0
  screenManager = do_GetService("@mozilla.org/gfx/screenmanager;1");
2066
0
  if (!screenManager) {
2067
0
    return nullptr;
2068
0
  }
2069
0
2070
0
  LayoutDeviceIntRect bounds = GetScreenBounds();
2071
0
  DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale());
2072
0
  nsCOMPtr<nsIScreen> screen;
2073
0
  screenManager->ScreenForRect(deskBounds.X(), deskBounds.Y(),
2074
0
                               deskBounds.Width(), deskBounds.Height(),
2075
0
                               getter_AddRefs(screen));
2076
0
  return screen.forget();
2077
0
}
2078
2079
nsresult
2080
nsIWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint, bool aLongTap,
2081
                                    nsIObserver* aObserver)
2082
0
{
2083
0
  AutoObserverNotifier notifier(aObserver, "touchtap");
2084
0
2085
0
  if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) {
2086
0
    sPointerIdCounter = 0;
2087
0
  }
2088
0
  int pointerId = sPointerIdCounter;
2089
0
  sPointerIdCounter++;
2090
0
  nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT,
2091
0
                                           aPoint, 1.0, 90, nullptr);
2092
0
  if (NS_FAILED(rv)) {
2093
0
    return rv;
2094
0
  }
2095
0
2096
0
  if (!aLongTap) {
2097
0
    return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE,
2098
0
                                      aPoint, 0, 0, nullptr);
2099
0
  }
2100
0
2101
0
  // initiate a long tap
2102
0
  int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay",
2103
0
                                   TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC);
2104
0
  if (!mLongTapTimer) {
2105
0
    mLongTapTimer = NS_NewTimer();
2106
0
    if (!mLongTapTimer) {
2107
0
      SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL,
2108
0
                                 aPoint, 0, 0, nullptr);
2109
0
      return NS_ERROR_UNEXPECTED;
2110
0
    }
2111
0
    // Windows requires recuring events, so we set this to a smaller window
2112
0
    // than the pref value.
2113
0
    int timeout = elapse;
2114
0
    if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) {
2115
0
      timeout = TOUCH_INJECT_PUMP_TIMER_MSEC;
2116
0
    }
2117
0
    mLongTapTimer->InitWithNamedFuncCallback(
2118
0
      OnLongTapTimerCallback,
2119
0
      this,
2120
0
      timeout,
2121
0
      nsITimer::TYPE_REPEATING_SLACK,
2122
0
      "nsIWidget::SynthesizeNativeTouchTap");
2123
0
  }
2124
0
2125
0
  // If we already have a long tap pending, cancel it. We only allow one long
2126
0
  // tap to be active at a time.
2127
0
  if (mLongTapTouchPoint) {
2128
0
    SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
2129
0
                               mLongTapTouchPoint->mPosition, 0, 0, nullptr);
2130
0
  }
2131
0
2132
0
  mLongTapTouchPoint =
2133
0
    MakeUnique<LongTapInfo>(pointerId, aPoint,
2134
0
                            TimeDuration::FromMilliseconds(elapse),
2135
0
                            aObserver);
2136
0
  notifier.SkipNotification();  // we'll do it in the long-tap callback
2137
0
  return NS_OK;
2138
0
}
2139
2140
// static
2141
void
2142
nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure)
2143
0
{
2144
0
  auto *self = static_cast<nsIWidget *>(aClosure);
2145
0
2146
0
  if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) >
2147
0
      TimeStamp::Now()) {
2148
#ifdef XP_WIN
2149
    // Windows needs us to keep pumping feedback to the digitizer, so update
2150
    // the pointer id with the same position.
2151
    self->SynthesizeNativeTouchPoint(self->mLongTapTouchPoint->mPointerId,
2152
                                     TOUCH_CONTACT,
2153
                                     self->mLongTapTouchPoint->mPosition,
2154
                                     1.0, 90, nullptr);
2155
#endif
2156
    return;
2157
0
  }
2158
0
2159
0
  AutoObserverNotifier notifier(self->mLongTapTouchPoint->mObserver, "touchtap");
2160
0
2161
0
  // finished, remove the touch point
2162
0
  self->mLongTapTimer->Cancel();
2163
0
  self->mLongTapTimer = nullptr;
2164
0
  self->SynthesizeNativeTouchPoint(self->mLongTapTouchPoint->mPointerId,
2165
0
                                   TOUCH_REMOVE,
2166
0
                                   self->mLongTapTouchPoint->mPosition,
2167
0
                                   0, 0, nullptr);
2168
0
  self->mLongTapTouchPoint = nullptr;
2169
0
}
2170
2171
nsresult
2172
nsIWidget::ClearNativeTouchSequence(nsIObserver* aObserver)
2173
0
{
2174
0
  AutoObserverNotifier notifier(aObserver, "cleartouch");
2175
0
2176
0
  if (!mLongTapTimer) {
2177
0
    return NS_OK;
2178
0
  }
2179
0
  mLongTapTimer->Cancel();
2180
0
  mLongTapTimer = nullptr;
2181
0
  SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL,
2182
0
                             mLongTapTouchPoint->mPosition, 0, 0, nullptr);
2183
0
  mLongTapTouchPoint = nullptr;
2184
0
  return NS_OK;
2185
0
}
2186
2187
MultiTouchInput
2188
nsBaseWidget::UpdateSynthesizedTouchState(MultiTouchInput* aState,
2189
                                          uint32_t aTime,
2190
                                          mozilla::TimeStamp aTimeStamp,
2191
                                          uint32_t aPointerId,
2192
                                          TouchPointerState aPointerState,
2193
                                          LayoutDeviceIntPoint aPoint,
2194
                                          double aPointerPressure,
2195
                                          uint32_t aPointerOrientation)
2196
0
{
2197
0
  ScreenIntPoint pointerScreenPoint = ViewAs<ScreenPixel>(aPoint,
2198
0
      PixelCastJustification::LayoutDeviceIsScreenForBounds);
2199
0
2200
0
  // We can't dispatch *aState directly because (a) dispatching
2201
0
  // it might inadvertently modify it and (b) in the case of touchend or
2202
0
  // touchcancel events aState will hold the touches that are
2203
0
  // still down whereas the input dispatched needs to hold the removed
2204
0
  // touch(es). We use |inputToDispatch| for this purpose.
2205
0
  MultiTouchInput inputToDispatch;
2206
0
  inputToDispatch.mInputType = MULTITOUCH_INPUT;
2207
0
  inputToDispatch.mTime = aTime;
2208
0
  inputToDispatch.mTimeStamp = aTimeStamp;
2209
0
2210
0
  int32_t index = aState->IndexOfTouch((int32_t)aPointerId);
2211
0
  if (aPointerState == TOUCH_CONTACT) {
2212
0
    if (index >= 0) {
2213
0
      // found an existing touch point, update it
2214
0
      SingleTouchData& point = aState->mTouches[index];
2215
0
      point.mScreenPoint = pointerScreenPoint;
2216
0
      point.mRotationAngle = (float)aPointerOrientation;
2217
0
      point.mForce = (float)aPointerPressure;
2218
0
      inputToDispatch.mType = MultiTouchInput::MULTITOUCH_MOVE;
2219
0
    } else {
2220
0
      // new touch point, add it
2221
0
      aState->mTouches.AppendElement(SingleTouchData(
2222
0
          (int32_t)aPointerId,
2223
0
          pointerScreenPoint,
2224
0
          ScreenSize(0, 0),
2225
0
          (float)aPointerOrientation,
2226
0
          (float)aPointerPressure));
2227
0
      inputToDispatch.mType = MultiTouchInput::MULTITOUCH_START;
2228
0
    }
2229
0
    inputToDispatch.mTouches = aState->mTouches;
2230
0
  } else {
2231
0
    MOZ_ASSERT(aPointerState == TOUCH_REMOVE || aPointerState == TOUCH_CANCEL);
2232
0
    // a touch point is being lifted, so remove it from the stored list
2233
0
    if (index >= 0) {
2234
0
      aState->mTouches.RemoveElementAt(index);
2235
0
    }
2236
0
    inputToDispatch.mType = (aPointerState == TOUCH_REMOVE
2237
0
        ? MultiTouchInput::MULTITOUCH_END
2238
0
        : MultiTouchInput::MULTITOUCH_CANCEL);
2239
0
    inputToDispatch.mTouches.AppendElement(SingleTouchData(
2240
0
        (int32_t)aPointerId,
2241
0
        pointerScreenPoint,
2242
0
        ScreenSize(0, 0),
2243
0
        (float)aPointerOrientation,
2244
0
        (float)aPointerPressure));
2245
0
  }
2246
0
2247
0
  return inputToDispatch;
2248
0
}
2249
2250
void
2251
nsBaseWidget::NotifyLiveResizeStarted()
2252
0
{
2253
0
  // If we have mLiveResizeListeners already non-empty, we should notify those
2254
0
  // listeners that the resize stopped before starting anew. In theory this
2255
0
  // should never happen because we shouldn't get nested live resize actions.
2256
0
  NotifyLiveResizeStopped();
2257
0
  MOZ_ASSERT(mLiveResizeListeners.IsEmpty());
2258
0
2259
0
  // If we can get the active tab parent for the current widget, suppress
2260
0
  // the displayport on it during the live resize.
2261
0
  if (!mWidgetListener) {
2262
0
    return;
2263
0
  }
2264
0
  nsCOMPtr<nsIXULWindow> xulWindow = mWidgetListener->GetXULWindow();
2265
0
  if (!xulWindow) {
2266
0
    return;
2267
0
  }
2268
0
  mLiveResizeListeners = xulWindow->GetLiveResizeListeners();
2269
0
  for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) {
2270
0
    mLiveResizeListeners[i]->LiveResizeStarted();
2271
0
  }
2272
0
}
2273
2274
void
2275
nsBaseWidget::NotifyLiveResizeStopped()
2276
0
{
2277
0
  if (!mLiveResizeListeners.IsEmpty()) {
2278
0
    for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) {
2279
0
      mLiveResizeListeners[i]->LiveResizeStopped();
2280
0
    }
2281
0
    mLiveResizeListeners.Clear();
2282
0
  }
2283
0
}
2284
2285
void
2286
nsBaseWidget::RegisterPluginWindowForRemoteUpdates()
2287
0
{
2288
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2289
  MOZ_ASSERT_UNREACHABLE("nsBaseWidget::RegisterPluginWindowForRemoteUpdates "
2290
                         "not implemented!");
2291
  return;
2292
#else
2293
0
  MOZ_ASSERT(NS_IsMainThread());
2294
0
  void* id = GetNativeData(NS_NATIVE_PLUGIN_ID);
2295
0
  if (!id) {
2296
0
    NS_WARNING("This is not a valid native widget!");
2297
0
    return;
2298
0
  }
2299
0
  MOZ_ASSERT(sPluginWidgetList);
2300
0
  sPluginWidgetList->Put(id, this);
2301
0
#endif
2302
0
}
2303
2304
void
2305
nsBaseWidget::UnregisterPluginWindowForRemoteUpdates()
2306
0
{
2307
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2308
  MOZ_ASSERT_UNREACHABLE("nsBaseWidget::UnregisterPluginWindowForRemoteUpdates "
2309
                         "not implemented!");
2310
  return;
2311
#else
2312
0
  MOZ_ASSERT(NS_IsMainThread());
2313
0
  void* id = GetNativeData(NS_NATIVE_PLUGIN_ID);
2314
0
  if (!id) {
2315
0
    NS_WARNING("This is not a valid native widget!");
2316
0
    return;
2317
0
  }
2318
0
  MOZ_ASSERT(sPluginWidgetList);
2319
0
  sPluginWidgetList->Remove(id);
2320
0
#endif
2321
0
}
2322
2323
nsresult
2324
nsBaseWidget::AsyncEnableDragDrop(bool aEnable)
2325
0
{
2326
0
  RefPtr<nsBaseWidget> kungFuDeathGrip = this;
2327
0
  return NS_IdleDispatchToCurrentThread(
2328
0
    NS_NewRunnableFunction("AsyncEnableDragDropFn",
2329
0
                           [this, aEnable, kungFuDeathGrip]() {
2330
0
                             EnableDragDrop(aEnable);
2331
0
                           }),
2332
0
    kAsyncDragDropTimeout);
2333
0
}
2334
2335
// static
2336
nsIWidget*
2337
nsIWidget::LookupRegisteredPluginWindow(uintptr_t aWindowID)
2338
0
{
2339
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2340
  MOZ_ASSERT_UNREACHABLE("nsBaseWidget::LookupRegisteredPluginWindow "
2341
                         "not implemented!");
2342
  return nullptr;
2343
#else
2344
0
  MOZ_ASSERT(NS_IsMainThread());
2345
0
  MOZ_ASSERT(sPluginWidgetList);
2346
0
  return sPluginWidgetList->GetWeak((void*)aWindowID);
2347
0
#endif
2348
0
}
2349
2350
// static
2351
void
2352
nsIWidget::UpdateRegisteredPluginWindowVisibility(uintptr_t aOwnerWidget,
2353
                                                  nsTArray<uintptr_t>& aPluginIds)
2354
0
{
2355
#if !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
2356
  MOZ_ASSERT_UNREACHABLE("nsBaseWidget::UpdateRegisteredPluginWindowVisibility"
2357
                         " not implemented!");
2358
  return;
2359
#else
2360
0
  MOZ_ASSERT(NS_IsMainThread());
2361
0
  MOZ_ASSERT(sPluginWidgetList);
2362
0
2363
0
  // Our visible list is associated with a compositor which is associated with
2364
0
  // a specific top level window. We use the parent widget during iteration
2365
0
  // to skip the plugin widgets owned by other top level windows.
2366
0
  for (auto iter = sPluginWidgetList->Iter(); !iter.Done(); iter.Next()) {
2367
0
    const void* windowId = iter.Key();
2368
0
    nsIWidget* widget = iter.UserData();
2369
0
2370
0
    MOZ_ASSERT(windowId);
2371
0
    MOZ_ASSERT(widget);
2372
0
2373
0
    if (!widget->Destroyed()) {
2374
0
      if ((uintptr_t)widget->GetParent() == aOwnerWidget) {
2375
0
        widget->Show(aPluginIds.Contains((uintptr_t)windowId));
2376
0
      }
2377
0
    }
2378
0
  }
2379
0
#endif
2380
0
}
2381
2382
#if defined(XP_WIN)
2383
// static
2384
void
2385
nsIWidget::CaptureRegisteredPlugins(uintptr_t aOwnerWidget)
2386
{
2387
  MOZ_ASSERT(NS_IsMainThread());
2388
  MOZ_ASSERT(sPluginWidgetList);
2389
2390
  // Our visible list is associated with a compositor which is associated with
2391
  // a specific top level window. We use the parent widget during iteration
2392
  // to skip the plugin widgets owned by other top level windows.
2393
  for (auto iter = sPluginWidgetList->Iter(); !iter.Done(); iter.Next()) {
2394
    DebugOnly<const void*> windowId = iter.Key();
2395
    nsIWidget* widget = iter.UserData();
2396
2397
    MOZ_ASSERT(windowId);
2398
    MOZ_ASSERT(widget);
2399
2400
    if (!widget->Destroyed() && widget->IsVisible()) {
2401
      if ((uintptr_t)widget->GetParent() == aOwnerWidget) {
2402
        widget->UpdateScrollCapture();
2403
      }
2404
    }
2405
  }
2406
}
2407
2408
uint64_t
2409
nsBaseWidget::CreateScrollCaptureContainer()
2410
{
2411
  mScrollCaptureContainer =
2412
    LayerManager::CreateImageContainer(ImageContainer::ASYNCHRONOUS);
2413
  if (!mScrollCaptureContainer) {
2414
    NS_WARNING("Failed to create ImageContainer for widget image capture.");
2415
    return ImageContainer::sInvalidAsyncContainerId;
2416
  }
2417
2418
  return mScrollCaptureContainer->GetAsyncContainerHandle().Value();
2419
}
2420
2421
void
2422
nsBaseWidget::UpdateScrollCapture()
2423
{
2424
  // Don't capture if no container or no size.
2425
  if (!mScrollCaptureContainer || mBounds.IsEmpty()) {
2426
    return;
2427
  }
2428
2429
  // If the derived class cannot take a snapshot, for example due to clipping,
2430
  // then it is responsible for creating a fallback. If null is returned, this
2431
  // means that we want to keep the existing snapshot.
2432
  RefPtr<gfx::SourceSurface> snapshot = CreateScrollSnapshot();
2433
  if (!snapshot) {
2434
    return;
2435
  }
2436
2437
  ImageContainer::NonOwningImage holder(new SourceSurfaceImage(snapshot));
2438
2439
  AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
2440
  imageList.AppendElement(holder);
2441
2442
  mScrollCaptureContainer->SetCurrentImages(imageList);
2443
}
2444
2445
void
2446
nsBaseWidget::DefaultFillScrollCapture(DrawTarget* aSnapshotDrawTarget)
2447
{
2448
  gfx::IntSize dtSize = aSnapshotDrawTarget->GetSize();
2449
  aSnapshotDrawTarget->FillRect(
2450
    gfx::Rect(0, 0, dtSize.width, dtSize.height),
2451
    gfx::ColorPattern(gfx::Color::FromABGR(kScrollCaptureFillColor)),
2452
    gfx::DrawOptions(1.f, gfx::CompositionOp::OP_SOURCE));
2453
  aSnapshotDrawTarget->Flush();
2454
}
2455
#endif
2456
2457
const IMENotificationRequests&
2458
nsIWidget::IMENotificationRequestsRef()
2459
0
{
2460
0
  TextEventDispatcher* dispatcher = GetTextEventDispatcher();
2461
0
  return dispatcher->IMENotificationRequestsRef();
2462
0
}
2463
2464
nsresult
2465
nsIWidget::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
2466
                                    nsIKeyEventInPluginCallback* aCallback)
2467
0
{
2468
0
  return NS_ERROR_NOT_IMPLEMENTED;
2469
0
}
2470
2471
void
2472
nsIWidget::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent)
2473
0
{
2474
0
}
2475
2476
void
2477
nsIWidget::GetEditCommands(nsIWidget::NativeKeyBindingsType aType,
2478
                           const WidgetKeyboardEvent& aEvent,
2479
                           nsTArray<CommandInt>& aCommands)
2480
0
{
2481
0
  MOZ_ASSERT(aEvent.IsTrusted());
2482
0
  MOZ_ASSERT(aCommands.IsEmpty());
2483
0
}
2484
2485
already_AddRefed<nsIBidiKeyboard>
2486
nsIWidget::CreateBidiKeyboard()
2487
0
{
2488
0
  if (XRE_IsContentProcess()) {
2489
0
    return CreateBidiKeyboardContentProcess();
2490
0
  }
2491
0
  return CreateBidiKeyboardInner();
2492
0
}
2493
2494
#ifdef ANDROID
2495
already_AddRefed<nsIBidiKeyboard>
2496
nsIWidget::CreateBidiKeyboardInner()
2497
{
2498
  // no bidi keyboard implementation
2499
  return nullptr;
2500
}
2501
#endif
2502
2503
namespace mozilla {
2504
namespace widget {
2505
2506
const char*
2507
ToChar(InputContext::Origin aOrigin)
2508
{
2509
  switch (aOrigin) {
2510
    case InputContext::ORIGIN_MAIN:
2511
      return "ORIGIN_MAIN";
2512
    case InputContext::ORIGIN_CONTENT:
2513
      return "ORIGIN_CONTENT";
2514
    default:
2515
      return "Unexpected value";
2516
  }
2517
}
2518
2519
const char*
2520
ToChar(IMEMessage aIMEMessage)
2521
{
2522
  switch (aIMEMessage) {
2523
    case NOTIFY_IME_OF_NOTHING:
2524
      return "NOTIFY_IME_OF_NOTHING";
2525
    case NOTIFY_IME_OF_FOCUS:
2526
      return "NOTIFY_IME_OF_FOCUS";
2527
    case NOTIFY_IME_OF_BLUR:
2528
      return "NOTIFY_IME_OF_BLUR";
2529
    case NOTIFY_IME_OF_SELECTION_CHANGE:
2530
      return "NOTIFY_IME_OF_SELECTION_CHANGE";
2531
    case NOTIFY_IME_OF_TEXT_CHANGE:
2532
      return "NOTIFY_IME_OF_TEXT_CHANGE";
2533
    case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
2534
      return "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED";
2535
    case NOTIFY_IME_OF_POSITION_CHANGE:
2536
      return "NOTIFY_IME_OF_POSITION_CHANGE";
2537
    case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
2538
      return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT";
2539
    case REQUEST_TO_COMMIT_COMPOSITION:
2540
      return "REQUEST_TO_COMMIT_COMPOSITION";
2541
    case REQUEST_TO_CANCEL_COMPOSITION:
2542
      return "REQUEST_TO_CANCEL_COMPOSITION";
2543
    default:
2544
      return "Unexpected value";
2545
  }
2546
}
2547
2548
void
2549
NativeIMEContext::Init(nsIWidget* aWidget)
2550
0
{
2551
0
  if (!aWidget) {
2552
0
    mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2553
0
    mOriginProcessID = static_cast<uint64_t>(-1);
2554
0
    return;
2555
0
  }
2556
0
  if (!XRE_IsContentProcess()) {
2557
0
    mRawNativeIMEContext = reinterpret_cast<uintptr_t>(
2558
0
      aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT));
2559
0
    mOriginProcessID = 0;
2560
0
    return;
2561
0
  }
2562
0
  // If this is created in a child process, aWidget is an instance of
2563
0
  // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT.
2564
0
  // Instead of that PuppetWidget::GetNativeIMEContext() returns cached
2565
0
  // native IME context of the parent process.
2566
0
  *this = aWidget->GetNativeIMEContext();
2567
0
}
2568
2569
void
2570
NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext)
2571
0
{
2572
0
  if (NS_WARN_IF(!aRawNativeIMEContext)) {
2573
0
    mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr);
2574
0
    mOriginProcessID = static_cast<uint64_t>(-1);
2575
0
    return;
2576
0
  }
2577
0
  mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext);
2578
0
  mOriginProcessID =
2579
0
    XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0;
2580
0
}
2581
2582
void
2583
IMENotification::TextChangeDataBase::MergeWith(
2584
                   const IMENotification::TextChangeDataBase& aOther)
2585
0
{
2586
0
  MOZ_ASSERT(aOther.IsValid(),
2587
0
             "Merging data must store valid data");
2588
0
  MOZ_ASSERT(aOther.mStartOffset <= aOther.mRemovedEndOffset,
2589
0
             "end of removed text must be same or larger than start");
2590
0
  MOZ_ASSERT(aOther.mStartOffset <= aOther.mAddedEndOffset,
2591
0
             "end of added text must be same or larger than start");
2592
0
2593
0
  if (!IsValid()) {
2594
0
    *this = aOther;
2595
0
    return;
2596
0
  }
2597
0
2598
0
  // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed
2599
0
  // text ranges.  I.e., mStartOffset should be the smallest offset of all
2600
0
  // modified text ranges in old text.  |mRemovedEndOffset| should be the
2601
0
  // largest end offset in old text of all modified text ranges.
2602
0
  // |mAddedEndOffset| represents the end offset of all inserted text ranges.
2603
0
  // I.e., only this is an offset in new text.
2604
0
  // In other words, between mStartOffset and |mRemovedEndOffset| of the
2605
0
  // premodified text was already removed.  And some text whose length is
2606
0
  // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|.  I.e.,
2607
0
  // this allows IME to mark dirty the modified text range with |mStartOffset|
2608
0
  // and |mRemovedEndOffset| if IME stores all text of the focused editor and
2609
0
  // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|.
2610
0
  // Additionally, IME can retrieve only the text between |mStartOffset| and
2611
0
  // |mAddedEndOffset| for updating stored text.
2612
0
2613
0
  // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they
2614
0
  // should be adjusted to be in same text. The |newData.mStartOffset| and
2615
0
  // |newData.mRemovedEndOffset| should be computed as in old text because
2616
0
  // |mStartOffset| and |mRemovedEndOffset| represent the modified text range
2617
0
  // in the old text but even if some text before the values of the newData
2618
0
  // has already been modified, the values don't include the changes.
2619
0
2620
0
  // For comparing new and old |mAddedEndOffset| values, they should be
2621
0
  // adjusted to be in same text.  The |oldData.mAddedEndOffset| should be
2622
0
  // computed as in the new text because |mAddedEndOffset| indicates the end
2623
0
  // offset of inserted text in the new text but |oldData.mAddedEndOffset|
2624
0
  // doesn't include any changes of the text before |newData.mAddedEndOffset|.
2625
0
2626
0
  const TextChangeDataBase& newData = aOther;
2627
0
  const TextChangeDataBase oldData = *this;
2628
0
2629
0
  // mCausedOnlyByComposition should be true only when all changes are caused
2630
0
  // by composition.
2631
0
  mCausedOnlyByComposition =
2632
0
    newData.mCausedOnlyByComposition && oldData.mCausedOnlyByComposition;
2633
0
2634
0
  // mIncludingChangesWithoutComposition should be true if at least one of
2635
0
  // merged changes occurred without composition.
2636
0
  mIncludingChangesWithoutComposition =
2637
0
    newData.mIncludingChangesWithoutComposition ||
2638
0
      oldData.mIncludingChangesWithoutComposition;
2639
0
2640
0
  // mIncludingChangesDuringComposition should be true when at least one of
2641
0
  // the merged non-composition changes occurred during the latest composition.
2642
0
  if (!newData.mCausedOnlyByComposition &&
2643
0
      !newData.mIncludingChangesDuringComposition) {
2644
0
    MOZ_ASSERT(newData.mIncludingChangesWithoutComposition);
2645
0
    MOZ_ASSERT(mIncludingChangesWithoutComposition);
2646
0
    // If new change is neither caused by composition nor occurred during
2647
0
    // composition, set mIncludingChangesDuringComposition to false because
2648
0
    // IME doesn't want outdated text changes as text change during current
2649
0
    // composition.
2650
0
    mIncludingChangesDuringComposition = false;
2651
0
  } else {
2652
0
    // Otherwise, set mIncludingChangesDuringComposition to true if either
2653
0
    // oldData or newData includes changes during composition.
2654
0
    mIncludingChangesDuringComposition =
2655
0
      newData.mIncludingChangesDuringComposition ||
2656
0
        oldData.mIncludingChangesDuringComposition;
2657
0
  }
2658
0
2659
0
  if (newData.mStartOffset >= oldData.mAddedEndOffset) {
2660
0
    // Case 1:
2661
0
    // If new start is after old end offset of added text, it means that text
2662
0
    // after the modified range is modified.  Like:
2663
0
    // added range of old change:             +----------+
2664
0
    // removed range of new change:                           +----------+
2665
0
    // So, the old start offset is always the smaller offset.
2666
0
    mStartOffset = oldData.mStartOffset;
2667
0
    // The new end offset of removed text is moved by the old change and we
2668
0
    // need to cancel the move of the old change for comparing the offsets in
2669
0
    // same text because it doesn't make sensce to compare offsets in different
2670
0
    // text.
2671
0
    uint32_t newRemovedEndOffsetInOldText =
2672
0
      newData.mRemovedEndOffset - oldData.Difference();
2673
0
    mRemovedEndOffset =
2674
0
      std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2675
0
    // The new end offset of added text is always the larger offset.
2676
0
    mAddedEndOffset = newData.mAddedEndOffset;
2677
0
    return;
2678
0
  }
2679
0
2680
0
  if (newData.mStartOffset >= oldData.mStartOffset) {
2681
0
    // If new start is in the modified range, it means that new data changes
2682
0
    // a part or all of the range.
2683
0
    mStartOffset = oldData.mStartOffset;
2684
0
    if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
2685
0
      // Case 2:
2686
0
      // If new end of removed text is greater than old end of added text, it
2687
0
      // means that all or a part of modified range modified again and text
2688
0
      // after the modified range is also modified.  Like:
2689
0
      // added range of old change:             +----------+
2690
0
      // removed range of new change:                   +----------+
2691
0
      // So, the new removed end offset is moved by the old change and we need
2692
0
      // to cancel the move of the old change for comparing the offsets in the
2693
0
      // same text because it doesn't make sense to compare the offsets in
2694
0
      // different text.
2695
0
      uint32_t newRemovedEndOffsetInOldText =
2696
0
        newData.mRemovedEndOffset - oldData.Difference();
2697
0
      mRemovedEndOffset =
2698
0
        std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2699
0
      // The old end of added text is replaced by new change. So, it should be
2700
0
      // same as the new start.  On the other hand, the new added end offset is
2701
0
      // always same or larger.  Therefore, the merged end offset of added
2702
0
      // text should be the new end offset of added text.
2703
0
      mAddedEndOffset = newData.mAddedEndOffset;
2704
0
      return;
2705
0
    }
2706
0
2707
0
    // Case 3:
2708
0
    // If new end of removed text is less than old end of added text, it means
2709
0
    // that only a part of the modified range is modified again.  Like:
2710
0
    // added range of old change:             +------------+
2711
0
    // removed range of new change:               +-----+
2712
0
    // So, the new end offset of removed text should be same as the old end
2713
0
    // offset of removed text.  Therefore, the merged end offset of removed
2714
0
    // text should be the old text change's |mRemovedEndOffset|.
2715
0
    mRemovedEndOffset = oldData.mRemovedEndOffset;
2716
0
    // The old end of added text is moved by new change.  So, we need to cancel
2717
0
    // the move of the new change for comparing the offsets in same text.
2718
0
    uint32_t oldAddedEndOffsetInNewText =
2719
0
      oldData.mAddedEndOffset + newData.Difference();
2720
0
    mAddedEndOffset =
2721
0
      std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2722
0
    return;
2723
0
  }
2724
0
2725
0
  if (newData.mRemovedEndOffset >= oldData.mStartOffset) {
2726
0
    // If new end of removed text is greater than old start (and new start is
2727
0
    // less than old start), it means that a part of modified range is modified
2728
0
    // again and some new text before the modified range is also modified.
2729
0
    MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
2730
0
      "new start offset should be less than old one here");
2731
0
    mStartOffset = newData.mStartOffset;
2732
0
    if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) {
2733
0
      // Case 4:
2734
0
      // If new end of removed text is greater than old end of added text, it
2735
0
      // means that all modified text and text after the modified range is
2736
0
      // modified.  Like:
2737
0
      // added range of old change:             +----------+
2738
0
      // removed range of new change:        +------------------+
2739
0
      // So, the new end of removed text is moved by the old change.  Therefore,
2740
0
      // we need to cancel the move of the old change for comparing the offsets
2741
0
      // in same text because it doesn't make sense to compare the offsets in
2742
0
      // different text.
2743
0
      uint32_t newRemovedEndOffsetInOldText =
2744
0
        newData.mRemovedEndOffset - oldData.Difference();
2745
0
      mRemovedEndOffset =
2746
0
        std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset);
2747
0
      // The old end of added text is replaced by new change.  So, the old end
2748
0
      // offset of added text is same as new text change's start offset.  Then,
2749
0
      // new change's end offset of added text is always same or larger than
2750
0
      // it.  Therefore, merged end offset of added text is always the new end
2751
0
      // offset of added text.
2752
0
      mAddedEndOffset = newData.mAddedEndOffset;
2753
0
      return;
2754
0
    }
2755
0
2756
0
    // Case 5:
2757
0
    // If new end of removed text is less than old end of added text, it
2758
0
    // means that only a part of the modified range is modified again.  Like:
2759
0
    // added range of old change:             +----------+
2760
0
    // removed range of new change:      +----------+
2761
0
    // So, the new end of removed text should be same as old end of removed
2762
0
    // text for preventing end of removed text to be modified.  Therefore,
2763
0
    // merged end offset of removed text is always the old end offset of removed
2764
0
    // text.
2765
0
    mRemovedEndOffset = oldData.mRemovedEndOffset;
2766
0
    // The old end of added text is moved by this change.  So, we need to
2767
0
    // cancel the move of the new change for comparing the offsets in same text
2768
0
    // because it doesn't make sense to compare the offsets in different text.
2769
0
    uint32_t oldAddedEndOffsetInNewText =
2770
0
      oldData.mAddedEndOffset + newData.Difference();
2771
0
    mAddedEndOffset =
2772
0
      std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2773
0
    return;
2774
0
  }
2775
0
2776
0
  // Case 6:
2777
0
  // Otherwise, i.e., both new end of added text and new start are less than
2778
0
  // old start, text before the modified range is modified.  Like:
2779
0
  // added range of old change:                  +----------+
2780
0
  // removed range of new change: +----------+
2781
0
  MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset,
2782
0
    "new start offset should be less than old one here");
2783
0
  mStartOffset = newData.mStartOffset;
2784
0
  MOZ_ASSERT(newData.mRemovedEndOffset < oldData.mRemovedEndOffset,
2785
0
     "new removed end offset should be less than old one here");
2786
0
  mRemovedEndOffset = oldData.mRemovedEndOffset;
2787
0
  // The end of added text should be adjusted with the new difference.
2788
0
  uint32_t oldAddedEndOffsetInNewText =
2789
0
    oldData.mAddedEndOffset + newData.Difference();
2790
0
  mAddedEndOffset =
2791
0
    std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText);
2792
0
}
2793
2794
#ifdef DEBUG
2795
2796
// Let's test the code of merging multiple text change data in debug build
2797
// and crash if one of them fails because this feature is very complex but
2798
// cannot be tested with mochitest.
2799
void
2800
IMENotification::TextChangeDataBase::Test()
2801
{
2802
  static bool gTestTextChangeEvent = true;
2803
  if (!gTestTextChangeEvent) {
2804
    return;
2805
  }
2806
  gTestTextChangeEvent = false;
2807
2808
  /****************************************************************************
2809
   * Case 1
2810
   ****************************************************************************/
2811
2812
  // Appending text
2813
  MergeWith(TextChangeData(10, 10, 20, false, false));
2814
  MergeWith(TextChangeData(20, 20, 35, false, false));
2815
  MOZ_ASSERT(mStartOffset == 10,
2816
    "Test 1-1-1: mStartOffset should be the first offset");
2817
  MOZ_ASSERT(mRemovedEndOffset == 10, // 20 - (20 - 10)
2818
    "Test 1-1-2: mRemovedEndOffset should be the first end of removed text");
2819
  MOZ_ASSERT(mAddedEndOffset == 35,
2820
    "Test 1-1-3: mAddedEndOffset should be the last end of added text");
2821
  Clear();
2822
2823
  // Removing text (longer line -> shorter line)
2824
  MergeWith(TextChangeData(10, 20, 10, false, false));
2825
  MergeWith(TextChangeData(10, 30, 10, false, false));
2826
  MOZ_ASSERT(mStartOffset == 10,
2827
    "Test 1-2-1: mStartOffset should be the first offset");
2828
  MOZ_ASSERT(mRemovedEndOffset == 40, // 30 + (10 - 20)
2829
    "Test 1-2-2: mRemovedEndOffset should be the the last end of removed text "
2830
    "with already removed length");
2831
  MOZ_ASSERT(mAddedEndOffset == 10,
2832
    "Test 1-2-3: mAddedEndOffset should be the last end of added text");
2833
  Clear();
2834
2835
  // Removing text (shorter line -> longer line)
2836
  MergeWith(TextChangeData(10, 20, 10, false, false));
2837
  MergeWith(TextChangeData(10, 15, 10, false, false));
2838
  MOZ_ASSERT(mStartOffset == 10,
2839
    "Test 1-3-1: mStartOffset should be the first offset");
2840
  MOZ_ASSERT(mRemovedEndOffset == 25, // 15 + (10 - 20)
2841
    "Test 1-3-2: mRemovedEndOffset should be the the last end of removed text "
2842
    "with already removed length");
2843
  MOZ_ASSERT(mAddedEndOffset == 10,
2844
    "Test 1-3-3: mAddedEndOffset should be the last end of added text");
2845
  Clear();
2846
2847
  // Appending text at different point (not sure if actually occurs)
2848
  MergeWith(TextChangeData(10, 10, 20, false, false));
2849
  MergeWith(TextChangeData(55, 55, 60, false, false));
2850
  MOZ_ASSERT(mStartOffset == 10,
2851
    "Test 1-4-1: mStartOffset should be the smallest offset");
2852
  MOZ_ASSERT(mRemovedEndOffset == 45, // 55 - (10 - 20)
2853
    "Test 1-4-2: mRemovedEndOffset should be the the largest end of removed "
2854
    "text without already added length");
2855
  MOZ_ASSERT(mAddedEndOffset == 60,
2856
    "Test 1-4-3: mAddedEndOffset should be the last end of added text");
2857
  Clear();
2858
2859
  // Removing text at different point (not sure if actually occurs)
2860
  MergeWith(TextChangeData(10, 20, 10, false, false));
2861
  MergeWith(TextChangeData(55, 68, 55, false, false));
2862
  MOZ_ASSERT(mStartOffset == 10,
2863
    "Test 1-5-1: mStartOffset should be the smallest offset");
2864
  MOZ_ASSERT(mRemovedEndOffset == 78, // 68 - (10 - 20)
2865
    "Test 1-5-2: mRemovedEndOffset should be the the largest end of removed "
2866
    "text with already removed length");
2867
  MOZ_ASSERT(mAddedEndOffset == 55,
2868
    "Test 1-5-3: mAddedEndOffset should be the largest end of added text");
2869
  Clear();
2870
2871
  // Replacing text and append text (becomes longer)
2872
  MergeWith(TextChangeData(30, 35, 32, false, false));
2873
  MergeWith(TextChangeData(32, 32, 40, false, false));
2874
  MOZ_ASSERT(mStartOffset == 30,
2875
    "Test 1-6-1: mStartOffset should be the smallest offset");
2876
  MOZ_ASSERT(mRemovedEndOffset == 35, // 32 - (32 - 35)
2877
    "Test 1-6-2: mRemovedEndOffset should be the the first end of removed "
2878
    "text");
2879
  MOZ_ASSERT(mAddedEndOffset == 40,
2880
    "Test 1-6-3: mAddedEndOffset should be the last end of added text");
2881
  Clear();
2882
2883
  // Replacing text and append text (becomes shorter)
2884
  MergeWith(TextChangeData(30, 35, 32, false, false));
2885
  MergeWith(TextChangeData(32, 32, 33, false, false));
2886
  MOZ_ASSERT(mStartOffset == 30,
2887
    "Test 1-7-1: mStartOffset should be the smallest offset");
2888
  MOZ_ASSERT(mRemovedEndOffset == 35, // 32 - (32 - 35)
2889
    "Test 1-7-2: mRemovedEndOffset should be the the first end of removed "
2890
    "text");
2891
  MOZ_ASSERT(mAddedEndOffset == 33,
2892
    "Test 1-7-3: mAddedEndOffset should be the last end of added text");
2893
  Clear();
2894
2895
  // Removing text and replacing text after first range (not sure if actually
2896
  // occurs)
2897
  MergeWith(TextChangeData(30, 35, 30, false, false));
2898
  MergeWith(TextChangeData(32, 34, 48, false, false));
2899
  MOZ_ASSERT(mStartOffset == 30,
2900
    "Test 1-8-1: mStartOffset should be the smallest offset");
2901
  MOZ_ASSERT(mRemovedEndOffset == 39, // 34 - (30 - 35)
2902
    "Test 1-8-2: mRemovedEndOffset should be the the first end of removed text "
2903
    "without already removed text");
2904
  MOZ_ASSERT(mAddedEndOffset == 48,
2905
    "Test 1-8-3: mAddedEndOffset should be the last end of added text");
2906
  Clear();
2907
2908
  // Removing text and replacing text after first range (not sure if actually
2909
  // occurs)
2910
  MergeWith(TextChangeData(30, 35, 30, false, false));
2911
  MergeWith(TextChangeData(32, 38, 36, false, false));
2912
  MOZ_ASSERT(mStartOffset == 30,
2913
    "Test 1-9-1: mStartOffset should be the smallest offset");
2914
  MOZ_ASSERT(mRemovedEndOffset == 43, // 38 - (30 - 35)
2915
    "Test 1-9-2: mRemovedEndOffset should be the the first end of removed text "
2916
    "without already removed text");
2917
  MOZ_ASSERT(mAddedEndOffset == 36,
2918
    "Test 1-9-3: mAddedEndOffset should be the last end of added text");
2919
  Clear();
2920
2921
  /****************************************************************************
2922
   * Case 2
2923
   ****************************************************************************/
2924
2925
  // Replacing text in around end of added text (becomes shorter) (not sure
2926
  // if actually occurs)
2927
  MergeWith(TextChangeData(50, 50, 55, false, false));
2928
  MergeWith(TextChangeData(53, 60, 54, false, false));
2929
  MOZ_ASSERT(mStartOffset == 50,
2930
    "Test 2-1-1: mStartOffset should be the smallest offset");
2931
  MOZ_ASSERT(mRemovedEndOffset == 55, // 60 - (55 - 50)
2932
    "Test 2-1-2: mRemovedEndOffset should be the the last end of removed text "
2933
    "without already added text length");
2934
  MOZ_ASSERT(mAddedEndOffset == 54,
2935
    "Test 2-1-3: mAddedEndOffset should be the last end of added text");
2936
  Clear();
2937
2938
  // Replacing text around end of added text (becomes longer) (not sure
2939
  // if actually occurs)
2940
  MergeWith(TextChangeData(50, 50, 55, false, false));
2941
  MergeWith(TextChangeData(54, 62, 68, false, false));
2942
  MOZ_ASSERT(mStartOffset == 50,
2943
    "Test 2-2-1: mStartOffset should be the smallest offset");
2944
  MOZ_ASSERT(mRemovedEndOffset == 57, // 62 - (55 - 50)
2945
    "Test 2-2-2: mRemovedEndOffset should be the the last end of removed text "
2946
    "without already added text length");
2947
  MOZ_ASSERT(mAddedEndOffset == 68,
2948
    "Test 2-2-3: mAddedEndOffset should be the last end of added text");
2949
  Clear();
2950
2951
  // Replacing text around end of replaced text (became shorter) (not sure if
2952
  // actually occurs)
2953
  MergeWith(TextChangeData(36, 48, 45, false, false));
2954
  MergeWith(TextChangeData(43, 50, 49, false, false));
2955
  MOZ_ASSERT(mStartOffset == 36,
2956
    "Test 2-3-1: mStartOffset should be the smallest offset");
2957
  MOZ_ASSERT(mRemovedEndOffset == 53, // 50 - (45 - 48)
2958
    "Test 2-3-2: mRemovedEndOffset should be the the last end of removed text "
2959
    "without already removed text length");
2960
  MOZ_ASSERT(mAddedEndOffset == 49,
2961
    "Test 2-3-3: mAddedEndOffset should be the last end of added text");
2962
  Clear();
2963
2964
  // Replacing text around end of replaced text (became longer) (not sure if
2965
  // actually occurs)
2966
  MergeWith(TextChangeData(36, 52, 53, false, false));
2967
  MergeWith(TextChangeData(43, 68, 61, false, false));
2968
  MOZ_ASSERT(mStartOffset == 36,
2969
    "Test 2-4-1: mStartOffset should be the smallest offset");
2970
  MOZ_ASSERT(mRemovedEndOffset == 67, // 68 - (53 - 52)
2971
    "Test 2-4-2: mRemovedEndOffset should be the the last end of removed text "
2972
    "without already added text length");
2973
  MOZ_ASSERT(mAddedEndOffset == 61,
2974
    "Test 2-4-3: mAddedEndOffset should be the last end of added text");
2975
  Clear();
2976
2977
  /****************************************************************************
2978
   * Case 3
2979
   ****************************************************************************/
2980
2981
  // Appending text in already added text (not sure if actually occurs)
2982
  MergeWith(TextChangeData(10, 10, 20, false, false));
2983
  MergeWith(TextChangeData(15, 15, 30, false, false));
2984
  MOZ_ASSERT(mStartOffset == 10,
2985
    "Test 3-1-1: mStartOffset should be the smallest offset");
2986
  MOZ_ASSERT(mRemovedEndOffset == 10,
2987
    "Test 3-1-2: mRemovedEndOffset should be the the first end of removed text");
2988
  MOZ_ASSERT(mAddedEndOffset == 35, // 20 + (30 - 15)
2989
    "Test 3-1-3: mAddedEndOffset should be the first end of added text with "
2990
    "added text length by the new change");
2991
  Clear();
2992
2993
  // Replacing text in added text (not sure if actually occurs)
2994
  MergeWith(TextChangeData(50, 50, 55, false, false));
2995
  MergeWith(TextChangeData(52, 53, 56, false, false));
2996
  MOZ_ASSERT(mStartOffset == 50,
2997
    "Test 3-2-1: mStartOffset should be the smallest offset");
2998
  MOZ_ASSERT(mRemovedEndOffset == 50,
2999
    "Test 3-2-2: mRemovedEndOffset should be the the first end of removed text");
3000
  MOZ_ASSERT(mAddedEndOffset == 58, // 55 + (56 - 53)
3001
    "Test 3-2-3: mAddedEndOffset should be the first end of added text with "
3002
    "added text length by the new change");
3003
  Clear();
3004
3005
  // Replacing text in replaced text (became shorter) (not sure if actually
3006
  // occurs)
3007
  MergeWith(TextChangeData(36, 48, 45, false, false));
3008
  MergeWith(TextChangeData(37, 38, 50, false, false));
3009
  MOZ_ASSERT(mStartOffset == 36,
3010
    "Test 3-3-1: mStartOffset should be the smallest offset");
3011
  MOZ_ASSERT(mRemovedEndOffset == 48,
3012
    "Test 3-3-2: mRemovedEndOffset should be the the first end of removed text");
3013
  MOZ_ASSERT(mAddedEndOffset == 57, // 45 + (50 - 38)
3014
    "Test 3-3-3: mAddedEndOffset should be the first end of added text with "
3015
    "added text length by the new change");
3016
  Clear();
3017
3018
  // Replacing text in replaced text (became longer) (not sure if actually
3019
  // occurs)
3020
  MergeWith(TextChangeData(32, 48, 53, false, false));
3021
  MergeWith(TextChangeData(43, 50, 52, false, false));
3022
  MOZ_ASSERT(mStartOffset == 32,
3023
    "Test 3-4-1: mStartOffset should be the smallest offset");
3024
  MOZ_ASSERT(mRemovedEndOffset == 48,
3025
    "Test 3-4-2: mRemovedEndOffset should be the the last end of removed text "
3026
    "without already added text length");
3027
  MOZ_ASSERT(mAddedEndOffset == 55, // 53 + (52 - 50)
3028
    "Test 3-4-3: mAddedEndOffset should be the first end of added text with "
3029
    "added text length by the new change");
3030
  Clear();
3031
3032
  // Replacing text in replaced text (became shorter) (not sure if actually
3033
  // occurs)
3034
  MergeWith(TextChangeData(36, 48, 50, false, false));
3035
  MergeWith(TextChangeData(37, 49, 47, false, false));
3036
  MOZ_ASSERT(mStartOffset == 36,
3037
    "Test 3-5-1: mStartOffset should be the smallest offset");
3038
  MOZ_ASSERT(mRemovedEndOffset == 48,
3039
    "Test 3-5-2: mRemovedEndOffset should be the the first end of removed "
3040
    "text");
3041
  MOZ_ASSERT(mAddedEndOffset == 48, // 50 + (47 - 49)
3042
    "Test 3-5-3: mAddedEndOffset should be the first end of added text without "
3043
    "removed text length by the new change");
3044
  Clear();
3045
3046
  // Replacing text in replaced text (became longer) (not sure if actually
3047
  // occurs)
3048
  MergeWith(TextChangeData(32, 48, 53, false, false));
3049
  MergeWith(TextChangeData(43, 50, 47, false, false));
3050
  MOZ_ASSERT(mStartOffset == 32,
3051
    "Test 3-6-1: mStartOffset should be the smallest offset");
3052
  MOZ_ASSERT(mRemovedEndOffset == 48,
3053
    "Test 3-6-2: mRemovedEndOffset should be the the last end of removed text "
3054
    "without already added text length");
3055
  MOZ_ASSERT(mAddedEndOffset == 50, // 53 + (47 - 50)
3056
    "Test 3-6-3: mAddedEndOffset should be the first end of added text without "
3057
    "removed text length by the new change");
3058
  Clear();
3059
3060
  /****************************************************************************
3061
   * Case 4
3062
   ****************************************************************************/
3063
3064
  // Replacing text all of already append text (not sure if actually occurs)
3065
  MergeWith(TextChangeData(50, 50, 55, false, false));
3066
  MergeWith(TextChangeData(44, 66, 68, false, false));
3067
  MOZ_ASSERT(mStartOffset == 44,
3068
    "Test 4-1-1: mStartOffset should be the smallest offset");
3069
  MOZ_ASSERT(mRemovedEndOffset == 61, // 66 - (55 - 50)
3070
    "Test 4-1-2: mRemovedEndOffset should be the the last end of removed text "
3071
    "without already added text length");
3072
  MOZ_ASSERT(mAddedEndOffset == 68,
3073
    "Test 4-1-3: mAddedEndOffset should be the last end of added text");
3074
  Clear();
3075
3076
  // Replacing text around a point in which text was removed (not sure if
3077
  // actually occurs)
3078
  MergeWith(TextChangeData(50, 62, 50, false, false));
3079
  MergeWith(TextChangeData(44, 66, 68, false, false));
3080
  MOZ_ASSERT(mStartOffset == 44,
3081
    "Test 4-2-1: mStartOffset should be the smallest offset");
3082
  MOZ_ASSERT(mRemovedEndOffset == 78, // 66 - (50 - 62)
3083
    "Test 4-2-2: mRemovedEndOffset should be the the last end of removed text "
3084
    "without already removed text length");
3085
  MOZ_ASSERT(mAddedEndOffset == 68,
3086
    "Test 4-2-3: mAddedEndOffset should be the last end of added text");
3087
  Clear();
3088
3089
  // Replacing text all replaced text (became shorter) (not sure if actually
3090
  // occurs)
3091
  MergeWith(TextChangeData(50, 62, 60, false, false));
3092
  MergeWith(TextChangeData(49, 128, 130, false, false));
3093
  MOZ_ASSERT(mStartOffset == 49,
3094
    "Test 4-3-1: mStartOffset should be the smallest offset");
3095
  MOZ_ASSERT(mRemovedEndOffset == 130, // 128 - (60 - 62)
3096
    "Test 4-3-2: mRemovedEndOffset should be the the last end of removed text "
3097
    "without already removed text length");
3098
  MOZ_ASSERT(mAddedEndOffset == 130,
3099
    "Test 4-3-3: mAddedEndOffset should be the last end of added text");
3100
  Clear();
3101
3102
  // Replacing text all replaced text (became longer) (not sure if actually
3103
  // occurs)
3104
  MergeWith(TextChangeData(50, 61, 73, false, false));
3105
  MergeWith(TextChangeData(44, 100, 50, false, false));
3106
  MOZ_ASSERT(mStartOffset == 44,
3107
    "Test 4-4-1: mStartOffset should be the smallest offset");
3108
  MOZ_ASSERT(mRemovedEndOffset == 88, // 100 - (73 - 61)
3109
    "Test 4-4-2: mRemovedEndOffset should be the the last end of removed text "
3110
    "with already added text length");
3111
  MOZ_ASSERT(mAddedEndOffset == 50,
3112
    "Test 4-4-3: mAddedEndOffset should be the last end of added text");
3113
  Clear();
3114
3115
  /****************************************************************************
3116
   * Case 5
3117
   ****************************************************************************/
3118
3119
  // Replacing text around start of added text (not sure if actually occurs)
3120
  MergeWith(TextChangeData(50, 50, 55, false, false));
3121
  MergeWith(TextChangeData(48, 52, 49, false, false));
3122
  MOZ_ASSERT(mStartOffset == 48,
3123
    "Test 5-1-1: mStartOffset should be the smallest offset");
3124
  MOZ_ASSERT(mRemovedEndOffset == 50,
3125
    "Test 5-1-2: mRemovedEndOffset should be the the first end of removed "
3126
    "text");
3127
  MOZ_ASSERT(mAddedEndOffset == 52, // 55 + (52 - 49)
3128
    "Test 5-1-3: mAddedEndOffset should be the first end of added text with "
3129
    "added text length by the new change");
3130
  Clear();
3131
3132
  // Replacing text around start of replaced text (became shorter) (not sure if
3133
  // actually occurs)
3134
  MergeWith(TextChangeData(50, 60, 58, false, false));
3135
  MergeWith(TextChangeData(43, 50, 48, false, false));
3136
  MOZ_ASSERT(mStartOffset == 43,
3137
    "Test 5-2-1: mStartOffset should be the smallest offset");
3138
  MOZ_ASSERT(mRemovedEndOffset == 60,
3139
    "Test 5-2-2: mRemovedEndOffset should be the the first end of removed "
3140
    "text");
3141
  MOZ_ASSERT(mAddedEndOffset == 56, // 58 + (48 - 50)
3142
    "Test 5-2-3: mAddedEndOffset should be the first end of added text without "
3143
    "removed text length by the new change");
3144
  Clear();
3145
3146
  // Replacing text around start of replaced text (became longer) (not sure if
3147
  // actually occurs)
3148
  MergeWith(TextChangeData(50, 60, 68, false, false));
3149
  MergeWith(TextChangeData(43, 55, 53, false, false));
3150
  MOZ_ASSERT(mStartOffset == 43,
3151
    "Test 5-3-1: mStartOffset should be the smallest offset");
3152
  MOZ_ASSERT(mRemovedEndOffset == 60,
3153
    "Test 5-3-2: mRemovedEndOffset should be the the first end of removed "
3154
    "text");
3155
  MOZ_ASSERT(mAddedEndOffset == 66, // 68 + (53 - 55)
3156
    "Test 5-3-3: mAddedEndOffset should be the first end of added text without "
3157
    "removed text length by the new change");
3158
  Clear();
3159
3160
  // Replacing text around start of replaced text (became shorter) (not sure if
3161
  // actually occurs)
3162
  MergeWith(TextChangeData(50, 60, 58, false, false));
3163
  MergeWith(TextChangeData(43, 50, 128, false, false));
3164
  MOZ_ASSERT(mStartOffset == 43,
3165
    "Test 5-4-1: mStartOffset should be the smallest offset");
3166
  MOZ_ASSERT(mRemovedEndOffset == 60,
3167
    "Test 5-4-2: mRemovedEndOffset should be the the first end of removed "
3168
    "text");
3169
  MOZ_ASSERT(mAddedEndOffset == 136, // 58 + (128 - 50)
3170
    "Test 5-4-3: mAddedEndOffset should be the first end of added text with "
3171
    "added text length by the new change");
3172
  Clear();
3173
3174
  // Replacing text around start of replaced text (became longer) (not sure if
3175
  // actually occurs)
3176
  MergeWith(TextChangeData(50, 60, 68, false, false));
3177
  MergeWith(TextChangeData(43, 55, 65, false, false));
3178
  MOZ_ASSERT(mStartOffset == 43,
3179
    "Test 5-5-1: mStartOffset should be the smallest offset");
3180
  MOZ_ASSERT(mRemovedEndOffset == 60,
3181
    "Test 5-5-2: mRemovedEndOffset should be the the first end of removed "
3182
    "text");
3183
  MOZ_ASSERT(mAddedEndOffset == 78, // 68 + (65 - 55)
3184
    "Test 5-5-3: mAddedEndOffset should be the first end of added text with "
3185
    "added text length by the new change");
3186
  Clear();
3187
3188
  /****************************************************************************
3189
   * Case 6
3190
   ****************************************************************************/
3191
3192
  // Appending text before already added text (not sure if actually occurs)
3193
  MergeWith(TextChangeData(30, 30, 45, false, false));
3194
  MergeWith(TextChangeData(10, 10, 20, false, false));
3195
  MOZ_ASSERT(mStartOffset == 10,
3196
    "Test 6-1-1: mStartOffset should be the smallest offset");
3197
  MOZ_ASSERT(mRemovedEndOffset == 30,
3198
    "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed "
3199
    "text");
3200
  MOZ_ASSERT(mAddedEndOffset == 55, // 45 + (20 - 10)
3201
    "Test 6-1-3: mAddedEndOffset should be the first end of added text with "
3202
    "added text length by the new change");
3203
  Clear();
3204
3205
  // Removing text before already removed text (not sure if actually occurs)
3206
  MergeWith(TextChangeData(30, 35, 30, false, false));
3207
  MergeWith(TextChangeData(10, 25, 10, false, false));
3208
  MOZ_ASSERT(mStartOffset == 10,
3209
    "Test 6-2-1: mStartOffset should be the smallest offset");
3210
  MOZ_ASSERT(mRemovedEndOffset == 35,
3211
    "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed "
3212
    "text");
3213
  MOZ_ASSERT(mAddedEndOffset == 15, // 30 - (25 - 10)
3214
    "Test 6-2-3: mAddedEndOffset should be the first end of added text with "
3215
    "removed text length by the new change");
3216
  Clear();
3217
3218
  // Replacing text before already replaced text (not sure if actually occurs)
3219
  MergeWith(TextChangeData(50, 65, 70, false, false));
3220
  MergeWith(TextChangeData(13, 24, 15, false, false));
3221
  MOZ_ASSERT(mStartOffset == 13,
3222
    "Test 6-3-1: mStartOffset should be the smallest offset");
3223
  MOZ_ASSERT(mRemovedEndOffset == 65,
3224
    "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed "
3225
    "text");
3226
  MOZ_ASSERT(mAddedEndOffset == 61, // 70 + (15 - 24)
3227
    "Test 6-3-3: mAddedEndOffset should be the first end of added text without "
3228
    "removed text length by the new change");
3229
  Clear();
3230
3231
  // Replacing text before already replaced text (not sure if actually occurs)
3232
  MergeWith(TextChangeData(50, 65, 70, false, false));
3233
  MergeWith(TextChangeData(13, 24, 36, false, false));
3234
  MOZ_ASSERT(mStartOffset == 13,
3235
    "Test 6-4-1: mStartOffset should be the smallest offset");
3236
  MOZ_ASSERT(mRemovedEndOffset == 65,
3237
    "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed "
3238
    "text");
3239
  MOZ_ASSERT(mAddedEndOffset == 82, // 70 + (36 - 24)
3240
    "Test 6-4-3: mAddedEndOffset should be the first end of added text without "
3241
    "removed text length by the new change");
3242
  Clear();
3243
}
3244
3245
#endif // #ifdef DEBUG
3246
3247
} // namespace widget
3248
} // namespace mozilla
3249
3250
#ifdef DEBUG
3251
//////////////////////////////////////////////////////////////
3252
//
3253
// Convert a GUI event message code to a string.
3254
// Makes it a lot easier to debug events.
3255
//
3256
// See gtk/nsWidget.cpp and windows/nsWindow.cpp
3257
// for a DebugPrintEvent() function that uses
3258
// this.
3259
//
3260
//////////////////////////////////////////////////////////////
3261
/* static */ nsAutoString
3262
nsBaseWidget::debug_GuiEventToString(WidgetGUIEvent* aGuiEvent)
3263
{
3264
  NS_ASSERTION(nullptr != aGuiEvent,"cmon, null gui event.");
3265
3266
  nsAutoString eventName(NS_LITERAL_STRING("UNKNOWN"));
3267
3268
#define _ASSIGN_eventName(_value,_name)\
3269
case _value: eventName.AssignLiteral(_name) ; break
3270
3271
  switch(aGuiEvent->mMessage)
3272
  {
3273
    _ASSIGN_eventName(eBlur,"eBlur");
3274
    _ASSIGN_eventName(eDrop,"eDrop");
3275
    _ASSIGN_eventName(eDragEnter,"eDragEnter");
3276
    _ASSIGN_eventName(eDragExit,"eDragExit");
3277
    _ASSIGN_eventName(eDragOver,"eDragOver");
3278
    _ASSIGN_eventName(eEditorInput,"eEditorInput");
3279
    _ASSIGN_eventName(eFocus,"eFocus");
3280
    _ASSIGN_eventName(eFocusIn,"eFocusIn");
3281
    _ASSIGN_eventName(eFocusOut,"eFocusOut");
3282
    _ASSIGN_eventName(eFormSelect,"eFormSelect");
3283
    _ASSIGN_eventName(eFormChange,"eFormChange");
3284
    _ASSIGN_eventName(eFormReset,"eFormReset");
3285
    _ASSIGN_eventName(eFormSubmit,"eFormSubmit");
3286
    _ASSIGN_eventName(eImageAbort,"eImageAbort");
3287
    _ASSIGN_eventName(eLoadError,"eLoadError");
3288
    _ASSIGN_eventName(eKeyDown,"eKeyDown");
3289
    _ASSIGN_eventName(eKeyPress,"eKeyPress");
3290
    _ASSIGN_eventName(eKeyUp,"eKeyUp");
3291
    _ASSIGN_eventName(eMouseEnterIntoWidget,"eMouseEnterIntoWidget");
3292
    _ASSIGN_eventName(eMouseExitFromWidget,"eMouseExitFromWidget");
3293
    _ASSIGN_eventName(eMouseDown,"eMouseDown");
3294
    _ASSIGN_eventName(eMouseUp,"eMouseUp");
3295
    _ASSIGN_eventName(eMouseClick,"eMouseClick");
3296
    _ASSIGN_eventName(eMouseAuxClick,"eMouseAuxClick");
3297
    _ASSIGN_eventName(eMouseDoubleClick,"eMouseDoubleClick");
3298
    _ASSIGN_eventName(eMouseMove,"eMouseMove");
3299
    _ASSIGN_eventName(eLoad,"eLoad");
3300
    _ASSIGN_eventName(ePopState,"ePopState");
3301
    _ASSIGN_eventName(eBeforeScriptExecute,"eBeforeScriptExecute");
3302
    _ASSIGN_eventName(eAfterScriptExecute,"eAfterScriptExecute");
3303
    _ASSIGN_eventName(eUnload,"eUnload");
3304
    _ASSIGN_eventName(eHashChange,"eHashChange");
3305
    _ASSIGN_eventName(eReadyStateChange,"eReadyStateChange");
3306
    _ASSIGN_eventName(eXULBroadcast, "eXULBroadcast");
3307
    _ASSIGN_eventName(eXULCommandUpdate, "eXULCommandUpdate");
3308
3309
#undef _ASSIGN_eventName
3310
3311
  default:
3312
    {
3313
    eventName.AssignLiteral("UNKNOWN: ");
3314
    eventName.AppendInt(aGuiEvent->mMessage);
3315
    }
3316
    break;
3317
  }
3318
3319
  return nsAutoString(eventName);
3320
}
3321
//////////////////////////////////////////////////////////////
3322
//
3323
// Code to deal with paint and event debug prefs.
3324
//
3325
//////////////////////////////////////////////////////////////
3326
struct PrefPair
3327
{
3328
  const char * name;
3329
  bool value;
3330
};
3331
3332
static PrefPair debug_PrefValues[] =
3333
{
3334
  { "nglayout.debug.crossing_event_dumping", false },
3335
  { "nglayout.debug.event_dumping", false },
3336
  { "nglayout.debug.invalidate_dumping", false },
3337
  { "nglayout.debug.motion_event_dumping", false },
3338
  { "nglayout.debug.paint_dumping", false },
3339
  { "nglayout.debug.paint_flashing", false }
3340
};
3341
3342
//////////////////////////////////////////////////////////////
3343
bool
3344
nsBaseWidget::debug_GetCachedBoolPref(const char * aPrefName)
3345
{
3346
  NS_ASSERTION(nullptr != aPrefName,"cmon, pref name is null.");
3347
3348
  for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++)
3349
  {
3350
    if (strcmp(debug_PrefValues[i].name, aPrefName) == 0)
3351
    {
3352
      return debug_PrefValues[i].value;
3353
    }
3354
  }
3355
3356
  return false;
3357
}
3358
//////////////////////////////////////////////////////////////
3359
static void debug_SetCachedBoolPref(const char * aPrefName,bool aValue)
3360
{
3361
  NS_ASSERTION(nullptr != aPrefName,"cmon, pref name is null.");
3362
3363
  for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++)
3364
  {
3365
    if (strcmp(debug_PrefValues[i].name, aPrefName) == 0)
3366
    {
3367
      debug_PrefValues[i].value = aValue;
3368
3369
      return;
3370
    }
3371
  }
3372
3373
  NS_ASSERTION(false, "cmon, this code is not reached dude.");
3374
}
3375
3376
//////////////////////////////////////////////////////////////
3377
class Debug_PrefObserver final : public nsIObserver {
3378
    ~Debug_PrefObserver() {}
3379
3380
  public:
3381
    NS_DECL_ISUPPORTS
3382
    NS_DECL_NSIOBSERVER
3383
};
3384
3385
NS_IMPL_ISUPPORTS(Debug_PrefObserver, nsIObserver)
3386
3387
NS_IMETHODIMP
3388
Debug_PrefObserver::Observe(nsISupports* subject, const char* topic,
3389
                            const char16_t* data)
3390
{
3391
  NS_ConvertUTF16toUTF8 prefName(data);
3392
3393
  bool value = Preferences::GetBool(prefName.get(), false);
3394
  debug_SetCachedBoolPref(prefName.get(), value);
3395
  return NS_OK;
3396
}
3397
3398
//////////////////////////////////////////////////////////////
3399
/* static */ void
3400
debug_RegisterPrefCallbacks()
3401
{
3402
  static bool once = true;
3403
3404
  if (!once) {
3405
    return;
3406
  }
3407
3408
  once = false;
3409
3410
  nsCOMPtr<nsIObserver> obs(new Debug_PrefObserver());
3411
  for (uint32_t i = 0; i < ArrayLength(debug_PrefValues); i++) {
3412
    // Initialize the pref values
3413
    debug_PrefValues[i].value =
3414
      Preferences::GetBool(debug_PrefValues[i].name, false);
3415
3416
    if (obs) {
3417
      // Register callbacks for when these change
3418
      nsCString name;
3419
      name.AssignLiteral(debug_PrefValues[i].name,
3420
                         strlen(debug_PrefValues[i].name));
3421
      Preferences::AddStrongObserver(obs, name);
3422
    }
3423
  }
3424
}
3425
//////////////////////////////////////////////////////////////
3426
static int32_t
3427
_GetPrintCount()
3428
{
3429
  static int32_t sCount = 0;
3430
3431
  return ++sCount;
3432
}
3433
//////////////////////////////////////////////////////////////
3434
/* static */ bool
3435
nsBaseWidget::debug_WantPaintFlashing()
3436
{
3437
  return debug_GetCachedBoolPref("nglayout.debug.paint_flashing");
3438
}
3439
//////////////////////////////////////////////////////////////
3440
/* static */ void
3441
nsBaseWidget::debug_DumpEvent(FILE *                aFileOut,
3442
                              nsIWidget *           aWidget,
3443
                              WidgetGUIEvent*       aGuiEvent,
3444
                              const char*           aWidgetName,
3445
                              int32_t               aWindowID)
3446
{
3447
  if (aGuiEvent->mMessage == eMouseMove) {
3448
    if (!debug_GetCachedBoolPref("nglayout.debug.motion_event_dumping"))
3449
      return;
3450
  }
3451
3452
  if (aGuiEvent->mMessage == eMouseEnterIntoWidget ||
3453
      aGuiEvent->mMessage == eMouseExitFromWidget) {
3454
    if (!debug_GetCachedBoolPref("nglayout.debug.crossing_event_dumping"))
3455
      return;
3456
  }
3457
3458
  if (!debug_GetCachedBoolPref("nglayout.debug.event_dumping"))
3459
    return;
3460
3461
  NS_LossyConvertUTF16toASCII tempString(debug_GuiEventToString(aGuiEvent).get());
3462
3463
  fprintf(aFileOut,
3464
          "%4d %-26s widget=%-8p name=%-12s id=0x%-6x refpt=%d,%d\n",
3465
          _GetPrintCount(),
3466
          tempString.get(),
3467
          (void *) aWidget,
3468
          aWidgetName,
3469
          aWindowID,
3470
          aGuiEvent->mRefPoint.x,
3471
          aGuiEvent->mRefPoint.y);
3472
}
3473
//////////////////////////////////////////////////////////////
3474
/* static */ void
3475
nsBaseWidget::debug_DumpPaintEvent(FILE *                aFileOut,
3476
                                   nsIWidget *           aWidget,
3477
                                   const nsIntRegion &   aRegion,
3478
                                   const char *          aWidgetName,
3479
                                   int32_t               aWindowID)
3480
{
3481
  NS_ASSERTION(nullptr != aFileOut,"cmon, null output FILE");
3482
  NS_ASSERTION(nullptr != aWidget,"cmon, the widget is null");
3483
3484
  if (!debug_GetCachedBoolPref("nglayout.debug.paint_dumping"))
3485
    return;
3486
3487
  nsIntRect rect = aRegion.GetBounds();
3488
  fprintf(aFileOut,
3489
          "%4d PAINT      widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d %3d,%-3d",
3490
          _GetPrintCount(),
3491
          (void *) aWidget,
3492
          aWidgetName,
3493
          aWindowID,
3494
          rect.X(), rect.Y(), rect.Width(), rect.Height()
3495
    );
3496
3497
  fprintf(aFileOut,"\n");
3498
}
3499
//////////////////////////////////////////////////////////////
3500
/* static */ void
3501
nsBaseWidget::debug_DumpInvalidate(FILE* aFileOut,
3502
                                   nsIWidget* aWidget,
3503
                                   const LayoutDeviceIntRect* aRect,
3504
                                   const char* aWidgetName,
3505
                                   int32_t aWindowID)
3506
{
3507
  if (!debug_GetCachedBoolPref("nglayout.debug.invalidate_dumping"))
3508
    return;
3509
3510
  NS_ASSERTION(nullptr != aFileOut,"cmon, null output FILE");
3511
  NS_ASSERTION(nullptr != aWidget,"cmon, the widget is null");
3512
3513
  fprintf(aFileOut,
3514
          "%4d Invalidate widget=%p name=%-12s id=0x%-6x",
3515
          _GetPrintCount(),
3516
          (void *) aWidget,
3517
          aWidgetName,
3518
          aWindowID);
3519
3520
  if (aRect) {
3521
    fprintf(aFileOut,
3522
            " rect=%3d,%-3d %3d,%-3d",
3523
            aRect->X(), aRect->Y(), aRect->Width(), aRect->Height());
3524
  } else {
3525
    fprintf(aFileOut,
3526
            " rect=%-15s",
3527
            "none");
3528
  }
3529
3530
  fprintf(aFileOut, "\n");
3531
}
3532
//////////////////////////////////////////////////////////////
3533
3534
#endif // DEBUG