Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / PuppetWidget.cpp
blobf47c8607efe1e30cb254aedb8ab9a6f38a7724ba
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 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 nsWidgetInitData* 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 nsWidgetInitData* 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, nsWidgetInitData* 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;
268 aEvent.mTime = PR_Now() / 1000;
271 nsresult PuppetWidget::DispatchEvent(WidgetGUIEvent* aEvent,
272 nsEventStatus& aStatus) {
273 #ifdef DEBUG
274 debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "PuppetWidget", 0);
275 #endif
277 MOZ_ASSERT(!mChild || mChild->mWindowType == eWindowType_popup,
278 "Unexpected event dispatch!");
280 MOZ_ASSERT(!aEvent->AsKeyboardEvent() ||
281 aEvent->mFlags.mIsSynthesizedForTests ||
282 aEvent->AsKeyboardEvent()->AreAllEditCommandsInitialized(),
283 "Non-sysnthesized keyboard events should have edit commands for "
284 "all types "
285 "before dispatched");
287 if (aEvent->mClass == eCompositionEventClass) {
288 // If we've already requested to commit/cancel the latest composition,
289 // TextComposition for the old composition has been destroyed. Then,
290 // the DOM tree needs to listen to next eCompositionStart and its
291 // following events. So, until we meet new eCompositionStart, let's
292 // discard all unnecessary composition events here.
293 if (mIgnoreCompositionEvents) {
294 if (aEvent->mMessage != eCompositionStart) {
295 aStatus = nsEventStatus_eIgnore;
296 return NS_OK;
298 // Now, we receive new eCompositionStart. Let's restart to handle
299 // composition in this process.
300 mIgnoreCompositionEvents = false;
302 // Store the latest native IME context of parent process's widget or
303 // TextEventDispatcher if it's in this process.
304 WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
305 #ifdef DEBUG
306 if (mNativeIMEContext.IsValid() &&
307 mNativeIMEContext != compositionEvent->mNativeIMEContext) {
308 RefPtr<TextComposition> composition =
309 IMEStateManager::GetTextCompositionFor(this);
310 MOZ_ASSERT(
311 !composition,
312 "When there is composition caused by old native IME context, "
313 "composition events caused by different native IME context are not "
314 "allowed");
316 #endif // #ifdef DEBUG
317 mNativeIMEContext = compositionEvent->mNativeIMEContext;
318 mContentCache.OnCompositionEvent(*compositionEvent);
321 // If the event is a composition event or a keyboard event, it should be
322 // dispatched with TextEventDispatcher if we could do that with current
323 // design. However, we cannot do that without big changes and the behavior
324 // is not so complicated for now. Therefore, we should just notify it
325 // of dispatching events and TextEventDispatcher should emulate the state
326 // with events here.
327 if (aEvent->mClass == eCompositionEventClass ||
328 aEvent->mClass == eKeyboardEventClass) {
329 TextEventDispatcher* dispatcher = GetTextEventDispatcher();
330 // However, if the event is being dispatched by the text event dispatcher
331 // or, there is native text event dispatcher listener, that means that
332 // native text input event handler is in this process like on Android,
333 // and the event is not synthesized for tests, the event is coming from
334 // the TextEventDispatcher. In these cases, we shouldn't notify
335 // TextEventDispatcher of dispatching the event.
336 if (!dispatcher->IsDispatchingEvent() &&
337 !(mNativeTextEventDispatcherListener &&
338 !aEvent->mFlags.mIsSynthesizedForTests)) {
339 DebugOnly<nsresult> rv =
340 dispatcher->BeginInputTransactionFor(aEvent, this);
341 NS_WARNING_ASSERTION(
342 NS_SUCCEEDED(rv),
343 "The text event dispatcher should always succeed to start input "
344 "transaction for the event");
348 aStatus = nsEventStatus_eIgnore;
350 if (GetCurrentWidgetListener()) {
351 aStatus =
352 GetCurrentWidgetListener()->HandleEvent(aEvent, mUseAttachedEvents);
355 return NS_OK;
358 nsIWidget::ContentAndAPZEventStatus PuppetWidget::DispatchInputEvent(
359 WidgetInputEvent* aEvent) {
360 ContentAndAPZEventStatus status;
361 if (!AsyncPanZoomEnabled()) {
362 DispatchEvent(aEvent, status.mContentStatus);
363 return status;
366 if (!mBrowserChild) {
367 return status;
370 switch (aEvent->mClass) {
371 case eWheelEventClass:
372 Unused << mBrowserChild->SendDispatchWheelEvent(*aEvent->AsWheelEvent());
373 break;
374 case eMouseEventClass:
375 Unused << mBrowserChild->SendDispatchMouseEvent(*aEvent->AsMouseEvent());
376 break;
377 case eKeyboardEventClass:
378 Unused << mBrowserChild->SendDispatchKeyboardEvent(
379 *aEvent->AsKeyboardEvent());
380 break;
381 case eTouchEventClass:
382 Unused << mBrowserChild->SendDispatchTouchEvent(*aEvent->AsTouchEvent());
383 break;
384 default:
385 MOZ_ASSERT_UNREACHABLE("unsupported event type");
388 return status;
391 nsresult PuppetWidget::SynthesizeNativeKeyEvent(
392 int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
393 uint32_t aModifierFlags, const nsAString& aCharacters,
394 const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
395 AutoObserverNotifier notifier(aObserver, "keyevent");
396 if (!mBrowserChild) {
397 return NS_ERROR_FAILURE;
399 mBrowserChild->SendSynthesizeNativeKeyEvent(
400 aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
401 aUnmodifiedCharacters, notifier.SaveObserver());
402 return NS_OK;
405 nsresult PuppetWidget::SynthesizeNativeMouseEvent(
406 mozilla::LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
407 MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
408 nsIObserver* aObserver) {
409 AutoObserverNotifier notifier(aObserver, "mouseevent");
410 if (!mBrowserChild) {
411 return NS_ERROR_FAILURE;
413 mBrowserChild->SendSynthesizeNativeMouseEvent(
414 aPoint, static_cast<uint32_t>(aNativeMessage),
415 static_cast<int16_t>(aButton), static_cast<uint32_t>(aModifierFlags),
416 notifier.SaveObserver());
417 return NS_OK;
420 nsresult PuppetWidget::SynthesizeNativeMouseMove(
421 mozilla::LayoutDeviceIntPoint aPoint, nsIObserver* aObserver) {
422 AutoObserverNotifier notifier(aObserver, "mousemove");
423 if (!mBrowserChild) {
424 return NS_ERROR_FAILURE;
426 mBrowserChild->SendSynthesizeNativeMouseMove(aPoint, notifier.SaveObserver());
427 return NS_OK;
430 nsresult PuppetWidget::SynthesizeNativeMouseScrollEvent(
431 mozilla::LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage,
432 double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
433 uint32_t aAdditionalFlags, nsIObserver* aObserver) {
434 AutoObserverNotifier notifier(aObserver, "mousescrollevent");
435 if (!mBrowserChild) {
436 return NS_ERROR_FAILURE;
438 mBrowserChild->SendSynthesizeNativeMouseScrollEvent(
439 aPoint, aNativeMessage, aDeltaX, aDeltaY, aDeltaZ, aModifierFlags,
440 aAdditionalFlags, notifier.SaveObserver());
441 return NS_OK;
444 nsresult PuppetWidget::SynthesizeNativeTouchPoint(
445 uint32_t aPointerId, TouchPointerState aPointerState,
446 LayoutDeviceIntPoint aPoint, double aPointerPressure,
447 uint32_t aPointerOrientation, nsIObserver* aObserver) {
448 AutoObserverNotifier notifier(aObserver, "touchpoint");
449 if (!mBrowserChild) {
450 return NS_ERROR_FAILURE;
452 mBrowserChild->SendSynthesizeNativeTouchPoint(
453 aPointerId, aPointerState, aPoint, aPointerPressure, aPointerOrientation,
454 notifier.SaveObserver());
455 return NS_OK;
458 nsresult PuppetWidget::SynthesizeNativeTouchPadPinch(
459 TouchpadGesturePhase aEventPhase, float aScale, LayoutDeviceIntPoint aPoint,
460 int32_t aModifierFlags) {
461 if (!mBrowserChild) {
462 return NS_ERROR_FAILURE;
464 mBrowserChild->SendSynthesizeNativeTouchPadPinch(aEventPhase, aScale, aPoint,
465 aModifierFlags);
466 return NS_OK;
469 nsresult PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
470 bool aLongTap,
471 nsIObserver* aObserver) {
472 AutoObserverNotifier notifier(aObserver, "touchtap");
473 if (!mBrowserChild) {
474 return NS_ERROR_FAILURE;
476 mBrowserChild->SendSynthesizeNativeTouchTap(aPoint, aLongTap,
477 notifier.SaveObserver());
478 return NS_OK;
481 nsresult PuppetWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
482 AutoObserverNotifier notifier(aObserver, "cleartouch");
483 if (!mBrowserChild) {
484 return NS_ERROR_FAILURE;
486 mBrowserChild->SendClearNativeTouchSequence(notifier.SaveObserver());
487 return NS_OK;
490 nsresult PuppetWidget::SynthesizeNativePenInput(
491 uint32_t aPointerId, TouchPointerState aPointerState,
492 LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
493 int32_t aTiltX, int32_t aTiltY, int32_t aButton, nsIObserver* aObserver) {
494 AutoObserverNotifier notifier(aObserver, "peninput");
495 if (!mBrowserChild) {
496 return NS_ERROR_FAILURE;
498 mBrowserChild->SendSynthesizeNativePenInput(
499 aPointerId, aPointerState, aPoint, aPressure, aRotation, aTiltX, aTiltY,
500 aButton, notifier.SaveObserver());
501 return NS_OK;
504 nsresult PuppetWidget::SynthesizeNativeTouchpadDoubleTap(
505 LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) {
506 if (!mBrowserChild) {
507 return NS_ERROR_FAILURE;
509 mBrowserChild->SendSynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags);
510 return NS_OK;
513 nsresult PuppetWidget::SynthesizeNativeTouchpadPan(
514 TouchpadGesturePhase aEventPhase, LayoutDeviceIntPoint aPoint,
515 double aDeltaX, double aDeltaY, int32_t aModifierFlags,
516 nsIObserver* aObserver) {
517 AutoObserverNotifier notifier(aObserver, "touchpadpanevent");
518 if (!mBrowserChild) {
519 return NS_ERROR_FAILURE;
521 mBrowserChild->SendSynthesizeNativeTouchpadPan(aEventPhase, aPoint, aDeltaX,
522 aDeltaY, aModifierFlags,
523 notifier.SaveObserver());
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, however, it may fail to get it because the editor may have
756 // already been blurred.
757 if (NS_WARN_IF(!mContentCache.CacheAll(this, &aIMENotification))) {
758 return NS_ERROR_FAILURE;
760 } else {
761 // When IME loses focus, we don't need to store anything.
762 mContentCache.Clear();
765 mIMENotificationRequestsOfParent =
766 IMENotificationRequests(IMENotificationRequests::NOTIFY_ALL);
767 RefPtr<PuppetWidget> self = this;
768 mBrowserChild->SendNotifyIMEFocus(mContentCache, aIMENotification)
769 ->Then(
770 GetMainThreadSerialEventTarget(), __func__,
771 [self](IMENotificationRequests&& aRequests) {
772 self->mIMENotificationRequestsOfParent = aRequests;
773 if (TextEventDispatcher* dispatcher =
774 self->GetTextEventDispatcher()) {
775 dispatcher->OnWidgetChangeIMENotificationRequests(self);
778 [self](mozilla::ipc::ResponseRejectReason&& aReason) {
779 NS_WARNING("SendNotifyIMEFocus got rejected.");
782 return NS_OK;
785 nsresult PuppetWidget::NotifyIMEOfCompositionUpdate(
786 const IMENotification& aIMENotification) {
787 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
789 if (NS_WARN_IF(!mBrowserChild)) {
790 return NS_ERROR_FAILURE;
793 if (NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
794 return NS_ERROR_FAILURE;
796 mBrowserChild->SendNotifyIMECompositionUpdate(mContentCache,
797 aIMENotification);
798 return NS_OK;
801 nsresult PuppetWidget::NotifyIMEOfTextChange(
802 const IMENotification& aIMENotification) {
803 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
804 MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
805 "Passed wrong notification");
807 if (!mBrowserChild) {
808 return NS_ERROR_FAILURE;
811 // FYI: text change notification is the first notification after
812 // a user operation changes the content. So, we need to modify
813 // the cache as far as possible here.
815 if (NS_WARN_IF(!mContentCache.CacheText(this, &aIMENotification))) {
816 return NS_ERROR_FAILURE;
819 // BrowserParent doesn't this this to cache. we don't send the notification
820 // if parent process doesn't request NOTIFY_TEXT_CHANGE.
821 if (mIMENotificationRequestsOfParent.WantTextChange()) {
822 mBrowserChild->SendNotifyIMETextChange(mContentCache, aIMENotification);
823 } else {
824 mBrowserChild->SendUpdateContentCache(mContentCache);
826 return NS_OK;
829 nsresult PuppetWidget::NotifyIMEOfSelectionChange(
830 const IMENotification& aIMENotification) {
831 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
832 MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE,
833 "Passed wrong notification");
834 if (!mBrowserChild) {
835 return NS_ERROR_FAILURE;
838 // Note that selection change must be notified after text change if it occurs.
839 // Therefore, we don't need to query text content again here.
840 mContentCache.SetSelection(this, aIMENotification.mSelectionChangeData);
842 mBrowserChild->SendNotifyIMESelection(mContentCache, aIMENotification);
844 return NS_OK;
847 nsresult PuppetWidget::NotifyIMEOfMouseButtonEvent(
848 const IMENotification& aIMENotification) {
849 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
850 if (!mBrowserChild) {
851 return NS_ERROR_FAILURE;
854 bool consumedByIME = false;
855 if (!mBrowserChild->SendNotifyIMEMouseButtonEvent(aIMENotification,
856 &consumedByIME)) {
857 return NS_ERROR_FAILURE;
860 return consumedByIME ? NS_SUCCESS_EVENT_CONSUMED : NS_OK;
863 nsresult PuppetWidget::NotifyIMEOfPositionChange(
864 const IMENotification& aIMENotification) {
865 MOZ_ASSERT(IMEStateManager::CanSendNotificationToWidget());
866 if (NS_WARN_IF(!mBrowserChild)) {
867 return NS_ERROR_FAILURE;
870 if (NS_WARN_IF(!mContentCache.CacheEditorRect(this, &aIMENotification))) {
871 return NS_ERROR_FAILURE;
873 if (NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
874 return NS_ERROR_FAILURE;
876 if (mIMENotificationRequestsOfParent.WantPositionChanged()) {
877 mBrowserChild->SendNotifyIMEPositionChange(mContentCache, aIMENotification);
878 } else {
879 mBrowserChild->SendUpdateContentCache(mContentCache);
881 return NS_OK;
884 struct CursorSurface {
885 UniquePtr<char[]> mData;
886 IntSize mSize;
889 void PuppetWidget::SetCursor(const Cursor& aCursor) {
890 if (!mBrowserChild) {
891 return;
894 const bool force = mUpdateCursor;
895 if (!force && mCursor == aCursor) {
896 return;
899 bool hasCustomCursor = false;
900 Maybe<mozilla::ipc::BigBuffer> customCursorData;
901 size_t length = 0;
902 IntSize customCursorSize;
903 int32_t stride = 0;
904 auto format = SurfaceFormat::B8G8R8A8;
905 ImageResolution resolution = aCursor.mResolution;
906 if (aCursor.IsCustom()) {
907 int32_t width = 0, height = 0;
908 aCursor.mContainer->GetWidth(&width);
909 aCursor.mContainer->GetHeight(&height);
910 const int32_t flags =
911 imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY;
912 RefPtr<SourceSurface> surface;
913 if (width && height &&
914 aCursor.mContainer->GetType() == imgIContainer::TYPE_VECTOR) {
915 // For vector images, scale to device pixels.
916 resolution.ScaleBy(GetDefaultScale().scale);
917 resolution.ApplyInverseTo(width, height);
918 surface = aCursor.mContainer->GetFrameAtSize(
919 {width, height}, imgIContainer::FRAME_CURRENT, flags);
920 } else {
921 // NOTE(emilio): We get the frame at the full size, ignoring resolution,
922 // because we're going to rasterize it, and we'd effectively lose the
923 // extra pixels if we rasterized to CustomCursorSize.
924 surface =
925 aCursor.mContainer->GetFrame(imgIContainer::FRAME_CURRENT, flags);
927 if (surface) {
928 if (RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface()) {
929 hasCustomCursor = true;
930 customCursorData =
931 nsContentUtils::GetSurfaceData(*dataSurface, &length, &stride);
932 customCursorSize = dataSurface->GetSize();
933 format = dataSurface->GetFormat();
938 if (!mBrowserChild->SendSetCursor(
939 aCursor.mDefaultCursor, hasCustomCursor, std::move(customCursorData),
940 customCursorSize.width, customCursorSize.height, resolution.mX,
941 resolution.mY, stride, format, aCursor.mHotspotX, aCursor.mHotspotY,
942 force)) {
943 return;
945 mCursor = aCursor;
946 mUpdateCursor = false;
949 void PuppetWidget::SetChild(PuppetWidget* aChild) {
950 MOZ_ASSERT(this != aChild, "can't parent a widget to itself");
951 MOZ_ASSERT(!aChild->mChild,
952 "fake widget 'hierarchy' only expected to have one level");
954 mChild = aChild;
957 NS_IMETHODIMP
958 PuppetWidget::WidgetPaintTask::Run() {
959 if (mWidget) {
960 mWidget->Paint();
962 return NS_OK;
965 void PuppetWidget::Paint() {
966 if (!GetCurrentWidgetListener()) return;
968 mWidgetPaintTask.Revoke();
970 RefPtr<PuppetWidget> strongThis(this);
972 GetCurrentWidgetListener()->WillPaintWindow(this);
974 if (GetCurrentWidgetListener()) {
975 GetCurrentWidgetListener()->DidPaintWindow();
979 void PuppetWidget::PaintNowIfNeeded() {
980 if (IsVisible() && mWidgetPaintTask.IsPending()) {
981 Paint();
985 void PuppetWidget::OnMemoryPressure(layers::MemoryPressureReason aWhy) {
986 if (aWhy != MemoryPressureReason::LOW_MEMORY_ONGOING && !mVisible &&
987 mWindowRenderer && mWindowRenderer->AsWebRender() &&
988 XRE_IsContentProcess()) {
989 mWindowRenderer->AsWebRender()->ClearCachedResources();
993 bool PuppetWidget::NeedsPaint() {
994 // e10s popups are handled by the parent process, so never should be painted
995 // here
996 if (XRE_IsContentProcess() &&
997 StaticPrefs::browser_tabs_remote_desktopbehavior() &&
998 mWindowType == eWindowType_popup) {
999 NS_WARNING("Trying to paint an e10s popup in the child process!");
1000 return false;
1003 return mVisible;
1006 LayoutDeviceIntPoint PuppetWidget::GetChromeOffset() {
1007 if (!GetOwningBrowserChild()) {
1008 NS_WARNING("PuppetWidget without Tab does not have chrome information.");
1009 return LayoutDeviceIntPoint();
1011 return GetOwningBrowserChild()->GetChromeOffset();
1014 LayoutDeviceIntPoint PuppetWidget::WidgetToScreenOffset() {
1015 return GetWindowPosition() + WidgetToTopLevelWidgetOffset();
1018 LayoutDeviceIntPoint PuppetWidget::GetWindowPosition() {
1019 if (!GetOwningBrowserChild()) {
1020 return LayoutDeviceIntPoint();
1023 int32_t winX, winY, winW, winH;
1024 NS_ENSURE_SUCCESS(
1025 GetOwningBrowserChild()->GetDimensions(0, &winX, &winY, &winW, &winH),
1026 LayoutDeviceIntPoint());
1027 return LayoutDeviceIntPoint(winX, winY) +
1028 GetOwningBrowserChild()->GetClientOffset();
1031 LayoutDeviceIntRect PuppetWidget::GetScreenBounds() {
1032 return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
1035 uint32_t PuppetWidget::GetMaxTouchPoints() const {
1036 return mBrowserChild ? mBrowserChild->MaxTouchPoints() : 0;
1039 void PuppetWidget::StartAsyncScrollbarDrag(
1040 const AsyncDragMetrics& aDragMetrics) {
1041 mBrowserChild->StartScrollbarDrag(aDragMetrics);
1044 ScreenIntMargin PuppetWidget::GetSafeAreaInsets() const {
1045 return mSafeAreaInsets;
1048 void PuppetWidget::UpdateSafeAreaInsets(
1049 const ScreenIntMargin& aSafeAreaInsets) {
1050 mSafeAreaInsets = aSafeAreaInsets;
1053 nsIWidgetListener* PuppetWidget::GetCurrentWidgetListener() {
1054 if (!mPreviouslyAttachedWidgetListener || !mAttachedWidgetListener) {
1055 return mAttachedWidgetListener;
1058 if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) {
1059 return mPreviouslyAttachedWidgetListener;
1062 return mAttachedWidgetListener;
1065 void PuppetWidget::ZoomToRect(const uint32_t& aPresShellId,
1066 const ScrollableLayerGuid::ViewID& aViewId,
1067 const CSSRect& aRect, const uint32_t& aFlags) {
1068 if (!mBrowserChild) {
1069 return;
1072 mBrowserChild->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
1075 void PuppetWidget::LookUpDictionary(
1076 const nsAString& aText, const nsTArray<mozilla::FontRange>& aFontRangeArray,
1077 const bool aIsVertical, const LayoutDeviceIntPoint& aPoint) {
1078 if (!mBrowserChild) {
1079 return;
1082 mBrowserChild->SendLookUpDictionary(aText, aFontRangeArray, aIsVertical,
1083 aPoint);
1086 bool PuppetWidget::HasPendingInputEvent() {
1087 if (!mBrowserChild) {
1088 return false;
1091 bool ret = false;
1093 mBrowserChild->GetIPCChannel()->PeekMessages(
1094 [&ret](const IPC::Message& aMsg) -> bool {
1095 if (nsContentUtils::IsMessageInputEvent(aMsg)) {
1096 ret = true;
1097 return false; // Stop peeking.
1099 return true;
1102 return ret;
1105 // TextEventDispatcherListener
1107 NS_IMETHODIMP
1108 PuppetWidget::NotifyIME(TextEventDispatcher* aTextEventDispatcher,
1109 const IMENotification& aIMENotification) {
1110 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1112 // If there is different text event dispatcher listener for handling
1113 // text event dispatcher, that means that native keyboard events and
1114 // IME events are handled in this process. Therefore, we don't need
1115 // to send any requests and notifications to the parent process.
1116 if (mNativeTextEventDispatcherListener) {
1117 return NS_ERROR_NOT_IMPLEMENTED;
1120 switch (aIMENotification.mMessage) {
1121 case REQUEST_TO_COMMIT_COMPOSITION:
1122 return RequestIMEToCommitComposition(false);
1123 case REQUEST_TO_CANCEL_COMPOSITION:
1124 return RequestIMEToCommitComposition(true);
1125 case NOTIFY_IME_OF_FOCUS:
1126 case NOTIFY_IME_OF_BLUR:
1127 return NotifyIMEOfFocusChange(aIMENotification);
1128 case NOTIFY_IME_OF_SELECTION_CHANGE:
1129 return NotifyIMEOfSelectionChange(aIMENotification);
1130 case NOTIFY_IME_OF_TEXT_CHANGE:
1131 return NotifyIMEOfTextChange(aIMENotification);
1132 case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
1133 return NotifyIMEOfCompositionUpdate(aIMENotification);
1134 case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
1135 return NotifyIMEOfMouseButtonEvent(aIMENotification);
1136 case NOTIFY_IME_OF_POSITION_CHANGE:
1137 return NotifyIMEOfPositionChange(aIMENotification);
1138 default:
1139 return NS_ERROR_NOT_IMPLEMENTED;
1142 return NS_ERROR_NOT_IMPLEMENTED;
1145 NS_IMETHODIMP_(IMENotificationRequests)
1146 PuppetWidget::GetIMENotificationRequests() {
1147 return IMENotificationRequests(
1148 mIMENotificationRequestsOfParent.mWantUpdates |
1149 IMENotificationRequests::NOTIFY_TEXT_CHANGE |
1150 IMENotificationRequests::NOTIFY_POSITION_CHANGE);
1153 NS_IMETHODIMP_(void)
1154 PuppetWidget::OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) {
1155 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1158 NS_IMETHODIMP_(void)
1159 PuppetWidget::WillDispatchKeyboardEvent(
1160 TextEventDispatcher* aTextEventDispatcher,
1161 WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress,
1162 void* aData) {
1163 MOZ_ASSERT(aTextEventDispatcher == mTextEventDispatcher);
1166 nsresult PuppetWidget::SetSystemFont(const nsCString& aFontName) {
1167 if (!mBrowserChild) {
1168 return NS_ERROR_FAILURE;
1171 mBrowserChild->SendSetSystemFont(aFontName);
1172 return NS_OK;
1175 nsresult PuppetWidget::GetSystemFont(nsCString& aFontName) {
1176 if (!mBrowserChild) {
1177 return NS_ERROR_FAILURE;
1179 mBrowserChild->SendGetSystemFont(&aFontName);
1180 return NS_OK;
1183 } // namespace widget
1184 } // namespace mozilla