Bug 1758698 [wpt PR 33120] - Add more referrer policy WPTs for Early Hints, a=testonly
[gecko.git] / widget / PuppetWidget.cpp
blob97a796b876dd2f170c483224f9a94527b47d0a40
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "base/basictypes.h"
10 #include "gfxPlatform.h"
11 #include "nsRefreshDriver.h"
12 #include "mozilla/dom/BrowserChild.h"
13 #include "mozilla/gfx/gfxVars.h"
14 #include "mozilla/IMEStateManager.h"
15 #include "mozilla/layers/APZChild.h"
16 #include "mozilla/layers/WebRenderLayerManager.h"
17 #include "mozilla/NativeKeyBindingsType.h"
18 #include "mozilla/Preferences.h"
19 #include "mozilla/PresShell.h"
20 #include "mozilla/SchedulerGroup.h"
21 #include "mozilla/StaticPrefs_browser.h"
22 #include "mozilla/StaticPrefs_gfx.h"
23 #include "mozilla/TextComposition.h"
24 #include "mozilla/TextEventDispatcher.h"
25 #include "mozilla/TextEvents.h"
26 #include "mozilla/Unused.h"
27 #include "PuppetWidget.h"
28 #include "nsContentUtils.h"
29 #include "nsIWidgetListener.h"
30 #include "imgIContainer.h"
31 #include "nsView.h"
32 #include "nsXPLookAndFeel.h"
33 #include "nsPrintfCString.h"
35 using namespace mozilla;
36 using namespace mozilla::dom;
37 using namespace mozilla::gfx;
38 using namespace mozilla::layers;
39 using namespace mozilla::widget;
41 static void InvalidateRegion(nsIWidget* aWidget,
42 const LayoutDeviceIntRegion& aRegion) {
43 for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
44 aWidget->Invalidate(iter.Get());
48 /*static*/
49 already_AddRefed<nsIWidget> nsIWidget::CreatePuppetWidget(
50 BrowserChild* aBrowserChild) {
51 MOZ_ASSERT(!aBrowserChild || nsIWidget::UsePuppetWidgets(),
52 "PuppetWidgets not allowed in this configuration");
54 nsCOMPtr<nsIWidget> widget = new PuppetWidget(aBrowserChild);
55 return widget.forget();
58 namespace mozilla {
59 namespace widget {
61 static bool IsPopup(const nsWidgetInitData* aInitData) {
62 return aInitData && aInitData->mWindowType == eWindowType_popup;
65 static bool MightNeedIMEFocus(const nsWidgetInitData* aInitData) {
66 // In the puppet-widget world, popup widgets are just dummies and
67 // shouldn't try to mess with IME state.
68 #ifdef MOZ_CROSS_PROCESS_IME
69 return !IsPopup(aInitData);
70 #else
71 return false;
72 #endif
75 // Arbitrary, fungible.
76 const size_t PuppetWidget::kMaxDimension = 4000;
78 NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget, nsBaseWidget,
79 TextEventDispatcherListener)
81 PuppetWidget::PuppetWidget(BrowserChild* aBrowserChild)
82 : mBrowserChild(aBrowserChild),
83 mMemoryPressureObserver(nullptr),
84 mDPI(-1),
85 mRounding(1),
86 mDefaultScale(-1),
87 mEnabled(false),
88 mVisible(false),
89 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);
105 mBounds = aRect;
106 mEnabled = true;
107 mVisible = true;
109 mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
110 IntSize(1, 1), SurfaceFormat::B8G8R8A8);
112 mNeedIMEStateInit = MightNeedIMEFocus(aInitData);
114 PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
115 if (parent) {
116 parent->SetChild(this);
117 mWindowRenderer = parent->GetWindowRenderer();
118 } else {
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);
128 return NS_OK;
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)))
148 ? widget.forget()
149 : nullptr);
152 void PuppetWidget::Destroy() {
153 if (mOnDestroyCalled) {
154 return;
156 mOnDestroyCalled = true;
158 Base::OnDestroy();
159 Base::Destroy();
160 if (mMemoryPressureObserver) {
161 mMemoryPressureObserver->Unregister();
162 mMemoryPressureObserver = nullptr;
164 mChild = 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;
177 mVisible = aState;
179 if (mChild) {
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
191 // bug 1323586).
192 mPreviouslyAttachedWidgetListener = nullptr;
193 Resize(mBounds.Width(), mBounds.Height(), false);
194 Invalidate(mBounds);
198 void PuppetWidget::Resize(double aWidth, double aHeight, bool aRepaint) {
199 LayoutDeviceIntRect oldBounds = mBounds;
200 mBounds.SizeTo(
201 LayoutDeviceIntSize(NSToIntRound(aWidth), NSToIntRound(aHeight)));
203 if (mChild) {
204 mChild->Resize(aWidth, aHeight, aRepaint);
205 return;
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(),
223 mBounds.Height());
225 mAttachedWidgetListener->WindowResized(this, mBounds.Width(),
226 mBounds.Height());
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) {
237 #ifdef DEBUG
238 debug_DumpInvalidate(stderr, this, &aRect, "PuppetWidget", 0);
239 #endif
241 if (mChild) {
242 mChild->Invalidate(aRect);
243 return;
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);
266 } else {
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) {
275 #ifdef DEBUG
276 debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "PuppetWidget", 0);
277 #endif
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 "
286 "all types "
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;
298 return NS_OK;
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();
307 #ifdef DEBUG
308 if (mNativeIMEContext.IsValid() &&
309 mNativeIMEContext != compositionEvent->mNativeIMEContext) {
310 RefPtr<TextComposition> composition =
311 IMEStateManager::GetTextCompositionFor(this);
312 MOZ_ASSERT(
313 !composition,
314 "When there is composition caused by old native IME context, "
315 "composition events caused by different native IME context are not "
316 "allowed");
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
328 // with events here.
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(
344 NS_SUCCEEDED(rv),
345 "The text event dispatcher should always succeed to start input "
346 "transaction for the event");
350 aStatus = nsEventStatus_eIgnore;
352 if (GetCurrentWidgetListener()) {
353 aStatus =
354 GetCurrentWidgetListener()->HandleEvent(aEvent, mUseAttachedEvents);
357 return NS_OK;
360 nsIWidget::ContentAndAPZEventStatus PuppetWidget::DispatchInputEvent(
361 WidgetInputEvent* aEvent) {
362 ContentAndAPZEventStatus status;
363 if (!AsyncPanZoomEnabled()) {
364 DispatchEvent(aEvent, status.mContentStatus);
365 return status;
368 if (!mBrowserChild) {
369 return status;
372 switch (aEvent->mClass) {
373 case eWheelEventClass:
374 Unused << mBrowserChild->SendDispatchWheelEvent(*aEvent->AsWheelEvent());
375 break;
376 case eMouseEventClass:
377 Unused << mBrowserChild->SendDispatchMouseEvent(*aEvent->AsMouseEvent());
378 break;
379 case eKeyboardEventClass:
380 Unused << mBrowserChild->SendDispatchKeyboardEvent(
381 *aEvent->AsKeyboardEvent());
382 break;
383 case eTouchEventClass:
384 Unused << mBrowserChild->SendDispatchTouchEvent(*aEvent->AsTouchEvent());
385 break;
386 default:
387 MOZ_ASSERT_UNREACHABLE("unsupported event type");
390 return status;
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());
405 return NS_OK;
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());
420 return NS_OK;
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());
430 return NS_OK;
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());
444 return NS_OK;
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());
458 return NS_OK;
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,
468 aModifierFlags);
469 return NS_OK;
472 nsresult PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
473 bool aLongTap,
474 nsIObserver* aObserver) {
475 AutoObserverNotifier notifier(aObserver, "touchtap");
476 if (!mBrowserChild) {
477 return NS_ERROR_FAILURE;
479 mBrowserChild->SendSynthesizeNativeTouchTap(aPoint, aLongTap,
480 notifier.SaveObserver());
481 return NS_OK;
484 nsresult PuppetWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
485 AutoObserverNotifier notifier(aObserver, "cleartouch");
486 if (!mBrowserChild) {
487 return NS_ERROR_FAILURE;
489 mBrowserChild->SendClearNativeTouchSequence(notifier.SaveObserver());
490 return NS_OK;
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());
504 return NS_OK;
507 nsresult PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
508 LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
509 if (!mBrowserChild) {
510 return NS_ERROR_FAILURE;
512 mBrowserChild->SendSynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags);
513 return NS_OK;
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);
524 return NS_OK;
527 void PuppetWidget::LockNativePointer() {
528 if (!mBrowserChild) {
529 return;
531 mBrowserChild->SendLockNativePointer();
534 void PuppetWidget::UnlockNativePointer() {
535 if (!mBrowserChild) {
536 return;
538 mBrowserChild->SendUnlockNativePointer();
541 void PuppetWidget::SetConfirmedTargetAPZC(
542 uint64_t aInputBlockId,
543 const nsTArray<ScrollableLayerGuid>& aTargets) const {
544 if (mBrowserChild) {
545 mBrowserChild->SetTargetAPZC(aInputBlockId, aTargets);
549 void PuppetWidget::UpdateZoomConstraints(
550 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
551 const Maybe<ZoomConstraints>& aConstraints) {
552 if (mBrowserChild) {
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))) {
567 return false;
569 if (NS_WARN_IF(!mBrowserChild)) {
570 return false;
572 mBrowserChild->RequestEditCommands(aType, aEvent, aCommands);
573 return true;
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)) {
603 return false;
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);
612 return true;
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())) {
624 return NS_OK;
627 // We've already requested to commit/cancel composition.
628 if (NS_WARN_IF(mIgnoreCompositionEvents)) {
629 #ifdef DEBUG
630 RefPtr<TextComposition> composition =
631 IMEStateManager::GetTextCompositionFor(this);
632 MOZ_ASSERT(!composition);
633 #endif // #ifdef DEBUG
634 return NS_OK;
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)) {
641 return NS_OK;
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.
658 if (!isCommitted) {
659 return NS_OK;
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);
669 #ifdef DEBUG
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.
683 return NS_OK;
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) {
695 return nullptr;
698 if (PresShell* presShell = mBrowserChild->GetTopLevelPresShell()) {
699 return presShell->GetRefreshDriver();
702 return nullptr;
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) {
713 return;
715 mBrowserChild->SendSetInputContext(aContext, aAction);
718 InputContext PuppetWidget::GetInputContext() {
719 // XXX Currently, we don't support retrieving IME open state from child
720 // process.
722 // If the cache of input context is valid, we can avoid to use synchronous
723 // IPC.
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;
734 if (mBrowserChild) {
735 mBrowserChild->SendGetInputContext(&context.mIMEState);
737 return context;
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;
753 if (gotFocus) {
754 // When IME gets focus, we should initialize all information of the
755 // content.
756 if (NS_WARN_IF(!mContentCache.CacheAll(this, &aIMENotification))) {
757 return NS_ERROR_FAILURE;
759 } else {
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)
768 ->Then(
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.");
781 return NS_OK;
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,
796 aIMENotification);
797 return NS_OK;
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);
822 } else {
823 mBrowserChild->SendUpdateContentCache(mContentCache);
825 return NS_OK;
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);
843 return NS_OK;
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,
855 &consumedByIME)) {
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);
877 } else {
878 mBrowserChild->SendUpdateContentCache(mContentCache);
880 return NS_OK;
883 struct CursorSurface {
884 UniquePtr<char[]> mData;
885 IntSize mSize;
888 void PuppetWidget::SetCursor(const Cursor& aCursor) {
889 if (!mBrowserChild) {
890 return;
893 const bool force = mUpdateCursor;
894 if (!force && mCursor == aCursor) {
895 return;
898 bool hasCustomCursor = false;
899 UniquePtr<char[]> customCursorData;
900 size_t length = 0;
901 IntSize customCursorSize;
902 int32_t stride = 0;
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);
919 } else {
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.
923 surface =
924 aCursor.mContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags);
926 if (surface) {
927 if (RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface()) {
928 hasCustomCursor = true;
929 customCursorData =
930 nsContentUtils::GetSurfaceData(*dataSurface, &length, &stride);
931 customCursorSize = dataSurface->GetSize();
932 format = dataSurface->GetFormat();
937 nsDependentCString cursorData(customCursorData ? customCursorData.get() : "",
938 length);
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,
943 force)) {
944 return;
946 mCursor = aCursor;
947 mUpdateCursor = false;
950 void PuppetWidget::SetChild(PuppetWidget* aChild) {
951 MOZ_ASSERT(this != aChild, "can't parent a widget to itself");
952 MOZ_ASSERT(!aChild->mChild,
953 "fake widget 'hierarchy' only expected to have one level");
955 mChild = aChild;
958 NS_IMETHODIMP
959 PuppetWidget::WidgetPaintTask::Run() {
960 if (mWidget) {
961 mWidget->Paint();
963 return NS_OK;
966 void PuppetWidget::Paint() {
967 if (!GetCurrentWidgetListener()) return;
969 mWidgetPaintTask.Revoke();
971 RefPtr<PuppetWidget> strongThis(this);
973 GetCurrentWidgetListener()->WillPaintWindow(this);
975 if (GetCurrentWidgetListener()) {
976 GetCurrentWidgetListener()->DidPaintWindow();
980 void PuppetWidget::PaintNowIfNeeded() {
981 if (IsVisible() && mWidgetPaintTask.IsPending()) {
982 Paint();
986 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy) {
987 if (aWhy != MemoryPressureReason::LOW_MEMORY_ONGOING && !mVisible &&
988 mWindowRenderer && mWindowRenderer->AsWebRender() &&
989 XRE_IsContentProcess()) {
990 mWindowRenderer->AsWebRender()->ClearCachedResources();
994 bool PuppetWidget::NeedsPaint() {
995 // e10s popups are handled by the parent process, so never should be painted
996 // here
997 if (XRE_IsContentProcess() &&
998 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
999 mWindowType == eWindowType_popup) {
1000 NS_WARNING("Trying to paint an e10s popup in the child process!");
1001 return false;
1004 return mVisible;
1007 float PuppetWidget::GetDPI() { return mDPI; }
1009 double PuppetWidget::GetDefaultScaleInternal() { return mDefaultScale; }
1011 int32_t PuppetWidget::RoundsWidgetCoordinatesTo() { return mRounding; }
1013 LayoutDeviceIntPoint PuppetWidget::GetChromeOffset() {
1014 if (!GetOwningBrowserChild()) {
1015 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1016 return LayoutDeviceIntPoint();
1018 return GetOwningBrowserChild()->GetChromeOffset();
1021 LayoutDeviceIntPoint PuppetWidget::WidgetToScreenOffset() {
1022 auto positionRalativeToWindow =
1023 WidgetToTopLevelWidgetTransform().TransformPoint(LayoutDevicePoint());
1025 return GetWindowPosition() +
1026 LayoutDeviceIntPoint::Round(positionRalativeToWindow);
1029 LayoutDeviceIntPoint PuppetWidget::GetWindowPosition() {
1030 if (!GetOwningBrowserChild()) {
1031 return LayoutDeviceIntPoint();
1034 int32_t winX, winY, winW, winH;
1035 NS_ENSURE_SUCCESS(
1036 GetOwningBrowserChild()->GetDimensions(0, &winX, &winY, &winW, &winH),
1037 LayoutDeviceIntPoint());
1038 return LayoutDeviceIntPoint(winX, winY) +
1039 GetOwningBrowserChild()->GetClientOffset();
1042 LayoutDeviceIntRect PuppetWidget::GetScreenBounds() {
1043 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
1046 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1047 return mBrowserChild ? mBrowserChild->MaxTouchPoints() : 0;
1050 void PuppetWidget::StartAsyncScrollbarDrag(
1051 const AsyncDragMetrics& aDragMetrics) {
1052 mBrowserChild->StartScrollbarDrag(aDragMetrics);
1055 ScreenIntMargin PuppetWidget::GetSafeAreaInsets() const {
1056 return mSafeAreaInsets;
1059 void PuppetWidget::UpdateSafeAreaInsets(
1060 const ScreenIntMargin& aSafeAreaInsets) {
1061 mSafeAreaInsets = aSafeAreaInsets;
1064 nsIWidgetListener* PuppetWidget::GetCurrentWidgetListener() {
1065 if (!mPreviouslyAttachedWidgetListener || !mAttachedWidgetListener) {
1066 return mAttachedWidgetListener;
1069 if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) {
1070 return mPreviouslyAttachedWidgetListener;
1073 return mAttachedWidgetListener;
1076 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId,
1077 const ScrollableLayerGuid::ViewID& aViewId,
1078 const CSSRect& aRect, const uint32_t& aFlags) {
1079 if (!mBrowserChild) {
1080 return;
1083 mBrowserChild->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
1086 void PuppetWidget::LookUpDictionary(
1087 const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
1088 const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
1089 if (!mBrowserChild) {
1090 return;
1093 mBrowserChild->SendLookUpDictionary(nsString(aText), aFontRangeArray,
1094 aIsVertical, aPoint);
1097 bool PuppetWidget::HasPendingInputEvent() {
1098 if (!mBrowserChild) {
1099 return false;
1102 bool ret = false;
1104 mBrowserChild->GetIPCChannel()->PeekMessages(
1105 [&ret](const IPC::Message& aMsg) -> bool {
1106 if (nsContentUtils::IsMessageInputEvent(aMsg)) {
1107 ret = true;
1108 return false; // Stop peeking.
1110 return true;
1113 return ret;
1116 // TextEventDispatcherListener
1118 NS_IMETHODIMP
1119 PuppetWidget::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
1120 const IMENotification& aIMENotification) {
1121 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1123 // If there is different text event dispatcher listener for handling
1124 // text event dispatcher, that means that native keyboard events and
1125 // IME events are handled in this process. Therefore, we don't need
1126 // to send any requests and notifications to the parent process.
1127 if (mNativeTextEventDispatcherListener) {
1128 return NS_ERROR_NOT_IMPLEMENTED;
1131 switch (aIMENotification.mMessage) {
1132 case REQUEST_TO_COMMIT_COMPOSITION:
1133 return RequestIMEToCommitComposition(false);
1134 case REQUEST_TO_CANCEL_COMPOSITION:
1135 return RequestIMEToCommitComposition(true);
1136 case NOTIFY_IME_OF_FOCUS:
1137 case NOTIFY_IME_OF_BLUR:
1138 return NotifyIMEOfFocusChange(aIMENotification);
1139 case NOTIFY_IME_OF_SELECTION_CHANGE:
1140 return NotifyIMEOfSelectionChange(aIMENotification);
1141 case NOTIFY_IME_OF_TEXT_CHANGE:
1142 return NotifyIMEOfTextChange(aIMENotification);
1143 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
1144 return NotifyIMEOfCompositionUpdate(aIMENotification);
1145 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
1146 return NotifyIMEOfMouseButtonEvent(aIMENotification);
1147 case NOTIFY_IME_OF_POSITION_CHANGE:
1148 return NotifyIMEOfPositionChange(aIMENotification);
1149 default:
1150 return NS_ERROR_NOT_IMPLEMENTED;
1153 return NS_ERROR_NOT_IMPLEMENTED;
1156 NS_IMETHODIMP_(IMENotificationRequests)
1157 PuppetWidget::GetIMENotificationRequests() {
1158 return IMENotificationRequests(
1159 mIMENotificationRequestsOfParent.mWantUpdates |
1160 IMENotificationRequests::NOTIFY_TEXT_CHANGE |
1161 IMENotificationRequests::NOTIFY_POSITION_CHANGE);
1164 NS_IMETHODIMP_(void)
1165 PuppetWidget::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
1166 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1169 NS_IMETHODIMP_(void)
1170 PuppetWidget::WillDispatchKeyboardEvent(
1171 TextEventDispatcher* aTextEventDispatcher,
1172 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
1173 void* aData) {
1174 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1177 nsresult PuppetWidget::SetSystemFont(const nsCString& aFontName) {
1178 if (!mBrowserChild) {
1179 return NS_ERROR_FAILURE;
1182 mBrowserChild->SendSetSystemFont(aFontName);
1183 return NS_OK;
1186 nsresult PuppetWidget::GetSystemFont(nsCString& aFontName) {
1187 if (!mBrowserChild) {
1188 return NS_ERROR_FAILURE;
1190 mBrowserChild->SendGetSystemFont(&aFontName);
1191 return NS_OK;
1194 } // namespace widget
1195 } // namespace mozilla