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 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
);
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),
89 mNeedIMEStateInit(false),
90 mIgnoreCompositionEvents(false) {
91 // Setting 'Unknown' means "not yet cached".
92 mInputContext
.mIMEState
.mEnabled
= IMEEnabled::Unknown
;
95 PuppetWidget::~PuppetWidget() { Destroy(); }
97 void PuppetWidget::InfallibleCreate(nsIWidget
* aParent
,
98 nsNativeWidget aNativeParent
,
99 const LayoutDeviceIntRect
& aRect
,
100 nsWidgetInitData
* aInitData
) {
101 MOZ_ASSERT(!aNativeParent
, "got a non-Puppet native parent");
103 BaseCreate(nullptr, aInitData
);
109 mDrawTarget
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
110 IntSize(1, 1), SurfaceFormat::B8G8R8A8
);
112 mNeedIMEStateInit
= MightNeedIMEFocus(aInitData
);
114 PuppetWidget
* parent
= static_cast<PuppetWidget
*>(aParent
);
116 parent
->SetChild(this);
117 mWindowRenderer
= parent
->GetWindowRenderer();
119 Resize(mBounds
.X(), mBounds
.Y(), mBounds
.Width(), mBounds
.Height(), false);
121 mMemoryPressureObserver
= MemoryPressureObserver::Create(this);
124 nsresult
PuppetWidget::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
125 const LayoutDeviceIntRect
& aRect
,
126 nsWidgetInitData
* aInitData
) {
127 InfallibleCreate(aParent
, aNativeParent
, aRect
, aInitData
);
131 void PuppetWidget::InitIMEState() {
132 MOZ_ASSERT(mBrowserChild
);
133 if (mNeedIMEStateInit
) {
134 mContentCache
.Clear();
135 mBrowserChild
->SendUpdateContentCache(mContentCache
);
136 mIMENotificationRequestsOfParent
= IMENotificationRequests();
137 mNeedIMEStateInit
= false;
141 already_AddRefed
<nsIWidget
> PuppetWidget::CreateChild(
142 const LayoutDeviceIntRect
& aRect
, nsWidgetInitData
* aInitData
,
143 bool aForceUseIWidgetParent
) {
144 bool isPopup
= IsPopup(aInitData
);
145 nsCOMPtr
<nsIWidget
> widget
= nsIWidget::CreatePuppetWidget(mBrowserChild
);
146 return ((widget
&& NS_SUCCEEDED(widget
->Create(isPopup
? nullptr : this,
147 nullptr, aRect
, aInitData
)))
152 void PuppetWidget::Destroy() {
153 if (mOnDestroyCalled
) {
156 mOnDestroyCalled
= true;
160 if (mMemoryPressureObserver
) {
161 mMemoryPressureObserver
->Unregister();
162 mMemoryPressureObserver
= nullptr;
165 if (mWindowRenderer
) {
166 mWindowRenderer
->Destroy();
168 mWindowRenderer
= nullptr;
169 mBrowserChild
= nullptr;
172 void PuppetWidget::Show(bool aState
) {
173 NS_ASSERTION(mEnabled
,
174 "does it make sense to Show()/Hide() a disabled widget?");
176 bool wasVisible
= mVisible
;
180 mChild
->mVisible
= aState
;
183 if (!wasVisible
&& mVisible
) {
184 // The previously attached widget listener is handy if
185 // we're transitioning from page to page without dropping
186 // layers (since we'll continue to show the old layers
187 // associated with that old widget listener). If the
188 // PuppetWidget was hidden, those layers are dropped,
189 // so the previously attached widget listener is really
190 // of no use anymore (and is actually actively harmful - see
192 mPreviouslyAttachedWidgetListener
= nullptr;
193 Resize(mBounds
.Width(), mBounds
.Height(), false);
198 void PuppetWidget::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
199 LayoutDeviceIntRect oldBounds
= mBounds
;
201 LayoutDeviceIntSize(NSToIntRound(aWidth
), NSToIntRound(aHeight
)));
204 mChild
->Resize(aWidth
, aHeight
, aRepaint
);
208 // XXX: roc says that |aRepaint| dictates whether or not to
209 // invalidate the expanded area
210 if (oldBounds
.Size() < mBounds
.Size() && aRepaint
) {
211 LayoutDeviceIntRegion
dirty(mBounds
);
212 dirty
.Sub(dirty
, oldBounds
);
213 InvalidateRegion(this, dirty
);
216 // call WindowResized() on both the current listener, and possibly
217 // also the previous one if we're in a state where we're drawing that one
218 // because the current one is paint suppressed
219 if (!oldBounds
.IsEqualEdges(mBounds
) && mAttachedWidgetListener
) {
220 if (GetCurrentWidgetListener() &&
221 GetCurrentWidgetListener() != mAttachedWidgetListener
) {
222 GetCurrentWidgetListener()->WindowResized(this, mBounds
.Width(),
225 mAttachedWidgetListener
->WindowResized(this, mBounds
.Width(),
230 void PuppetWidget::SetFocus(Raise aRaise
, CallerType aCallerType
) {
231 if (aRaise
== Raise::Yes
&& mBrowserChild
) {
232 mBrowserChild
->SendRequestFocus(true, aCallerType
);
236 void PuppetWidget::Invalidate(const LayoutDeviceIntRect
& aRect
) {
238 debug_DumpInvalidate(stderr
, this, &aRect
, "PuppetWidget", 0);
242 mChild
->Invalidate(aRect
);
246 if (mBrowserChild
&& !aRect
.IsEmpty() && !mWidgetPaintTask
.IsPending()) {
247 mWidgetPaintTask
= new WidgetPaintTask(this);
248 nsCOMPtr
<nsIRunnable
> event(mWidgetPaintTask
.get());
249 SchedulerGroup::Dispatch(TaskCategory::Other
, event
.forget());
253 mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
254 PuppetWidget::WidgetToTopLevelWidgetTransform() {
255 if (!GetOwningBrowserChild()) {
256 NS_WARNING("PuppetWidget without Tab does not have transform information.");
257 return mozilla::LayoutDeviceToLayoutDeviceMatrix4x4();
259 return GetOwningBrowserChild()->GetChildToParentConversionMatrix();
262 void PuppetWidget::InitEvent(WidgetGUIEvent
& aEvent
,
263 LayoutDeviceIntPoint
* aPoint
) {
264 if (nullptr == aPoint
) {
265 aEvent
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
267 // use the point override if provided
268 aEvent
.mRefPoint
= *aPoint
;
270 aEvent
.mTime
= PR_Now() / 1000;
273 nsresult
PuppetWidget::DispatchEvent(WidgetGUIEvent
* aEvent
,
274 nsEventStatus
& aStatus
) {
276 debug_DumpEvent(stdout
, aEvent
->mWidget
, aEvent
, "PuppetWidget", 0);
279 MOZ_ASSERT(!mChild
|| mChild
->mWindowType
== eWindowType_popup
,
280 "Unexpected event dispatch!");
282 MOZ_ASSERT(!aEvent
->AsKeyboardEvent() ||
283 aEvent
->mFlags
.mIsSynthesizedForTests
||
284 aEvent
->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
285 "Non-sysnthesized keyboard events should have edit commands for "
287 "before dispatched");
289 if (aEvent
->mClass
== eCompositionEventClass
) {
290 // If we've already requested to commit/cancel the latest composition,
291 // TextComposition for the old composition has been destroyed. Then,
292 // the DOM tree needs to listen to next eCompositionStart and its
293 // following events. So, until we meet new eCompositionStart, let's
294 // discard all unnecessary composition events here.
295 if (mIgnoreCompositionEvents
) {
296 if (aEvent
->mMessage
!= eCompositionStart
) {
297 aStatus
= nsEventStatus_eIgnore
;
300 // Now, we receive new eCompositionStart. Let's restart to handle
301 // composition in this process.
302 mIgnoreCompositionEvents
= false;
304 // Store the latest native IME context of parent process's widget or
305 // TextEventDispatcher if it's in this process.
306 WidgetCompositionEvent
* compositionEvent
= aEvent
->AsCompositionEvent();
308 if (mNativeIMEContext
.IsValid() &&
309 mNativeIMEContext
!= compositionEvent
->mNativeIMEContext
) {
310 RefPtr
<TextComposition
> composition
=
311 IMEStateManager::GetTextCompositionFor(this);
314 "When there is composition caused by old native IME context, "
315 "composition events caused by different native IME context are not "
318 #endif // #ifdef DEBUG
319 mNativeIMEContext
= compositionEvent
->mNativeIMEContext
;
320 mContentCache
.OnCompositionEvent(*compositionEvent
);
323 // If the event is a composition event or a keyboard event, it should be
324 // dispatched with TextEventDispatcher if we could do that with current
325 // design. However, we cannot do that without big changes and the behavior
326 // is not so complicated for now. Therefore, we should just notify it
327 // of dispatching events and TextEventDispatcher should emulate the state
329 if (aEvent
->mClass
== eCompositionEventClass
||
330 aEvent
->mClass
== eKeyboardEventClass
) {
331 TextEventDispatcher
* dispatcher
= GetTextEventDispatcher();
332 // However, if the event is being dispatched by the text event dispatcher
333 // or, there is native text event dispatcher listener, that means that
334 // native text input event handler is in this process like on Android,
335 // and the event is not synthesized for tests, the event is coming from
336 // the TextEventDispatcher. In these cases, we shouldn't notify
337 // TextEventDispatcher of dispatching the event.
338 if (!dispatcher
->IsDispatchingEvent() &&
339 !(mNativeTextEventDispatcherListener
&&
340 !aEvent
->mFlags
.mIsSynthesizedForTests
)) {
341 DebugOnly
<nsresult
> rv
=
342 dispatcher
->BeginInputTransactionFor(aEvent
, this);
343 NS_WARNING_ASSERTION(
345 "The text event dispatcher should always succeed to start input "
346 "transaction for the event");
350 aStatus
= nsEventStatus_eIgnore
;
352 if (GetCurrentWidgetListener()) {
354 GetCurrentWidgetListener()->HandleEvent(aEvent
, mUseAttachedEvents
);
360 nsIWidget::ContentAndAPZEventStatus
PuppetWidget::DispatchInputEvent(
361 WidgetInputEvent
* aEvent
) {
362 ContentAndAPZEventStatus status
;
363 if (!AsyncPanZoomEnabled()) {
364 DispatchEvent(aEvent
, status
.mContentStatus
);
368 if (!mBrowserChild
) {
372 switch (aEvent
->mClass
) {
373 case eWheelEventClass
:
374 Unused
<< mBrowserChild
->SendDispatchWheelEvent(*aEvent
->AsWheelEvent());
376 case eMouseEventClass
:
377 Unused
<< mBrowserChild
->SendDispatchMouseEvent(*aEvent
->AsMouseEvent());
379 case eKeyboardEventClass
:
380 Unused
<< mBrowserChild
->SendDispatchKeyboardEvent(
381 *aEvent
->AsKeyboardEvent());
383 case eTouchEventClass
:
384 Unused
<< mBrowserChild
->SendDispatchTouchEvent(*aEvent
->AsTouchEvent());
387 MOZ_ASSERT_UNREACHABLE("unsupported event type");
393 nsresult
PuppetWidget::SynthesizeNativeKeyEvent(
394 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
395 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
396 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
397 AutoObserverNotifier
notifier(aObserver
, "keyevent");
398 if (!mBrowserChild
) {
399 return NS_ERROR_FAILURE
;
401 mBrowserChild
->SendSynthesizeNativeKeyEvent(
402 aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
,
403 nsString(aCharacters
), nsString(aUnmodifiedCharacters
),
404 notifier
.SaveObserver());
408 nsresult
PuppetWidget::SynthesizeNativeMouseEvent(
409 mozilla::LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
410 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
411 nsIObserver
* aObserver
) {
412 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
413 if (!mBrowserChild
) {
414 return NS_ERROR_FAILURE
;
416 mBrowserChild
->SendSynthesizeNativeMouseEvent(
417 aPoint
, static_cast<uint32_t>(aNativeMessage
),
418 static_cast<int16_t>(aButton
), static_cast<uint32_t>(aModifierFlags
),
419 notifier
.SaveObserver());
423 nsresult
PuppetWidget::SynthesizeNativeMouseMove(
424 mozilla::LayoutDeviceIntPoint aPoint
, nsIObserver
* aObserver
) {
425 AutoObserverNotifier
notifier(aObserver
, "mousemove");
426 if (!mBrowserChild
) {
427 return NS_ERROR_FAILURE
;
429 mBrowserChild
->SendSynthesizeNativeMouseMove(aPoint
, notifier
.SaveObserver());
433 nsresult
PuppetWidget::SynthesizeNativeMouseScrollEvent(
434 mozilla::LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
,
435 double aDeltaX
, double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
436 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
437 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
438 if (!mBrowserChild
) {
439 return NS_ERROR_FAILURE
;
441 mBrowserChild
->SendSynthesizeNativeMouseScrollEvent(
442 aPoint
, aNativeMessage
, aDeltaX
, aDeltaY
, aDeltaZ
, aModifierFlags
,
443 aAdditionalFlags
, notifier
.SaveObserver());
447 nsresult
PuppetWidget::SynthesizeNativeTouchPoint(
448 uint32_t aPointerId
, TouchPointerState aPointerState
,
449 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
450 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
451 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
452 if (!mBrowserChild
) {
453 return NS_ERROR_FAILURE
;
455 mBrowserChild
->SendSynthesizeNativeTouchPoint(
456 aPointerId
, aPointerState
, aPoint
, aPointerPressure
, aPointerOrientation
,
457 notifier
.SaveObserver());
461 nsresult
PuppetWidget::SynthesizeNativeTouchPadPinch(
462 TouchpadGesturePhase aEventPhase
, float aScale
, LayoutDeviceIntPoint aPoint
,
463 int32_t aModifierFlags
) {
464 if (!mBrowserChild
) {
465 return NS_ERROR_FAILURE
;
467 mBrowserChild
->SendSynthesizeNativeTouchPadPinch(aEventPhase
, aScale
, aPoint
,
472 nsresult
PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint
,
474 nsIObserver
* aObserver
) {
475 AutoObserverNotifier
notifier(aObserver
, "touchtap");
476 if (!mBrowserChild
) {
477 return NS_ERROR_FAILURE
;
479 mBrowserChild
->SendSynthesizeNativeTouchTap(aPoint
, aLongTap
,
480 notifier
.SaveObserver());
484 nsresult
PuppetWidget::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
485 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
486 if (!mBrowserChild
) {
487 return NS_ERROR_FAILURE
;
489 mBrowserChild
->SendClearNativeTouchSequence(notifier
.SaveObserver());
493 nsresult
PuppetWidget::SynthesizeNativePenInput(
494 uint32_t aPointerId
, TouchPointerState aPointerState
,
495 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
496 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
497 AutoObserverNotifier
notifier(aObserver
, "peninput");
498 if (!mBrowserChild
) {
499 return NS_ERROR_FAILURE
;
501 mBrowserChild
->SendSynthesizeNativePenInput(
502 aPointerId
, aPointerState
, aPoint
, aPressure
, aRotation
, aTiltX
, aTiltY
,
503 aButton
, notifier
.SaveObserver());
507 nsresult
PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
508 LayoutDeviceIntPoint aPoint
, uint32_t aModifierFlags
) {
509 if (!mBrowserChild
) {
510 return NS_ERROR_FAILURE
;
512 mBrowserChild
->SendSynthesizeNativeTouchpadDoubleTap(aPoint
, aModifierFlags
);
516 nsresult
PuppetWidget::SynthesizeNativeTouchpadPan(
517 TouchpadGesturePhase aEventPhase
, LayoutDeviceIntPoint aPoint
,
518 double aDeltaX
, double aDeltaY
, int32_t aModifierFlags
) {
519 if (!mBrowserChild
) {
520 return NS_ERROR_FAILURE
;
522 mBrowserChild
->SendSynthesizeNativeTouchpadPan(aEventPhase
, aPoint
, aDeltaX
,
523 aDeltaY
, aModifierFlags
);
527 void PuppetWidget::LockNativePointer() {
528 if (!mBrowserChild
) {
531 mBrowserChild
->SendLockNativePointer();
534 void PuppetWidget::UnlockNativePointer() {
535 if (!mBrowserChild
) {
538 mBrowserChild
->SendUnlockNativePointer();
541 void PuppetWidget::SetConfirmedTargetAPZC(
542 uint64_t aInputBlockId
,
543 const nsTArray
<ScrollableLayerGuid
>& aTargets
) const {
545 mBrowserChild
->SetTargetAPZC(aInputBlockId
, aTargets
);
549 void PuppetWidget::UpdateZoomConstraints(
550 const uint32_t& aPresShellId
, const ScrollableLayerGuid::ViewID
& aViewId
,
551 const Maybe
<ZoomConstraints
>& aConstraints
) {
553 mBrowserChild
->DoUpdateZoomConstraints(aPresShellId
, aViewId
, aConstraints
);
557 bool PuppetWidget::AsyncPanZoomEnabled() const {
558 return mBrowserChild
&& mBrowserChild
->AsyncPanZoomEnabled();
561 bool PuppetWidget::GetEditCommands(NativeKeyBindingsType aType
,
562 const WidgetKeyboardEvent
& aEvent
,
563 nsTArray
<CommandInt
>& aCommands
) {
564 MOZ_ASSERT(!aEvent
.mFlags
.mIsSynthesizedForTests
);
565 // Validate the arguments.
566 if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType
, aEvent
, aCommands
))) {
569 if (NS_WARN_IF(!mBrowserChild
)) {
572 mBrowserChild
->RequestEditCommands(aType
, aEvent
, aCommands
);
576 WindowRenderer
* PuppetWidget::GetWindowRenderer() {
577 if (!mWindowRenderer
) {
578 if (XRE_IsParentProcess()) {
579 // On the parent process there is no CompositorBridgeChild which confuses
580 // some layers code, so we use basic layers instead. Note that we create
581 mWindowRenderer
= new FallbackRenderer
;
582 return mWindowRenderer
;
585 // If we know for sure that the parent side of this BrowserChild is not
586 // connected to the compositor, we don't want to use a "remote" layer
587 // manager like WebRender or Client. Instead we use a Basic one which
588 // can do drawing in this process.
589 MOZ_ASSERT(!mBrowserChild
||
590 mBrowserChild
->IsLayersConnected() != Some(true));
591 mWindowRenderer
= CreateFallbackRenderer();
594 return mWindowRenderer
;
597 bool PuppetWidget::CreateRemoteLayerManager(
598 const std::function
<bool(WebRenderLayerManager
*)>& aInitializeFunc
) {
599 RefPtr
<WebRenderLayerManager
> lm
= new WebRenderLayerManager(this);
600 MOZ_ASSERT(mBrowserChild
);
602 if (!aInitializeFunc(lm
)) {
606 // Force the old LM to self destruct, otherwise if the reference dangles we
607 // could fail to revoke the most recent transaction. We only want to replace
608 // it if we successfully create its successor because a partially initialized
609 // layer manager is worse than a fully initialized but shutdown layer manager.
610 DestroyLayerManager();
611 mWindowRenderer
= std::move(lm
);
615 nsresult
PuppetWidget::RequestIMEToCommitComposition(bool aCancel
) {
616 if (!mBrowserChild
) {
617 return NS_ERROR_FAILURE
;
620 MOZ_ASSERT(!Destroyed());
622 // There must not be composition which is caused by the PuppetWidget instance.
623 if (NS_WARN_IF(!mNativeIMEContext
.IsValid())) {
627 // We've already requested to commit/cancel composition.
628 if (NS_WARN_IF(mIgnoreCompositionEvents
)) {
630 RefPtr
<TextComposition
> composition
=
631 IMEStateManager::GetTextCompositionFor(this);
632 MOZ_ASSERT(!composition
);
633 #endif // #ifdef DEBUG
637 RefPtr
<TextComposition
> composition
=
638 IMEStateManager::GetTextCompositionFor(this);
639 // This method shouldn't be called when there is no text composition instance.
640 if (NS_WARN_IF(!composition
)) {
644 MOZ_DIAGNOSTIC_ASSERT(
645 composition
->IsRequestingCommitOrCancelComposition(),
646 "Requesting commit or cancel composition should be requested via "
647 "TextComposition instance");
649 bool isCommitted
= false;
650 nsAutoString committedString
;
651 if (NS_WARN_IF(!mBrowserChild
->SendRequestIMEToCommitComposition(
652 aCancel
, &isCommitted
, &committedString
))) {
653 return NS_ERROR_FAILURE
;
656 // If the composition wasn't committed synchronously, we need to wait async
657 // composition events for destroying the TextComposition instance.
662 // Dispatch eCompositionCommit event.
663 WidgetCompositionEvent
compositionCommitEvent(true, eCompositionCommit
, this);
664 InitEvent(compositionCommitEvent
, nullptr);
665 compositionCommitEvent
.mData
= committedString
;
666 nsEventStatus status
= nsEventStatus_eIgnore
;
667 DispatchEvent(&compositionCommitEvent
, status
);
670 RefPtr
<TextComposition
> currentComposition
=
671 IMEStateManager::GetTextCompositionFor(this);
672 MOZ_ASSERT(!currentComposition
);
673 #endif // #ifdef DEBUG
675 // Ignore the following composition events until we receive new
676 // eCompositionStart event.
677 mIgnoreCompositionEvents
= true;
679 Unused
<< mBrowserChild
->SendOnEventNeedingAckHandled(
680 eCompositionCommitRequestHandled
);
682 // NOTE: PuppetWidget might be destroyed already.
686 // When this widget caches input context and currently managed by
687 // IMEStateManager, the cache is valid.
688 bool PuppetWidget::HaveValidInputContextCache() const {
689 return (mInputContext
.mIMEState
.mEnabled
!= IMEEnabled::Unknown
&&
690 IMEStateManager::GetWidgetForActiveInputContext() == this);
693 nsRefreshDriver
* PuppetWidget::GetTopLevelRefreshDriver() const {
694 if (!mBrowserChild
) {
698 if (PresShell
* presShell
= mBrowserChild
->GetTopLevelPresShell()) {
699 return presShell
->GetRefreshDriver();
705 void PuppetWidget::SetInputContext(const InputContext
& aContext
,
706 const InputContextAction
& aAction
) {
707 mInputContext
= aContext
;
708 // Any widget instances cannot cache IME open state because IME open state
709 // can be changed by user but native IME may not notify us of changing the
710 // open state on some platforms.
711 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN_STATE_NOT_SUPPORTED
;
712 if (!mBrowserChild
) {
715 mBrowserChild
->SendSetInputContext(aContext
, aAction
);
718 InputContext
PuppetWidget::GetInputContext() {
719 // XXX Currently, we don't support retrieving IME open state from child
722 // If the cache of input context is valid, we can avoid to use synchronous
724 if (HaveValidInputContextCache()) {
725 return mInputContext
;
728 NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
730 // Don't cache InputContext here because this process isn't managing IME
731 // state of the chrome widget. So, we cannot modify mInputContext when
732 // chrome widget is set to new context.
733 InputContext context
;
735 mBrowserChild
->SendGetInputContext(&context
.mIMEState
);
740 NativeIMEContext
PuppetWidget::GetNativeIMEContext() {
741 return mNativeIMEContext
;
744 nsresult
PuppetWidget::NotifyIMEOfFocusChange(
745 const IMENotification
& aIMENotification
) {
746 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
748 if (!mBrowserChild
) {
749 return NS_ERROR_FAILURE
;
752 bool gotFocus
= aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
;
754 // When IME gets focus, we should initialize all information of the
756 if (NS_WARN_IF(!mContentCache
.CacheAll(this, &aIMENotification
))) {
757 return NS_ERROR_FAILURE
;
760 // When IME loses focus, we don't need to store anything.
761 mContentCache
.Clear();
764 mIMENotificationRequestsOfParent
=
765 IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL
);
766 RefPtr
<PuppetWidget
> self
= this;
767 mBrowserChild
->SendNotifyIMEFocus(mContentCache
, aIMENotification
)
769 GetMainThreadSerialEventTarget(), __func__
,
770 [self
](IMENotificationRequests
&& aRequests
) {
771 self
->mIMENotificationRequestsOfParent
= aRequests
;
772 if (TextEventDispatcher
* dispatcher
=
773 self
->GetTextEventDispatcher()) {
774 dispatcher
->OnWidgetChangeIMENotificationRequests(self
);
777 [self
](mozilla::ipc::ResponseRejectReason
&& aReason
) {
778 NS_WARNING("SendNotifyIMEFocus got rejected.");
784 nsresult
PuppetWidget::NotifyIMEOfCompositionUpdate(
785 const IMENotification
& aIMENotification
) {
786 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
788 if (NS_WARN_IF(!mBrowserChild
)) {
789 return NS_ERROR_FAILURE
;
792 if (NS_WARN_IF(!mContentCache
.CacheSelection(this, &aIMENotification
))) {
793 return NS_ERROR_FAILURE
;
795 mBrowserChild
->SendNotifyIMECompositionUpdate(mContentCache
,
800 nsresult
PuppetWidget::NotifyIMEOfTextChange(
801 const IMENotification
& aIMENotification
) {
802 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
803 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
,
804 "Passed wrong notification");
806 if (!mBrowserChild
) {
807 return NS_ERROR_FAILURE
;
810 // FYI: text change notification is the first notification after
811 // a user operation changes the content. So, we need to modify
812 // the cache as far as possible here.
814 if (NS_WARN_IF(!mContentCache
.CacheText(this, &aIMENotification
))) {
815 return NS_ERROR_FAILURE
;
818 // BrowserParent doesn't this this to cache. we don't send the notification
819 // if parent process doesn't request NOTIFY_TEXT_CHANGE.
820 if (mIMENotificationRequestsOfParent
.WantTextChange()) {
821 mBrowserChild
->SendNotifyIMETextChange(mContentCache
, aIMENotification
);
823 mBrowserChild
->SendUpdateContentCache(mContentCache
);
828 nsresult
PuppetWidget::NotifyIMEOfSelectionChange(
829 const IMENotification
& aIMENotification
) {
830 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
831 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
,
832 "Passed wrong notification");
833 if (!mBrowserChild
) {
834 return NS_ERROR_FAILURE
;
837 // Note that selection change must be notified after text change if it occurs.
838 // Therefore, we don't need to query text content again here.
839 mContentCache
.SetSelection(this, aIMENotification
.mSelectionChangeData
);
841 mBrowserChild
->SendNotifyIMESelection(mContentCache
, aIMENotification
);
846 nsresult
PuppetWidget::NotifyIMEOfMouseButtonEvent(
847 const IMENotification
& aIMENotification
) {
848 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
849 if (!mBrowserChild
) {
850 return NS_ERROR_FAILURE
;
853 bool consumedByIME
= false;
854 if (!mBrowserChild
->SendNotifyIMEMouseButtonEvent(aIMENotification
,
856 return NS_ERROR_FAILURE
;
859 return consumedByIME
? NS_SUCCESS_EVENT_CONSUMED
: NS_OK
;
862 nsresult
PuppetWidget::NotifyIMEOfPositionChange(
863 const IMENotification
& aIMENotification
) {
864 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
865 if (NS_WARN_IF(!mBrowserChild
)) {
866 return NS_ERROR_FAILURE
;
869 if (NS_WARN_IF(!mContentCache
.CacheEditorRect(this, &aIMENotification
))) {
870 return NS_ERROR_FAILURE
;
872 if (NS_WARN_IF(!mContentCache
.CacheSelection(this, &aIMENotification
))) {
873 return NS_ERROR_FAILURE
;
875 if (mIMENotificationRequestsOfParent
.WantPositionChanged()) {
876 mBrowserChild
->SendNotifyIMEPositionChange(mContentCache
, aIMENotification
);
878 mBrowserChild
->SendUpdateContentCache(mContentCache
);
883 struct CursorSurface
{
884 UniquePtr
<char[]> mData
;
888 void PuppetWidget::SetCursor(const Cursor
& aCursor
) {
889 if (!mBrowserChild
) {
893 const bool force
= mUpdateCursor
;
894 if (!force
&& mCursor
== aCursor
) {
898 bool hasCustomCursor
= false;
899 UniquePtr
<char[]> customCursorData
;
901 IntSize customCursorSize
;
903 auto format
= SurfaceFormat::B8G8R8A8
;
904 ImageResolution resolution
= aCursor
.mResolution
;
905 if (aCursor
.IsCustom()) {
906 int32_t width
= 0, height
= 0;
907 aCursor
.mContainer
->GetWidth(&width
);
908 aCursor
.mContainer
->GetHeight(&height
);
909 const int32_t flags
=
910 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
;
911 RefPtr
<SourceSurface
> surface
;
912 if (width
&& height
&&
913 aCursor
.mContainer
->GetType() == imgIContainer::TYPE_VECTOR
) {
914 // For vector images, scale to device pixels.
915 resolution
.ScaleBy(GetDefaultScale().scale
);
916 resolution
.ApplyInverseTo(width
, height
);
917 surface
= aCursor
.mContainer
->GetFrameAtSize(
918 {width
, height
}, imgIContainer::FRAME_CURRENT
, flags
);
920 // NOTE(emilio): We get the frame at the full size, ignoring resolution,
921 // because we're going to rasterize it, and we'd effectively lose the
922 // extra pixels if we rasterized to CustomCursorSize.
924 aCursor
.mContainer
->GetFrame(imgIContainer::FRAME_CURRENT
, flags
);
927 if (RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface()) {
928 hasCustomCursor
= true;
929 customCursorData
= nsContentUtils::GetSurfaceData(
930 WrapNotNull(dataSurface
), &length
, &stride
);
931 customCursorSize
= dataSurface
->GetSize();
932 format
= dataSurface
->GetFormat();
937 nsDependentCString
cursorData(customCursorData
? customCursorData
.get() : "",
939 if (!mBrowserChild
->SendSetCursor(
940 aCursor
.mDefaultCursor
, hasCustomCursor
, cursorData
,
941 customCursorSize
.width
, customCursorSize
.height
, resolution
.mX
,
942 resolution
.mY
, stride
, format
, aCursor
.mHotspotX
, aCursor
.mHotspotY
,
949 void PuppetWidget::SetChild(PuppetWidget
* aChild
) {
950 MOZ_ASSERT(this != aChild
, "can't parent a widget to itself");
951 MOZ_ASSERT(!aChild
->mChild
,
952 "fake widget 'hierarchy' only expected to have one level");
958 PuppetWidget::WidgetPaintTask::Run() {
965 void PuppetWidget::Paint() {
966 if (!GetCurrentWidgetListener()) return;
968 mWidgetPaintTask
.Revoke();
970 RefPtr
<PuppetWidget
> strongThis(this);
972 GetCurrentWidgetListener()->WillPaintWindow(this);
974 if (GetCurrentWidgetListener()) {
975 GetCurrentWidgetListener()->DidPaintWindow();
979 void PuppetWidget::PaintNowIfNeeded() {
980 if (IsVisible() && mWidgetPaintTask
.IsPending()) {
985 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy
) {
986 if (aWhy
!= MemoryPressureReason::LOW_MEMORY_ONGOING
&& !mVisible
&&
987 mWindowRenderer
&& mWindowRenderer
->AsWebRender() &&
988 XRE_IsContentProcess()) {
989 mWindowRenderer
->AsWebRender()->ClearCachedResources();
993 bool PuppetWidget::NeedsPaint() {
994 // e10s popups are handled by the parent process, so never should be painted
996 if (XRE_IsContentProcess() &&
997 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
998 mWindowType
== eWindowType_popup
) {
999 NS_WARNING("Trying to paint an e10s popup in the child process!");
1006 float PuppetWidget::GetDPI() { return mDPI
; }
1008 double PuppetWidget::GetDefaultScaleInternal() { return mDefaultScale
; }
1010 int32_t PuppetWidget::RoundsWidgetCoordinatesTo() { return mRounding
; }
1012 LayoutDeviceIntPoint
PuppetWidget::GetChromeOffset() {
1013 if (!GetOwningBrowserChild()) {
1014 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1015 return LayoutDeviceIntPoint();
1017 return GetOwningBrowserChild()->GetChromeOffset();
1020 LayoutDeviceIntPoint
PuppetWidget::WidgetToScreenOffset() {
1021 auto positionRalativeToWindow
=
1022 WidgetToTopLevelWidgetTransform().TransformPoint(LayoutDevicePoint());
1024 return GetWindowPosition() +
1025 LayoutDeviceIntPoint::Round(positionRalativeToWindow
);
1028 LayoutDeviceIntPoint
PuppetWidget::GetWindowPosition() {
1029 if (!GetOwningBrowserChild()) {
1030 return LayoutDeviceIntPoint();
1033 int32_t winX
, winY
, winW
, winH
;
1035 GetOwningBrowserChild()->GetDimensions(0, &winX
, &winY
, &winW
, &winH
),
1036 LayoutDeviceIntPoint());
1037 return LayoutDeviceIntPoint(winX
, winY
) +
1038 GetOwningBrowserChild()->GetClientOffset();
1041 LayoutDeviceIntRect
PuppetWidget::GetScreenBounds() {
1042 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds
.Size());
1045 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1046 return mBrowserChild
? mBrowserChild
->MaxTouchPoints() : 0;
1049 void PuppetWidget::StartAsyncScrollbarDrag(
1050 const AsyncDragMetrics
& aDragMetrics
) {
1051 mBrowserChild
->StartScrollbarDrag(aDragMetrics
);
1054 ScreenIntMargin
PuppetWidget::GetSafeAreaInsets() const {
1055 return mSafeAreaInsets
;
1058 void PuppetWidget::UpdateSafeAreaInsets(
1059 const ScreenIntMargin
& aSafeAreaInsets
) {
1060 mSafeAreaInsets
= aSafeAreaInsets
;
1063 nsIWidgetListener
* PuppetWidget::GetCurrentWidgetListener() {
1064 if (!mPreviouslyAttachedWidgetListener
|| !mAttachedWidgetListener
) {
1065 return mAttachedWidgetListener
;
1068 if (mAttachedWidgetListener
->GetView()->IsPrimaryFramePaintSuppressed()) {
1069 return mPreviouslyAttachedWidgetListener
;
1072 return mAttachedWidgetListener
;
1075 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId
,
1076 const ScrollableLayerGuid::ViewID
& aViewId
,
1077 const CSSRect
& aRect
, const uint32_t& aFlags
) {
1078 if (!mBrowserChild
) {
1082 mBrowserChild
->ZoomToRect(aPresShellId
, aViewId
, aRect
, aFlags
);
1085 void PuppetWidget::LookUpDictionary(
1086 const nsAString
& aText
, const nsTArray
<mozilla::FontRange
>& aFontRangeArray
,
1087 const bool aIsVertical
, const LayoutDeviceIntPoint
& aPoint
) {
1088 if (!mBrowserChild
) {
1092 mBrowserChild
->SendLookUpDictionary(nsString(aText
), aFontRangeArray
,
1093 aIsVertical
, aPoint
);
1096 bool PuppetWidget::HasPendingInputEvent() {
1097 if (!mBrowserChild
) {
1103 mBrowserChild
->GetIPCChannel()->PeekMessages(
1104 [&ret
](const IPC::Message
& aMsg
) -> bool {
1105 if (nsContentUtils::IsMessageInputEvent(aMsg
)) {
1107 return false; // Stop peeking.
1115 // TextEventDispatcherListener
1118 PuppetWidget::NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
1119 const IMENotification
& aIMENotification
) {
1120 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1122 // If there is different text event dispatcher listener for handling
1123 // text event dispatcher, that means that native keyboard events and
1124 // IME events are handled in this process. Therefore, we don't need
1125 // to send any requests and notifications to the parent process.
1126 if (mNativeTextEventDispatcherListener
) {
1127 return NS_ERROR_NOT_IMPLEMENTED
;
1130 switch (aIMENotification
.mMessage
) {
1131 case REQUEST_TO_COMMIT_COMPOSITION
:
1132 return RequestIMEToCommitComposition(false);
1133 case REQUEST_TO_CANCEL_COMPOSITION
:
1134 return RequestIMEToCommitComposition(true);
1135 case NOTIFY_IME_OF_FOCUS
:
1136 case NOTIFY_IME_OF_BLUR
:
1137 return NotifyIMEOfFocusChange(aIMENotification
);
1138 case NOTIFY_IME_OF_SELECTION_CHANGE
:
1139 return NotifyIMEOfSelectionChange(aIMENotification
);
1140 case NOTIFY_IME_OF_TEXT_CHANGE
:
1141 return NotifyIMEOfTextChange(aIMENotification
);
1142 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
1143 return NotifyIMEOfCompositionUpdate(aIMENotification
);
1144 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
1145 return NotifyIMEOfMouseButtonEvent(aIMENotification
);
1146 case NOTIFY_IME_OF_POSITION_CHANGE
:
1147 return NotifyIMEOfPositionChange(aIMENotification
);
1149 return NS_ERROR_NOT_IMPLEMENTED
;
1152 return NS_ERROR_NOT_IMPLEMENTED
;
1155 NS_IMETHODIMP_(IMENotificationRequests
)
1156 PuppetWidget::GetIMENotificationRequests() {
1157 return IMENotificationRequests(
1158 mIMENotificationRequestsOfParent
.mWantUpdates
|
1159 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
1160 IMENotificationRequests::NOTIFY_POSITION_CHANGE
);
1163 NS_IMETHODIMP_(void)
1164 PuppetWidget::OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) {
1165 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1168 NS_IMETHODIMP_(void)
1169 PuppetWidget::WillDispatchKeyboardEvent(
1170 TextEventDispatcher
* aTextEventDispatcher
,
1171 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aIndexOfKeypress
,
1173 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1176 nsresult
PuppetWidget::SetSystemFont(const nsCString
& aFontName
) {
1177 if (!mBrowserChild
) {
1178 return NS_ERROR_FAILURE
;
1181 mBrowserChild
->SendSetSystemFont(aFontName
);
1185 nsresult
PuppetWidget::GetSystemFont(nsCString
& aFontName
) {
1186 if (!mBrowserChild
) {
1187 return NS_ERROR_FAILURE
;
1189 mBrowserChild
->SendGetSystemFont(&aFontName
);
1193 } // namespace widget
1194 } // namespace mozilla