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