1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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"
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());
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();
61 static bool IsPopup(const widget::InitData
* aInitData
) {
62 return aInitData
&& aInitData
->mWindowType
== WindowType::Popup
;
65 static bool MightNeedIMEFocus(const widget::InitData
* 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
);
75 NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget
, nsBaseWidget
,
76 TextEventDispatcherListener
)
78 PuppetWidget::PuppetWidget(BrowserChild
* aBrowserChild
)
79 : mBrowserChild(aBrowserChild
),
80 mMemoryPressureObserver(nullptr),
83 mSizeMode(nsSizeMode_Normal
),
84 mNeedIMEStateInit(false),
85 mIgnoreCompositionEvents(false) {
86 // Setting 'Unknown' means "not yet cached".
87 mInputContext
.mIMEState
.mEnabled
= IMEEnabled::Unknown
;
90 PuppetWidget::~PuppetWidget() { Destroy(); }
92 void PuppetWidget::InfallibleCreate(nsIWidget
* aParent
,
93 nsNativeWidget aNativeParent
,
94 const LayoutDeviceIntRect
& aRect
,
95 widget::InitData
* aInitData
) {
96 MOZ_ASSERT(!aNativeParent
, "got a non-Puppet native parent");
98 BaseCreate(nullptr, aInitData
);
104 mDrawTarget
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
105 IntSize(1, 1), SurfaceFormat::B8G8R8A8
);
107 mNeedIMEStateInit
= MightNeedIMEFocus(aInitData
);
109 PuppetWidget
* parent
= static_cast<PuppetWidget
*>(aParent
);
111 parent
->SetChild(this);
112 mWindowRenderer
= parent
->GetWindowRenderer();
114 Resize(mBounds
.X(), mBounds
.Y(), mBounds
.Width(), mBounds
.Height(), false);
116 mMemoryPressureObserver
= MemoryPressureObserver::Create(this);
119 nsresult
PuppetWidget::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
120 const LayoutDeviceIntRect
& aRect
,
121 widget::InitData
* aInitData
) {
122 InfallibleCreate(aParent
, aNativeParent
, aRect
, aInitData
);
126 void PuppetWidget::InitIMEState() {
127 MOZ_ASSERT(mBrowserChild
);
128 if (mNeedIMEStateInit
) {
129 mContentCache
.Clear();
130 mBrowserChild
->SendUpdateContentCache(mContentCache
);
131 mIMENotificationRequestsOfParent
= IMENotificationRequests();
132 mNeedIMEStateInit
= false;
136 already_AddRefed
<nsIWidget
> PuppetWidget::CreateChild(
137 const LayoutDeviceIntRect
& aRect
, widget::InitData
* aInitData
,
138 bool aForceUseIWidgetParent
) {
139 bool isPopup
= IsPopup(aInitData
);
140 nsCOMPtr
<nsIWidget
> widget
= nsIWidget::CreatePuppetWidget(mBrowserChild
);
141 return ((widget
&& NS_SUCCEEDED(widget
->Create(isPopup
? nullptr : this,
142 nullptr, aRect
, aInitData
)))
147 void PuppetWidget::Destroy() {
148 if (mOnDestroyCalled
) {
151 mOnDestroyCalled
= true;
155 if (mMemoryPressureObserver
) {
156 mMemoryPressureObserver
->Unregister();
157 mMemoryPressureObserver
= nullptr;
160 if (mWindowRenderer
) {
161 mWindowRenderer
->Destroy();
163 mWindowRenderer
= nullptr;
164 mBrowserChild
= nullptr;
167 void PuppetWidget::Show(bool aState
) {
168 NS_ASSERTION(mEnabled
,
169 "does it make sense to Show()/Hide() a disabled widget?");
171 bool wasVisible
= mVisible
;
175 mChild
->mVisible
= aState
;
178 if (!wasVisible
&& mVisible
) {
179 // The previously attached widget listener is handy if
180 // we're transitioning from page to page without dropping
181 // layers (since we'll continue to show the old layers
182 // associated with that old widget listener). If the
183 // PuppetWidget was hidden, those layers are dropped,
184 // so the previously attached widget listener is really
185 // of no use anymore (and is actually actively harmful - see
187 mPreviouslyAttachedWidgetListener
= nullptr;
188 Resize(mBounds
.Width(), mBounds
.Height(), false);
193 void PuppetWidget::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
194 LayoutDeviceIntRect oldBounds
= mBounds
;
196 LayoutDeviceIntSize(NSToIntRound(aWidth
), NSToIntRound(aHeight
)));
199 mChild
->Resize(aWidth
, aHeight
, aRepaint
);
203 // XXX: roc says that |aRepaint| dictates whether or not to
204 // invalidate the expanded area
205 if (oldBounds
.Size() < mBounds
.Size() && aRepaint
) {
206 LayoutDeviceIntRegion
dirty(mBounds
);
207 dirty
.Sub(dirty
, oldBounds
);
208 InvalidateRegion(this, dirty
);
211 // call WindowResized() on both the current listener, and possibly
212 // also the previous one if we're in a state where we're drawing that one
213 // because the current one is paint suppressed
214 if (!oldBounds
.IsEqualEdges(mBounds
) && mAttachedWidgetListener
) {
215 if (GetCurrentWidgetListener() &&
216 GetCurrentWidgetListener() != mAttachedWidgetListener
) {
217 GetCurrentWidgetListener()->WindowResized(this, mBounds
.Width(),
220 mAttachedWidgetListener
->WindowResized(this, mBounds
.Width(),
225 void PuppetWidget::SetFocus(Raise aRaise
, CallerType aCallerType
) {
226 if (aRaise
== Raise::Yes
&& mBrowserChild
) {
227 mBrowserChild
->SendRequestFocus(true, aCallerType
);
231 void PuppetWidget::Invalidate(const LayoutDeviceIntRect
& aRect
) {
233 debug_DumpInvalidate(stderr
, this, &aRect
, "PuppetWidget", 0);
237 mChild
->Invalidate(aRect
);
241 if (mBrowserChild
&& !aRect
.IsEmpty() && !mWidgetPaintTask
.IsPending()) {
242 mWidgetPaintTask
= new WidgetPaintTask(this);
243 nsCOMPtr
<nsIRunnable
> event(mWidgetPaintTask
.get());
244 SchedulerGroup::Dispatch(TaskCategory::Other
, event
.forget());
248 mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
249 PuppetWidget::WidgetToTopLevelWidgetTransform() {
250 if (!GetOwningBrowserChild()) {
251 NS_WARNING("PuppetWidget without Tab does not have transform information.");
252 return mozilla::LayoutDeviceToLayoutDeviceMatrix4x4();
254 return GetOwningBrowserChild()->GetChildToParentConversionMatrix();
257 void PuppetWidget::InitEvent(WidgetGUIEvent
& aEvent
,
258 LayoutDeviceIntPoint
* aPoint
) {
259 if (nullptr == aPoint
) {
260 aEvent
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
262 // use the point override if provided
263 aEvent
.mRefPoint
= *aPoint
;
267 nsresult
PuppetWidget::DispatchEvent(WidgetGUIEvent
* aEvent
,
268 nsEventStatus
& aStatus
) {
270 debug_DumpEvent(stdout
, aEvent
->mWidget
, aEvent
, "PuppetWidget", 0);
273 MOZ_ASSERT(!mChild
|| mChild
->mWindowType
== WindowType::Popup
,
274 "Unexpected event dispatch!");
276 MOZ_ASSERT(!aEvent
->AsKeyboardEvent() ||
277 aEvent
->mFlags
.mIsSynthesizedForTests
||
278 aEvent
->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
279 "Non-sysnthesized keyboard events should have edit commands for "
281 "before dispatched");
283 if (aEvent
->mClass
== eCompositionEventClass
) {
284 // If we've already requested to commit/cancel the latest composition,
285 // TextComposition for the old composition has been destroyed. Then,
286 // the DOM tree needs to listen to next eCompositionStart and its
287 // following events. So, until we meet new eCompositionStart, let's
288 // discard all unnecessary composition events here.
289 if (mIgnoreCompositionEvents
) {
290 if (aEvent
->mMessage
!= eCompositionStart
) {
291 aStatus
= nsEventStatus_eIgnore
;
294 // Now, we receive new eCompositionStart. Let's restart to handle
295 // composition in this process.
296 mIgnoreCompositionEvents
= false;
298 // Store the latest native IME context of parent process's widget or
299 // TextEventDispatcher if it's in this process.
300 WidgetCompositionEvent
* compositionEvent
= aEvent
->AsCompositionEvent();
302 if (mNativeIMEContext
.IsValid() &&
303 mNativeIMEContext
!= compositionEvent
->mNativeIMEContext
) {
304 RefPtr
<TextComposition
> composition
=
305 IMEStateManager::GetTextCompositionFor(this);
308 "When there is composition caused by old native IME context, "
309 "composition events caused by different native IME context are not "
312 #endif // #ifdef DEBUG
313 mNativeIMEContext
= compositionEvent
->mNativeIMEContext
;
314 mContentCache
.OnCompositionEvent(*compositionEvent
);
317 // If the event is a composition event or a keyboard event, it should be
318 // dispatched with TextEventDispatcher if we could do that with current
319 // design. However, we cannot do that without big changes and the behavior
320 // is not so complicated for now. Therefore, we should just notify it
321 // of dispatching events and TextEventDispatcher should emulate the state
323 if (aEvent
->mClass
== eCompositionEventClass
||
324 aEvent
->mClass
== eKeyboardEventClass
) {
325 TextEventDispatcher
* dispatcher
= GetTextEventDispatcher();
326 // However, if the event is being dispatched by the text event dispatcher
327 // or, there is native text event dispatcher listener, that means that
328 // native text input event handler is in this process like on Android,
329 // and the event is not synthesized for tests, the event is coming from
330 // the TextEventDispatcher. In these cases, we shouldn't notify
331 // TextEventDispatcher of dispatching the event.
332 if (!dispatcher
->IsDispatchingEvent() &&
333 !(mNativeTextEventDispatcherListener
&&
334 !aEvent
->mFlags
.mIsSynthesizedForTests
)) {
335 DebugOnly
<nsresult
> rv
=
336 dispatcher
->BeginInputTransactionFor(aEvent
, this);
337 NS_WARNING_ASSERTION(
339 "The text event dispatcher should always succeed to start input "
340 "transaction for the event");
344 aStatus
= nsEventStatus_eIgnore
;
346 if (GetCurrentWidgetListener()) {
348 GetCurrentWidgetListener()->HandleEvent(aEvent
, mUseAttachedEvents
);
354 nsIWidget::ContentAndAPZEventStatus
PuppetWidget::DispatchInputEvent(
355 WidgetInputEvent
* aEvent
) {
356 ContentAndAPZEventStatus status
;
357 if (!AsyncPanZoomEnabled()) {
358 DispatchEvent(aEvent
, status
.mContentStatus
);
362 if (!mBrowserChild
) {
366 switch (aEvent
->mClass
) {
367 case eWheelEventClass
:
368 Unused
<< mBrowserChild
->SendDispatchWheelEvent(*aEvent
->AsWheelEvent());
370 case eMouseEventClass
:
371 Unused
<< mBrowserChild
->SendDispatchMouseEvent(*aEvent
->AsMouseEvent());
373 case eKeyboardEventClass
:
374 Unused
<< mBrowserChild
->SendDispatchKeyboardEvent(
375 *aEvent
->AsKeyboardEvent());
377 case eTouchEventClass
:
378 Unused
<< mBrowserChild
->SendDispatchTouchEvent(*aEvent
->AsTouchEvent());
381 MOZ_ASSERT_UNREACHABLE("unsupported event type");
387 nsresult
PuppetWidget::SynthesizeNativeKeyEvent(
388 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
389 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
390 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
391 AutoObserverNotifier
notifier(aObserver
, "keyevent");
392 if (!mBrowserChild
) {
393 return NS_ERROR_FAILURE
;
395 mBrowserChild
->SendSynthesizeNativeKeyEvent(
396 aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
, aCharacters
,
397 aUnmodifiedCharacters
, notifier
.SaveObserver());
401 nsresult
PuppetWidget::SynthesizeNativeMouseEvent(
402 mozilla::LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
403 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
404 nsIObserver
* aObserver
) {
405 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
406 if (!mBrowserChild
) {
407 return NS_ERROR_FAILURE
;
409 mBrowserChild
->SendSynthesizeNativeMouseEvent(
410 aPoint
, static_cast<uint32_t>(aNativeMessage
),
411 static_cast<int16_t>(aButton
), static_cast<uint32_t>(aModifierFlags
),
412 notifier
.SaveObserver());
416 nsresult
PuppetWidget::SynthesizeNativeMouseMove(
417 mozilla::LayoutDeviceIntPoint aPoint
, nsIObserver
* aObserver
) {
418 AutoObserverNotifier
notifier(aObserver
, "mousemove");
419 if (!mBrowserChild
) {
420 return NS_ERROR_FAILURE
;
422 mBrowserChild
->SendSynthesizeNativeMouseMove(aPoint
, notifier
.SaveObserver());
426 nsresult
PuppetWidget::SynthesizeNativeMouseScrollEvent(
427 mozilla::LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
,
428 double aDeltaX
, double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
429 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
430 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
431 if (!mBrowserChild
) {
432 return NS_ERROR_FAILURE
;
434 mBrowserChild
->SendSynthesizeNativeMouseScrollEvent(
435 aPoint
, aNativeMessage
, aDeltaX
, aDeltaY
, aDeltaZ
, aModifierFlags
,
436 aAdditionalFlags
, notifier
.SaveObserver());
440 nsresult
PuppetWidget::SynthesizeNativeTouchPoint(
441 uint32_t aPointerId
, TouchPointerState aPointerState
,
442 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
443 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
444 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
445 if (!mBrowserChild
) {
446 return NS_ERROR_FAILURE
;
448 mBrowserChild
->SendSynthesizeNativeTouchPoint(
449 aPointerId
, aPointerState
, aPoint
, aPointerPressure
, aPointerOrientation
,
450 notifier
.SaveObserver());
454 nsresult
PuppetWidget::SynthesizeNativeTouchPadPinch(
455 TouchpadGesturePhase aEventPhase
, float aScale
, LayoutDeviceIntPoint aPoint
,
456 int32_t aModifierFlags
) {
457 if (!mBrowserChild
) {
458 return NS_ERROR_FAILURE
;
460 mBrowserChild
->SendSynthesizeNativeTouchPadPinch(aEventPhase
, aScale
, aPoint
,
465 nsresult
PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint
,
467 nsIObserver
* aObserver
) {
468 AutoObserverNotifier
notifier(aObserver
, "touchtap");
469 if (!mBrowserChild
) {
470 return NS_ERROR_FAILURE
;
472 mBrowserChild
->SendSynthesizeNativeTouchTap(aPoint
, aLongTap
,
473 notifier
.SaveObserver());
477 nsresult
PuppetWidget::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
478 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
479 if (!mBrowserChild
) {
480 return NS_ERROR_FAILURE
;
482 mBrowserChild
->SendClearNativeTouchSequence(notifier
.SaveObserver());
486 nsresult
PuppetWidget::SynthesizeNativePenInput(
487 uint32_t aPointerId
, TouchPointerState aPointerState
,
488 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
489 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
490 AutoObserverNotifier
notifier(aObserver
, "peninput");
491 if (!mBrowserChild
) {
492 return NS_ERROR_FAILURE
;
494 mBrowserChild
->SendSynthesizeNativePenInput(
495 aPointerId
, aPointerState
, aPoint
, aPressure
, aRotation
, aTiltX
, aTiltY
,
496 aButton
, notifier
.SaveObserver());
500 nsresult
PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
501 LayoutDeviceIntPoint aPoint
, uint32_t aModifierFlags
) {
502 if (!mBrowserChild
) {
503 return NS_ERROR_FAILURE
;
505 mBrowserChild
->SendSynthesizeNativeTouchpadDoubleTap(aPoint
, aModifierFlags
);
509 nsresult
PuppetWidget::SynthesizeNativeTouchpadPan(
510 TouchpadGesturePhase aEventPhase
, LayoutDeviceIntPoint aPoint
,
511 double aDeltaX
, double aDeltaY
, int32_t aModifierFlags
,
512 nsIObserver
* aObserver
) {
513 AutoObserverNotifier
notifier(aObserver
, "touchpadpanevent");
514 if (!mBrowserChild
) {
515 return NS_ERROR_FAILURE
;
517 mBrowserChild
->SendSynthesizeNativeTouchpadPan(aEventPhase
, aPoint
, aDeltaX
,
518 aDeltaY
, aModifierFlags
,
519 notifier
.SaveObserver());
523 void PuppetWidget::LockNativePointer() {
524 if (!mBrowserChild
) {
527 mBrowserChild
->SendLockNativePointer();
530 void PuppetWidget::UnlockNativePointer() {
531 if (!mBrowserChild
) {
534 mBrowserChild
->SendUnlockNativePointer();
537 void PuppetWidget::SetConfirmedTargetAPZC(
538 uint64_t aInputBlockId
,
539 const nsTArray
<ScrollableLayerGuid
>& aTargets
) const {
541 mBrowserChild
->SetTargetAPZC(aInputBlockId
, aTargets
);
545 void PuppetWidget::UpdateZoomConstraints(
546 const uint32_t& aPresShellId
, const ScrollableLayerGuid::ViewID
& aViewId
,
547 const Maybe
<ZoomConstraints
>& aConstraints
) {
549 mBrowserChild
->DoUpdateZoomConstraints(aPresShellId
, aViewId
, aConstraints
);
553 bool PuppetWidget::AsyncPanZoomEnabled() const {
554 return mBrowserChild
&& mBrowserChild
->AsyncPanZoomEnabled();
557 bool PuppetWidget::GetEditCommands(NativeKeyBindingsType aType
,
558 const WidgetKeyboardEvent
& aEvent
,
559 nsTArray
<CommandInt
>& aCommands
) {
560 MOZ_ASSERT(!aEvent
.mFlags
.mIsSynthesizedForTests
);
561 // Validate the arguments.
562 if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType
, aEvent
, aCommands
))) {
565 if (NS_WARN_IF(!mBrowserChild
)) {
568 mBrowserChild
->RequestEditCommands(aType
, aEvent
, aCommands
);
572 WindowRenderer
* PuppetWidget::GetWindowRenderer() {
573 if (!mWindowRenderer
) {
574 if (XRE_IsParentProcess()) {
575 // On the parent process there is no CompositorBridgeChild which confuses
576 // some layers code, so we use basic layers instead. Note that we create
577 mWindowRenderer
= new FallbackRenderer
;
578 return mWindowRenderer
;
581 // If we know for sure that the parent side of this BrowserChild is not
582 // connected to the compositor, we don't want to use a "remote" layer
583 // manager like WebRender or Client. Instead we use a Basic one which
584 // can do drawing in this process.
585 MOZ_ASSERT(!mBrowserChild
||
586 mBrowserChild
->IsLayersConnected() != Some(true));
587 mWindowRenderer
= CreateFallbackRenderer();
590 return mWindowRenderer
;
593 bool PuppetWidget::CreateRemoteLayerManager(
594 const std::function
<bool(WebRenderLayerManager
*)>& aInitializeFunc
) {
595 RefPtr
<WebRenderLayerManager
> lm
= new WebRenderLayerManager(this);
596 MOZ_ASSERT(mBrowserChild
);
598 if (!aInitializeFunc(lm
)) {
602 // Force the old LM to self destruct, otherwise if the reference dangles we
603 // could fail to revoke the most recent transaction. We only want to replace
604 // it if we successfully create its successor because a partially initialized
605 // layer manager is worse than a fully initialized but shutdown layer manager.
606 DestroyLayerManager();
607 mWindowRenderer
= std::move(lm
);
611 nsresult
PuppetWidget::RequestIMEToCommitComposition(bool aCancel
) {
612 if (!mBrowserChild
) {
613 return NS_ERROR_FAILURE
;
616 MOZ_ASSERT(!Destroyed());
618 // There must not be composition which is caused by the PuppetWidget instance.
619 if (NS_WARN_IF(!mNativeIMEContext
.IsValid())) {
623 // We've already requested to commit/cancel composition.
624 if (NS_WARN_IF(mIgnoreCompositionEvents
)) {
626 RefPtr
<TextComposition
> composition
=
627 IMEStateManager::GetTextCompositionFor(this);
628 MOZ_ASSERT(!composition
);
629 #endif // #ifdef DEBUG
633 RefPtr
<TextComposition
> composition
=
634 IMEStateManager::GetTextCompositionFor(this);
635 // This method shouldn't be called when there is no text composition instance.
636 if (NS_WARN_IF(!composition
)) {
640 MOZ_DIAGNOSTIC_ASSERT(
641 composition
->IsRequestingCommitOrCancelComposition(),
642 "Requesting commit or cancel composition should be requested via "
643 "TextComposition instance");
645 bool isCommitted
= false;
646 nsAutoString committedString
;
647 if (NS_WARN_IF(!mBrowserChild
->SendRequestIMEToCommitComposition(
648 aCancel
, composition
->Id(), &isCommitted
, &committedString
))) {
649 return NS_ERROR_FAILURE
;
652 // If the composition wasn't committed synchronously, we need to wait async
653 // composition events for destroying the TextComposition instance.
658 // Dispatch eCompositionCommit event.
659 WidgetCompositionEvent
compositionCommitEvent(true, eCompositionCommit
, this);
660 InitEvent(compositionCommitEvent
, nullptr);
661 compositionCommitEvent
.mData
= committedString
;
662 nsEventStatus status
= nsEventStatus_eIgnore
;
663 DispatchEvent(&compositionCommitEvent
, status
);
666 RefPtr
<TextComposition
> currentComposition
=
667 IMEStateManager::GetTextCompositionFor(this);
668 MOZ_ASSERT(!currentComposition
);
669 #endif // #ifdef DEBUG
671 // Ignore the following composition events until we receive new
672 // eCompositionStart event.
673 mIgnoreCompositionEvents
= true;
675 Unused
<< mBrowserChild
->SendOnEventNeedingAckHandled(
676 eCompositionCommitRequestHandled
, composition
->Id());
678 // NOTE: PuppetWidget might be destroyed already.
682 // When this widget caches input context and currently managed by
683 // IMEStateManager, the cache is valid.
684 bool PuppetWidget::HaveValidInputContextCache() const {
685 return (mInputContext
.mIMEState
.mEnabled
!= IMEEnabled::Unknown
&&
686 IMEStateManager::GetWidgetForActiveInputContext() == this);
689 nsRefreshDriver
* PuppetWidget::GetTopLevelRefreshDriver() const {
690 if (!mBrowserChild
) {
694 if (PresShell
* presShell
= mBrowserChild
->GetTopLevelPresShell()) {
695 return presShell
->GetRefreshDriver();
701 void PuppetWidget::SetInputContext(const InputContext
& aContext
,
702 const InputContextAction
& aAction
) {
703 mInputContext
= aContext
;
704 // Any widget instances cannot cache IME open state because IME open state
705 // can be changed by user but native IME may not notify us of changing the
706 // open state on some platforms.
707 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN_STATE_NOT_SUPPORTED
;
708 if (!mBrowserChild
) {
711 mBrowserChild
->SendSetInputContext(aContext
, aAction
);
714 InputContext
PuppetWidget::GetInputContext() {
715 // XXX Currently, we don't support retrieving IME open state from child
718 // If the cache of input context is valid, we can avoid to use synchronous
720 if (HaveValidInputContextCache()) {
721 return mInputContext
;
724 NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
726 // Don't cache InputContext here because this process isn't managing IME
727 // state of the chrome widget. So, we cannot modify mInputContext when
728 // chrome widget is set to new context.
729 InputContext context
;
731 mBrowserChild
->SendGetInputContext(&context
.mIMEState
);
736 NativeIMEContext
PuppetWidget::GetNativeIMEContext() {
737 return mNativeIMEContext
;
740 nsresult
PuppetWidget::NotifyIMEOfFocusChange(
741 const IMENotification
& aIMENotification
) {
742 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
744 if (!mBrowserChild
) {
745 return NS_ERROR_FAILURE
;
748 bool gotFocus
= aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
;
750 // When IME gets focus, we should initialize all information of the
751 // content, however, it may fail to get it because the editor may have
752 // already been blurred.
753 if (NS_WARN_IF(!mContentCache
.CacheAll(this, &aIMENotification
))) {
754 return NS_ERROR_FAILURE
;
757 // When IME loses focus, we don't need to store anything.
758 mContentCache
.Clear();
761 mIMENotificationRequestsOfParent
=
762 IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL
);
763 RefPtr
<PuppetWidget
> self
= this;
764 mBrowserChild
->SendNotifyIMEFocus(mContentCache
, aIMENotification
)
766 GetMainThreadSerialEventTarget(), __func__
,
767 [self
](IMENotificationRequests
&& aRequests
) {
768 self
->mIMENotificationRequestsOfParent
= aRequests
;
769 if (TextEventDispatcher
* dispatcher
=
770 self
->GetTextEventDispatcher()) {
771 dispatcher
->OnWidgetChangeIMENotificationRequests(self
);
774 [self
](mozilla::ipc::ResponseRejectReason
&& aReason
) {
775 NS_WARNING("SendNotifyIMEFocus got rejected.");
781 nsresult
PuppetWidget::NotifyIMEOfCompositionUpdate(
782 const IMENotification
& aIMENotification
) {
783 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
785 if (NS_WARN_IF(!mBrowserChild
)) {
786 return NS_ERROR_FAILURE
;
790 !mContentCache
.CacheCaretAndTextRects(this, &aIMENotification
))) {
791 return NS_ERROR_FAILURE
;
793 mBrowserChild
->SendNotifyIMECompositionUpdate(mContentCache
,
798 nsresult
PuppetWidget::NotifyIMEOfTextChange(
799 const IMENotification
& aIMENotification
) {
800 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
801 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
,
802 "Passed wrong notification");
804 if (!mBrowserChild
) {
805 return NS_ERROR_FAILURE
;
808 // FYI: text change notification is the first notification after
809 // a user operation changes the content. So, we need to modify
810 // the cache as far as possible here.
812 if (NS_WARN_IF(!mContentCache
.CacheText(this, &aIMENotification
))) {
813 return NS_ERROR_FAILURE
;
816 // BrowserParent doesn't this this to cache. we don't send the notification
817 // if parent process doesn't request NOTIFY_TEXT_CHANGE.
818 if (mIMENotificationRequestsOfParent
.WantTextChange()) {
819 mBrowserChild
->SendNotifyIMETextChange(mContentCache
, aIMENotification
);
821 mBrowserChild
->SendUpdateContentCache(mContentCache
);
826 nsresult
PuppetWidget::NotifyIMEOfSelectionChange(
827 const IMENotification
& aIMENotification
) {
828 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
829 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
,
830 "Passed wrong notification");
831 if (!mBrowserChild
) {
832 return NS_ERROR_FAILURE
;
835 // Note that selection change must be notified after text change if it occurs.
836 // Therefore, we don't need to query text content again here.
837 if (MOZ_UNLIKELY(!mContentCache
.SetSelection(
838 this, aIMENotification
.mSelectionChangeData
))) {
839 // If there is no text cache yet, caching text will cache selection too.
840 // Therefore, in the case, we don't need to notify IME of selection change
845 mBrowserChild
->SendNotifyIMESelection(mContentCache
, aIMENotification
);
850 nsresult
PuppetWidget::NotifyIMEOfMouseButtonEvent(
851 const IMENotification
& aIMENotification
) {
852 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
853 if (!mBrowserChild
) {
854 return NS_ERROR_FAILURE
;
857 bool consumedByIME
= false;
858 if (!mBrowserChild
->SendNotifyIMEMouseButtonEvent(aIMENotification
,
860 return NS_ERROR_FAILURE
;
863 return consumedByIME
? NS_SUCCESS_EVENT_CONSUMED
: NS_OK
;
866 nsresult
PuppetWidget::NotifyIMEOfPositionChange(
867 const IMENotification
& aIMENotification
) {
868 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
869 if (NS_WARN_IF(!mBrowserChild
)) {
870 return NS_ERROR_FAILURE
;
873 if (NS_WARN_IF(!mContentCache
.CacheEditorRect(this, &aIMENotification
))) {
874 return NS_ERROR_FAILURE
;
877 !mContentCache
.CacheCaretAndTextRects(this, &aIMENotification
))) {
878 return NS_ERROR_FAILURE
;
880 if (mIMENotificationRequestsOfParent
.WantPositionChanged()) {
881 mBrowserChild
->SendNotifyIMEPositionChange(mContentCache
, aIMENotification
);
883 mBrowserChild
->SendUpdateContentCache(mContentCache
);
888 struct CursorSurface
{
889 UniquePtr
<char[]> mData
;
893 void PuppetWidget::SetCursor(const Cursor
& aCursor
) {
894 if (!mBrowserChild
) {
898 const bool force
= mUpdateCursor
;
899 if (!force
&& mCursor
== aCursor
) {
903 bool hasCustomCursor
= false;
904 Maybe
<mozilla::ipc::BigBuffer
> customCursorData
;
906 IntSize customCursorSize
;
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
);
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.
929 aCursor
.mContainer
->GetFrame(imgIContainer::FRAME_CURRENT
, flags
);
932 if (RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface()) {
933 hasCustomCursor
= true;
935 nsContentUtils::GetSurfaceData(*dataSurface
, &length
, &stride
);
936 customCursorSize
= dataSurface
->GetSize();
937 format
= dataSurface
->GetFormat();
942 if (!mBrowserChild
->SendSetCursor(
943 aCursor
.mDefaultCursor
, hasCustomCursor
, std::move(customCursorData
),
944 customCursorSize
.width
, customCursorSize
.height
, resolution
.mX
,
945 resolution
.mY
, stride
, format
, aCursor
.mHotspotX
, aCursor
.mHotspotY
,
950 mUpdateCursor
= false;
953 void PuppetWidget::SetChild(PuppetWidget
* aChild
) {
954 MOZ_ASSERT(this != aChild
, "can't parent a widget to itself");
955 MOZ_ASSERT(!aChild
->mChild
,
956 "fake widget 'hierarchy' only expected to have one level");
962 PuppetWidget::WidgetPaintTask::Run() {
969 void PuppetWidget::Paint() {
970 if (!GetCurrentWidgetListener()) return;
972 mWidgetPaintTask
.Revoke();
974 RefPtr
<PuppetWidget
> strongThis(this);
976 GetCurrentWidgetListener()->WillPaintWindow(this);
978 if (GetCurrentWidgetListener()) {
979 GetCurrentWidgetListener()->DidPaintWindow();
983 void PuppetWidget::PaintNowIfNeeded() {
984 if (IsVisible() && mWidgetPaintTask
.IsPending()) {
989 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy
) {
990 if (aWhy
!= MemoryPressureReason::LOW_MEMORY_ONGOING
&& !mVisible
&&
991 mWindowRenderer
&& mWindowRenderer
->AsWebRender() &&
992 XRE_IsContentProcess()) {
993 mWindowRenderer
->AsWebRender()->ClearCachedResources();
997 bool PuppetWidget::NeedsPaint() {
998 // e10s popups are handled by the parent process, so never should be painted
1000 if (XRE_IsContentProcess() &&
1001 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
1002 mWindowType
== WindowType::Popup
) {
1003 NS_WARNING("Trying to paint an e10s popup in the child process!");
1010 LayoutDeviceIntPoint
PuppetWidget::GetChromeOffset() {
1011 if (!GetOwningBrowserChild()) {
1012 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1013 return LayoutDeviceIntPoint();
1015 return GetOwningBrowserChild()->GetChromeOffset();
1018 LayoutDeviceIntPoint
PuppetWidget::WidgetToScreenOffset() {
1019 return GetWindowPosition() + WidgetToTopLevelWidgetOffset();
1022 LayoutDeviceIntPoint
PuppetWidget::GetWindowPosition() {
1023 if (!GetOwningBrowserChild()) {
1024 return LayoutDeviceIntPoint();
1027 int32_t winX
, winY
, winW
, winH
;
1028 NS_ENSURE_SUCCESS(GetOwningBrowserChild()->GetDimensions(
1029 DimensionKind::Outer
, &winX
, &winY
, &winW
, &winH
),
1030 LayoutDeviceIntPoint());
1031 return LayoutDeviceIntPoint(winX
, winY
) +
1032 GetOwningBrowserChild()->GetClientOffset();
1035 LayoutDeviceIntRect
PuppetWidget::GetScreenBounds() {
1036 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds
.Size());
1039 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1040 return mBrowserChild
? mBrowserChild
->MaxTouchPoints() : 0;
1043 void PuppetWidget::StartAsyncScrollbarDrag(
1044 const AsyncDragMetrics
& aDragMetrics
) {
1045 mBrowserChild
->StartScrollbarDrag(aDragMetrics
);
1048 ScreenIntMargin
PuppetWidget::GetSafeAreaInsets() const {
1049 return mSafeAreaInsets
;
1052 void PuppetWidget::UpdateSafeAreaInsets(
1053 const ScreenIntMargin
& aSafeAreaInsets
) {
1054 mSafeAreaInsets
= aSafeAreaInsets
;
1057 nsIWidgetListener
* PuppetWidget::GetCurrentWidgetListener() {
1058 if (!mPreviouslyAttachedWidgetListener
|| !mAttachedWidgetListener
) {
1059 return mAttachedWidgetListener
;
1062 if (mAttachedWidgetListener
->GetView()->IsPrimaryFramePaintSuppressed()) {
1063 return mPreviouslyAttachedWidgetListener
;
1066 return mAttachedWidgetListener
;
1069 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId
,
1070 const ScrollableLayerGuid::ViewID
& aViewId
,
1071 const CSSRect
& aRect
, const uint32_t& aFlags
) {
1072 if (!mBrowserChild
) {
1076 mBrowserChild
->ZoomToRect(aPresShellId
, aViewId
, aRect
, aFlags
);
1079 void PuppetWidget::LookUpDictionary(
1080 const nsAString
& aText
, const nsTArray
<mozilla::FontRange
>& aFontRangeArray
,
1081 const bool aIsVertical
, const LayoutDeviceIntPoint
& aPoint
) {
1082 if (!mBrowserChild
) {
1086 mBrowserChild
->SendLookUpDictionary(aText
, aFontRangeArray
, aIsVertical
,
1090 bool PuppetWidget::HasPendingInputEvent() {
1091 if (!mBrowserChild
) {
1097 mBrowserChild
->GetIPCChannel()->PeekMessages(
1098 [&ret
](const IPC::Message
& aMsg
) -> bool {
1099 if (nsContentUtils::IsMessageInputEvent(aMsg
)) {
1101 return false; // Stop peeking.
1109 // TextEventDispatcherListener
1112 PuppetWidget::NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
1113 const IMENotification
& aIMENotification
) {
1114 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1116 // If there is different text event dispatcher listener for handling
1117 // text event dispatcher, that means that native keyboard events and
1118 // IME events are handled in this process. Therefore, we don't need
1119 // to send any requests and notifications to the parent process.
1120 if (mNativeTextEventDispatcherListener
) {
1121 return NS_ERROR_NOT_IMPLEMENTED
;
1124 switch (aIMENotification
.mMessage
) {
1125 case REQUEST_TO_COMMIT_COMPOSITION
:
1126 return RequestIMEToCommitComposition(false);
1127 case REQUEST_TO_CANCEL_COMPOSITION
:
1128 return RequestIMEToCommitComposition(true);
1129 case NOTIFY_IME_OF_FOCUS
:
1130 case NOTIFY_IME_OF_BLUR
:
1131 return NotifyIMEOfFocusChange(aIMENotification
);
1132 case NOTIFY_IME_OF_SELECTION_CHANGE
:
1133 return NotifyIMEOfSelectionChange(aIMENotification
);
1134 case NOTIFY_IME_OF_TEXT_CHANGE
:
1135 return NotifyIMEOfTextChange(aIMENotification
);
1136 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
1137 return NotifyIMEOfCompositionUpdate(aIMENotification
);
1138 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
1139 return NotifyIMEOfMouseButtonEvent(aIMENotification
);
1140 case NOTIFY_IME_OF_POSITION_CHANGE
:
1141 return NotifyIMEOfPositionChange(aIMENotification
);
1143 return NS_ERROR_NOT_IMPLEMENTED
;
1147 NS_IMETHODIMP_(IMENotificationRequests
)
1148 PuppetWidget::GetIMENotificationRequests() {
1149 return IMENotificationRequests(
1150 mIMENotificationRequestsOfParent
.mWantUpdates
|
1151 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
1152 IMENotificationRequests::NOTIFY_POSITION_CHANGE
);
1155 NS_IMETHODIMP_(void)
1156 PuppetWidget::OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) {
1157 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1160 NS_IMETHODIMP_(void)
1161 PuppetWidget::WillDispatchKeyboardEvent(
1162 TextEventDispatcher
* aTextEventDispatcher
,
1163 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aIndexOfKeypress
,
1165 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1168 nsresult
PuppetWidget::SetSystemFont(const nsCString
& aFontName
) {
1169 if (!mBrowserChild
) {
1170 return NS_ERROR_FAILURE
;
1173 mBrowserChild
->SendSetSystemFont(aFontName
);
1177 nsresult
PuppetWidget::GetSystemFont(nsCString
& aFontName
) {
1178 if (!mBrowserChild
) {
1179 return NS_ERROR_FAILURE
;
1181 mBrowserChild
->SendGetSystemFont(&aFontName
);
1185 } // namespace widget
1186 } // namespace mozilla