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/Hal.h"
15 #include "mozilla/IMEStateManager.h"
16 #include "mozilla/layers/APZChild.h"
17 #include "mozilla/layers/WebRenderLayerManager.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::hal
;
38 using namespace mozilla::gfx
;
39 using namespace mozilla::layers
;
40 using namespace mozilla::widget
;
42 static void InvalidateRegion(nsIWidget
* aWidget
,
43 const LayoutDeviceIntRegion
& aRegion
) {
44 for (auto iter
= aRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
45 aWidget
->Invalidate(iter
.Get());
50 already_AddRefed
<nsIWidget
> nsIWidget::CreatePuppetWidget(
51 BrowserChild
* aBrowserChild
) {
52 MOZ_ASSERT(!aBrowserChild
|| nsIWidget::UsePuppetWidgets(),
53 "PuppetWidgets not allowed in this configuration");
55 nsCOMPtr
<nsIWidget
> widget
= new PuppetWidget(aBrowserChild
);
56 return widget
.forget();
62 static bool IsPopup(const nsWidgetInitData
* aInitData
) {
63 return aInitData
&& aInitData
->mWindowType
== eWindowType_popup
;
66 static bool MightNeedIMEFocus(const nsWidgetInitData
* aInitData
) {
67 // In the puppet-widget world, popup widgets are just dummies and
68 // shouldn't try to mess with IME state.
69 #ifdef MOZ_CROSS_PROCESS_IME
70 return !IsPopup(aInitData
);
76 // Arbitrary, fungible.
77 const size_t PuppetWidget::kMaxDimension
= 4000;
79 NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget
, nsBaseWidget
,
80 TextEventDispatcherListener
)
82 PuppetWidget::PuppetWidget(BrowserChild
* aBrowserChild
)
83 : mBrowserChild(aBrowserChild
),
84 mMemoryPressureObserver(nullptr),
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
);
110 mDrawTarget
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
111 IntSize(1, 1), SurfaceFormat::B8G8R8A8
);
113 mNeedIMEStateInit
= MightNeedIMEFocus(aInitData
);
115 PuppetWidget
* parent
= static_cast<PuppetWidget
*>(aParent
);
117 parent
->SetChild(this);
118 mWindowRenderer
= parent
->GetWindowRenderer();
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
);
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
)))
153 void PuppetWidget::Destroy() {
154 if (mOnDestroyCalled
) {
157 mOnDestroyCalled
= true;
161 if (mMemoryPressureObserver
) {
162 mMemoryPressureObserver
->Unregister();
163 mMemoryPressureObserver
= 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
;
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
193 mPreviouslyAttachedWidgetListener
= nullptr;
194 Resize(mBounds
.Width(), mBounds
.Height(), false);
199 void PuppetWidget::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
200 LayoutDeviceIntRect oldBounds
= mBounds
;
202 LayoutDeviceIntSize(NSToIntRound(aWidth
), NSToIntRound(aHeight
)));
205 mChild
->Resize(aWidth
, aHeight
, aRepaint
);
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(),
226 mAttachedWidgetListener
->WindowResized(this, mBounds
.Width(),
231 nsresult
PuppetWidget::ConfigureChildren(
232 const nsTArray
<Configuration
>& aConfigurations
) {
233 for (uint32_t i
= 0; i
< aConfigurations
.Length(); ++i
) {
234 const Configuration
& configuration
= aConfigurations
[i
];
235 PuppetWidget
* w
= static_cast<PuppetWidget
*>(configuration
.mChild
.get());
236 NS_ASSERTION(w
->GetParent() == this, "Configured widget is not a child");
237 w
->SetWindowClipRegion(configuration
.mClipRegion
, true);
238 LayoutDeviceIntRect bounds
= w
->GetBounds();
239 if (bounds
.Size() != configuration
.mBounds
.Size()) {
240 w
->Resize(configuration
.mBounds
.X(), configuration
.mBounds
.Y(),
241 configuration
.mBounds
.Width(), configuration
.mBounds
.Height(),
243 } else if (bounds
.TopLeft() != configuration
.mBounds
.TopLeft()) {
244 w
->Move(configuration
.mBounds
.X(), configuration
.mBounds
.Y());
246 w
->SetWindowClipRegion(configuration
.mClipRegion
, false);
251 void PuppetWidget::SetFocus(Raise aRaise
, CallerType aCallerType
) {
252 if (aRaise
== Raise::Yes
&& mBrowserChild
) {
253 mBrowserChild
->SendRequestFocus(true, aCallerType
);
257 void PuppetWidget::Invalidate(const LayoutDeviceIntRect
& aRect
) {
259 debug_DumpInvalidate(stderr
, this, &aRect
, "PuppetWidget", 0);
263 mChild
->Invalidate(aRect
);
267 if (mBrowserChild
&& !aRect
.IsEmpty() && !mWidgetPaintTask
.IsPending()) {
268 mWidgetPaintTask
= new WidgetPaintTask(this);
269 nsCOMPtr
<nsIRunnable
> event(mWidgetPaintTask
.get());
270 SchedulerGroup::Dispatch(TaskCategory::Other
, event
.forget());
274 mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
275 PuppetWidget::WidgetToTopLevelWidgetTransform() {
276 if (!GetOwningBrowserChild()) {
277 NS_WARNING("PuppetWidget without Tab does not have transform information.");
278 return mozilla::LayoutDeviceToLayoutDeviceMatrix4x4();
280 return GetOwningBrowserChild()->GetChildToParentConversionMatrix();
283 void PuppetWidget::InitEvent(WidgetGUIEvent
& aEvent
,
284 LayoutDeviceIntPoint
* aPoint
) {
285 if (nullptr == aPoint
) {
286 aEvent
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
288 // use the point override if provided
289 aEvent
.mRefPoint
= *aPoint
;
291 aEvent
.mTime
= PR_Now() / 1000;
294 nsresult
PuppetWidget::DispatchEvent(WidgetGUIEvent
* aEvent
,
295 nsEventStatus
& aStatus
) {
297 debug_DumpEvent(stdout
, aEvent
->mWidget
, aEvent
, "PuppetWidget", 0);
300 MOZ_ASSERT(!mChild
|| mChild
->mWindowType
== eWindowType_popup
,
301 "Unexpected event dispatch!");
303 MOZ_ASSERT(!aEvent
->AsKeyboardEvent() ||
304 aEvent
->mFlags
.mIsSynthesizedForTests
||
305 aEvent
->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
306 "Non-sysnthesized keyboard events should have edit commands for "
308 "before dispatched");
310 if (aEvent
->mClass
== eCompositionEventClass
) {
311 // If we've already requested to commit/cancel the latest composition,
312 // TextComposition for the old composition has been destroyed. Then,
313 // the DOM tree needs to listen to next eCompositionStart and its
314 // following events. So, until we meet new eCompositionStart, let's
315 // discard all unnecessary composition events here.
316 if (mIgnoreCompositionEvents
) {
317 if (aEvent
->mMessage
!= eCompositionStart
) {
318 aStatus
= nsEventStatus_eIgnore
;
321 // Now, we receive new eCompositionStart. Let's restart to handle
322 // composition in this process.
323 mIgnoreCompositionEvents
= false;
325 // Store the latest native IME context of parent process's widget or
326 // TextEventDispatcher if it's in this process.
327 WidgetCompositionEvent
* compositionEvent
= aEvent
->AsCompositionEvent();
329 if (mNativeIMEContext
.IsValid() &&
330 mNativeIMEContext
!= compositionEvent
->mNativeIMEContext
) {
331 RefPtr
<TextComposition
> composition
=
332 IMEStateManager::GetTextCompositionFor(this);
335 "When there is composition caused by old native IME context, "
336 "composition events caused by different native IME context are not "
339 #endif // #ifdef DEBUG
340 mNativeIMEContext
= compositionEvent
->mNativeIMEContext
;
341 mContentCache
.OnCompositionEvent(*compositionEvent
);
344 // If the event is a composition event or a keyboard event, it should be
345 // dispatched with TextEventDispatcher if we could do that with current
346 // design. However, we cannot do that without big changes and the behavior
347 // is not so complicated for now. Therefore, we should just notify it
348 // of dispatching events and TextEventDispatcher should emulate the state
350 if (aEvent
->mClass
== eCompositionEventClass
||
351 aEvent
->mClass
== eKeyboardEventClass
) {
352 TextEventDispatcher
* dispatcher
= GetTextEventDispatcher();
353 // However, if the event is being dispatched by the text event dispatcher
354 // or, there is native text event dispatcher listener, that means that
355 // native text input event handler is in this process like on Android,
356 // and the event is not synthesized for tests, the event is coming from
357 // the TextEventDispatcher. In these cases, we shouldn't notify
358 // TextEventDispatcher of dispatching the event.
359 if (!dispatcher
->IsDispatchingEvent() &&
360 !(mNativeTextEventDispatcherListener
&&
361 !aEvent
->mFlags
.mIsSynthesizedForTests
)) {
362 DebugOnly
<nsresult
> rv
=
363 dispatcher
->BeginInputTransactionFor(aEvent
, this);
364 NS_WARNING_ASSERTION(
366 "The text event dispatcher should always succeed to start input "
367 "transaction for the event");
371 aStatus
= nsEventStatus_eIgnore
;
373 if (GetCurrentWidgetListener()) {
375 GetCurrentWidgetListener()->HandleEvent(aEvent
, mUseAttachedEvents
);
381 nsIWidget::ContentAndAPZEventStatus
PuppetWidget::DispatchInputEvent(
382 WidgetInputEvent
* aEvent
) {
383 ContentAndAPZEventStatus status
;
384 if (!AsyncPanZoomEnabled()) {
385 DispatchEvent(aEvent
, status
.mContentStatus
);
389 if (!mBrowserChild
) {
393 switch (aEvent
->mClass
) {
394 case eWheelEventClass
:
395 Unused
<< mBrowserChild
->SendDispatchWheelEvent(*aEvent
->AsWheelEvent());
397 case eMouseEventClass
:
398 Unused
<< mBrowserChild
->SendDispatchMouseEvent(*aEvent
->AsMouseEvent());
400 case eKeyboardEventClass
:
401 Unused
<< mBrowserChild
->SendDispatchKeyboardEvent(
402 *aEvent
->AsKeyboardEvent());
404 case eTouchEventClass
:
405 Unused
<< mBrowserChild
->SendDispatchTouchEvent(*aEvent
->AsTouchEvent());
408 MOZ_ASSERT_UNREACHABLE("unsupported event type");
414 nsresult
PuppetWidget::SynthesizeNativeKeyEvent(
415 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
416 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
417 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
418 AutoObserverNotifier
notifier(aObserver
, "keyevent");
419 if (!mBrowserChild
) {
420 return NS_ERROR_FAILURE
;
422 mBrowserChild
->SendSynthesizeNativeKeyEvent(
423 aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
,
424 nsString(aCharacters
), nsString(aUnmodifiedCharacters
),
425 notifier
.SaveObserver());
429 nsresult
PuppetWidget::SynthesizeNativeMouseEvent(
430 mozilla::LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
431 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
432 nsIObserver
* aObserver
) {
433 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
434 if (!mBrowserChild
) {
435 return NS_ERROR_FAILURE
;
437 mBrowserChild
->SendSynthesizeNativeMouseEvent(
438 aPoint
, static_cast<uint32_t>(aNativeMessage
),
439 static_cast<int16_t>(aButton
), static_cast<uint32_t>(aModifierFlags
),
440 notifier
.SaveObserver());
444 nsresult
PuppetWidget::SynthesizeNativeMouseMove(
445 mozilla::LayoutDeviceIntPoint aPoint
, nsIObserver
* aObserver
) {
446 AutoObserverNotifier
notifier(aObserver
, "mousemove");
447 if (!mBrowserChild
) {
448 return NS_ERROR_FAILURE
;
450 mBrowserChild
->SendSynthesizeNativeMouseMove(aPoint
, notifier
.SaveObserver());
454 nsresult
PuppetWidget::SynthesizeNativeMouseScrollEvent(
455 mozilla::LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
,
456 double aDeltaX
, double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
457 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
458 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
459 if (!mBrowserChild
) {
460 return NS_ERROR_FAILURE
;
462 mBrowserChild
->SendSynthesizeNativeMouseScrollEvent(
463 aPoint
, aNativeMessage
, aDeltaX
, aDeltaY
, aDeltaZ
, aModifierFlags
,
464 aAdditionalFlags
, notifier
.SaveObserver());
468 nsresult
PuppetWidget::SynthesizeNativeTouchPoint(
469 uint32_t aPointerId
, TouchPointerState aPointerState
,
470 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
471 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
472 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
473 if (!mBrowserChild
) {
474 return NS_ERROR_FAILURE
;
476 mBrowserChild
->SendSynthesizeNativeTouchPoint(
477 aPointerId
, aPointerState
, aPoint
, aPointerPressure
, aPointerOrientation
,
478 notifier
.SaveObserver());
482 nsresult
PuppetWidget::SynthesizeNativeTouchPadPinch(
483 TouchpadGesturePhase aEventPhase
, float aScale
, LayoutDeviceIntPoint aPoint
,
484 int32_t aModifierFlags
) {
485 if (!mBrowserChild
) {
486 return NS_ERROR_FAILURE
;
488 mBrowserChild
->SendSynthesizeNativeTouchPadPinch(aEventPhase
, aScale
, aPoint
,
493 nsresult
PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint
,
495 nsIObserver
* aObserver
) {
496 AutoObserverNotifier
notifier(aObserver
, "touchtap");
497 if (!mBrowserChild
) {
498 return NS_ERROR_FAILURE
;
500 mBrowserChild
->SendSynthesizeNativeTouchTap(aPoint
, aLongTap
,
501 notifier
.SaveObserver());
505 nsresult
PuppetWidget::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
506 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
507 if (!mBrowserChild
) {
508 return NS_ERROR_FAILURE
;
510 mBrowserChild
->SendClearNativeTouchSequence(notifier
.SaveObserver());
514 nsresult
PuppetWidget::SynthesizeNativePenInput(
515 uint32_t aPointerId
, TouchPointerState aPointerState
,
516 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
517 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
518 AutoObserverNotifier
notifier(aObserver
, "peninput");
519 if (!mBrowserChild
) {
520 return NS_ERROR_FAILURE
;
522 mBrowserChild
->SendSynthesizeNativePenInput(
523 aPointerId
, aPointerState
, aPoint
, aPressure
, aRotation
, aTiltX
, aTiltY
,
524 aButton
, notifier
.SaveObserver());
528 nsresult
PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
529 LayoutDeviceIntPoint aPoint
, uint32_t aModifierFlags
) {
530 if (!mBrowserChild
) {
531 return NS_ERROR_FAILURE
;
533 mBrowserChild
->SendSynthesizeNativeTouchpadDoubleTap(aPoint
, aModifierFlags
);
537 nsresult
PuppetWidget::SynthesizeNativeTouchpadPan(
538 TouchpadGesturePhase aEventPhase
, LayoutDeviceIntPoint aPoint
,
539 double aDeltaX
, double aDeltaY
, int32_t aModifierFlags
) {
540 if (!mBrowserChild
) {
541 return NS_ERROR_FAILURE
;
543 mBrowserChild
->SendSynthesizeNativeTouchpadPan(aEventPhase
, aPoint
, aDeltaX
,
544 aDeltaY
, aModifierFlags
);
548 void PuppetWidget::LockNativePointer() {
549 if (!mBrowserChild
) {
552 mBrowserChild
->SendLockNativePointer();
555 void PuppetWidget::UnlockNativePointer() {
556 if (!mBrowserChild
) {
559 mBrowserChild
->SendUnlockNativePointer();
562 void PuppetWidget::SetConfirmedTargetAPZC(
563 uint64_t aInputBlockId
,
564 const nsTArray
<ScrollableLayerGuid
>& aTargets
) const {
566 mBrowserChild
->SetTargetAPZC(aInputBlockId
, aTargets
);
570 void PuppetWidget::UpdateZoomConstraints(
571 const uint32_t& aPresShellId
, const ScrollableLayerGuid::ViewID
& aViewId
,
572 const Maybe
<ZoomConstraints
>& aConstraints
) {
574 mBrowserChild
->DoUpdateZoomConstraints(aPresShellId
, aViewId
, aConstraints
);
578 bool PuppetWidget::AsyncPanZoomEnabled() const {
579 return mBrowserChild
&& mBrowserChild
->AsyncPanZoomEnabled();
582 bool PuppetWidget::GetEditCommands(NativeKeyBindingsType aType
,
583 const WidgetKeyboardEvent
& aEvent
,
584 nsTArray
<CommandInt
>& aCommands
) {
585 MOZ_ASSERT(!aEvent
.mFlags
.mIsSynthesizedForTests
);
586 // Validate the arguments.
587 if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType
, aEvent
, aCommands
))) {
590 if (NS_WARN_IF(!mBrowserChild
)) {
593 mBrowserChild
->RequestEditCommands(aType
, aEvent
, aCommands
);
597 WindowRenderer
* PuppetWidget::GetWindowRenderer() {
598 if (!mWindowRenderer
) {
599 if (XRE_IsParentProcess()) {
600 // On the parent process there is no CompositorBridgeChild which confuses
601 // some layers code, so we use basic layers instead. Note that we create
602 mWindowRenderer
= new FallbackRenderer
;
603 return mWindowRenderer
;
606 // If we know for sure that the parent side of this BrowserChild is not
607 // connected to the compositor, we don't want to use a "remote" layer
608 // manager like WebRender or Client. Instead we use a Basic one which
609 // can do drawing in this process.
610 MOZ_ASSERT(!mBrowserChild
||
611 mBrowserChild
->IsLayersConnected() != Some(true));
612 mWindowRenderer
= CreateBasicLayerManager();
615 return mWindowRenderer
;
618 bool PuppetWidget::CreateRemoteLayerManager(
619 const std::function
<bool(WebRenderLayerManager
*)>& aInitializeFunc
) {
620 RefPtr
<WebRenderLayerManager
> lm
= new WebRenderLayerManager(this);
621 MOZ_ASSERT(mBrowserChild
);
623 if (!aInitializeFunc(lm
)) {
627 // Force the old LM to self destruct, otherwise if the reference dangles we
628 // could fail to revoke the most recent transaction. We only want to replace
629 // it if we successfully create its successor because a partially initialized
630 // layer manager is worse than a fully initialized but shutdown layer manager.
631 DestroyLayerManager();
632 mWindowRenderer
= std::move(lm
);
636 nsresult
PuppetWidget::RequestIMEToCommitComposition(bool aCancel
) {
637 if (!mBrowserChild
) {
638 return NS_ERROR_FAILURE
;
641 MOZ_ASSERT(!Destroyed());
643 // There must not be composition which is caused by the PuppetWidget instance.
644 if (NS_WARN_IF(!mNativeIMEContext
.IsValid())) {
648 // We've already requested to commit/cancel composition.
649 if (NS_WARN_IF(mIgnoreCompositionEvents
)) {
651 RefPtr
<TextComposition
> composition
=
652 IMEStateManager::GetTextCompositionFor(this);
653 MOZ_ASSERT(!composition
);
654 #endif // #ifdef DEBUG
658 RefPtr
<TextComposition
> composition
=
659 IMEStateManager::GetTextCompositionFor(this);
660 // This method shouldn't be called when there is no text composition instance.
661 if (NS_WARN_IF(!composition
)) {
665 MOZ_DIAGNOSTIC_ASSERT(
666 composition
->IsRequestingCommitOrCancelComposition(),
667 "Requesting commit or cancel composition should be requested via "
668 "TextComposition instance");
670 bool isCommitted
= false;
671 nsAutoString committedString
;
672 if (NS_WARN_IF(!mBrowserChild
->SendRequestIMEToCommitComposition(
673 aCancel
, &isCommitted
, &committedString
))) {
674 return NS_ERROR_FAILURE
;
677 // If the composition wasn't committed synchronously, we need to wait async
678 // composition events for destroying the TextComposition instance.
683 // Dispatch eCompositionCommit event.
684 WidgetCompositionEvent
compositionCommitEvent(true, eCompositionCommit
, this);
685 InitEvent(compositionCommitEvent
, nullptr);
686 compositionCommitEvent
.mData
= committedString
;
687 nsEventStatus status
= nsEventStatus_eIgnore
;
688 DispatchEvent(&compositionCommitEvent
, status
);
691 RefPtr
<TextComposition
> currentComposition
=
692 IMEStateManager::GetTextCompositionFor(this);
693 MOZ_ASSERT(!currentComposition
);
694 #endif // #ifdef DEBUG
696 // Ignore the following composition events until we receive new
697 // eCompositionStart event.
698 mIgnoreCompositionEvents
= true;
700 Unused
<< mBrowserChild
->SendOnEventNeedingAckHandled(
701 eCompositionCommitRequestHandled
);
703 // NOTE: PuppetWidget might be destroyed already.
707 // When this widget caches input context and currently managed by
708 // IMEStateManager, the cache is valid.
709 bool PuppetWidget::HaveValidInputContextCache() const {
710 return (mInputContext
.mIMEState
.mEnabled
!= IMEEnabled::Unknown
&&
711 IMEStateManager::GetWidgetForActiveInputContext() == this);
714 nsRefreshDriver
* PuppetWidget::GetTopLevelRefreshDriver() const {
715 if (!mBrowserChild
) {
719 if (PresShell
* presShell
= mBrowserChild
->GetTopLevelPresShell()) {
720 return presShell
->GetRefreshDriver();
726 void PuppetWidget::SetInputContext(const InputContext
& aContext
,
727 const InputContextAction
& aAction
) {
728 mInputContext
= aContext
;
729 // Any widget instances cannot cache IME open state because IME open state
730 // can be changed by user but native IME may not notify us of changing the
731 // open state on some platforms.
732 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN_STATE_NOT_SUPPORTED
;
733 if (!mBrowserChild
) {
736 mBrowserChild
->SendSetInputContext(aContext
, aAction
);
739 InputContext
PuppetWidget::GetInputContext() {
740 // XXX Currently, we don't support retrieving IME open state from child
743 // If the cache of input context is valid, we can avoid to use synchronous
745 if (HaveValidInputContextCache()) {
746 return mInputContext
;
749 NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
751 // Don't cache InputContext here because this process isn't managing IME
752 // state of the chrome widget. So, we cannot modify mInputContext when
753 // chrome widget is set to new context.
754 InputContext context
;
756 mBrowserChild
->SendGetInputContext(&context
.mIMEState
);
761 NativeIMEContext
PuppetWidget::GetNativeIMEContext() {
762 return mNativeIMEContext
;
765 nsresult
PuppetWidget::NotifyIMEOfFocusChange(
766 const IMENotification
& aIMENotification
) {
767 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
769 if (!mBrowserChild
) {
770 return NS_ERROR_FAILURE
;
773 bool gotFocus
= aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
;
775 // When IME gets focus, we should initialize all information of the
777 if (NS_WARN_IF(!mContentCache
.CacheAll(this, &aIMENotification
))) {
778 return NS_ERROR_FAILURE
;
781 // When IME loses focus, we don't need to store anything.
782 mContentCache
.Clear();
785 mIMENotificationRequestsOfParent
=
786 IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL
);
787 RefPtr
<PuppetWidget
> self
= this;
788 mBrowserChild
->SendNotifyIMEFocus(mContentCache
, aIMENotification
)
790 GetMainThreadSerialEventTarget(), __func__
,
791 [self
](IMENotificationRequests
&& aRequests
) {
792 self
->mIMENotificationRequestsOfParent
= aRequests
;
793 if (TextEventDispatcher
* dispatcher
=
794 self
->GetTextEventDispatcher()) {
795 dispatcher
->OnWidgetChangeIMENotificationRequests(self
);
798 [self
](mozilla::ipc::ResponseRejectReason
&& aReason
) {
799 NS_WARNING("SendNotifyIMEFocus got rejected.");
805 nsresult
PuppetWidget::NotifyIMEOfCompositionUpdate(
806 const IMENotification
& aIMENotification
) {
807 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
809 if (NS_WARN_IF(!mBrowserChild
)) {
810 return NS_ERROR_FAILURE
;
813 if (NS_WARN_IF(!mContentCache
.CacheSelection(this, &aIMENotification
))) {
814 return NS_ERROR_FAILURE
;
816 mBrowserChild
->SendNotifyIMECompositionUpdate(mContentCache
,
821 nsresult
PuppetWidget::NotifyIMEOfTextChange(
822 const IMENotification
& aIMENotification
) {
823 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
824 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
,
825 "Passed wrong notification");
827 if (!mBrowserChild
) {
828 return NS_ERROR_FAILURE
;
831 // FYI: text change notification is the first notification after
832 // a user operation changes the content. So, we need to modify
833 // the cache as far as possible here.
835 if (NS_WARN_IF(!mContentCache
.CacheText(this, &aIMENotification
))) {
836 return NS_ERROR_FAILURE
;
839 // BrowserParent doesn't this this to cache. we don't send the notification
840 // if parent process doesn't request NOTIFY_TEXT_CHANGE.
841 if (mIMENotificationRequestsOfParent
.WantTextChange()) {
842 mBrowserChild
->SendNotifyIMETextChange(mContentCache
, aIMENotification
);
844 mBrowserChild
->SendUpdateContentCache(mContentCache
);
849 nsresult
PuppetWidget::NotifyIMEOfSelectionChange(
850 const IMENotification
& aIMENotification
) {
851 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
852 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
,
853 "Passed wrong notification");
854 if (!mBrowserChild
) {
855 return NS_ERROR_FAILURE
;
858 // Note that selection change must be notified after text change if it occurs.
859 // Therefore, we don't need to query text content again here.
860 mContentCache
.SetSelection(
861 this, aIMENotification
.mSelectionChangeData
.mOffset
,
862 aIMENotification
.mSelectionChangeData
.Length(),
863 aIMENotification
.mSelectionChangeData
.mReversed
,
864 aIMENotification
.mSelectionChangeData
.GetWritingMode());
866 mBrowserChild
->SendNotifyIMESelection(mContentCache
, aIMENotification
);
871 nsresult
PuppetWidget::NotifyIMEOfMouseButtonEvent(
872 const IMENotification
& aIMENotification
) {
873 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
874 if (!mBrowserChild
) {
875 return NS_ERROR_FAILURE
;
878 bool consumedByIME
= false;
879 if (!mBrowserChild
->SendNotifyIMEMouseButtonEvent(aIMENotification
,
881 return NS_ERROR_FAILURE
;
884 return consumedByIME
? NS_SUCCESS_EVENT_CONSUMED
: NS_OK
;
887 nsresult
PuppetWidget::NotifyIMEOfPositionChange(
888 const IMENotification
& aIMENotification
) {
889 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
890 if (NS_WARN_IF(!mBrowserChild
)) {
891 return NS_ERROR_FAILURE
;
894 if (NS_WARN_IF(!mContentCache
.CacheEditorRect(this, &aIMENotification
))) {
895 return NS_ERROR_FAILURE
;
897 if (NS_WARN_IF(!mContentCache
.CacheSelection(this, &aIMENotification
))) {
898 return NS_ERROR_FAILURE
;
900 if (mIMENotificationRequestsOfParent
.WantPositionChanged()) {
901 mBrowserChild
->SendNotifyIMEPositionChange(mContentCache
, aIMENotification
);
903 mBrowserChild
->SendUpdateContentCache(mContentCache
);
908 struct CursorSurface
{
909 UniquePtr
<char[]> mData
;
913 void PuppetWidget::SetCursor(const Cursor
& aCursor
) {
914 if (!mBrowserChild
) {
918 const bool force
= mUpdateCursor
;
919 if (!force
&& mCursor
== aCursor
) {
923 bool hasCustomCursor
= false;
924 UniquePtr
<char[]> customCursorData
;
926 IntSize customCursorSize
;
928 auto format
= SurfaceFormat::B8G8R8A8
;
929 ImageResolution resolution
= aCursor
.mResolution
;
930 if (aCursor
.IsCustom()) {
931 int32_t width
= 0, height
= 0;
932 aCursor
.mContainer
->GetWidth(&width
);
933 aCursor
.mContainer
->GetHeight(&height
);
934 const int32_t flags
=
935 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
;
936 RefPtr
<SourceSurface
> surface
;
937 if (width
&& height
&&
938 aCursor
.mContainer
->GetType() == imgIContainer::TYPE_VECTOR
) {
939 // For vector images, scale to device pixels.
940 resolution
.ScaleBy(GetDefaultScale().scale
);
941 resolution
.ApplyInverseTo(width
, height
);
942 surface
= aCursor
.mContainer
->GetFrameAtSize(
943 {width
, height
}, imgIContainer::FRAME_CURRENT
, flags
);
945 // NOTE(emilio): We get the frame at the full size, ignoring resolution,
946 // because we're going to rasterize it, and we'd effectively lose the
947 // extra pixels if we rasterized to CustomCursorSize.
949 aCursor
.mContainer
->GetFrame(imgIContainer::FRAME_CURRENT
, flags
);
952 if (RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface()) {
953 hasCustomCursor
= true;
954 customCursorData
= nsContentUtils::GetSurfaceData(
955 WrapNotNull(dataSurface
), &length
, &stride
);
956 customCursorSize
= dataSurface
->GetSize();
957 format
= dataSurface
->GetFormat();
962 nsDependentCString
cursorData(customCursorData
? customCursorData
.get() : "",
964 if (!mBrowserChild
->SendSetCursor(
965 aCursor
.mDefaultCursor
, hasCustomCursor
, cursorData
,
966 customCursorSize
.width
, customCursorSize
.height
, resolution
.mX
,
967 resolution
.mY
, stride
, format
, aCursor
.mHotspotX
, aCursor
.mHotspotY
,
974 void PuppetWidget::SetChild(PuppetWidget
* aChild
) {
975 MOZ_ASSERT(this != aChild
, "can't parent a widget to itself");
976 MOZ_ASSERT(!aChild
->mChild
,
977 "fake widget 'hierarchy' only expected to have one level");
983 PuppetWidget::WidgetPaintTask::Run() {
990 void PuppetWidget::Paint() {
991 if (!GetCurrentWidgetListener()) return;
993 mWidgetPaintTask
.Revoke();
995 RefPtr
<PuppetWidget
> strongThis(this);
997 GetCurrentWidgetListener()->WillPaintWindow(this);
999 if (GetCurrentWidgetListener()) {
1000 GetCurrentWidgetListener()->DidPaintWindow();
1004 void PuppetWidget::PaintNowIfNeeded() {
1005 if (IsVisible() && mWidgetPaintTask
.IsPending()) {
1010 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy
) {
1011 if (aWhy
!= MemoryPressureReason::LOW_MEMORY_ONGOING
&& !mVisible
&&
1012 mWindowRenderer
&& mWindowRenderer
->AsWebRender() &&
1013 XRE_IsContentProcess()) {
1014 mWindowRenderer
->AsWebRender()->ClearCachedResources();
1018 bool PuppetWidget::NeedsPaint() {
1019 // e10s popups are handled by the parent process, so never should be painted
1021 if (XRE_IsContentProcess() &&
1022 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
1023 mWindowType
== eWindowType_popup
) {
1024 NS_WARNING("Trying to paint an e10s popup in the child process!");
1031 float PuppetWidget::GetDPI() { return mDPI
; }
1033 double PuppetWidget::GetDefaultScaleInternal() { return mDefaultScale
; }
1035 int32_t PuppetWidget::RoundsWidgetCoordinatesTo() { return mRounding
; }
1037 LayoutDeviceIntPoint
PuppetWidget::GetChromeOffset() {
1038 if (!GetOwningBrowserChild()) {
1039 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1040 return LayoutDeviceIntPoint();
1042 return GetOwningBrowserChild()->GetChromeOffset();
1045 LayoutDeviceIntPoint
PuppetWidget::WidgetToScreenOffset() {
1046 auto positionRalativeToWindow
=
1047 WidgetToTopLevelWidgetTransform().TransformPoint(LayoutDevicePoint());
1049 return GetWindowPosition() +
1050 LayoutDeviceIntPoint::Round(positionRalativeToWindow
);
1053 LayoutDeviceIntPoint
PuppetWidget::GetWindowPosition() {
1054 if (!GetOwningBrowserChild()) {
1055 return LayoutDeviceIntPoint();
1058 int32_t winX
, winY
, winW
, winH
;
1060 GetOwningBrowserChild()->GetDimensions(0, &winX
, &winY
, &winW
, &winH
),
1061 LayoutDeviceIntPoint());
1062 return LayoutDeviceIntPoint(winX
, winY
) +
1063 GetOwningBrowserChild()->GetClientOffset();
1066 LayoutDeviceIntRect
PuppetWidget::GetScreenBounds() {
1067 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds
.Size());
1070 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1071 return mBrowserChild
? mBrowserChild
->MaxTouchPoints() : 0;
1074 void PuppetWidget::StartAsyncScrollbarDrag(
1075 const AsyncDragMetrics
& aDragMetrics
) {
1076 mBrowserChild
->StartScrollbarDrag(aDragMetrics
);
1079 PuppetScreen::PuppetScreen(void* nativeScreen
) {}
1081 PuppetScreen::~PuppetScreen() = default;
1083 static ScreenConfiguration
ScreenConfig() {
1084 ScreenConfiguration config
;
1085 hal::GetCurrentScreenConfiguration(&config
);
1089 nsIntSize
PuppetWidget::GetScreenDimensions() {
1090 nsIntRect r
= ScreenConfig().rect();
1091 return nsIntSize(r
.Width(), r
.Height());
1095 PuppetScreen::GetRect(int32_t* outLeft
, int32_t* outTop
, int32_t* outWidth
,
1096 int32_t* outHeight
) {
1097 nsIntRect r
= ScreenConfig().rect();
1098 r
.GetRect(outLeft
, outTop
, outWidth
, outHeight
);
1103 PuppetScreen::GetAvailRect(int32_t* outLeft
, int32_t* outTop
, int32_t* outWidth
,
1104 int32_t* outHeight
) {
1105 return GetRect(outLeft
, outTop
, outWidth
, outHeight
);
1109 PuppetScreen::GetPixelDepth(int32_t* aPixelDepth
) {
1110 *aPixelDepth
= ScreenConfig().pixelDepth();
1115 PuppetScreen::GetColorDepth(int32_t* aColorDepth
) {
1116 *aColorDepth
= ScreenConfig().colorDepth();
1120 NS_IMPL_ISUPPORTS(PuppetScreenManager
, nsIScreenManager
)
1122 PuppetScreenManager::PuppetScreenManager() {
1123 mOneScreen
= new PuppetScreen(nullptr);
1126 PuppetScreenManager::~PuppetScreenManager() = default;
1129 PuppetScreenManager::GetPrimaryScreen(nsIScreen
** outScreen
) {
1130 NS_IF_ADDREF(*outScreen
= mOneScreen
.get());
1135 PuppetScreenManager::GetTotalScreenPixels(int64_t* aTotalScreenPixels
) {
1136 MOZ_ASSERT(aTotalScreenPixels
);
1138 int32_t x
, y
, width
, height
;
1139 x
= y
= width
= height
= 0;
1140 mOneScreen
->GetRect(&x
, &y
, &width
, &height
);
1141 *aTotalScreenPixels
= width
* height
;
1143 *aTotalScreenPixels
= 0;
1149 PuppetScreenManager::ScreenForRect(int32_t inLeft
, int32_t inTop
,
1150 int32_t inWidth
, int32_t inHeight
,
1151 nsIScreen
** outScreen
) {
1152 return GetPrimaryScreen(outScreen
);
1155 ScreenIntMargin
PuppetWidget::GetSafeAreaInsets() const {
1156 return mSafeAreaInsets
;
1159 void PuppetWidget::UpdateSafeAreaInsets(
1160 const ScreenIntMargin
& aSafeAreaInsets
) {
1161 mSafeAreaInsets
= aSafeAreaInsets
;
1164 nsIWidgetListener
* PuppetWidget::GetCurrentWidgetListener() {
1165 if (!mPreviouslyAttachedWidgetListener
|| !mAttachedWidgetListener
) {
1166 return mAttachedWidgetListener
;
1169 if (mAttachedWidgetListener
->GetView()->IsPrimaryFramePaintSuppressed()) {
1170 return mPreviouslyAttachedWidgetListener
;
1173 return mAttachedWidgetListener
;
1176 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId
,
1177 const ScrollableLayerGuid::ViewID
& aViewId
,
1178 const CSSRect
& aRect
, const uint32_t& aFlags
) {
1179 if (!mBrowserChild
) {
1183 mBrowserChild
->ZoomToRect(aPresShellId
, aViewId
, aRect
, aFlags
);
1186 void PuppetWidget::LookUpDictionary(
1187 const nsAString
& aText
, const nsTArray
<mozilla::FontRange
>& aFontRangeArray
,
1188 const bool aIsVertical
, const LayoutDeviceIntPoint
& aPoint
) {
1189 if (!mBrowserChild
) {
1193 mBrowserChild
->SendLookUpDictionary(nsString(aText
), aFontRangeArray
,
1194 aIsVertical
, aPoint
);
1197 bool PuppetWidget::HasPendingInputEvent() {
1198 if (!mBrowserChild
) {
1204 mBrowserChild
->GetIPCChannel()->PeekMessages(
1205 [&ret
](const IPC::Message
& aMsg
) -> bool {
1206 if (nsContentUtils::IsMessageInputEvent(aMsg
)) {
1208 return false; // Stop peeking.
1216 // TextEventDispatcherListener
1219 PuppetWidget::NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
1220 const IMENotification
& aIMENotification
) {
1221 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1223 // If there is different text event dispatcher listener for handling
1224 // text event dispatcher, that means that native keyboard events and
1225 // IME events are handled in this process. Therefore, we don't need
1226 // to send any requests and notifications to the parent process.
1227 if (mNativeTextEventDispatcherListener
) {
1228 return NS_ERROR_NOT_IMPLEMENTED
;
1231 switch (aIMENotification
.mMessage
) {
1232 case REQUEST_TO_COMMIT_COMPOSITION
:
1233 return RequestIMEToCommitComposition(false);
1234 case REQUEST_TO_CANCEL_COMPOSITION
:
1235 return RequestIMEToCommitComposition(true);
1236 case NOTIFY_IME_OF_FOCUS
:
1237 case NOTIFY_IME_OF_BLUR
:
1238 return NotifyIMEOfFocusChange(aIMENotification
);
1239 case NOTIFY_IME_OF_SELECTION_CHANGE
:
1240 return NotifyIMEOfSelectionChange(aIMENotification
);
1241 case NOTIFY_IME_OF_TEXT_CHANGE
:
1242 return NotifyIMEOfTextChange(aIMENotification
);
1243 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
1244 return NotifyIMEOfCompositionUpdate(aIMENotification
);
1245 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
1246 return NotifyIMEOfMouseButtonEvent(aIMENotification
);
1247 case NOTIFY_IME_OF_POSITION_CHANGE
:
1248 return NotifyIMEOfPositionChange(aIMENotification
);
1250 return NS_ERROR_NOT_IMPLEMENTED
;
1253 return NS_ERROR_NOT_IMPLEMENTED
;
1256 NS_IMETHODIMP_(IMENotificationRequests
)
1257 PuppetWidget::GetIMENotificationRequests() {
1258 return IMENotificationRequests(
1259 mIMENotificationRequestsOfParent
.mWantUpdates
|
1260 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
1261 IMENotificationRequests::NOTIFY_POSITION_CHANGE
);
1264 NS_IMETHODIMP_(void)
1265 PuppetWidget::OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) {
1266 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1269 NS_IMETHODIMP_(void)
1270 PuppetWidget::WillDispatchKeyboardEvent(
1271 TextEventDispatcher
* aTextEventDispatcher
,
1272 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aIndexOfKeypress
,
1274 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1277 nsresult
PuppetWidget::SetSystemFont(const nsCString
& aFontName
) {
1278 if (!mBrowserChild
) {
1279 return NS_ERROR_FAILURE
;
1282 mBrowserChild
->SendSetSystemFont(aFontName
);
1286 nsresult
PuppetWidget::GetSystemFont(nsCString
& aFontName
) {
1287 if (!mBrowserChild
) {
1288 return NS_ERROR_FAILURE
;
1290 mBrowserChild
->SendGetSystemFont(&aFontName
);
1294 } // namespace widget
1295 } // namespace mozilla