/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(¤tRects); |
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 |