Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / widget / PuppetWidget.cpp
blob98510b93e0b3f7e819d46f6a38d109f7198d4acb
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "base/basictypes.h"
10 #include "gfxPlatform.h"
11 #include "nsRefreshDriver.h"
12 #include "mozilla/dom/BrowserChild.h"
13 #include "mozilla/gfx/gfxVars.h"
14 #include "mozilla/IMEStateManager.h"
15 #include "mozilla/layers/APZChild.h"
16 #include "mozilla/layers/WebRenderLayerManager.h"
17 #include "mozilla/NativeKeyBindingsType.h"
18 #include "mozilla/Preferences.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/SchedulerGroup.h"
21 #include "mozilla/StaticPrefs_browser.h"
22 #include "mozilla/StaticPrefs_gfx.h"
23 #include "mozilla/TextComposition.h"
24 #include "mozilla/TextEventDispatcher.h"
25 #include "mozilla/TextEvents.h"
26 #include "mozilla/Unused.h"
27 #include "PuppetWidget.h"
28 #include "nsContentUtils.h"
29 #include "nsIWidgetListener.h"
30 #include "imgIContainer.h"
31 #include "nsView.h"
32 #include "nsXPLookAndFeel.h"
33 #include "nsPrintfCString.h"
35 using namespace mozilla;
36 using namespace mozilla::dom;
37 using namespace mozilla::gfx;
38 using namespace mozilla::layers;
39 using namespace mozilla::widget;
41 static void InvalidateRegion(nsIWidget* aWidget,
42 const LayoutDeviceIntRegion& aRegion) {
43 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
44 aWidget->Invalidate(iter.Get());
48 /*static*/
49 already_AddRefed<nsIWidget> nsIWidget::CreatePuppetWidget(
50 BrowserChild* aBrowserChild) {
51 MOZ_ASSERT(!aBrowserChild || nsIWidget::UsePuppetWidgets(),
52 "PuppetWidgets not allowed in this configuration");
54 nsCOMPtr<nsIWidget> widget = new PuppetWidget(aBrowserChild);
55 return widget.forget();
58 namespace mozilla {
59 namespace widget {
61 static bool IsPopup(const nsWidgetInitData* aInitData) {
62 return aInitData && aInitData->mWindowType == eWindowType_popup;
65 static bool MightNeedIMEFocus(const nsWidgetInitData* aInitData) {
66 // In the puppet-widget world, popup widgets are just dummies and
67 // shouldn't try to mess with IME state.
68 #ifdef MOZ_CROSS_PROCESS_IME
69 return !IsPopup(aInitData);
70 #else
71 return false;
72 #endif
75 // Arbitrary, fungible.
76 const size_t PuppetWidget::kMaxDimension = 4000;
78 NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget, nsBaseWidget,
79 TextEventDispatcherListener)
81 PuppetWidget::PuppetWidget(BrowserChild* aBrowserChild)
82 : mBrowserChild(aBrowserChild),
83 mMemoryPressureObserver(nullptr),
84 mDPI(-1),
85 mRounding(1),
86 mDefaultScale(-1),
87 mEnabled(false),
88 mVisible(false),
89 mSizeMode(nsSizeMode_Normal),
90 mNeedIMEStateInit(false),
91 mIgnoreCompositionEvents(false) {
92 // Setting 'Unknown' means "not yet cached".
93 mInputContext.mIMEState.mEnabled = IMEEnabled::Unknown;
96 PuppetWidget::~PuppetWidget() { Destroy(); }
98 void PuppetWidget::InfallibleCreate(nsIWidget* aParent,
99 nsNativeWidget aNativeParent,
100 const LayoutDeviceIntRect& aRect,
101 nsWidgetInitData* aInitData) {
102 MOZ_ASSERT(!aNativeParent, "got a non-Puppet native parent");
104 BaseCreate(nullptr, aInitData);
106 mBounds = aRect;
107 mEnabled = true;
108 mVisible = true;
110 mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
111 IntSize(1, 1), SurfaceFormat::B8G8R8A8);
113 mNeedIMEStateInit = MightNeedIMEFocus(aInitData);
115 PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
116 if (parent) {
117 parent->SetChild(this);
118 mWindowRenderer = parent->GetWindowRenderer();
119 } else {
120 Resize(mBounds.X(), mBounds.Y(), mBounds.Width(), mBounds.Height(), false);
122 mMemoryPressureObserver = MemoryPressureObserver::Create(this);
125 nsresult PuppetWidget::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
126 const LayoutDeviceIntRect& aRect,
127 nsWidgetInitData* aInitData) {
128 InfallibleCreate(aParent, aNativeParent, aRect, aInitData);
129 return NS_OK;
132 void PuppetWidget::InitIMEState() {
133 MOZ_ASSERT(mBrowserChild);
134 if (mNeedIMEStateInit) {
135 mContentCache.Clear();
136 mBrowserChild->SendUpdateContentCache(mContentCache);
137 mIMENotificationRequestsOfParent = IMENotificationRequests();
138 mNeedIMEStateInit = false;
142 already_AddRefed<nsIWidget> PuppetWidget::CreateChild(
143 const LayoutDeviceIntRect& aRect, nsWidgetInitData* aInitData,
144 bool aForceUseIWidgetParent) {
145 bool isPopup = IsPopup(aInitData);
146 nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(mBrowserChild);
147 return ((widget && NS_SUCCEEDED(widget->Create(isPopup ? nullptr : this,
148 nullptr, aRect, aInitData)))
149 ? widget.forget()
150 : nullptr);
153 void PuppetWidget::Destroy() {
154 if (mOnDestroyCalled) {
155 return;
157 mOnDestroyCalled = true;
159 Base::OnDestroy();
160 Base::Destroy();
161 if (mMemoryPressureObserver) {
162 mMemoryPressureObserver->Unregister();
163 mMemoryPressureObserver = nullptr;
165 mChild = nullptr;
166 if (mWindowRenderer) {
167 mWindowRenderer->Destroy();
169 mWindowRenderer = nullptr;
170 mBrowserChild = nullptr;
173 void PuppetWidget::Show(bool aState) {
174 NS_ASSERTION(mEnabled,
175 "does it make sense to Show()/Hide() a disabled widget?");
177 bool wasVisible = mVisible;
178 mVisible = aState;
180 if (mChild) {
181 mChild->mVisible = aState;
184 if (!wasVisible && mVisible) {
185 // The previously attached widget listener is handy if
186 // we're transitioning from page to page without dropping
187 // layers (since we'll continue to show the old layers
188 // associated with that old widget listener). If the
189 // PuppetWidget was hidden, those layers are dropped,
190 // so the previously attached widget listener is really
191 // of no use anymore (and is actually actively harmful - see
192 // bug 1323586).
193 mPreviouslyAttachedWidgetListener = nullptr;
194 Resize(mBounds.Width(), mBounds.Height(), false);
195 Invalidate(mBounds);
199 void PuppetWidget::Resize(double aWidth, double aHeight, bool aRepaint) {
200 LayoutDeviceIntRect oldBounds = mBounds;
201 mBounds.SizeTo(
202 LayoutDeviceIntSize(NSToIntRound(aWidth), NSToIntRound(aHeight)));
204 if (mChild) {
205 mChild->Resize(aWidth, aHeight, aRepaint);
206 return;
209 // XXX: roc says that |aRepaint| dictates whether or not to
210 // invalidate the expanded area
211 if (oldBounds.Size() < mBounds.Size() && aRepaint) {
212 LayoutDeviceIntRegion dirty(mBounds);
213 dirty.Sub(dirty, oldBounds);
214 InvalidateRegion(this, dirty);
217 // call WindowResized() on both the current listener, and possibly
218 // also the previous one if we're in a state where we're drawing that one
219 // because the current one is paint suppressed
220 if (!oldBounds.IsEqualEdges(mBounds) && mAttachedWidgetListener) {
221 if (GetCurrentWidgetListener() &&
222 GetCurrentWidgetListener() != mAttachedWidgetListener) {
223 GetCurrentWidgetListener()->WindowResized(this, mBounds.Width(),
224 mBounds.Height());
226 mAttachedWidgetListener->WindowResized(this, mBounds.Width(),
227 mBounds.Height());
231 void PuppetWidget::SetFocus(Raise aRaise, CallerType aCallerType) {
232 if (aRaise == Raise::Yes && mBrowserChild) {
233 mBrowserChild->SendRequestFocus(true, aCallerType);
237 void PuppetWidget::Invalidate(const LayoutDeviceIntRect& aRect) {
238 #ifdef DEBUG
239 debug_DumpInvalidate(stderr, this, &aRect, "PuppetWidget", 0);
240 #endif
242 if (mChild) {
243 mChild->Invalidate(aRect);
244 return;
247 if (mBrowserChild && !aRect.IsEmpty() && !mWidgetPaintTask.IsPending()) {
248 mWidgetPaintTask = new WidgetPaintTask(this);
249 nsCOMPtr<nsIRunnable> event(mWidgetPaintTask.get());
250 SchedulerGroup::Dispatch(TaskCategory::Other, event.forget());
254 mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
255 PuppetWidget::WidgetToTopLevelWidgetTransform() {
256 if (!GetOwningBrowserChild()) {
257 NS_WARNING("PuppetWidget without Tab does not have transform information.");
258 return mozilla::LayoutDeviceToLayoutDeviceMatrix4x4();
260 return GetOwningBrowserChild()->GetChildToParentConversionMatrix();
263 void PuppetWidget::InitEvent(WidgetGUIEvent& aEvent,
264 LayoutDeviceIntPoint* aPoint) {
265 if (nullptr == aPoint) {
266 aEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
267 } else {
268 // use the point override if provided
269 aEvent.mRefPoint = *aPoint;
271 aEvent.mTime = PR_Now() / 1000;
274 nsresult PuppetWidget::DispatchEvent(WidgetGUIEvent* aEvent,
275 nsEventStatus& aStatus) {
276 #ifdef DEBUG
277 debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "PuppetWidget", 0);
278 #endif
280 MOZ_ASSERT(!mChild || mChild->mWindowType == eWindowType_popup,
281 "Unexpected event dispatch!");
283 MOZ_ASSERT(!aEvent->AsKeyboardEvent() ||
284 aEvent->mFlags.mIsSynthesizedForTests ||
285 aEvent->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
286 "Non-sysnthesized keyboard events should have edit commands for "
287 "all types "
288 "before dispatched");
290 if (aEvent->mClass == eCompositionEventClass) {
291 // If we've already requested to commit/cancel the latest composition,
292 // TextComposition for the old composition has been destroyed. Then,
293 // the DOM tree needs to listen to next eCompositionStart and its
294 // following events. So, until we meet new eCompositionStart, let's
295 // discard all unnecessary composition events here.
296 if (mIgnoreCompositionEvents) {
297 if (aEvent->mMessage != eCompositionStart) {
298 aStatus = nsEventStatus_eIgnore;
299 return NS_OK;
301 // Now, we receive new eCompositionStart. Let's restart to handle
302 // composition in this process.
303 mIgnoreCompositionEvents = false;
305 // Store the latest native IME context of parent process's widget or
306 // TextEventDispatcher if it's in this process.
307 WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
308 #ifdef DEBUG
309 if (mNativeIMEContext.IsValid() &&
310 mNativeIMEContext != compositionEvent->mNativeIMEContext) {
311 RefPtr<TextComposition> composition =
312 IMEStateManager::GetTextCompositionFor(this);
313 MOZ_ASSERT(
314 !composition,
315 "When there is composition caused by old native IME context, "
316 "composition events caused by different native IME context are not "
317 "allowed");
319 #endif // #ifdef DEBUG
320 mNativeIMEContext = compositionEvent->mNativeIMEContext;
321 mContentCache.OnCompositionEvent(*compositionEvent);
324 // If the event is a composition event or a keyboard event, it should be
325 // dispatched with TextEventDispatcher if we could do that with current
326 // design. However, we cannot do that without big changes and the behavior
327 // is not so complicated for now. Therefore, we should just notify it
328 // of dispatching events and TextEventDispatcher should emulate the state
329 // with events here.
330 if (aEvent->mClass == eCompositionEventClass ||
331 aEvent->mClass == eKeyboardEventClass) {
332 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
333 // However, if the event is being dispatched by the text event dispatcher
334 // or, there is native text event dispatcher listener, that means that
335 // native text input event handler is in this process like on Android,
336 // and the event is not synthesized for tests, the event is coming from
337 // the TextEventDispatcher. In these cases, we shouldn't notify
338 // TextEventDispatcher of dispatching the event.
339 if (!dispatcher->IsDispatchingEvent() &&
340 !(mNativeTextEventDispatcherListener &&
341 !aEvent->mFlags.mIsSynthesizedForTests)) {
342 DebugOnly<nsresult> rv =
343 dispatcher->BeginInputTransactionFor(aEvent, this);
344 NS_WARNING_ASSERTION(
345 NS_SUCCEEDED(rv),
346 "The text event dispatcher should always succeed to start input "
347 "transaction for the event");
351 aStatus = nsEventStatus_eIgnore;
353 if (GetCurrentWidgetListener()) {
354 aStatus =
355 GetCurrentWidgetListener()->HandleEvent(aEvent, mUseAttachedEvents);
358 return NS_OK;
361 nsIWidget::ContentAndAPZEventStatus PuppetWidget::DispatchInputEvent(
362 WidgetInputEvent* aEvent) {
363 ContentAndAPZEventStatus status;
364 if (!AsyncPanZoomEnabled()) {
365 DispatchEvent(aEvent, status.mContentStatus);
366 return status;
369 if (!mBrowserChild) {
370 return status;
373 switch (aEvent->mClass) {
374 case eWheelEventClass:
375 Unused << mBrowserChild->SendDispatchWheelEvent(*aEvent->AsWheelEvent());
376 break;
377 case eMouseEventClass:
378 Unused << mBrowserChild->SendDispatchMouseEvent(*aEvent->AsMouseEvent());
379 break;
380 case eKeyboardEventClass:
381 Unused << mBrowserChild->SendDispatchKeyboardEvent(
382 *aEvent->AsKeyboardEvent());
383 break;
384 case eTouchEventClass:
385 Unused << mBrowserChild->SendDispatchTouchEvent(*aEvent->AsTouchEvent());
386 break;
387 default:
388 MOZ_ASSERT_UNREACHABLE("unsupported event type");
391 return status;
394 nsresult PuppetWidget::SynthesizeNativeKeyEvent(
395 int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
396 uint32_t aModifierFlags, const nsAString& aCharacters,
397 const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
398 AutoObserverNotifier notifier(aObserver, "keyevent");
399 if (!mBrowserChild) {
400 return NS_ERROR_FAILURE;
402 mBrowserChild->SendSynthesizeNativeKeyEvent(
403 aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags,
404 nsString(aCharacters), nsString(aUnmodifiedCharacters),
405 notifier.SaveObserver());
406 return NS_OK;
409 nsresult PuppetWidget::SynthesizeNativeMouseEvent(
410 mozilla::LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
411 MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
412 nsIObserver* aObserver) {
413 AutoObserverNotifier notifier(aObserver, "mouseevent");
414 if (!mBrowserChild) {
415 return NS_ERROR_FAILURE;
417 mBrowserChild->SendSynthesizeNativeMouseEvent(
418 aPoint, static_cast<uint32_t>(aNativeMessage),
419 static_cast<int16_t>(aButton), static_cast<uint32_t>(aModifierFlags),
420 notifier.SaveObserver());
421 return NS_OK;
424 nsresult PuppetWidget::SynthesizeNativeMouseMove(
425 mozilla::LayoutDeviceIntPoint aPoint, nsIObserver* aObserver) {
426 AutoObserverNotifier notifier(aObserver, "mousemove");
427 if (!mBrowserChild) {
428 return NS_ERROR_FAILURE;
430 mBrowserChild->SendSynthesizeNativeMouseMove(aPoint, notifier.SaveObserver());
431 return NS_OK;
434 nsresult PuppetWidget::SynthesizeNativeMouseScrollEvent(
435 mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
436 double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
437 uint32_t aAdditionalFlags, nsIObserver* aObserver) {
438 AutoObserverNotifier notifier(aObserver, "mousescrollevent");
439 if (!mBrowserChild) {
440 return NS_ERROR_FAILURE;
442 mBrowserChild->SendSynthesizeNativeMouseScrollEvent(
443 aPoint, aNativeMessage, aDeltaX, aDeltaY, aDeltaZ, aModifierFlags,
444 aAdditionalFlags, notifier.SaveObserver());
445 return NS_OK;
448 nsresult PuppetWidget::SynthesizeNativeTouchPoint(
449 uint32_t aPointerId, TouchPointerState aPointerState,
450 LayoutDeviceIntPoint aPoint, double aPointerPressure,
451 uint32_t aPointerOrientation, nsIObserver* aObserver) {
452 AutoObserverNotifier notifier(aObserver, "touchpoint");
453 if (!mBrowserChild) {
454 return NS_ERROR_FAILURE;
456 mBrowserChild->SendSynthesizeNativeTouchPoint(
457 aPointerId, aPointerState, aPoint, aPointerPressure, aPointerOrientation,
458 notifier.SaveObserver());
459 return NS_OK;
462 nsresult PuppetWidget::SynthesizeNativeTouchPadPinch(
463 TouchpadGesturePhase aEventPhase, float aScale, LayoutDeviceIntPoint aPoint,
464 int32_t aModifierFlags) {
465 if (!mBrowserChild) {
466 return NS_ERROR_FAILURE;
468 mBrowserChild->SendSynthesizeNativeTouchPadPinch(aEventPhase, aScale, aPoint,
469 aModifierFlags);
470 return NS_OK;
473 nsresult PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
474 bool aLongTap,
475 nsIObserver* aObserver) {
476 AutoObserverNotifier notifier(aObserver, "touchtap");
477 if (!mBrowserChild) {
478 return NS_ERROR_FAILURE;
480 mBrowserChild->SendSynthesizeNativeTouchTap(aPoint, aLongTap,
481 notifier.SaveObserver());
482 return NS_OK;
485 nsresult PuppetWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
486 AutoObserverNotifier notifier(aObserver, "cleartouch");
487 if (!mBrowserChild) {
488 return NS_ERROR_FAILURE;
490 mBrowserChild->SendClearNativeTouchSequence(notifier.SaveObserver());
491 return NS_OK;
494 nsresult PuppetWidget::SynthesizeNativePenInput(
495 uint32_t aPointerId, TouchPointerState aPointerState,
496 LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
497 int32_t aTiltX, int32_t aTiltY, int32_t aButton, nsIObserver* aObserver) {
498 AutoObserverNotifier notifier(aObserver, "peninput");
499 if (!mBrowserChild) {
500 return NS_ERROR_FAILURE;
502 mBrowserChild->SendSynthesizeNativePenInput(
503 aPointerId, aPointerState, aPoint, aPressure, aRotation, aTiltX, aTiltY,
504 aButton, notifier.SaveObserver());
505 return NS_OK;
508 nsresult PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
509 LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
510 if (!mBrowserChild) {
511 return NS_ERROR_FAILURE;
513 mBrowserChild->SendSynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags);
514 return NS_OK;
517 nsresult PuppetWidget::SynthesizeNativeTouchpadPan(
518 TouchpadGesturePhase aEventPhase, LayoutDeviceIntPoint aPoint,
519 double aDeltaX, double aDeltaY, int32_t aModifierFlags,
520 nsIObserver* aObserver) {
521 AutoObserverNotifier notifier(aObserver, "touchpadpanevent");
522 if (!mBrowserChild) {
523 return NS_ERROR_FAILURE;
525 mBrowserChild->SendSynthesizeNativeTouchpadPan(aEventPhase, aPoint, aDeltaX,
526 aDeltaY, aModifierFlags,
527 notifier.SaveObserver());
528 return NS_OK;
531 void PuppetWidget::LockNativePointer() {
532 if (!mBrowserChild) {
533 return;
535 mBrowserChild->SendLockNativePointer();
538 void PuppetWidget::UnlockNativePointer() {
539 if (!mBrowserChild) {
540 return;
542 mBrowserChild->SendUnlockNativePointer();
545 void PuppetWidget::SetConfirmedTargetAPZC(
546 uint64_t aInputBlockId,
547 const nsTArray<ScrollableLayerGuid>& aTargets) const {
548 if (mBrowserChild) {
549 mBrowserChild->SetTargetAPZC(aInputBlockId, aTargets);
553 void PuppetWidget::UpdateZoomConstraints(
554 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
555 const Maybe<ZoomConstraints>& aConstraints) {
556 if (mBrowserChild) {
557 mBrowserChild->DoUpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
561 bool PuppetWidget::AsyncPanZoomEnabled() const {
562 return mBrowserChild && mBrowserChild->AsyncPanZoomEnabled();
565 bool PuppetWidget::GetEditCommands(NativeKeyBindingsType aType,
566 const WidgetKeyboardEvent& aEvent,
567 nsTArray<CommandInt>& aCommands) {
568 MOZ_ASSERT(!aEvent.mFlags.mIsSynthesizedForTests);
569 // Validate the arguments.
570 if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType, aEvent, aCommands))) {
571 return false;
573 if (NS_WARN_IF(!mBrowserChild)) {
574 return false;
576 mBrowserChild->RequestEditCommands(aType, aEvent, aCommands);
577 return true;
580 WindowRenderer* PuppetWidget::GetWindowRenderer() {
581 if (!mWindowRenderer) {
582 if (XRE_IsParentProcess()) {
583 // On the parent process there is no CompositorBridgeChild which confuses
584 // some layers code, so we use basic layers instead. Note that we create
585 mWindowRenderer = new FallbackRenderer;
586 return mWindowRenderer;
589 // If we know for sure that the parent side of this BrowserChild is not
590 // connected to the compositor, we don't want to use a "remote" layer
591 // manager like WebRender or Client. Instead we use a Basic one which
592 // can do drawing in this process.
593 MOZ_ASSERT(!mBrowserChild ||
594 mBrowserChild->IsLayersConnected() != Some(true));
595 mWindowRenderer = CreateFallbackRenderer();
598 return mWindowRenderer;
601 bool PuppetWidget::CreateRemoteLayerManager(
602 const std::function<bool(WebRenderLayerManager*)>& aInitializeFunc) {
603 RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this);
604 MOZ_ASSERT(mBrowserChild);
606 if (!aInitializeFunc(lm)) {
607 return false;
610 // Force the old LM to self destruct, otherwise if the reference dangles we
611 // could fail to revoke the most recent transaction. We only want to replace
612 // it if we successfully create its successor because a partially initialized
613 // layer manager is worse than a fully initialized but shutdown layer manager.
614 DestroyLayerManager();
615 mWindowRenderer = std::move(lm);
616 return true;
619 nsresult PuppetWidget::RequestIMEToCommitComposition(bool aCancel) {
620 if (!mBrowserChild) {
621 return NS_ERROR_FAILURE;
624 MOZ_ASSERT(!Destroyed());
626 // There must not be composition which is caused by the PuppetWidget instance.
627 if (NS_WARN_IF(!mNativeIMEContext.IsValid())) {
628 return NS_OK;
631 // We've already requested to commit/cancel composition.
632 if (NS_WARN_IF(mIgnoreCompositionEvents)) {
633 #ifdef DEBUG
634 RefPtr<TextComposition> composition =
635 IMEStateManager::GetTextCompositionFor(this);
636 MOZ_ASSERT(!composition);
637 #endif // #ifdef DEBUG
638 return NS_OK;
641 RefPtr<TextComposition> composition =
642 IMEStateManager::GetTextCompositionFor(this);
643 // This method shouldn't be called when there is no text composition instance.
644 if (NS_WARN_IF(!composition)) {
645 return NS_OK;
648 MOZ_DIAGNOSTIC_ASSERT(
649 composition->IsRequestingCommitOrCancelComposition(),
650 "Requesting commit or cancel composition should be requested via "
651 "TextComposition instance");
653 bool isCommitted = false;
654 nsAutoString committedString;
655 if (NS_WARN_IF(!mBrowserChild->SendRequestIMEToCommitComposition(
656 aCancel, &isCommitted, &committedString))) {
657 return NS_ERROR_FAILURE;
660 // If the composition wasn't committed synchronously, we need to wait async
661 // composition events for destroying the TextComposition instance.
662 if (!isCommitted) {
663 return NS_OK;
666 // Dispatch eCompositionCommit event.
667 WidgetCompositionEvent compositionCommitEvent(true, eCompositionCommit, this);
668 InitEvent(compositionCommitEvent, nullptr);
669 compositionCommitEvent.mData = committedString;
670 nsEventStatus status = nsEventStatus_eIgnore;
671 DispatchEvent(&compositionCommitEvent, status);
673 #ifdef DEBUG
674 RefPtr<TextComposition> currentComposition =
675 IMEStateManager::GetTextCompositionFor(this);
676 MOZ_ASSERT(!currentComposition);
677 #endif // #ifdef DEBUG
679 // Ignore the following composition events until we receive new
680 // eCompositionStart event.
681 mIgnoreCompositionEvents = true;
683 Unused << mBrowserChild->SendOnEventNeedingAckHandled(
684 eCompositionCommitRequestHandled);
686 // NOTE: PuppetWidget might be destroyed already.
687 return NS_OK;
690 // When this widget caches input context and currently managed by
691 // IMEStateManager, the cache is valid.
692 bool PuppetWidget::HaveValidInputContextCache() const {
693 return (mInputContext.mIMEState.mEnabled != IMEEnabled::Unknown &&
694 IMEStateManager::GetWidgetForActiveInputContext() == this);
697 nsRefreshDriver* PuppetWidget::GetTopLevelRefreshDriver() const {
698 if (!mBrowserChild) {
699 return nullptr;
702 if (PresShell* presShell = mBrowserChild->GetTopLevelPresShell()) {
703 return presShell->GetRefreshDriver();
706 return nullptr;
709 void PuppetWidget::SetInputContext(const InputContext& aContext,
710 const InputContextAction& aAction) {
711 mInputContext = aContext;
712 // Any widget instances cannot cache IME open state because IME open state
713 // can be changed by user but native IME may not notify us of changing the
714 // open state on some platforms.
715 mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
716 if (!mBrowserChild) {
717 return;
719 mBrowserChild->SendSetInputContext(aContext, aAction);
722 InputContext PuppetWidget::GetInputContext() {
723 // XXX Currently, we don't support retrieving IME open state from child
724 // process.
726 // If the cache of input context is valid, we can avoid to use synchronous
727 // IPC.
728 if (HaveValidInputContextCache()) {
729 return mInputContext;
732 NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
734 // Don't cache InputContext here because this process isn't managing IME
735 // state of the chrome widget. So, we cannot modify mInputContext when
736 // chrome widget is set to new context.
737 InputContext context;
738 if (mBrowserChild) {
739 mBrowserChild->SendGetInputContext(&context.mIMEState);
741 return context;
744 NativeIMEContext PuppetWidget::GetNativeIMEContext() {
745 return mNativeIMEContext;
748 nsresult PuppetWidget::NotifyIMEOfFocusChange(
749 const IMENotification& aIMENotification) {
750 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
752 if (!mBrowserChild) {
753 return NS_ERROR_FAILURE;
756 bool gotFocus = aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS;
757 if (gotFocus) {
758 // When IME gets focus, we should initialize all information of the
759 // content, however, it may fail to get it because the editor may have
760 // already been blurred.
761 if (NS_WARN_IF(!mContentCache.CacheAll(this, &aIMENotification))) {
762 return NS_ERROR_FAILURE;
764 } else {
765 // When IME loses focus, we don't need to store anything.
766 mContentCache.Clear();
769 mIMENotificationRequestsOfParent =
770 IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL);
771 RefPtr<PuppetWidget> self = this;
772 mBrowserChild->SendNotifyIMEFocus(mContentCache, aIMENotification)
773 ->Then(
774 GetMainThreadSerialEventTarget(), __func__,
775 [self](IMENotificationRequests&& aRequests) {
776 self->mIMENotificationRequestsOfParent = aRequests;
777 if (TextEventDispatcher* dispatcher =
778 self->GetTextEventDispatcher()) {
779 dispatcher->OnWidgetChangeIMENotificationRequests(self);
782 [self](mozilla::ipc::ResponseRejectReason&& aReason) {
783 NS_WARNING("SendNotifyIMEFocus got rejected.");
786 return NS_OK;
789 nsresult PuppetWidget::NotifyIMEOfCompositionUpdate(
790 const IMENotification& aIMENotification) {
791 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
793 if (NS_WARN_IF(!mBrowserChild)) {
794 return NS_ERROR_FAILURE;
797 if (NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
798 return NS_ERROR_FAILURE;
800 mBrowserChild->SendNotifyIMECompositionUpdate(mContentCache,
801 aIMENotification);
802 return NS_OK;
805 nsresult PuppetWidget::NotifyIMEOfTextChange(
806 const IMENotification& aIMENotification) {
807 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
808 MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
809 "Passed wrong notification");
811 if (!mBrowserChild) {
812 return NS_ERROR_FAILURE;
815 // FYI: text change notification is the first notification after
816 // a user operation changes the content. So, we need to modify
817 // the cache as far as possible here.
819 if (NS_WARN_IF(!mContentCache.CacheText(this, &aIMENotification))) {
820 return NS_ERROR_FAILURE;
823 // BrowserParent doesn't this this to cache. we don't send the notification
824 // if parent process doesn't request NOTIFY_TEXT_CHANGE.
825 if (mIMENotificationRequestsOfParent.WantTextChange()) {
826 mBrowserChild->SendNotifyIMETextChange(mContentCache, aIMENotification);
827 } else {
828 mBrowserChild->SendUpdateContentCache(mContentCache);
830 return NS_OK;
833 nsresult PuppetWidget::NotifyIMEOfSelectionChange(
834 const IMENotification& aIMENotification) {
835 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
836 MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE,
837 "Passed wrong notification");
838 if (!mBrowserChild) {
839 return NS_ERROR_FAILURE;
842 // Note that selection change must be notified after text change if it occurs.
843 // Therefore, we don't need to query text content again here.
844 mContentCache.SetSelection(this, aIMENotification.mSelectionChangeData);
846 mBrowserChild->SendNotifyIMESelection(mContentCache, aIMENotification);
848 return NS_OK;
851 nsresult PuppetWidget::NotifyIMEOfMouseButtonEvent(
852 const IMENotification& aIMENotification) {
853 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
854 if (!mBrowserChild) {
855 return NS_ERROR_FAILURE;
858 bool consumedByIME = false;
859 if (!mBrowserChild->SendNotifyIMEMouseButtonEvent(aIMENotification,
860 &consumedByIME)) {
861 return NS_ERROR_FAILURE;
864 return consumedByIME ? NS_SUCCESS_EVENT_CONSUMED : NS_OK;
867 nsresult PuppetWidget::NotifyIMEOfPositionChange(
868 const IMENotification& aIMENotification) {
869 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
870 if (NS_WARN_IF(!mBrowserChild)) {
871 return NS_ERROR_FAILURE;
874 if (NS_WARN_IF(!mContentCache.CacheEditorRect(this, &aIMENotification))) {
875 return NS_ERROR_FAILURE;
877 if (NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
878 return NS_ERROR_FAILURE;
880 if (mIMENotificationRequestsOfParent.WantPositionChanged()) {
881 mBrowserChild->SendNotifyIMEPositionChange(mContentCache, aIMENotification);
882 } else {
883 mBrowserChild->SendUpdateContentCache(mContentCache);
885 return NS_OK;
888 struct CursorSurface {
889 UniquePtr<char[]> mData;
890 IntSize mSize;
893 void PuppetWidget::SetCursor(const Cursor& aCursor) {
894 if (!mBrowserChild) {
895 return;
898 const bool force = mUpdateCursor;
899 if (!force && mCursor == aCursor) {
900 return;
903 bool hasCustomCursor = false;
904 UniquePtr<char[]> customCursorData;
905 size_t length = 0;
906 IntSize customCursorSize;
907 int32_t stride = 0;
908 auto format = SurfaceFormat::B8G8R8A8;
909 ImageResolution resolution = aCursor.mResolution;
910 if (aCursor.IsCustom()) {
911 int32_t width = 0, height = 0;
912 aCursor.mContainer->GetWidth(&width);
913 aCursor.mContainer->GetHeight(&height);
914 const int32_t flags =
915 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
916 RefPtr<SourceSurface> surface;
917 if (width && height &&
918 aCursor.mContainer->GetType() == imgIContainer::TYPE_VECTOR) {
919 // For vector images, scale to device pixels.
920 resolution.ScaleBy(GetDefaultScale().scale);
921 resolution.ApplyInverseTo(width, height);
922 surface = aCursor.mContainer->GetFrameAtSize(
923 {width, height}, imgIContainer::FRAME_CURRENT, flags);
924 } else {
925 // NOTE(emilio): We get the frame at the full size, ignoring resolution,
926 // because we're going to rasterize it, and we'd effectively lose the
927 // extra pixels if we rasterized to CustomCursorSize.
928 surface =
929 aCursor.mContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags);
931 if (surface) {
932 if (RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface()) {
933 hasCustomCursor = true;
934 customCursorData =
935 nsContentUtils::GetSurfaceData(*dataSurface, &length, &stride);
936 customCursorSize = dataSurface->GetSize();
937 format = dataSurface->GetFormat();
942 nsDependentCString cursorData(customCursorData ? customCursorData.get() : "",
943 length);
944 if (!mBrowserChild->SendSetCursor(
945 aCursor.mDefaultCursor, hasCustomCursor, cursorData,
946 customCursorSize.width, customCursorSize.height, resolution.mX,
947 resolution.mY, stride, format, aCursor.mHotspotX, aCursor.mHotspotY,
948 force)) {
949 return;
951 mCursor = aCursor;
952 mUpdateCursor = false;
955 void PuppetWidget::SetChild(PuppetWidget* aChild) {
956 MOZ_ASSERT(this != aChild, "can't parent a widget to itself");
957 MOZ_ASSERT(!aChild->mChild,
958 "fake widget 'hierarchy' only expected to have one level");
960 mChild = aChild;
963 NS_IMETHODIMP
964 PuppetWidget::WidgetPaintTask::Run() {
965 if (mWidget) {
966 mWidget->Paint();
968 return NS_OK;
971 void PuppetWidget::Paint() {
972 if (!GetCurrentWidgetListener()) return;
974 mWidgetPaintTask.Revoke();
976 RefPtr<PuppetWidget> strongThis(this);
978 GetCurrentWidgetListener()->WillPaintWindow(this);
980 if (GetCurrentWidgetListener()) {
981 GetCurrentWidgetListener()->DidPaintWindow();
985 void PuppetWidget::PaintNowIfNeeded() {
986 if (IsVisible() && mWidgetPaintTask.IsPending()) {
987 Paint();
991 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy) {
992 if (aWhy != MemoryPressureReason::LOW_MEMORY_ONGOING && !mVisible &&
993 mWindowRenderer && mWindowRenderer->AsWebRender() &&
994 XRE_IsContentProcess()) {
995 mWindowRenderer->AsWebRender()->ClearCachedResources();
999 bool PuppetWidget::NeedsPaint() {
1000 // e10s popups are handled by the parent process, so never should be painted
1001 // here
1002 if (XRE_IsContentProcess() &&
1003 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
1004 mWindowType == eWindowType_popup) {
1005 NS_WARNING("Trying to paint an e10s popup in the child process!");
1006 return false;
1009 return mVisible;
1012 float PuppetWidget::GetDPI() { return mDPI; }
1014 double PuppetWidget::GetDefaultScaleInternal() { return mDefaultScale; }
1016 int32_t PuppetWidget::RoundsWidgetCoordinatesTo() { return mRounding; }
1018 LayoutDeviceIntPoint PuppetWidget::GetChromeOffset() {
1019 if (!GetOwningBrowserChild()) {
1020 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1021 return LayoutDeviceIntPoint();
1023 return GetOwningBrowserChild()->GetChromeOffset();
1026 LayoutDeviceIntPoint PuppetWidget::WidgetToScreenOffset() {
1027 return GetWindowPosition() + WidgetToTopLevelWidgetOffset();
1030 LayoutDeviceIntPoint PuppetWidget::GetWindowPosition() {
1031 if (!GetOwningBrowserChild()) {
1032 return LayoutDeviceIntPoint();
1035 int32_t winX, winY, winW, winH;
1036 NS_ENSURE_SUCCESS(
1037 GetOwningBrowserChild()->GetDimensions(0, &winX, &winY, &winW, &winH),
1038 LayoutDeviceIntPoint());
1039 return LayoutDeviceIntPoint(winX, winY) +
1040 GetOwningBrowserChild()->GetClientOffset();
1043 LayoutDeviceIntRect PuppetWidget::GetScreenBounds() {
1044 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
1047 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1048 return mBrowserChild ? mBrowserChild->MaxTouchPoints() : 0;
1051 void PuppetWidget::StartAsyncScrollbarDrag(
1052 const AsyncDragMetrics& aDragMetrics) {
1053 mBrowserChild->StartScrollbarDrag(aDragMetrics);
1056 ScreenIntMargin PuppetWidget::GetSafeAreaInsets() const {
1057 return mSafeAreaInsets;
1060 void PuppetWidget::UpdateSafeAreaInsets(
1061 const ScreenIntMargin& aSafeAreaInsets) {
1062 mSafeAreaInsets = aSafeAreaInsets;
1065 nsIWidgetListener* PuppetWidget::GetCurrentWidgetListener() {
1066 if (!mPreviouslyAttachedWidgetListener || !mAttachedWidgetListener) {
1067 return mAttachedWidgetListener;
1070 if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) {
1071 return mPreviouslyAttachedWidgetListener;
1074 return mAttachedWidgetListener;
1077 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId,
1078 const ScrollableLayerGuid::ViewID& aViewId,
1079 const CSSRect& aRect, const uint32_t& aFlags) {
1080 if (!mBrowserChild) {
1081 return;
1084 mBrowserChild->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
1087 void PuppetWidget::LookUpDictionary(
1088 const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
1089 const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
1090 if (!mBrowserChild) {
1091 return;
1094 mBrowserChild->SendLookUpDictionary(nsString(aText), aFontRangeArray,
1095 aIsVertical, aPoint);
1098 bool PuppetWidget::HasPendingInputEvent() {
1099 if (!mBrowserChild) {
1100 return false;
1103 bool ret = false;
1105 mBrowserChild->GetIPCChannel()->PeekMessages(
1106 [&ret](const IPC::Message& aMsg) -> bool {
1107 if (nsContentUtils::IsMessageInputEvent(aMsg)) {
1108 ret = true;
1109 return false; // Stop peeking.
1111 return true;
1114 return ret;
1117 // TextEventDispatcherListener
1119 NS_IMETHODIMP
1120 PuppetWidget::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
1121 const IMENotification& aIMENotification) {
1122 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1124 // If there is different text event dispatcher listener for handling
1125 // text event dispatcher, that means that native keyboard events and
1126 // IME events are handled in this process. Therefore, we don't need
1127 // to send any requests and notifications to the parent process.
1128 if (mNativeTextEventDispatcherListener) {
1129 return NS_ERROR_NOT_IMPLEMENTED;
1132 switch (aIMENotification.mMessage) {
1133 case REQUEST_TO_COMMIT_COMPOSITION:
1134 return RequestIMEToCommitComposition(false);
1135 case REQUEST_TO_CANCEL_COMPOSITION:
1136 return RequestIMEToCommitComposition(true);
1137 case NOTIFY_IME_OF_FOCUS:
1138 case NOTIFY_IME_OF_BLUR:
1139 return NotifyIMEOfFocusChange(aIMENotification);
1140 case NOTIFY_IME_OF_SELECTION_CHANGE:
1141 return NotifyIMEOfSelectionChange(aIMENotification);
1142 case NOTIFY_IME_OF_TEXT_CHANGE:
1143 return NotifyIMEOfTextChange(aIMENotification);
1144 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
1145 return NotifyIMEOfCompositionUpdate(aIMENotification);
1146 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
1147 return NotifyIMEOfMouseButtonEvent(aIMENotification);
1148 case NOTIFY_IME_OF_POSITION_CHANGE:
1149 return NotifyIMEOfPositionChange(aIMENotification);
1150 default:
1151 return NS_ERROR_NOT_IMPLEMENTED;
1154 return NS_ERROR_NOT_IMPLEMENTED;
1157 NS_IMETHODIMP_(IMENotificationRequests)
1158 PuppetWidget::GetIMENotificationRequests() {
1159 return IMENotificationRequests(
1160 mIMENotificationRequestsOfParent.mWantUpdates |
1161 IMENotificationRequests::NOTIFY_TEXT_CHANGE |
1162 IMENotificationRequests::NOTIFY_POSITION_CHANGE);
1165 NS_IMETHODIMP_(void)
1166 PuppetWidget::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
1167 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1170 NS_IMETHODIMP_(void)
1171 PuppetWidget::WillDispatchKeyboardEvent(
1172 TextEventDispatcher* aTextEventDispatcher,
1173 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
1174 void* aData) {
1175 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1178 nsresult PuppetWidget::SetSystemFont(const nsCString& aFontName) {
1179 if (!mBrowserChild) {
1180 return NS_ERROR_FAILURE;
1183 mBrowserChild->SendSetSystemFont(aFontName);
1184 return NS_OK;
1187 nsresult PuppetWidget::GetSystemFont(nsCString& aFontName) {
1188 if (!mBrowserChild) {
1189 return NS_ERROR_FAILURE;
1191 mBrowserChild->SendGetSystemFont(&aFontName);
1192 return NS_OK;
1195 } // namespace widget
1196 } // namespace mozilla