Bug 1831502 [wpt PR 39860] - HTML: sync html5lib-tests, a=testonly
[gecko.git] / widget / PuppetWidget.cpp
blob99d536c180fa95febba36af9d4c207d7a619aa06
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 // 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 mEnabled(false),
85 mVisible(false),
86 mSizeMode(nsSizeMode_Normal),
87 mNeedIMEStateInit(false),
88 mIgnoreCompositionEvents(false) {
89 // Setting 'Unknown' means "not yet cached".
90 mInputContext.mIMEState.mEnabled = IMEEnabled::Unknown;
93 PuppetWidget::~PuppetWidget() { Destroy(); }
95 void PuppetWidget::InfallibleCreate(nsIWidget* aParent,
96 nsNativeWidget aNativeParent,
97 const LayoutDeviceIntRect& aRect,
98 widget::InitData* aInitData) {
99 MOZ_ASSERT(!aNativeParent, "got a non-Puppet native parent");
101 BaseCreate(nullptr, aInitData);
103 mBounds = aRect;
104 mEnabled = true;
105 mVisible = true;
107 mDrawTarget = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
108 IntSize(1, 1), SurfaceFormat::B8G8R8A8);
110 mNeedIMEStateInit = MightNeedIMEFocus(aInitData);
112 PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
113 if (parent) {
114 parent->SetChild(this);
115 mWindowRenderer = parent->GetWindowRenderer();
116 } else {
117 Resize(mBounds.X(), mBounds.Y(), mBounds.Width(), mBounds.Height(), false);
119 mMemoryPressureObserver = MemoryPressureObserver::Create(this);
122 nsresult PuppetWidget::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
123 const LayoutDeviceIntRect& aRect,
124 widget::InitData* aInitData) {
125 InfallibleCreate(aParent, aNativeParent, aRect, aInitData);
126 return NS_OK;
129 void PuppetWidget::InitIMEState() {
130 MOZ_ASSERT(mBrowserChild);
131 if (mNeedIMEStateInit) {
132 mContentCache.Clear();
133 mBrowserChild->SendUpdateContentCache(mContentCache);
134 mIMENotificationRequestsOfParent = IMENotificationRequests();
135 mNeedIMEStateInit = false;
139 already_AddRefed<nsIWidget> PuppetWidget::CreateChild(
140 const LayoutDeviceIntRect& aRect, widget::InitData* aInitData,
141 bool aForceUseIWidgetParent) {
142 bool isPopup = IsPopup(aInitData);
143 nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(mBrowserChild);
144 return ((widget && NS_SUCCEEDED(widget->Create(isPopup ? nullptr : this,
145 nullptr, aRect, aInitData)))
146 ? widget.forget()
147 : nullptr);
150 void PuppetWidget::Destroy() {
151 if (mOnDestroyCalled) {
152 return;
154 mOnDestroyCalled = true;
156 Base::OnDestroy();
157 Base::Destroy();
158 if (mMemoryPressureObserver) {
159 mMemoryPressureObserver->Unregister();
160 mMemoryPressureObserver = nullptr;
162 mChild = nullptr;
163 if (mWindowRenderer) {
164 mWindowRenderer->Destroy();
166 mWindowRenderer = nullptr;
167 mBrowserChild = nullptr;
170 void PuppetWidget::Show(bool aState) {
171 NS_ASSERTION(mEnabled,
172 "does it make sense to Show()/Hide() a disabled widget?");
174 bool wasVisible = mVisible;
175 mVisible = aState;
177 if (mChild) {
178 mChild->mVisible = aState;
181 if (!wasVisible && mVisible) {
182 // The previously attached widget listener is handy if
183 // we're transitioning from page to page without dropping
184 // layers (since we'll continue to show the old layers
185 // associated with that old widget listener). If the
186 // PuppetWidget was hidden, those layers are dropped,
187 // so the previously attached widget listener is really
188 // of no use anymore (and is actually actively harmful - see
189 // bug 1323586).
190 mPreviouslyAttachedWidgetListener = nullptr;
191 Resize(mBounds.Width(), mBounds.Height(), false);
192 Invalidate(mBounds);
196 void PuppetWidget::Resize(double aWidth, double aHeight, bool aRepaint) {
197 LayoutDeviceIntRect oldBounds = mBounds;
198 mBounds.SizeTo(
199 LayoutDeviceIntSize(NSToIntRound(aWidth), NSToIntRound(aHeight)));
201 if (mChild) {
202 mChild->Resize(aWidth, aHeight, aRepaint);
203 return;
206 // XXX: roc says that |aRepaint| dictates whether or not to
207 // invalidate the expanded area
208 if (oldBounds.Size() < mBounds.Size() && aRepaint) {
209 LayoutDeviceIntRegion dirty(mBounds);
210 dirty.Sub(dirty, oldBounds);
211 InvalidateRegion(this, dirty);
214 // call WindowResized() on both the current listener, and possibly
215 // also the previous one if we're in a state where we're drawing that one
216 // because the current one is paint suppressed
217 if (!oldBounds.IsEqualEdges(mBounds) && mAttachedWidgetListener) {
218 if (GetCurrentWidgetListener() &&
219 GetCurrentWidgetListener() != mAttachedWidgetListener) {
220 GetCurrentWidgetListener()->WindowResized(this, mBounds.Width(),
221 mBounds.Height());
223 mAttachedWidgetListener->WindowResized(this, mBounds.Width(),
224 mBounds.Height());
228 void PuppetWidget::SetFocus(Raise aRaise, CallerType aCallerType) {
229 if (aRaise == Raise::Yes && mBrowserChild) {
230 mBrowserChild->SendRequestFocus(true, aCallerType);
234 void PuppetWidget::Invalidate(const LayoutDeviceIntRect& aRect) {
235 #ifdef DEBUG
236 debug_DumpInvalidate(stderr, this, &aRect, "PuppetWidget", 0);
237 #endif
239 if (mChild) {
240 mChild->Invalidate(aRect);
241 return;
244 if (mBrowserChild && !aRect.IsEmpty() && !mWidgetPaintTask.IsPending()) {
245 mWidgetPaintTask = new WidgetPaintTask(this);
246 nsCOMPtr<nsIRunnable> event(mWidgetPaintTask.get());
247 SchedulerGroup::Dispatch(TaskCategory::Other, event.forget());
251 mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
252 PuppetWidget::WidgetToTopLevelWidgetTransform() {
253 if (!GetOwningBrowserChild()) {
254 NS_WARNING("PuppetWidget without Tab does not have transform information.");
255 return mozilla::LayoutDeviceToLayoutDeviceMatrix4x4();
257 return GetOwningBrowserChild()->GetChildToParentConversionMatrix();
260 void PuppetWidget::InitEvent(WidgetGUIEvent& aEvent,
261 LayoutDeviceIntPoint* aPoint) {
262 if (nullptr == aPoint) {
263 aEvent.mRefPoint = LayoutDeviceIntPoint(0, 0);
264 } else {
265 // use the point override if provided
266 aEvent.mRefPoint = *aPoint;
270 nsresult PuppetWidget::DispatchEvent(WidgetGUIEvent* aEvent,
271 nsEventStatus& aStatus) {
272 #ifdef DEBUG
273 debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "PuppetWidget", 0);
274 #endif
276 MOZ_ASSERT(!mChild || mChild->mWindowType == WindowType::Popup,
277 "Unexpected event dispatch!");
279 MOZ_ASSERT(!aEvent->AsKeyboardEvent() ||
280 aEvent->mFlags.mIsSynthesizedForTests ||
281 aEvent->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
282 "Non-sysnthesized keyboard events should have edit commands for "
283 "all types "
284 "before dispatched");
286 if (aEvent->mClass == eCompositionEventClass) {
287 // If we've already requested to commit/cancel the latest composition,
288 // TextComposition for the old composition has been destroyed. Then,
289 // the DOM tree needs to listen to next eCompositionStart and its
290 // following events. So, until we meet new eCompositionStart, let's
291 // discard all unnecessary composition events here.
292 if (mIgnoreCompositionEvents) {
293 if (aEvent->mMessage != eCompositionStart) {
294 aStatus = nsEventStatus_eIgnore;
295 return NS_OK;
297 // Now, we receive new eCompositionStart. Let's restart to handle
298 // composition in this process.
299 mIgnoreCompositionEvents = false;
301 // Store the latest native IME context of parent process's widget or
302 // TextEventDispatcher if it's in this process.
303 WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
304 #ifdef DEBUG
305 if (mNativeIMEContext.IsValid() &&
306 mNativeIMEContext != compositionEvent->mNativeIMEContext) {
307 RefPtr<TextComposition> composition =
308 IMEStateManager::GetTextCompositionFor(this);
309 MOZ_ASSERT(
310 !composition,
311 "When there is composition caused by old native IME context, "
312 "composition events caused by different native IME context are not "
313 "allowed");
315 #endif // #ifdef DEBUG
316 mNativeIMEContext = compositionEvent->mNativeIMEContext;
317 mContentCache.OnCompositionEvent(*compositionEvent);
320 // If the event is a composition event or a keyboard event, it should be
321 // dispatched with TextEventDispatcher if we could do that with current
322 // design. However, we cannot do that without big changes and the behavior
323 // is not so complicated for now. Therefore, we should just notify it
324 // of dispatching events and TextEventDispatcher should emulate the state
325 // with events here.
326 if (aEvent->mClass == eCompositionEventClass ||
327 aEvent->mClass == eKeyboardEventClass) {
328 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
329 // However, if the event is being dispatched by the text event dispatcher
330 // or, there is native text event dispatcher listener, that means that
331 // native text input event handler is in this process like on Android,
332 // and the event is not synthesized for tests, the event is coming from
333 // the TextEventDispatcher. In these cases, we shouldn't notify
334 // TextEventDispatcher of dispatching the event.
335 if (!dispatcher->IsDispatchingEvent() &&
336 !(mNativeTextEventDispatcherListener &&
337 !aEvent->mFlags.mIsSynthesizedForTests)) {
338 DebugOnly<nsresult> rv =
339 dispatcher->BeginInputTransactionFor(aEvent, this);
340 NS_WARNING_ASSERTION(
341 NS_SUCCEEDED(rv),
342 "The text event dispatcher should always succeed to start input "
343 "transaction for the event");
347 aStatus = nsEventStatus_eIgnore;
349 if (GetCurrentWidgetListener()) {
350 aStatus =
351 GetCurrentWidgetListener()->HandleEvent(aEvent, mUseAttachedEvents);
354 return NS_OK;
357 nsIWidget::ContentAndAPZEventStatus PuppetWidget::DispatchInputEvent(
358 WidgetInputEvent* aEvent) {
359 ContentAndAPZEventStatus status;
360 if (!AsyncPanZoomEnabled()) {
361 DispatchEvent(aEvent, status.mContentStatus);
362 return status;
365 if (!mBrowserChild) {
366 return status;
369 switch (aEvent->mClass) {
370 case eWheelEventClass:
371 Unused << mBrowserChild->SendDispatchWheelEvent(*aEvent->AsWheelEvent());
372 break;
373 case eMouseEventClass:
374 Unused << mBrowserChild->SendDispatchMouseEvent(*aEvent->AsMouseEvent());
375 break;
376 case eKeyboardEventClass:
377 Unused << mBrowserChild->SendDispatchKeyboardEvent(
378 *aEvent->AsKeyboardEvent());
379 break;
380 case eTouchEventClass:
381 Unused << mBrowserChild->SendDispatchTouchEvent(*aEvent->AsTouchEvent());
382 break;
383 default:
384 MOZ_ASSERT_UNREACHABLE("unsupported event type");
387 return status;
390 nsresult PuppetWidget::SynthesizeNativeKeyEvent(
391 int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
392 uint32_t aModifierFlags, const nsAString& aCharacters,
393 const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
394 AutoObserverNotifier notifier(aObserver, "keyevent");
395 if (!mBrowserChild) {
396 return NS_ERROR_FAILURE;
398 mBrowserChild->SendSynthesizeNativeKeyEvent(
399 aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
400 aUnmodifiedCharacters, notifier.SaveObserver());
401 return NS_OK;
404 nsresult PuppetWidget::SynthesizeNativeMouseEvent(
405 mozilla::LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
406 MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
407 nsIObserver* aObserver) {
408 AutoObserverNotifier notifier(aObserver, "mouseevent");
409 if (!mBrowserChild) {
410 return NS_ERROR_FAILURE;
412 mBrowserChild->SendSynthesizeNativeMouseEvent(
413 aPoint, static_cast<uint32_t>(aNativeMessage),
414 static_cast<int16_t>(aButton), static_cast<uint32_t>(aModifierFlags),
415 notifier.SaveObserver());
416 return NS_OK;
419 nsresult PuppetWidget::SynthesizeNativeMouseMove(
420 mozilla::LayoutDeviceIntPoint aPoint, nsIObserver* aObserver) {
421 AutoObserverNotifier notifier(aObserver, "mousemove");
422 if (!mBrowserChild) {
423 return NS_ERROR_FAILURE;
425 mBrowserChild->SendSynthesizeNativeMouseMove(aPoint, notifier.SaveObserver());
426 return NS_OK;
429 nsresult PuppetWidget::SynthesizeNativeMouseScrollEvent(
430 mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
431 double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
432 uint32_t aAdditionalFlags, nsIObserver* aObserver) {
433 AutoObserverNotifier notifier(aObserver, "mousescrollevent");
434 if (!mBrowserChild) {
435 return NS_ERROR_FAILURE;
437 mBrowserChild->SendSynthesizeNativeMouseScrollEvent(
438 aPoint, aNativeMessage, aDeltaX, aDeltaY, aDeltaZ, aModifierFlags,
439 aAdditionalFlags, notifier.SaveObserver());
440 return NS_OK;
443 nsresult PuppetWidget::SynthesizeNativeTouchPoint(
444 uint32_t aPointerId, TouchPointerState aPointerState,
445 LayoutDeviceIntPoint aPoint, double aPointerPressure,
446 uint32_t aPointerOrientation, nsIObserver* aObserver) {
447 AutoObserverNotifier notifier(aObserver, "touchpoint");
448 if (!mBrowserChild) {
449 return NS_ERROR_FAILURE;
451 mBrowserChild->SendSynthesizeNativeTouchPoint(
452 aPointerId, aPointerState, aPoint, aPointerPressure, aPointerOrientation,
453 notifier.SaveObserver());
454 return NS_OK;
457 nsresult PuppetWidget::SynthesizeNativeTouchPadPinch(
458 TouchpadGesturePhase aEventPhase, float aScale, LayoutDeviceIntPoint aPoint,
459 int32_t aModifierFlags) {
460 if (!mBrowserChild) {
461 return NS_ERROR_FAILURE;
463 mBrowserChild->SendSynthesizeNativeTouchPadPinch(aEventPhase, aScale, aPoint,
464 aModifierFlags);
465 return NS_OK;
468 nsresult PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
469 bool aLongTap,
470 nsIObserver* aObserver) {
471 AutoObserverNotifier notifier(aObserver, "touchtap");
472 if (!mBrowserChild) {
473 return NS_ERROR_FAILURE;
475 mBrowserChild->SendSynthesizeNativeTouchTap(aPoint, aLongTap,
476 notifier.SaveObserver());
477 return NS_OK;
480 nsresult PuppetWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
481 AutoObserverNotifier notifier(aObserver, "cleartouch");
482 if (!mBrowserChild) {
483 return NS_ERROR_FAILURE;
485 mBrowserChild->SendClearNativeTouchSequence(notifier.SaveObserver());
486 return NS_OK;
489 nsresult PuppetWidget::SynthesizeNativePenInput(
490 uint32_t aPointerId, TouchPointerState aPointerState,
491 LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
492 int32_t aTiltX, int32_t aTiltY, int32_t aButton, nsIObserver* aObserver) {
493 AutoObserverNotifier notifier(aObserver, "peninput");
494 if (!mBrowserChild) {
495 return NS_ERROR_FAILURE;
497 mBrowserChild->SendSynthesizeNativePenInput(
498 aPointerId, aPointerState, aPoint, aPressure, aRotation, aTiltX, aTiltY,
499 aButton, notifier.SaveObserver());
500 return NS_OK;
503 nsresult PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
504 LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
505 if (!mBrowserChild) {
506 return NS_ERROR_FAILURE;
508 mBrowserChild->SendSynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags);
509 return NS_OK;
512 nsresult PuppetWidget::SynthesizeNativeTouchpadPan(
513 TouchpadGesturePhase aEventPhase, LayoutDeviceIntPoint aPoint,
514 double aDeltaX, double aDeltaY, int32_t aModifierFlags,
515 nsIObserver* aObserver) {
516 AutoObserverNotifier notifier(aObserver, "touchpadpanevent");
517 if (!mBrowserChild) {
518 return NS_ERROR_FAILURE;
520 mBrowserChild->SendSynthesizeNativeTouchpadPan(aEventPhase, aPoint, aDeltaX,
521 aDeltaY, aModifierFlags,
522 notifier.SaveObserver());
523 return NS_OK;
526 void PuppetWidget::LockNativePointer() {
527 if (!mBrowserChild) {
528 return;
530 mBrowserChild->SendLockNativePointer();
533 void PuppetWidget::UnlockNativePointer() {
534 if (!mBrowserChild) {
535 return;
537 mBrowserChild->SendUnlockNativePointer();
540 void PuppetWidget::SetConfirmedTargetAPZC(
541 uint64_t aInputBlockId,
542 const nsTArray<ScrollableLayerGuid>& aTargets) const {
543 if (mBrowserChild) {
544 mBrowserChild->SetTargetAPZC(aInputBlockId, aTargets);
548 void PuppetWidget::UpdateZoomConstraints(
549 const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId,
550 const Maybe<ZoomConstraints>& aConstraints) {
551 if (mBrowserChild) {
552 mBrowserChild->DoUpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
556 bool PuppetWidget::AsyncPanZoomEnabled() const {
557 return mBrowserChild && mBrowserChild->AsyncPanZoomEnabled();
560 bool PuppetWidget::GetEditCommands(NativeKeyBindingsType aType,
561 const WidgetKeyboardEvent& aEvent,
562 nsTArray<CommandInt>& aCommands) {
563 MOZ_ASSERT(!aEvent.mFlags.mIsSynthesizedForTests);
564 // Validate the arguments.
565 if (NS_WARN_IF(!nsIWidget::GetEditCommands(aType, aEvent, aCommands))) {
566 return false;
568 if (NS_WARN_IF(!mBrowserChild)) {
569 return false;
571 mBrowserChild->RequestEditCommands(aType, aEvent, aCommands);
572 return true;
575 WindowRenderer* PuppetWidget::GetWindowRenderer() {
576 if (!mWindowRenderer) {
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 mWindowRenderer = new FallbackRenderer;
581 return mWindowRenderer;
584 // If we know for sure that the parent side of this BrowserChild is not
585 // connected to the compositor, we don't want to use a "remote" layer
586 // manager like WebRender or Client. Instead we use a Basic one which
587 // can do drawing in this process.
588 MOZ_ASSERT(!mBrowserChild ||
589 mBrowserChild->IsLayersConnected() != Some(true));
590 mWindowRenderer = CreateFallbackRenderer();
593 return mWindowRenderer;
596 bool PuppetWidget::CreateRemoteLayerManager(
597 const std::function<bool(WebRenderLayerManager*)>& aInitializeFunc) {
598 RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this);
599 MOZ_ASSERT(mBrowserChild);
601 if (!aInitializeFunc(lm)) {
602 return false;
605 // Force the old LM to self destruct, otherwise if the reference dangles we
606 // could fail to revoke the most recent transaction. We only want to replace
607 // it if we successfully create its successor because a partially initialized
608 // layer manager is worse than a fully initialized but shutdown layer manager.
609 DestroyLayerManager();
610 mWindowRenderer = std::move(lm);
611 return true;
614 nsresult PuppetWidget::RequestIMEToCommitComposition(bool aCancel) {
615 if (!mBrowserChild) {
616 return NS_ERROR_FAILURE;
619 MOZ_ASSERT(!Destroyed());
621 // There must not be composition which is caused by the PuppetWidget instance.
622 if (NS_WARN_IF(!mNativeIMEContext.IsValid())) {
623 return NS_OK;
626 // We've already requested to commit/cancel composition.
627 if (NS_WARN_IF(mIgnoreCompositionEvents)) {
628 #ifdef DEBUG
629 RefPtr<TextComposition> composition =
630 IMEStateManager::GetTextCompositionFor(this);
631 MOZ_ASSERT(!composition);
632 #endif // #ifdef DEBUG
633 return NS_OK;
636 RefPtr<TextComposition> composition =
637 IMEStateManager::GetTextCompositionFor(this);
638 // This method shouldn't be called when there is no text composition instance.
639 if (NS_WARN_IF(!composition)) {
640 return NS_OK;
643 MOZ_DIAGNOSTIC_ASSERT(
644 composition->IsRequestingCommitOrCancelComposition(),
645 "Requesting commit or cancel composition should be requested via "
646 "TextComposition instance");
648 bool isCommitted = false;
649 nsAutoString committedString;
650 if (NS_WARN_IF(!mBrowserChild->SendRequestIMEToCommitComposition(
651 aCancel, &isCommitted, &committedString))) {
652 return NS_ERROR_FAILURE;
655 // If the composition wasn't committed synchronously, we need to wait async
656 // composition events for destroying the TextComposition instance.
657 if (!isCommitted) {
658 return NS_OK;
661 // Dispatch eCompositionCommit event.
662 WidgetCompositionEvent compositionCommitEvent(true, eCompositionCommit, this);
663 InitEvent(compositionCommitEvent, nullptr);
664 compositionCommitEvent.mData = committedString;
665 nsEventStatus status = nsEventStatus_eIgnore;
666 DispatchEvent(&compositionCommitEvent, status);
668 #ifdef DEBUG
669 RefPtr<TextComposition> currentComposition =
670 IMEStateManager::GetTextCompositionFor(this);
671 MOZ_ASSERT(!currentComposition);
672 #endif // #ifdef DEBUG
674 // Ignore the following composition events until we receive new
675 // eCompositionStart event.
676 mIgnoreCompositionEvents = true;
678 Unused << mBrowserChild->SendOnEventNeedingAckHandled(
679 eCompositionCommitRequestHandled);
681 // NOTE: PuppetWidget might be destroyed already.
682 return NS_OK;
685 // When this widget caches input context and currently managed by
686 // IMEStateManager, the cache is valid.
687 bool PuppetWidget::HaveValidInputContextCache() const {
688 return (mInputContext.mIMEState.mEnabled != IMEEnabled::Unknown &&
689 IMEStateManager::GetWidgetForActiveInputContext() == this);
692 nsRefreshDriver* PuppetWidget::GetTopLevelRefreshDriver() const {
693 if (!mBrowserChild) {
694 return nullptr;
697 if (PresShell* presShell = mBrowserChild->GetTopLevelPresShell()) {
698 return presShell->GetRefreshDriver();
701 return nullptr;
704 void PuppetWidget::SetInputContext(const InputContext& aContext,
705 const InputContextAction& aAction) {
706 mInputContext = aContext;
707 // Any widget instances cannot cache IME open state because IME open state
708 // can be changed by user but native IME may not notify us of changing the
709 // open state on some platforms.
710 mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
711 if (!mBrowserChild) {
712 return;
714 mBrowserChild->SendSetInputContext(aContext, aAction);
717 InputContext PuppetWidget::GetInputContext() {
718 // XXX Currently, we don't support retrieving IME open state from child
719 // process.
721 // If the cache of input context is valid, we can avoid to use synchronous
722 // IPC.
723 if (HaveValidInputContextCache()) {
724 return mInputContext;
727 NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
729 // Don't cache InputContext here because this process isn't managing IME
730 // state of the chrome widget. So, we cannot modify mInputContext when
731 // chrome widget is set to new context.
732 InputContext context;
733 if (mBrowserChild) {
734 mBrowserChild->SendGetInputContext(&context.mIMEState);
736 return context;
739 NativeIMEContext PuppetWidget::GetNativeIMEContext() {
740 return mNativeIMEContext;
743 nsresult PuppetWidget::NotifyIMEOfFocusChange(
744 const IMENotification& aIMENotification) {
745 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
747 if (!mBrowserChild) {
748 return NS_ERROR_FAILURE;
751 bool gotFocus = aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS;
752 if (gotFocus) {
753 // When IME gets focus, we should initialize all information of the
754 // content, however, it may fail to get it because the editor may have
755 // already been blurred.
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 Maybe<mozilla::ipc::BigBuffer> 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 if (!mBrowserChild->SendSetCursor(
938 aCursor.mDefaultCursor, hasCustomCursor, std::move(customCursorData),
939 customCursorSize.width, customCursorSize.height, resolution.mX,
940 resolution.mY, stride, format, aCursor.mHotspotX, aCursor.mHotspotY,
941 force)) {
942 return;
944 mCursor = aCursor;
945 mUpdateCursor = false;
948 void PuppetWidget::SetChild(PuppetWidget* aChild) {
949 MOZ_ASSERT(this != aChild, "can't parent a widget to itself");
950 MOZ_ASSERT(!aChild->mChild,
951 "fake widget 'hierarchy' only expected to have one level");
953 mChild = aChild;
956 NS_IMETHODIMP
957 PuppetWidget::WidgetPaintTask::Run() {
958 if (mWidget) {
959 mWidget->Paint();
961 return NS_OK;
964 void PuppetWidget::Paint() {
965 if (!GetCurrentWidgetListener()) return;
967 mWidgetPaintTask.Revoke();
969 RefPtr<PuppetWidget> strongThis(this);
971 GetCurrentWidgetListener()->WillPaintWindow(this);
973 if (GetCurrentWidgetListener()) {
974 GetCurrentWidgetListener()->DidPaintWindow();
978 void PuppetWidget::PaintNowIfNeeded() {
979 if (IsVisible() && mWidgetPaintTask.IsPending()) {
980 Paint();
984 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy) {
985 if (aWhy != MemoryPressureReason::LOW_MEMORY_ONGOING && !mVisible &&
986 mWindowRenderer && mWindowRenderer->AsWebRender() &&
987 XRE_IsContentProcess()) {
988 mWindowRenderer->AsWebRender()->ClearCachedResources();
992 bool PuppetWidget::NeedsPaint() {
993 // e10s popups are handled by the parent process, so never should be painted
994 // here
995 if (XRE_IsContentProcess() &&
996 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
997 mWindowType == WindowType::Popup) {
998 NS_WARNING("Trying to paint an e10s popup in the child process!");
999 return false;
1002 return mVisible;
1005 LayoutDeviceIntPoint PuppetWidget::GetChromeOffset() {
1006 if (!GetOwningBrowserChild()) {
1007 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1008 return LayoutDeviceIntPoint();
1010 return GetOwningBrowserChild()->GetChromeOffset();
1013 LayoutDeviceIntPoint PuppetWidget::WidgetToScreenOffset() {
1014 return GetWindowPosition() + WidgetToTopLevelWidgetOffset();
1017 LayoutDeviceIntPoint PuppetWidget::GetWindowPosition() {
1018 if (!GetOwningBrowserChild()) {
1019 return LayoutDeviceIntPoint();
1022 int32_t winX, winY, winW, winH;
1023 NS_ENSURE_SUCCESS(GetOwningBrowserChild()->GetDimensions(
1024 DimensionKind::Outer, &winX, &winY, &winW, &winH),
1025 LayoutDeviceIntPoint());
1026 return LayoutDeviceIntPoint(winX, winY) +
1027 GetOwningBrowserChild()->GetClientOffset();
1030 LayoutDeviceIntRect PuppetWidget::GetScreenBounds() {
1031 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
1034 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1035 return mBrowserChild ? mBrowserChild->MaxTouchPoints() : 0;
1038 void PuppetWidget::StartAsyncScrollbarDrag(
1039 const AsyncDragMetrics& aDragMetrics) {
1040 mBrowserChild->StartScrollbarDrag(aDragMetrics);
1043 ScreenIntMargin PuppetWidget::GetSafeAreaInsets() const {
1044 return mSafeAreaInsets;
1047 void PuppetWidget::UpdateSafeAreaInsets(
1048 const ScreenIntMargin& aSafeAreaInsets) {
1049 mSafeAreaInsets = aSafeAreaInsets;
1052 nsIWidgetListener* PuppetWidget::GetCurrentWidgetListener() {
1053 if (!mPreviouslyAttachedWidgetListener || !mAttachedWidgetListener) {
1054 return mAttachedWidgetListener;
1057 if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) {
1058 return mPreviouslyAttachedWidgetListener;
1061 return mAttachedWidgetListener;
1064 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId,
1065 const ScrollableLayerGuid::ViewID& aViewId,
1066 const CSSRect& aRect, const uint32_t& aFlags) {
1067 if (!mBrowserChild) {
1068 return;
1071 mBrowserChild->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
1074 void PuppetWidget::LookUpDictionary(
1075 const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
1076 const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
1077 if (!mBrowserChild) {
1078 return;
1081 mBrowserChild->SendLookUpDictionary(aText, aFontRangeArray, aIsVertical,
1082 aPoint);
1085 bool PuppetWidget::HasPendingInputEvent() {
1086 if (!mBrowserChild) {
1087 return false;
1090 bool ret = false;
1092 mBrowserChild->GetIPCChannel()->PeekMessages(
1093 [&ret](const IPC::Message& aMsg) -> bool {
1094 if (nsContentUtils::IsMessageInputEvent(aMsg)) {
1095 ret = true;
1096 return false; // Stop peeking.
1098 return true;
1101 return ret;
1104 // TextEventDispatcherListener
1106 NS_IMETHODIMP
1107 PuppetWidget::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
1108 const IMENotification& aIMENotification) {
1109 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1111 // If there is different text event dispatcher listener for handling
1112 // text event dispatcher, that means that native keyboard events and
1113 // IME events are handled in this process. Therefore, we don't need
1114 // to send any requests and notifications to the parent process.
1115 if (mNativeTextEventDispatcherListener) {
1116 return NS_ERROR_NOT_IMPLEMENTED;
1119 switch (aIMENotification.mMessage) {
1120 case REQUEST_TO_COMMIT_COMPOSITION:
1121 return RequestIMEToCommitComposition(false);
1122 case REQUEST_TO_CANCEL_COMPOSITION:
1123 return RequestIMEToCommitComposition(true);
1124 case NOTIFY_IME_OF_FOCUS:
1125 case NOTIFY_IME_OF_BLUR:
1126 return NotifyIMEOfFocusChange(aIMENotification);
1127 case NOTIFY_IME_OF_SELECTION_CHANGE:
1128 return NotifyIMEOfSelectionChange(aIMENotification);
1129 case NOTIFY_IME_OF_TEXT_CHANGE:
1130 return NotifyIMEOfTextChange(aIMENotification);
1131 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
1132 return NotifyIMEOfCompositionUpdate(aIMENotification);
1133 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
1134 return NotifyIMEOfMouseButtonEvent(aIMENotification);
1135 case NOTIFY_IME_OF_POSITION_CHANGE:
1136 return NotifyIMEOfPositionChange(aIMENotification);
1137 default:
1138 return NS_ERROR_NOT_IMPLEMENTED;
1142 NS_IMETHODIMP_(IMENotificationRequests)
1143 PuppetWidget::GetIMENotificationRequests() {
1144 return IMENotificationRequests(
1145 mIMENotificationRequestsOfParent.mWantUpdates |
1146 IMENotificationRequests::NOTIFY_TEXT_CHANGE |
1147 IMENotificationRequests::NOTIFY_POSITION_CHANGE);
1150 NS_IMETHODIMP_(void)
1151 PuppetWidget::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
1152 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1155 NS_IMETHODIMP_(void)
1156 PuppetWidget::WillDispatchKeyboardEvent(
1157 TextEventDispatcher* aTextEventDispatcher,
1158 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
1159 void* aData) {
1160 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1163 nsresult PuppetWidget::SetSystemFont(const nsCString& aFontName) {
1164 if (!mBrowserChild) {
1165 return NS_ERROR_FAILURE;
1168 mBrowserChild->SendSetSystemFont(aFontName);
1169 return NS_OK;
1172 nsresult PuppetWidget::GetSystemFont(nsCString& aFontName) {
1173 if (!mBrowserChild) {
1174 return NS_ERROR_FAILURE;
1176 mBrowserChild->SendGetSystemFont(&aFontName);
1177 return NS_OK;
1180 } // namespace widget
1181 } // namespace mozilla