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 "ClientLayerManager.h"
11 #include "gfxPlatform.h"
12 #include "nsRefreshDriver.h"
13 #include "mozilla/dom/BrowserChild.h"
14 #include "mozilla/gfx/gfxVars.h"
15 #include "mozilla/Hal.h"
16 #include "mozilla/IMEStateManager.h"
17 #include "mozilla/layers/APZChild.h"
18 #include "mozilla/layers/PLayerTransactionChild.h"
19 #include "mozilla/layers/WebRenderLayerManager.h"
20 #include "mozilla/Preferences.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/SchedulerGroup.h"
23 #include "mozilla/StaticPrefs_browser.h"
24 #include "mozilla/TextComposition.h"
25 #include "mozilla/TextEventDispatcher.h"
26 #include "mozilla/TextEvents.h"
27 #include "mozilla/Unused.h"
28 #include "BasicLayers.h"
29 #include "PuppetWidget.h"
30 #include "nsContentUtils.h"
31 #include "nsIWidgetListener.h"
32 #include "imgIContainer.h"
34 #include "nsXPLookAndFeel.h"
35 #include "nsPrintfCString.h"
37 using namespace mozilla
;
38 using namespace mozilla::dom
;
39 using namespace mozilla::hal
;
40 using namespace mozilla::gfx
;
41 using namespace mozilla::layers
;
42 using namespace mozilla::widget
;
44 static void InvalidateRegion(nsIWidget
* aWidget
,
45 const LayoutDeviceIntRegion
& aRegion
) {
46 for (auto iter
= aRegion
.RectIter(); !iter
.Done(); iter
.Next()) {
47 aWidget
->Invalidate(iter
.Get());
52 already_AddRefed
<nsIWidget
> nsIWidget::CreatePuppetWidget(
53 BrowserChild
* aBrowserChild
) {
54 MOZ_ASSERT(!aBrowserChild
|| nsIWidget::UsePuppetWidgets(),
55 "PuppetWidgets not allowed in this configuration");
57 nsCOMPtr
<nsIWidget
> widget
= new PuppetWidget(aBrowserChild
);
58 return widget
.forget();
64 static bool IsPopup(const nsWidgetInitData
* aInitData
) {
65 return aInitData
&& aInitData
->mWindowType
== eWindowType_popup
;
68 static bool MightNeedIMEFocus(const nsWidgetInitData
* aInitData
) {
69 // In the puppet-widget world, popup widgets are just dummies and
70 // shouldn't try to mess with IME state.
71 #ifdef MOZ_CROSS_PROCESS_IME
72 return !IsPopup(aInitData
);
78 // Arbitrary, fungible.
79 const size_t PuppetWidget::kMaxDimension
= 4000;
81 NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget
, nsBaseWidget
,
82 TextEventDispatcherListener
)
84 PuppetWidget::PuppetWidget(BrowserChild
* aBrowserChild
)
85 : mBrowserChild(aBrowserChild
),
86 mMemoryPressureObserver(nullptr),
94 mNeedIMEStateInit(false),
95 mIgnoreCompositionEvents(false) {
96 // Setting 'Unknown' means "not yet cached".
97 mInputContext
.mIMEState
.mEnabled
= IMEEnabled::Unknown
;
100 PuppetWidget::~PuppetWidget() { Destroy(); }
102 void PuppetWidget::InfallibleCreate(nsIWidget
* aParent
,
103 nsNativeWidget aNativeParent
,
104 const LayoutDeviceIntRect
& aRect
,
105 nsWidgetInitData
* aInitData
) {
106 MOZ_ASSERT(!aNativeParent
, "got a non-Puppet native parent");
108 BaseCreate(nullptr, aInitData
);
114 mDrawTarget
= gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
115 IntSize(1, 1), SurfaceFormat::B8G8R8A8
);
117 mNeedIMEStateInit
= MightNeedIMEFocus(aInitData
);
119 PuppetWidget
* parent
= static_cast<PuppetWidget
*>(aParent
);
121 parent
->SetChild(this);
122 mLayerManager
= parent
->GetLayerManager();
124 Resize(mBounds
.X(), mBounds
.Y(), mBounds
.Width(), mBounds
.Height(), false);
126 mMemoryPressureObserver
= MemoryPressureObserver::Create(this);
129 nsresult
PuppetWidget::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
130 const LayoutDeviceIntRect
& aRect
,
131 nsWidgetInitData
* aInitData
) {
132 InfallibleCreate(aParent
, aNativeParent
, aRect
, aInitData
);
136 void PuppetWidget::InitIMEState() {
137 MOZ_ASSERT(mBrowserChild
);
138 if (mNeedIMEStateInit
) {
139 mContentCache
.Clear();
140 mBrowserChild
->SendUpdateContentCache(mContentCache
);
141 mIMENotificationRequestsOfParent
= IMENotificationRequests();
142 mNeedIMEStateInit
= false;
146 already_AddRefed
<nsIWidget
> PuppetWidget::CreateChild(
147 const LayoutDeviceIntRect
& aRect
, nsWidgetInitData
* aInitData
,
148 bool aForceUseIWidgetParent
) {
149 bool isPopup
= IsPopup(aInitData
);
150 nsCOMPtr
<nsIWidget
> widget
= nsIWidget::CreatePuppetWidget(mBrowserChild
);
151 return ((widget
&& NS_SUCCEEDED(widget
->Create(isPopup
? nullptr : this,
152 nullptr, aRect
, aInitData
)))
157 void PuppetWidget::Destroy() {
158 if (mOnDestroyCalled
) {
161 mOnDestroyCalled
= true;
165 if (mMemoryPressureObserver
) {
166 mMemoryPressureObserver
->Unregister();
167 mMemoryPressureObserver
= nullptr;
171 mLayerManager
->Destroy();
173 mLayerManager
= nullptr;
174 mBrowserChild
= nullptr;
177 void PuppetWidget::Show(bool aState
) {
178 NS_ASSERTION(mEnabled
,
179 "does it make sense to Show()/Hide() a disabled widget?");
181 bool wasVisible
= mVisible
;
185 mChild
->mVisible
= aState
;
188 if (!wasVisible
&& mVisible
) {
189 // The previously attached widget listener is handy if
190 // we're transitioning from page to page without dropping
191 // layers (since we'll continue to show the old layers
192 // associated with that old widget listener). If the
193 // PuppetWidget was hidden, those layers are dropped,
194 // so the previously attached widget listener is really
195 // of no use anymore (and is actually actively harmful - see
197 mPreviouslyAttachedWidgetListener
= nullptr;
198 Resize(mBounds
.Width(), mBounds
.Height(), false);
203 void PuppetWidget::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
204 LayoutDeviceIntRect oldBounds
= mBounds
;
206 LayoutDeviceIntSize(NSToIntRound(aWidth
), NSToIntRound(aHeight
)));
209 mChild
->Resize(aWidth
, aHeight
, aRepaint
);
213 // XXX: roc says that |aRepaint| dictates whether or not to
214 // invalidate the expanded area
215 if (oldBounds
.Size() < mBounds
.Size() && aRepaint
) {
216 LayoutDeviceIntRegion
dirty(mBounds
);
217 dirty
.Sub(dirty
, oldBounds
);
218 InvalidateRegion(this, dirty
);
221 // call WindowResized() on both the current listener, and possibly
222 // also the previous one if we're in a state where we're drawing that one
223 // because the current one is paint suppressed
224 if (!oldBounds
.IsEqualEdges(mBounds
) && mAttachedWidgetListener
) {
225 if (GetCurrentWidgetListener() &&
226 GetCurrentWidgetListener() != mAttachedWidgetListener
) {
227 GetCurrentWidgetListener()->WindowResized(this, mBounds
.Width(),
230 mAttachedWidgetListener
->WindowResized(this, mBounds
.Width(),
235 nsresult
PuppetWidget::ConfigureChildren(
236 const nsTArray
<Configuration
>& aConfigurations
) {
237 for (uint32_t i
= 0; i
< aConfigurations
.Length(); ++i
) {
238 const Configuration
& configuration
= aConfigurations
[i
];
239 PuppetWidget
* w
= static_cast<PuppetWidget
*>(configuration
.mChild
.get());
240 NS_ASSERTION(w
->GetParent() == this, "Configured widget is not a child");
241 w
->SetWindowClipRegion(configuration
.mClipRegion
, true);
242 LayoutDeviceIntRect bounds
= w
->GetBounds();
243 if (bounds
.Size() != configuration
.mBounds
.Size()) {
244 w
->Resize(configuration
.mBounds
.X(), configuration
.mBounds
.Y(),
245 configuration
.mBounds
.Width(), configuration
.mBounds
.Height(),
247 } else if (bounds
.TopLeft() != configuration
.mBounds
.TopLeft()) {
248 w
->Move(configuration
.mBounds
.X(), configuration
.mBounds
.Y());
250 w
->SetWindowClipRegion(configuration
.mClipRegion
, false);
255 void PuppetWidget::SetFocus(Raise aRaise
, CallerType aCallerType
) {
256 if (aRaise
== Raise::Yes
&& mBrowserChild
) {
257 mBrowserChild
->SendRequestFocus(true, aCallerType
);
261 void PuppetWidget::Invalidate(const LayoutDeviceIntRect
& aRect
) {
263 debug_DumpInvalidate(stderr
, this, &aRect
, "PuppetWidget", 0);
267 mChild
->Invalidate(aRect
);
271 if (mBrowserChild
&& !aRect
.IsEmpty() && !mWidgetPaintTask
.IsPending()) {
272 mWidgetPaintTask
= new WidgetPaintTask(this);
273 nsCOMPtr
<nsIRunnable
> event(mWidgetPaintTask
.get());
274 SchedulerGroup::Dispatch(TaskCategory::Other
, event
.forget());
278 mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
279 PuppetWidget::WidgetToTopLevelWidgetTransform() {
280 if (!GetOwningBrowserChild()) {
281 NS_WARNING("PuppetWidget without Tab does not have transform information.");
282 return mozilla::LayoutDeviceToLayoutDeviceMatrix4x4();
284 return GetOwningBrowserChild()->GetChildToParentConversionMatrix();
287 void PuppetWidget::InitEvent(WidgetGUIEvent
& aEvent
,
288 LayoutDeviceIntPoint
* aPoint
) {
289 if (nullptr == aPoint
) {
290 aEvent
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
292 // use the point override if provided
293 aEvent
.mRefPoint
= *aPoint
;
295 aEvent
.mTime
= PR_Now() / 1000;
298 nsresult
PuppetWidget::DispatchEvent(WidgetGUIEvent
* aEvent
,
299 nsEventStatus
& aStatus
) {
301 debug_DumpEvent(stdout
, aEvent
->mWidget
, aEvent
, "PuppetWidget", 0);
304 MOZ_ASSERT(!mChild
|| mChild
->mWindowType
== eWindowType_popup
,
305 "Unexpected event dispatch!");
307 MOZ_ASSERT(!aEvent
->AsKeyboardEvent() ||
308 aEvent
->mFlags
.mIsSynthesizedForTests
||
309 aEvent
->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
310 "Non-sysnthesized keyboard events should have edit commands for "
312 "before dispatched");
314 if (aEvent
->mClass
== eCompositionEventClass
) {
315 // If we've already requested to commit/cancel the latest composition,
316 // TextComposition for the old composition has been destroyed. Then,
317 // the DOM tree needs to listen to next eCompositionStart and its
318 // following events. So, until we meet new eCompositionStart, let's
319 // discard all unnecessary composition events here.
320 if (mIgnoreCompositionEvents
) {
321 if (aEvent
->mMessage
!= eCompositionStart
) {
322 aStatus
= nsEventStatus_eIgnore
;
325 // Now, we receive new eCompositionStart. Let's restart to handle
326 // composition in this process.
327 mIgnoreCompositionEvents
= false;
329 // Store the latest native IME context of parent process's widget or
330 // TextEventDispatcher if it's in this process.
331 WidgetCompositionEvent
* compositionEvent
= aEvent
->AsCompositionEvent();
333 if (mNativeIMEContext
.IsValid() &&
334 mNativeIMEContext
!= compositionEvent
->mNativeIMEContext
) {
335 RefPtr
<TextComposition
> composition
=
336 IMEStateManager::GetTextCompositionFor(this);
339 "When there is composition caused by old native IME context, "
340 "composition events caused by different native IME context are not "
343 #endif // #ifdef DEBUG
344 mNativeIMEContext
= compositionEvent
->mNativeIMEContext
;
345 mContentCache
.OnCompositionEvent(*compositionEvent
);
348 // If the event is a composition event or a keyboard event, it should be
349 // dispatched with TextEventDispatcher if we could do that with current
350 // design. However, we cannot do that without big changes and the behavior
351 // is not so complicated for now. Therefore, we should just notify it
352 // of dispatching events and TextEventDispatcher should emulate the state
354 if (aEvent
->mClass
== eCompositionEventClass
||
355 aEvent
->mClass
== eKeyboardEventClass
) {
356 TextEventDispatcher
* dispatcher
= GetTextEventDispatcher();
357 // However, if the event is being dispatched by the text event dispatcher
358 // or, there is native text event dispatcher listener, that means that
359 // native text input event handler is in this process like on Android,
360 // and the event is not synthesized for tests, the event is coming from
361 // the TextEventDispatcher. In these cases, we shouldn't notify
362 // TextEventDispatcher of dispatching the event.
363 if (!dispatcher
->IsDispatchingEvent() &&
364 !(mNativeTextEventDispatcherListener
&&
365 !aEvent
->mFlags
.mIsSynthesizedForTests
)) {
366 DebugOnly
<nsresult
> rv
=
367 dispatcher
->BeginInputTransactionFor(aEvent
, this);
368 NS_WARNING_ASSERTION(
370 "The text event dispatcher should always succeed to start input "
371 "transaction for the event");
375 aStatus
= nsEventStatus_eIgnore
;
377 if (GetCurrentWidgetListener()) {
379 GetCurrentWidgetListener()->HandleEvent(aEvent
, mUseAttachedEvents
);
385 nsIWidget::ContentAndAPZEventStatus
PuppetWidget::DispatchInputEvent(
386 WidgetInputEvent
* aEvent
) {
387 ContentAndAPZEventStatus status
;
388 if (!AsyncPanZoomEnabled()) {
389 DispatchEvent(aEvent
, status
.mContentStatus
);
393 if (!mBrowserChild
) {
397 switch (aEvent
->mClass
) {
398 case eWheelEventClass
:
399 Unused
<< mBrowserChild
->SendDispatchWheelEvent(*aEvent
->AsWheelEvent());
401 case eMouseEventClass
:
402 Unused
<< mBrowserChild
->SendDispatchMouseEvent(*aEvent
->AsMouseEvent());
404 case eKeyboardEventClass
:
405 Unused
<< mBrowserChild
->SendDispatchKeyboardEvent(
406 *aEvent
->AsKeyboardEvent());
409 MOZ_ASSERT_UNREACHABLE("unsupported event type");
415 nsresult
PuppetWidget::SynthesizeNativeKeyEvent(
416 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
417 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
418 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
419 AutoObserverNotifier
notifier(aObserver
, "keyevent");
420 if (!mBrowserChild
) {
421 return NS_ERROR_FAILURE
;
423 mBrowserChild
->SendSynthesizeNativeKeyEvent(
424 aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
,
425 nsString(aCharacters
), nsString(aUnmodifiedCharacters
),
426 notifier
.SaveObserver());
430 nsresult
PuppetWidget::SynthesizeNativeMouseEvent(
431 mozilla::LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
432 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
433 nsIObserver
* aObserver
) {
434 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
435 if (!mBrowserChild
) {
436 return NS_ERROR_FAILURE
;
438 mBrowserChild
->SendSynthesizeNativeMouseEvent(
439 aPoint
, static_cast<uint32_t>(aNativeMessage
),
440 static_cast<int16_t>(aButton
), static_cast<uint32_t>(aModifierFlags
),
441 notifier
.SaveObserver());
445 nsresult
PuppetWidget::SynthesizeNativeMouseMove(
446 mozilla::LayoutDeviceIntPoint aPoint
, nsIObserver
* aObserver
) {
447 AutoObserverNotifier
notifier(aObserver
, "mousemove");
448 if (!mBrowserChild
) {
449 return NS_ERROR_FAILURE
;
451 mBrowserChild
->SendSynthesizeNativeMouseMove(aPoint
, notifier
.SaveObserver());
455 nsresult
PuppetWidget::SynthesizeNativeMouseScrollEvent(
456 mozilla::LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
,
457 double aDeltaX
, double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
458 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
459 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
460 if (!mBrowserChild
) {
461 return NS_ERROR_FAILURE
;
463 mBrowserChild
->SendSynthesizeNativeMouseScrollEvent(
464 aPoint
, aNativeMessage
, aDeltaX
, aDeltaY
, aDeltaZ
, aModifierFlags
,
465 aAdditionalFlags
, notifier
.SaveObserver());
469 nsresult
PuppetWidget::SynthesizeNativeTouchPoint(
470 uint32_t aPointerId
, TouchPointerState aPointerState
,
471 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
472 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
473 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
474 if (!mBrowserChild
) {
475 return NS_ERROR_FAILURE
;
477 mBrowserChild
->SendSynthesizeNativeTouchPoint(
478 aPointerId
, aPointerState
, aPoint
, aPointerPressure
, aPointerOrientation
,
479 notifier
.SaveObserver());
483 nsresult
PuppetWidget::SynthesizeNativeTouchPadPinch(
484 TouchpadPinchPhase aEventPhase
, float aScale
, LayoutDeviceIntPoint aPoint
,
485 int32_t aModifierFlags
) {
486 if (!mBrowserChild
) {
487 return NS_ERROR_FAILURE
;
489 mBrowserChild
->SendSynthesizeNativeTouchPadPinch(aEventPhase
, aScale
, aPoint
,
494 nsresult
PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint
,
496 nsIObserver
* aObserver
) {
497 AutoObserverNotifier
notifier(aObserver
, "touchtap");
498 if (!mBrowserChild
) {
499 return NS_ERROR_FAILURE
;
501 mBrowserChild
->SendSynthesizeNativeTouchTap(aPoint
, aLongTap
,
502 notifier
.SaveObserver());
506 nsresult
PuppetWidget::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
507 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
508 if (!mBrowserChild
) {
509 return NS_ERROR_FAILURE
;
511 mBrowserChild
->SendClearNativeTouchSequence(notifier
.SaveObserver());
515 nsresult
PuppetWidget::SynthesizeNativePenInput(
516 uint32_t aPointerId
, TouchPointerState aPointerState
,
517 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
518 int32_t aTiltX
, int32_t aTiltY
, nsIObserver
* aObserver
) {
519 AutoObserverNotifier
notifier(aObserver
, "peninput");
520 if (!mBrowserChild
) {
521 return NS_ERROR_FAILURE
;
523 mBrowserChild
->SendSynthesizeNativePenInput(aPointerId
, aPointerState
, aPoint
,
524 aPressure
, aRotation
, aTiltX
,
525 aTiltY
, notifier
.SaveObserver());
529 nsresult
PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
530 LayoutDeviceIntPoint aPoint
, uint32_t aModifierFlags
) {
531 if (!mBrowserChild
) {
532 return NS_ERROR_FAILURE
;
534 mBrowserChild
->SendSynthesizeNativeTouchpadDoubleTap(aPoint
, aModifierFlags
);
538 void PuppetWidget::SetConfirmedTargetAPZC(
539 uint64_t aInputBlockId
,
540 const nsTArray
<ScrollableLayerGuid
>& aTargets
) const {
542 mBrowserChild
->SetTargetAPZC(aInputBlockId
, aTargets
);
546 void PuppetWidget::UpdateZoomConstraints(
547 const uint32_t& aPresShellId
, const ScrollableLayerGuid::ViewID
& aViewId
,
548 const Maybe
<ZoomConstraints
>& aConstraints
) {
550 mBrowserChild
->DoUpdateZoomConstraints(aPresShellId
, aViewId
, aConstraints
);
554 bool PuppetWidget::AsyncPanZoomEnabled() const {
555 return mBrowserChild
&& mBrowserChild
->AsyncPanZoomEnabled();
558 bool PuppetWidget::GetEditCommands(NativeKeyBindingsType aType
,
559 const WidgetKeyboardEvent
& aEvent
,
560 nsTArray
<CommandInt
>& aCommands
) {
561 MOZ_ASSERT(!aEvent
.mFlags
.mIsSynthesizedForTests
);
562 // Validate the arguments.
563 if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType
, aEvent
, aCommands
))) {
566 if (NS_WARN_IF(!mBrowserChild
)) {
569 mBrowserChild
->RequestEditCommands(aType
, aEvent
, aCommands
);
573 LayerManager
* PuppetWidget::GetLayerManager(
574 PLayerTransactionChild
* aShadowManager
, LayersBackend aBackendHint
,
575 LayerManagerPersistence aPersistence
) {
576 if (!mLayerManager
) {
577 if (XRE_IsParentProcess()) {
578 // On the parent process there is no CompositorBridgeChild which confuses
579 // some layers code, so we use basic layers instead. Note that we create
580 // a non-retaining layer manager since we don't care about performance.
581 mLayerManager
= new BasicLayerManager(BasicLayerManager::BLM_OFFSCREEN
);
582 return mLayerManager
;
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 mLayerManager
= new BasicLayerManager(this);
594 return mLayerManager
;
597 bool PuppetWidget::CreateRemoteLayerManager(
598 const std::function
<bool(LayerManager
*)>& aInitializeFunc
) {
599 RefPtr
<LayerManager
> lm
;
600 MOZ_ASSERT(mBrowserChild
);
601 if (mBrowserChild
->GetCompositorOptions().UseWebRender()) {
602 lm
= new WebRenderLayerManager(this);
604 lm
= new ClientLayerManager(this);
607 if (!aInitializeFunc(lm
)) {
611 // Force the old LM to self destruct, otherwise if the reference dangles we
612 // could fail to revoke the most recent transaction. We only want to replace
613 // it if we successfully create its successor because a partially initialized
614 // layer manager is worse than a fully initialized but shutdown layer manager.
615 DestroyLayerManager();
616 mLayerManager
= std::move(lm
);
620 nsresult
PuppetWidget::RequestIMEToCommitComposition(bool aCancel
) {
621 if (!mBrowserChild
) {
622 return NS_ERROR_FAILURE
;
625 MOZ_ASSERT(!Destroyed());
627 // There must not be composition which is caused by the PuppetWidget instance.
628 if (NS_WARN_IF(!mNativeIMEContext
.IsValid())) {
632 // We've already requested to commit/cancel composition.
633 if (NS_WARN_IF(mIgnoreCompositionEvents
)) {
635 RefPtr
<TextComposition
> composition
=
636 IMEStateManager::GetTextCompositionFor(this);
637 MOZ_ASSERT(!composition
);
638 #endif // #ifdef DEBUG
642 RefPtr
<TextComposition
> composition
=
643 IMEStateManager::GetTextCompositionFor(this);
644 // This method shouldn't be called when there is no text composition instance.
645 if (NS_WARN_IF(!composition
)) {
649 MOZ_DIAGNOSTIC_ASSERT(
650 composition
->IsRequestingCommitOrCancelComposition(),
651 "Requesting commit or cancel composition should be requested via "
652 "TextComposition instance");
654 bool isCommitted
= false;
655 nsAutoString committedString
;
656 if (NS_WARN_IF(!mBrowserChild
->SendRequestIMEToCommitComposition(
657 aCancel
, &isCommitted
, &committedString
))) {
658 return NS_ERROR_FAILURE
;
661 // If the composition wasn't committed synchronously, we need to wait async
662 // composition events for destroying the TextComposition instance.
667 // Dispatch eCompositionCommit event.
668 WidgetCompositionEvent
compositionCommitEvent(true, eCompositionCommit
, this);
669 InitEvent(compositionCommitEvent
, nullptr);
670 compositionCommitEvent
.mData
= committedString
;
671 nsEventStatus status
= nsEventStatus_eIgnore
;
672 DispatchEvent(&compositionCommitEvent
, status
);
675 RefPtr
<TextComposition
> currentComposition
=
676 IMEStateManager::GetTextCompositionFor(this);
677 MOZ_ASSERT(!currentComposition
);
678 #endif // #ifdef DEBUG
680 // Ignore the following composition events until we receive new
681 // eCompositionStart event.
682 mIgnoreCompositionEvents
= true;
684 Unused
<< mBrowserChild
->SendOnEventNeedingAckHandled(
685 eCompositionCommitRequestHandled
);
687 // NOTE: PuppetWidget might be destroyed already.
691 // When this widget caches input context and currently managed by
692 // IMEStateManager, the cache is valid.
693 bool PuppetWidget::HaveValidInputContextCache() const {
694 return (mInputContext
.mIMEState
.mEnabled
!= IMEEnabled::Unknown
&&
695 IMEStateManager::GetWidgetForActiveInputContext() == this);
698 nsRefreshDriver
* PuppetWidget::GetTopLevelRefreshDriver() const {
699 if (!mBrowserChild
) {
703 if (PresShell
* presShell
= mBrowserChild
->GetTopLevelPresShell()) {
704 return presShell
->GetRefreshDriver();
710 void PuppetWidget::SetInputContext(const InputContext
& aContext
,
711 const InputContextAction
& aAction
) {
712 mInputContext
= aContext
;
713 // Any widget instances cannot cache IME open state because IME open state
714 // can be changed by user but native IME may not notify us of changing the
715 // open state on some platforms.
716 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN_STATE_NOT_SUPPORTED
;
717 if (!mBrowserChild
) {
720 mBrowserChild
->SendSetInputContext(aContext
, aAction
);
723 InputContext
PuppetWidget::GetInputContext() {
724 // XXX Currently, we don't support retrieving IME open state from child
727 // If the cache of input context is valid, we can avoid to use synchronous
729 if (HaveValidInputContextCache()) {
730 return mInputContext
;
733 NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
735 // Don't cache InputContext here because this process isn't managing IME
736 // state of the chrome widget. So, we cannot modify mInputContext when
737 // chrome widget is set to new context.
738 InputContext context
;
740 mBrowserChild
->SendGetInputContext(&context
.mIMEState
);
745 NativeIMEContext
PuppetWidget::GetNativeIMEContext() {
746 return mNativeIMEContext
;
749 nsresult
PuppetWidget::NotifyIMEOfFocusChange(
750 const IMENotification
& aIMENotification
) {
751 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
753 if (!mBrowserChild
) {
754 return NS_ERROR_FAILURE
;
757 bool gotFocus
= aIMENotification
.mMessage
== NOTIFY_IME_OF_FOCUS
;
759 // When IME gets focus, we should initialize all information of the
761 if (NS_WARN_IF(!mContentCache
.CacheAll(this, &aIMENotification
))) {
762 return NS_ERROR_FAILURE
;
765 // When IME loses focus, we don't need to store anything.
766 mContentCache
.Clear();
769 mIMENotificationRequestsOfParent
=
770 IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL
);
771 RefPtr
<PuppetWidget
> self
= this;
772 mBrowserChild
->SendNotifyIMEFocus(mContentCache
, aIMENotification
)
774 GetMainThreadSerialEventTarget(), __func__
,
775 [self
](IMENotificationRequests
&& aRequests
) {
776 self
->mIMENotificationRequestsOfParent
= aRequests
;
777 if (TextEventDispatcher
* dispatcher
=
778 self
->GetTextEventDispatcher()) {
779 dispatcher
->OnWidgetChangeIMENotificationRequests(self
);
782 [self
](mozilla::ipc::ResponseRejectReason
&& aReason
) {
783 NS_WARNING("SendNotifyIMEFocus got rejected.");
789 nsresult
PuppetWidget::NotifyIMEOfCompositionUpdate(
790 const IMENotification
& aIMENotification
) {
791 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
793 if (NS_WARN_IF(!mBrowserChild
)) {
794 return NS_ERROR_FAILURE
;
797 if (NS_WARN_IF(!mContentCache
.CacheSelection(this, &aIMENotification
))) {
798 return NS_ERROR_FAILURE
;
800 mBrowserChild
->SendNotifyIMECompositionUpdate(mContentCache
,
805 nsresult
PuppetWidget::NotifyIMEOfTextChange(
806 const IMENotification
& aIMENotification
) {
807 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
808 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_TEXT_CHANGE
,
809 "Passed wrong notification");
811 if (!mBrowserChild
) {
812 return NS_ERROR_FAILURE
;
815 // FYI: text change notification is the first notification after
816 // a user operation changes the content. So, we need to modify
817 // the cache as far as possible here.
819 if (NS_WARN_IF(!mContentCache
.CacheText(this, &aIMENotification
))) {
820 return NS_ERROR_FAILURE
;
823 // BrowserParent doesn't this this to cache. we don't send the notification
824 // if parent process doesn't request NOTIFY_TEXT_CHANGE.
825 if (mIMENotificationRequestsOfParent
.WantTextChange()) {
826 mBrowserChild
->SendNotifyIMETextChange(mContentCache
, aIMENotification
);
828 mBrowserChild
->SendUpdateContentCache(mContentCache
);
833 nsresult
PuppetWidget::NotifyIMEOfSelectionChange(
834 const IMENotification
& aIMENotification
) {
835 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
836 MOZ_ASSERT(aIMENotification
.mMessage
== NOTIFY_IME_OF_SELECTION_CHANGE
,
837 "Passed wrong notification");
838 if (!mBrowserChild
) {
839 return NS_ERROR_FAILURE
;
842 // Note that selection change must be notified after text change if it occurs.
843 // Therefore, we don't need to query text content again here.
844 mContentCache
.SetSelection(
845 this, aIMENotification
.mSelectionChangeData
.mOffset
,
846 aIMENotification
.mSelectionChangeData
.Length(),
847 aIMENotification
.mSelectionChangeData
.mReversed
,
848 aIMENotification
.mSelectionChangeData
.GetWritingMode());
850 mBrowserChild
->SendNotifyIMESelection(mContentCache
, aIMENotification
);
855 nsresult
PuppetWidget::NotifyIMEOfMouseButtonEvent(
856 const IMENotification
& aIMENotification
) {
857 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
858 if (!mBrowserChild
) {
859 return NS_ERROR_FAILURE
;
862 bool consumedByIME
= false;
863 if (!mBrowserChild
->SendNotifyIMEMouseButtonEvent(aIMENotification
,
865 return NS_ERROR_FAILURE
;
868 return consumedByIME
? NS_SUCCESS_EVENT_CONSUMED
: NS_OK
;
871 nsresult
PuppetWidget::NotifyIMEOfPositionChange(
872 const IMENotification
& aIMENotification
) {
873 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
874 if (NS_WARN_IF(!mBrowserChild
)) {
875 return NS_ERROR_FAILURE
;
878 if (NS_WARN_IF(!mContentCache
.CacheEditorRect(this, &aIMENotification
))) {
879 return NS_ERROR_FAILURE
;
881 if (NS_WARN_IF(!mContentCache
.CacheSelection(this, &aIMENotification
))) {
882 return NS_ERROR_FAILURE
;
884 if (mIMENotificationRequestsOfParent
.WantPositionChanged()) {
885 mBrowserChild
->SendNotifyIMEPositionChange(mContentCache
, aIMENotification
);
887 mBrowserChild
->SendUpdateContentCache(mContentCache
);
892 struct CursorSurface
{
893 UniquePtr
<char[]> mData
;
897 void PuppetWidget::SetCursor(nsCursor aCursor
, imgIContainer
* aCursorImage
,
898 uint32_t aHotspotX
, uint32_t aHotspotY
) {
899 if (!mBrowserChild
) {
903 // Don't cache on windows, Windowless flash breaks this via async cursor
906 if (!mUpdateCursor
&& mCursor
== aCursor
&& mCustomCursor
== aCursorImage
&&
908 (mCursorHotspotX
== aHotspotX
&& mCursorHotspotY
== aHotspotY
))) {
913 bool hasCustomCursor
= false;
914 UniquePtr
<char[]> customCursorData
;
916 IntSize customCursorSize
;
918 auto format
= SurfaceFormat::B8G8R8A8
;
919 bool force
= mUpdateCursor
;
922 RefPtr
<SourceSurface
> surface
= aCursorImage
->GetFrame(
923 imgIContainer::FRAME_CURRENT
,
924 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
926 if (RefPtr
<DataSourceSurface
> dataSurface
= surface
->GetDataSurface()) {
927 hasCustomCursor
= true;
928 customCursorData
= nsContentUtils::GetSurfaceData(
929 WrapNotNull(dataSurface
), &length
, &stride
);
930 customCursorSize
= dataSurface
->GetSize();
931 format
= dataSurface
->GetFormat();
936 mCustomCursor
= nullptr;
938 nsDependentCString
cursorData(customCursorData
? customCursorData
.get() : "",
940 if (!mBrowserChild
->SendSetCursor(aCursor
, hasCustomCursor
, cursorData
,
941 customCursorSize
.width
,
942 customCursorSize
.height
, stride
, format
,
943 aHotspotX
, aHotspotY
, force
)) {
948 mCustomCursor
= aCursorImage
;
949 mCursorHotspotX
= aHotspotX
;
950 mCursorHotspotY
= aHotspotY
;
951 mUpdateCursor
= false;
954 void PuppetWidget::ClearCachedCursor() {
955 nsBaseWidget::ClearCachedCursor();
956 mCustomCursor
= nullptr;
959 void PuppetWidget::SetChild(PuppetWidget
* aChild
) {
960 MOZ_ASSERT(this != aChild
, "can't parent a widget to itself");
961 MOZ_ASSERT(!aChild
->mChild
,
962 "fake widget 'hierarchy' only expected to have one level");
968 PuppetWidget::WidgetPaintTask::Run() {
975 void PuppetWidget::Paint() {
976 if (!GetCurrentWidgetListener()) return;
978 mWidgetPaintTask
.Revoke();
980 RefPtr
<PuppetWidget
> strongThis(this);
982 GetCurrentWidgetListener()->WillPaintWindow(this);
984 if (GetCurrentWidgetListener()) {
985 GetCurrentWidgetListener()->DidPaintWindow();
989 void PuppetWidget::PaintNowIfNeeded() {
990 if (IsVisible() && mWidgetPaintTask
.IsPending()) {
995 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy
) {
996 if (aWhy
!= MemoryPressureReason::LOW_MEMORY_ONGOING
&& !mVisible
&&
997 mLayerManager
&& XRE_IsContentProcess()) {
998 mLayerManager
->ClearCachedResources();
1002 bool PuppetWidget::NeedsPaint() {
1003 // e10s popups are handled by the parent process, so never should be painted
1005 if (XRE_IsContentProcess() &&
1006 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
1007 mWindowType
== eWindowType_popup
) {
1008 NS_WARNING("Trying to paint an e10s popup in the child process!");
1015 float PuppetWidget::GetDPI() { return mDPI
; }
1017 double PuppetWidget::GetDefaultScaleInternal() { return mDefaultScale
; }
1019 int32_t PuppetWidget::RoundsWidgetCoordinatesTo() { return mRounding
; }
1021 LayoutDeviceIntPoint
PuppetWidget::GetChromeOffset() {
1022 if (!GetOwningBrowserChild()) {
1023 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1024 return LayoutDeviceIntPoint();
1026 return GetOwningBrowserChild()->GetChromeOffset();
1029 LayoutDeviceIntPoint
PuppetWidget::WidgetToScreenOffset() {
1030 auto positionRalativeToWindow
=
1031 WidgetToTopLevelWidgetTransform().TransformPoint(LayoutDevicePoint());
1033 return GetWindowPosition() +
1034 LayoutDeviceIntPoint::Round(positionRalativeToWindow
);
1037 LayoutDeviceIntPoint
PuppetWidget::GetWindowPosition() {
1038 if (!GetOwningBrowserChild()) {
1039 return LayoutDeviceIntPoint();
1042 int32_t winX
, winY
, winW
, winH
;
1044 GetOwningBrowserChild()->GetDimensions(0, &winX
, &winY
, &winW
, &winH
),
1045 LayoutDeviceIntPoint());
1046 return LayoutDeviceIntPoint(winX
, winY
) +
1047 GetOwningBrowserChild()->GetClientOffset();
1050 LayoutDeviceIntRect
PuppetWidget::GetScreenBounds() {
1051 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds
.Size());
1054 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1055 return mBrowserChild
? mBrowserChild
->MaxTouchPoints() : 0;
1058 void PuppetWidget::StartAsyncScrollbarDrag(
1059 const AsyncDragMetrics
& aDragMetrics
) {
1060 mBrowserChild
->StartScrollbarDrag(aDragMetrics
);
1063 PuppetScreen::PuppetScreen(void* nativeScreen
) {}
1065 PuppetScreen::~PuppetScreen() = default;
1067 static ScreenConfiguration
ScreenConfig() {
1068 ScreenConfiguration config
;
1069 hal::GetCurrentScreenConfiguration(&config
);
1073 nsIntSize
PuppetWidget::GetScreenDimensions() {
1074 nsIntRect r
= ScreenConfig().rect();
1075 return nsIntSize(r
.Width(), r
.Height());
1079 PuppetScreen::GetRect(int32_t* outLeft
, int32_t* outTop
, int32_t* outWidth
,
1080 int32_t* outHeight
) {
1081 nsIntRect r
= ScreenConfig().rect();
1082 r
.GetRect(outLeft
, outTop
, outWidth
, outHeight
);
1087 PuppetScreen::GetAvailRect(int32_t* outLeft
, int32_t* outTop
, int32_t* outWidth
,
1088 int32_t* outHeight
) {
1089 return GetRect(outLeft
, outTop
, outWidth
, outHeight
);
1093 PuppetScreen::GetPixelDepth(int32_t* aPixelDepth
) {
1094 *aPixelDepth
= ScreenConfig().pixelDepth();
1099 PuppetScreen::GetColorDepth(int32_t* aColorDepth
) {
1100 *aColorDepth
= ScreenConfig().colorDepth();
1104 NS_IMPL_ISUPPORTS(PuppetScreenManager
, nsIScreenManager
)
1106 PuppetScreenManager::PuppetScreenManager() {
1107 mOneScreen
= new PuppetScreen(nullptr);
1110 PuppetScreenManager::~PuppetScreenManager() = default;
1113 PuppetScreenManager::GetPrimaryScreen(nsIScreen
** outScreen
) {
1114 NS_IF_ADDREF(*outScreen
= mOneScreen
.get());
1119 PuppetScreenManager::GetTotalScreenPixels(int64_t* aTotalScreenPixels
) {
1120 MOZ_ASSERT(aTotalScreenPixels
);
1122 int32_t x
, y
, width
, height
;
1123 x
= y
= width
= height
= 0;
1124 mOneScreen
->GetRect(&x
, &y
, &width
, &height
);
1125 *aTotalScreenPixels
= width
* height
;
1127 *aTotalScreenPixels
= 0;
1133 PuppetScreenManager::ScreenForRect(int32_t inLeft
, int32_t inTop
,
1134 int32_t inWidth
, int32_t inHeight
,
1135 nsIScreen
** outScreen
) {
1136 return GetPrimaryScreen(outScreen
);
1139 ScreenIntMargin
PuppetWidget::GetSafeAreaInsets() const {
1140 return mSafeAreaInsets
;
1143 void PuppetWidget::UpdateSafeAreaInsets(
1144 const ScreenIntMargin
& aSafeAreaInsets
) {
1145 mSafeAreaInsets
= aSafeAreaInsets
;
1148 nsIWidgetListener
* PuppetWidget::GetCurrentWidgetListener() {
1149 if (!mPreviouslyAttachedWidgetListener
|| !mAttachedWidgetListener
) {
1150 return mAttachedWidgetListener
;
1153 if (mAttachedWidgetListener
->GetView()->IsPrimaryFramePaintSuppressed()) {
1154 return mPreviouslyAttachedWidgetListener
;
1157 return mAttachedWidgetListener
;
1160 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId
,
1161 const ScrollableLayerGuid::ViewID
& aViewId
,
1162 const CSSRect
& aRect
, const uint32_t& aFlags
) {
1163 if (!mBrowserChild
) {
1167 mBrowserChild
->ZoomToRect(aPresShellId
, aViewId
, aRect
, aFlags
);
1170 void PuppetWidget::LookUpDictionary(
1171 const nsAString
& aText
, const nsTArray
<mozilla::FontRange
>& aFontRangeArray
,
1172 const bool aIsVertical
, const LayoutDeviceIntPoint
& aPoint
) {
1173 if (!mBrowserChild
) {
1177 mBrowserChild
->SendLookUpDictionary(nsString(aText
), aFontRangeArray
,
1178 aIsVertical
, aPoint
);
1181 bool PuppetWidget::HasPendingInputEvent() {
1182 if (!mBrowserChild
) {
1188 mBrowserChild
->GetIPCChannel()->PeekMessages(
1189 [&ret
](const IPC::Message
& aMsg
) -> bool {
1190 if (nsContentUtils::IsMessageInputEvent(aMsg
)) {
1192 return false; // Stop peeking.
1200 // TextEventDispatcherListener
1203 PuppetWidget::NotifyIME(TextEventDispatcher
* aTextEventDispatcher
,
1204 const IMENotification
& aIMENotification
) {
1205 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1207 // If there is different text event dispatcher listener for handling
1208 // text event dispatcher, that means that native keyboard events and
1209 // IME events are handled in this process. Therefore, we don't need
1210 // to send any requests and notifications to the parent process.
1211 if (mNativeTextEventDispatcherListener
) {
1212 return NS_ERROR_NOT_IMPLEMENTED
;
1215 switch (aIMENotification
.mMessage
) {
1216 case REQUEST_TO_COMMIT_COMPOSITION
:
1217 return RequestIMEToCommitComposition(false);
1218 case REQUEST_TO_CANCEL_COMPOSITION
:
1219 return RequestIMEToCommitComposition(true);
1220 case NOTIFY_IME_OF_FOCUS
:
1221 case NOTIFY_IME_OF_BLUR
:
1222 return NotifyIMEOfFocusChange(aIMENotification
);
1223 case NOTIFY_IME_OF_SELECTION_CHANGE
:
1224 return NotifyIMEOfSelectionChange(aIMENotification
);
1225 case NOTIFY_IME_OF_TEXT_CHANGE
:
1226 return NotifyIMEOfTextChange(aIMENotification
);
1227 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED
:
1228 return NotifyIMEOfCompositionUpdate(aIMENotification
);
1229 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT
:
1230 return NotifyIMEOfMouseButtonEvent(aIMENotification
);
1231 case NOTIFY_IME_OF_POSITION_CHANGE
:
1232 return NotifyIMEOfPositionChange(aIMENotification
);
1234 return NS_ERROR_NOT_IMPLEMENTED
;
1237 return NS_ERROR_NOT_IMPLEMENTED
;
1240 NS_IMETHODIMP_(IMENotificationRequests
)
1241 PuppetWidget::GetIMENotificationRequests() {
1242 return IMENotificationRequests(
1243 mIMENotificationRequestsOfParent
.mWantUpdates
|
1244 IMENotificationRequests::NOTIFY_TEXT_CHANGE
|
1245 IMENotificationRequests::NOTIFY_POSITION_CHANGE
);
1248 NS_IMETHODIMP_(void)
1249 PuppetWidget::OnRemovedFrom(TextEventDispatcher
* aTextEventDispatcher
) {
1250 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1253 NS_IMETHODIMP_(void)
1254 PuppetWidget::WillDispatchKeyboardEvent(
1255 TextEventDispatcher
* aTextEventDispatcher
,
1256 WidgetKeyboardEvent
& aKeyboardEvent
, uint32_t aIndexOfKeypress
,
1258 MOZ_ASSERT(aTextEventDispatcher
== mTextEventDispatcher
);
1261 nsresult
PuppetWidget::SetSystemFont(const nsCString
& aFontName
) {
1262 if (!mBrowserChild
) {
1263 return NS_ERROR_FAILURE
;
1266 mBrowserChild
->SendSetSystemFont(aFontName
);
1270 nsresult
PuppetWidget::GetSystemFont(nsCString
& aFontName
) {
1271 if (!mBrowserChild
) {
1272 return NS_ERROR_FAILURE
;
1274 mBrowserChild
->SendGetSystemFont(&aFontName
);
1278 } // namespace widget
1279 } // namespace mozilla