summaryrefslogtreecommitdiff
path: root/widget/PuppetWidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/PuppetWidget.cpp')
-rw-r--r--widget/PuppetWidget.cpp1580
1 files changed, 1580 insertions, 0 deletions
diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp
new file mode 100644
index 0000000000..1fa13ee6fb
--- /dev/null
+++ b/widget/PuppetWidget.cpp
@@ -0,0 +1,1580 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+
+#include "ClientLayerManager.h"
+#include "gfxPlatform.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/Hal.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/PLayerTransactionChild.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TextComposition.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/Unused.h"
+#include "PuppetWidget.h"
+#include "nsContentUtils.h"
+#include "nsIWidgetListener.h"
+#include "imgIContainer.h"
+#include "nsView.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::hal;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::widget;
+
+static void
+InvalidateRegion(nsIWidget* aWidget, const LayoutDeviceIntRegion& aRegion)
+{
+ for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
+ aWidget->Invalidate(iter.Get());
+ }
+}
+
+/*static*/ already_AddRefed<nsIWidget>
+nsIWidget::CreatePuppetWidget(TabChild* aTabChild)
+{
+ MOZ_ASSERT(!aTabChild || nsIWidget::UsePuppetWidgets(),
+ "PuppetWidgets not allowed in this configuration");
+
+ nsCOMPtr<nsIWidget> widget = new PuppetWidget(aTabChild);
+ return widget.forget();
+}
+
+namespace mozilla {
+namespace widget {
+
+static bool
+IsPopup(const nsWidgetInitData* aInitData)
+{
+ return aInitData && aInitData->mWindowType == eWindowType_popup;
+}
+
+static bool
+MightNeedIMEFocus(const nsWidgetInitData* aInitData)
+{
+ // In the puppet-widget world, popup widgets are just dummies and
+ // shouldn't try to mess with IME state.
+#ifdef MOZ_CROSS_PROCESS_IME
+ return !IsPopup(aInitData);
+#else
+ return false;
+#endif
+}
+
+// Arbitrary, fungible.
+const size_t PuppetWidget::kMaxDimension = 4000;
+
+NS_IMPL_ISUPPORTS_INHERITED0(PuppetWidget, nsBaseWidget)
+
+PuppetWidget::PuppetWidget(TabChild* aTabChild)
+ : mTabChild(aTabChild)
+ , mMemoryPressureObserver(nullptr)
+ , mDPI(-1)
+ , mRounding(-1)
+ , mDefaultScale(-1)
+ , mCursorHotspotX(0)
+ , mCursorHotspotY(0)
+ , mNativeKeyCommandsValid(false)
+{
+ MOZ_COUNT_CTOR(PuppetWidget);
+
+ mSingleLineCommands.SetCapacity(4);
+ mMultiLineCommands.SetCapacity(4);
+ mRichTextCommands.SetCapacity(4);
+
+ // Setting 'Unknown' means "not yet cached".
+ mInputContext.mIMEState.mEnabled = IMEState::UNKNOWN;
+}
+
+PuppetWidget::~PuppetWidget()
+{
+ MOZ_COUNT_DTOR(PuppetWidget);
+
+ Destroy();
+}
+
+void
+PuppetWidget::InfallibleCreate(nsIWidget* aParent,
+ nsNativeWidget aNativeParent,
+ const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData)
+{
+ MOZ_ASSERT(!aNativeParent, "got a non-Puppet native parent");
+
+ BaseCreate(nullptr, aInitData);
+
+ mBounds = aRect;
+ mEnabled = true;
+ mVisible = true;
+
+ mDrawTarget = gfxPlatform::GetPlatform()->
+ CreateOffscreenContentDrawTarget(IntSize(1, 1), SurfaceFormat::B8G8R8A8);
+
+ mNeedIMEStateInit = MightNeedIMEFocus(aInitData);
+
+ PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
+ if (parent) {
+ parent->SetChild(this);
+ mLayerManager = parent->GetLayerManager();
+ }
+ else {
+ Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
+ }
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ mMemoryPressureObserver = new MemoryPressureObserver(this);
+ obs->AddObserver(mMemoryPressureObserver, "memory-pressure", false);
+ }
+}
+
+nsresult
+PuppetWidget::Create(nsIWidget* aParent,
+ nsNativeWidget aNativeParent,
+ const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData)
+{
+ InfallibleCreate(aParent, aNativeParent, aRect, aInitData);
+ return NS_OK;
+}
+
+void
+PuppetWidget::InitIMEState()
+{
+ MOZ_ASSERT(mTabChild);
+ if (mNeedIMEStateInit) {
+ mContentCache.Clear();
+ mTabChild->SendUpdateContentCache(mContentCache);
+ mIMEPreferenceOfParent = nsIMEUpdatePreference();
+ mNeedIMEStateInit = false;
+ }
+}
+
+already_AddRefed<nsIWidget>
+PuppetWidget::CreateChild(const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData,
+ bool aForceUseIWidgetParent)
+{
+ bool isPopup = IsPopup(aInitData);
+ nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(mTabChild);
+ return ((widget &&
+ NS_SUCCEEDED(widget->Create(isPopup ? nullptr: this, nullptr, aRect,
+ aInitData))) ?
+ widget.forget() : nullptr);
+}
+
+void
+PuppetWidget::Destroy()
+{
+ if (mOnDestroyCalled) {
+ return;
+ }
+ mOnDestroyCalled = true;
+
+ Base::OnDestroy();
+ Base::Destroy();
+ mPaintTask.Revoke();
+ if (mMemoryPressureObserver) {
+ mMemoryPressureObserver->Remove();
+ }
+ mMemoryPressureObserver = nullptr;
+ mChild = nullptr;
+ if (mLayerManager) {
+ mLayerManager->Destroy();
+ }
+ mLayerManager = nullptr;
+ mTabChild = nullptr;
+}
+
+NS_IMETHODIMP
+PuppetWidget::Show(bool aState)
+{
+ NS_ASSERTION(mEnabled,
+ "does it make sense to Show()/Hide() a disabled widget?");
+
+ bool wasVisible = mVisible;
+ mVisible = aState;
+
+ if (mChild) {
+ mChild->mVisible = aState;
+ }
+
+ if (!wasVisible && mVisible) {
+ // The previously attached widget listener is handy if
+ // we're transitioning from page to page without dropping
+ // layers (since we'll continue to show the old layers
+ // associated with that old widget listener). If the
+ // PuppetWidget was hidden, those layers are dropped,
+ // so the previously attached widget listener is really
+ // of no use anymore (and is actually actively harmful - see
+ // bug 1323586).
+ mPreviouslyAttachedWidgetListener = nullptr;
+ Resize(mBounds.width, mBounds.height, false);
+ Invalidate(mBounds);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::Resize(double aWidth,
+ double aHeight,
+ bool aRepaint)
+{
+ LayoutDeviceIntRect oldBounds = mBounds;
+ mBounds.SizeTo(LayoutDeviceIntSize(NSToIntRound(aWidth),
+ NSToIntRound(aHeight)));
+
+ if (mChild) {
+ return mChild->Resize(aWidth, aHeight, aRepaint);
+ }
+
+ // XXX: roc says that |aRepaint| dictates whether or not to
+ // invalidate the expanded area
+ if (oldBounds.Size() < mBounds.Size() && aRepaint) {
+ LayoutDeviceIntRegion dirty(mBounds);
+ dirty.Sub(dirty, oldBounds);
+ InvalidateRegion(this, dirty);
+ }
+
+ // call WindowResized() on both the current listener, and possibly
+ // also the previous one if we're in a state where we're drawing that one
+ // because the current one is paint suppressed
+ if (!oldBounds.IsEqualEdges(mBounds) && mAttachedWidgetListener) {
+ if (GetCurrentWidgetListener() &&
+ GetCurrentWidgetListener() != mAttachedWidgetListener) {
+ GetCurrentWidgetListener()->WindowResized(this, mBounds.width, mBounds.height);
+ }
+ mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
+{
+ for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
+ const Configuration& configuration = aConfigurations[i];
+ PuppetWidget* w = static_cast<PuppetWidget*>(configuration.mChild.get());
+ NS_ASSERTION(w->GetParent() == this,
+ "Configured widget is not a child");
+ w->SetWindowClipRegion(configuration.mClipRegion, true);
+ LayoutDeviceIntRect bounds = w->GetBounds();
+ if (bounds.Size() != configuration.mBounds.Size()) {
+ w->Resize(configuration.mBounds.x, configuration.mBounds.y,
+ configuration.mBounds.width, configuration.mBounds.height,
+ true);
+ } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) {
+ w->Move(configuration.mBounds.x, configuration.mBounds.y);
+ }
+ w->SetWindowClipRegion(configuration.mClipRegion, false);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::SetFocus(bool aRaise)
+{
+ if (aRaise && mTabChild) {
+ mTabChild->SendRequestFocus(true);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::Invalidate(const LayoutDeviceIntRect& aRect)
+{
+#ifdef DEBUG
+ debug_DumpInvalidate(stderr, this, &aRect, "PuppetWidget", 0);
+#endif
+
+ if (mChild) {
+ return mChild->Invalidate(aRect);
+ }
+
+ mDirtyRegion.Or(mDirtyRegion, aRect);
+
+ if (!mDirtyRegion.IsEmpty() && !mPaintTask.IsPending()) {
+ mPaintTask = new PaintTask(this);
+ return NS_DispatchToCurrentThread(mPaintTask.get());
+ }
+
+ return NS_OK;
+}
+
+void
+PuppetWidget::InitEvent(WidgetGUIEvent& event, LayoutDeviceIntPoint* aPoint)
+{
+ if (nullptr == aPoint) {
+ event.mRefPoint = LayoutDeviceIntPoint(0, 0);
+ } else {
+ // use the point override if provided
+ event.mRefPoint = *aPoint;
+ }
+ event.mTime = PR_Now() / 1000;
+}
+
+NS_IMETHODIMP
+PuppetWidget::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus)
+{
+#ifdef DEBUG
+ debug_DumpEvent(stdout, event->mWidget, event, "PuppetWidget", 0);
+#endif
+
+ MOZ_ASSERT(!mChild || mChild->mWindowType == eWindowType_popup,
+ "Unexpected event dispatch!");
+
+ AutoCacheNativeKeyCommands autoCache(this);
+ if (event->mFlags.mIsSynthesizedForTests && !mNativeKeyCommandsValid) {
+ WidgetKeyboardEvent* keyEvent = event->AsKeyboardEvent();
+ if (keyEvent) {
+ mTabChild->RequestNativeKeyBindings(&autoCache, keyEvent);
+ }
+ }
+
+ if (event->mClass == eCompositionEventClass) {
+ // Store the latest native IME context of parent process's widget or
+ // TextEventDispatcher if it's in this process.
+ WidgetCompositionEvent* compositionEvent = event->AsCompositionEvent();
+#ifdef DEBUG
+ if (mNativeIMEContext.IsValid() &&
+ mNativeIMEContext != compositionEvent->mNativeIMEContext) {
+ RefPtr<TextComposition> composition =
+ IMEStateManager::GetTextCompositionFor(this);
+ MOZ_ASSERT(!composition,
+ "When there is composition caused by old native IME context, "
+ "composition events caused by different native IME context are not "
+ "allowed");
+ }
+#endif // #ifdef DEBUG
+ mNativeIMEContext = compositionEvent->mNativeIMEContext;
+ }
+
+ aStatus = nsEventStatus_eIgnore;
+
+ if (GetCurrentWidgetListener()) {
+ aStatus = GetCurrentWidgetListener()->HandleEvent(event, mUseAttachedEvents);
+ }
+
+ return NS_OK;
+}
+
+nsEventStatus
+PuppetWidget::DispatchInputEvent(WidgetInputEvent* aEvent)
+{
+ if (!AsyncPanZoomEnabled()) {
+ nsEventStatus status = nsEventStatus_eIgnore;
+ DispatchEvent(aEvent, status);
+ return status;
+ }
+
+ if (!mTabChild) {
+ return nsEventStatus_eIgnore;
+ }
+
+ switch (aEvent->mClass) {
+ case eWheelEventClass:
+ Unused <<
+ mTabChild->SendDispatchWheelEvent(*aEvent->AsWheelEvent());
+ break;
+ case eMouseEventClass:
+ Unused <<
+ mTabChild->SendDispatchMouseEvent(*aEvent->AsMouseEvent());
+ break;
+ case eKeyboardEventClass:
+ Unused <<
+ mTabChild->SendDispatchKeyboardEvent(*aEvent->AsKeyboardEvent());
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unsupported event type");
+ }
+
+ return nsEventStatus_eIgnore;
+}
+
+nsresult
+PuppetWidget::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
+ int32_t aNativeKeyCode,
+ uint32_t aModifierFlags,
+ const nsAString& aCharacters,
+ const nsAString& aUnmodifiedCharacters,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "keyevent");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendSynthesizeNativeKeyEvent(aNativeKeyboardLayout, aNativeKeyCode,
+ aModifierFlags, nsString(aCharacters), nsString(aUnmodifiedCharacters),
+ notifier.SaveObserver());
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::SynthesizeNativeMouseEvent(mozilla::LayoutDeviceIntPoint aPoint,
+ uint32_t aNativeMessage,
+ uint32_t aModifierFlags,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "mouseevent");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendSynthesizeNativeMouseEvent(aPoint, aNativeMessage,
+ aModifierFlags, notifier.SaveObserver());
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::SynthesizeNativeMouseMove(mozilla::LayoutDeviceIntPoint aPoint,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "mousemove");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendSynthesizeNativeMouseMove(aPoint, notifier.SaveObserver());
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::SynthesizeNativeMouseScrollEvent(mozilla::LayoutDeviceIntPoint aPoint,
+ uint32_t aNativeMessage,
+ double aDeltaX,
+ double aDeltaY,
+ double aDeltaZ,
+ uint32_t aModifierFlags,
+ uint32_t aAdditionalFlags,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "mousescrollevent");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendSynthesizeNativeMouseScrollEvent(aPoint, aNativeMessage,
+ aDeltaX, aDeltaY, aDeltaZ, aModifierFlags, aAdditionalFlags,
+ notifier.SaveObserver());
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::SynthesizeNativeTouchPoint(uint32_t aPointerId,
+ TouchPointerState aPointerState,
+ LayoutDeviceIntPoint aPoint,
+ double aPointerPressure,
+ uint32_t aPointerOrientation,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "touchpoint");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendSynthesizeNativeTouchPoint(aPointerId, aPointerState,
+ aPoint, aPointerPressure, aPointerOrientation,
+ notifier.SaveObserver());
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
+ bool aLongTap,
+ nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "touchtap");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendSynthesizeNativeTouchTap(aPoint, aLongTap,
+ notifier.SaveObserver());
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::ClearNativeTouchSequence(nsIObserver* aObserver)
+{
+ AutoObserverNotifier notifier(aObserver, "cleartouch");
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendClearNativeTouchSequence(notifier.SaveObserver());
+ return NS_OK;
+}
+
+void
+PuppetWidget::SetConfirmedTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) const
+{
+ if (mTabChild) {
+ mTabChild->SetTargetAPZC(aInputBlockId, aTargets);
+ }
+}
+
+void
+PuppetWidget::UpdateZoomConstraints(const uint32_t& aPresShellId,
+ const FrameMetrics::ViewID& aViewId,
+ const Maybe<ZoomConstraints>& aConstraints)
+{
+ if (mTabChild) {
+ mTabChild->DoUpdateZoomConstraints(aPresShellId, aViewId, aConstraints);
+ }
+}
+
+bool
+PuppetWidget::AsyncPanZoomEnabled() const
+{
+ return mTabChild && mTabChild->AsyncPanZoomEnabled();
+}
+
+NS_IMETHODIMP_(bool)
+PuppetWidget::ExecuteNativeKeyBinding(NativeKeyBindingsType aType,
+ const mozilla::WidgetKeyboardEvent& aEvent,
+ DoCommandCallback aCallback,
+ void* aCallbackData)
+{
+ // B2G doesn't have native key bindings.
+#ifdef MOZ_WIDGET_GONK
+ return false;
+#else // #ifdef MOZ_WIDGET_GONK
+ AutoCacheNativeKeyCommands autoCache(this);
+ if (!aEvent.mWidget && !mNativeKeyCommandsValid) {
+ MOZ_ASSERT(!aEvent.mFlags.mIsSynthesizedForTests);
+ // Abort if untrusted to avoid leaking system settings
+ if (NS_WARN_IF(!aEvent.IsTrusted())) {
+ return false;
+ }
+ mTabChild->RequestNativeKeyBindings(&autoCache, &aEvent);
+ }
+
+ MOZ_ASSERT(mNativeKeyCommandsValid);
+
+ const nsTArray<mozilla::CommandInt>* commands = nullptr;
+ switch (aType) {
+ case nsIWidget::NativeKeyBindingsForSingleLineEditor:
+ commands = &mSingleLineCommands;
+ break;
+ case nsIWidget::NativeKeyBindingsForMultiLineEditor:
+ commands = &mMultiLineCommands;
+ break;
+ case nsIWidget::NativeKeyBindingsForRichTextEditor:
+ commands = &mRichTextCommands;
+ break;
+ default:
+ MOZ_CRASH("Invalid type");
+ break;
+ }
+
+ if (commands->IsEmpty()) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < commands->Length(); i++) {
+ aCallback(static_cast<mozilla::Command>((*commands)[i]), aCallbackData);
+ }
+ return true;
+#endif
+}
+
+LayerManager*
+PuppetWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
+ LayersBackend aBackendHint,
+ LayerManagerPersistence aPersistence)
+{
+ if (!mLayerManager) {
+ mLayerManager = new ClientLayerManager(this);
+ }
+ ShadowLayerForwarder* lf = mLayerManager->AsShadowForwarder();
+ if (lf && !lf->HasShadowManager() && aShadowManager) {
+ lf->SetShadowManager(aShadowManager);
+ }
+ return mLayerManager;
+}
+
+LayerManager*
+PuppetWidget::RecreateLayerManager(PLayerTransactionChild* aShadowManager)
+{
+ mLayerManager = new ClientLayerManager(this);
+ if (ShadowLayerForwarder* lf = mLayerManager->AsShadowForwarder()) {
+ lf->SetShadowManager(aShadowManager);
+ }
+ return mLayerManager;
+}
+
+nsresult
+PuppetWidget::RequestIMEToCommitComposition(bool aCancel)
+{
+#ifdef MOZ_CROSS_PROCESS_IME
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(!Destroyed());
+
+ // There must not be composition which is caused by the PuppetWidget instance.
+ if (NS_WARN_IF(!mNativeIMEContext.IsValid())) {
+ return NS_OK;
+ }
+
+ RefPtr<TextComposition> composition =
+ IMEStateManager::GetTextCompositionFor(this);
+ // This method shouldn't be called when there is no text composition instance.
+ if (NS_WARN_IF(!composition)) {
+ return NS_OK;
+ }
+
+ bool isCommitted = false;
+ nsAutoString committedString;
+ if (NS_WARN_IF(!mTabChild->SendRequestIMEToCommitComposition(
+ aCancel, &isCommitted, &committedString))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // If the composition wasn't committed synchronously, we need to wait async
+ // composition events for destroying the TextComposition instance.
+ if (!isCommitted) {
+ return NS_OK;
+ }
+
+ // Dispatch eCompositionCommit event.
+ WidgetCompositionEvent compositionCommitEvent(true, eCompositionCommit, this);
+ InitEvent(compositionCommitEvent, nullptr);
+ compositionCommitEvent.mData = committedString;
+ nsEventStatus status = nsEventStatus_eIgnore;
+ DispatchEvent(&compositionCommitEvent, status);
+
+ // NOTE: PuppetWidget might be destroyed already.
+
+#endif // #ifdef MOZ_CROSS_PROCESS_IME
+
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::NotifyIMEInternal(const IMENotification& aIMENotification)
+{
+ switch (aIMENotification.mMessage) {
+ case REQUEST_TO_COMMIT_COMPOSITION:
+ return RequestIMEToCommitComposition(false);
+ case REQUEST_TO_CANCEL_COMPOSITION:
+ return RequestIMEToCommitComposition(true);
+ case NOTIFY_IME_OF_FOCUS:
+ case NOTIFY_IME_OF_BLUR:
+ return NotifyIMEOfFocusChange(aIMENotification);
+ case NOTIFY_IME_OF_SELECTION_CHANGE:
+ return NotifyIMEOfSelectionChange(aIMENotification);
+ case NOTIFY_IME_OF_TEXT_CHANGE:
+ return NotifyIMEOfTextChange(aIMENotification);
+ case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED:
+ return NotifyIMEOfCompositionUpdate(aIMENotification);
+ case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT:
+ return NotifyIMEOfMouseButtonEvent(aIMENotification);
+ case NOTIFY_IME_OF_POSITION_CHANGE:
+ return NotifyIMEOfPositionChange(aIMENotification);
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+}
+
+NS_IMETHODIMP
+PuppetWidget::StartPluginIME(const mozilla::WidgetKeyboardEvent& aKeyboardEvent,
+ int32_t aPanelX, int32_t aPanelY,
+ nsString& aCommitted)
+{
+ if (!mTabChild ||
+ !mTabChild->SendStartPluginIME(aKeyboardEvent, aPanelX,
+ aPanelY, &aCommitted)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+void
+PuppetWidget::SetPluginFocused(bool& aFocused)
+{
+ if (mTabChild) {
+ mTabChild->SendSetPluginFocused(aFocused);
+ }
+}
+
+void
+PuppetWidget::DefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent)
+{
+ if (!mTabChild) {
+ return;
+ }
+ mTabChild->SendDefaultProcOfPluginEvent(aEvent);
+}
+
+NS_IMETHODIMP_(void)
+PuppetWidget::SetInputContext(const InputContext& aContext,
+ const InputContextAction& aAction)
+{
+ mInputContext = aContext;
+ // Any widget instances cannot cache IME open state because IME open state
+ // can be changed by user but native IME may not notify us of changing the
+ // open state on some platforms.
+ mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
+
+#ifndef MOZ_CROSS_PROCESS_IME
+ return;
+#endif
+
+ if (!mTabChild) {
+ return;
+ }
+ mTabChild->SendSetInputContext(
+ static_cast<int32_t>(aContext.mIMEState.mEnabled),
+ static_cast<int32_t>(aContext.mIMEState.mOpen),
+ aContext.mHTMLInputType,
+ aContext.mHTMLInputInputmode,
+ aContext.mActionHint,
+ static_cast<int32_t>(aAction.mCause),
+ static_cast<int32_t>(aAction.mFocusChange));
+}
+
+NS_IMETHODIMP_(InputContext)
+PuppetWidget::GetInputContext()
+{
+#ifndef MOZ_CROSS_PROCESS_IME
+ return InputContext();
+#endif
+
+ // XXX Currently, we don't support retrieving IME open state from child
+ // process.
+
+ // When this widget caches input context and currently managed by
+ // IMEStateManager, the cache is valid. Only in this case, we can
+ // avoid to use synchronous IPC.
+ if (mInputContext.mIMEState.mEnabled != IMEState::UNKNOWN &&
+ IMEStateManager::GetWidgetForActiveInputContext() == this) {
+ return mInputContext;
+ }
+
+ NS_WARNING("PuppetWidget::GetInputContext() needs to retrieve it with IPC");
+
+ // Don't cache InputContext here because this process isn't managing IME
+ // state of the chrome widget. So, we cannot modify mInputContext when
+ // chrome widget is set to new context.
+ InputContext context;
+ if (mTabChild) {
+ int32_t enabled, open;
+ mTabChild->SendGetInputContext(&enabled, &open);
+ context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(enabled);
+ context.mIMEState.mOpen = static_cast<IMEState::Open>(open);
+ }
+ return context;
+}
+
+NS_IMETHODIMP_(NativeIMEContext)
+PuppetWidget::GetNativeIMEContext()
+{
+ return mNativeIMEContext;
+}
+
+nsresult
+PuppetWidget::NotifyIMEOfFocusChange(const IMENotification& aIMENotification)
+{
+#ifndef MOZ_CROSS_PROCESS_IME
+ return NS_OK;
+#endif
+
+ if (!mTabChild)
+ return NS_ERROR_FAILURE;
+
+ bool gotFocus = aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS;
+ if (gotFocus) {
+ if (mInputContext.mIMEState.mEnabled != IMEState::PLUGIN) {
+ // When IME gets focus, we should initalize all information of the
+ // content.
+ if (NS_WARN_IF(!mContentCache.CacheAll(this, &aIMENotification))) {
+ return NS_ERROR_FAILURE;
+ }
+ } else {
+ // However, if a plugin has focus, only the editor rect information is
+ // available.
+ if (NS_WARN_IF(!mContentCache.CacheEditorRect(this, &aIMENotification))) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ } else {
+ // When IME loses focus, we don't need to store anything.
+ mContentCache.Clear();
+ }
+
+ mIMEPreferenceOfParent = nsIMEUpdatePreference();
+ if (!mTabChild->SendNotifyIMEFocus(mContentCache, aIMENotification,
+ &mIMEPreferenceOfParent)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::NotifyIMEOfCompositionUpdate(
+ const IMENotification& aIMENotification)
+{
+#ifndef MOZ_CROSS_PROCESS_IME
+ return NS_OK;
+#endif
+
+ NS_ENSURE_TRUE(mTabChild, NS_ERROR_FAILURE);
+
+ if (mInputContext.mIMEState.mEnabled != IMEState::PLUGIN &&
+ NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
+ return NS_ERROR_FAILURE;
+ }
+ mTabChild->SendNotifyIMECompositionUpdate(mContentCache, aIMENotification);
+ return NS_OK;
+}
+
+nsIMEUpdatePreference
+PuppetWidget::GetIMEUpdatePreference()
+{
+#ifdef MOZ_CROSS_PROCESS_IME
+ // e10s requires IME content cache in in the TabParent for handling query
+ // content event only with the parent process. Therefore, this process
+ // needs to receive a lot of information from the focused editor to sent
+ // the latest content to the parent process.
+ if (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN) {
+ // But if a plugin has focus, we cannot receive text nor selection change
+ // in the plugin. Therefore, PuppetWidget needs to receive only position
+ // change event for updating the editor rect cache.
+ return nsIMEUpdatePreference(mIMEPreferenceOfParent.mWantUpdates |
+ nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE);
+ }
+ return nsIMEUpdatePreference(mIMEPreferenceOfParent.mWantUpdates |
+ nsIMEUpdatePreference::NOTIFY_TEXT_CHANGE |
+ nsIMEUpdatePreference::NOTIFY_POSITION_CHANGE );
+#else
+ // B2G doesn't handle IME as widget-level.
+ return nsIMEUpdatePreference();
+#endif
+}
+
+nsresult
+PuppetWidget::NotifyIMEOfTextChange(const IMENotification& aIMENotification)
+{
+ MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_TEXT_CHANGE,
+ "Passed wrong notification");
+
+#ifndef MOZ_CROSS_PROCESS_IME
+ return NS_OK;
+#endif
+
+ if (!mTabChild)
+ return NS_ERROR_FAILURE;
+
+ // While a plugin has focus, text change notification shouldn't be available.
+ if (NS_WARN_IF(mInputContext.mIMEState.mEnabled == IMEState::PLUGIN)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // FYI: text change notification is the first notification after
+ // a user operation changes the content. So, we need to modify
+ // the cache as far as possible here.
+
+ if (NS_WARN_IF(!mContentCache.CacheText(this, &aIMENotification))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // TabParent doesn't this this to cache. we don't send the notification
+ // if parent process doesn't request NOTIFY_TEXT_CHANGE.
+ if (mIMEPreferenceOfParent.WantTextChange()) {
+ mTabChild->SendNotifyIMETextChange(mContentCache, aIMENotification);
+ } else {
+ mTabChild->SendUpdateContentCache(mContentCache);
+ }
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::NotifyIMEOfSelectionChange(
+ const IMENotification& aIMENotification)
+{
+ MOZ_ASSERT(aIMENotification.mMessage == NOTIFY_IME_OF_SELECTION_CHANGE,
+ "Passed wrong notification");
+
+#ifndef MOZ_CROSS_PROCESS_IME
+ return NS_OK;
+#endif
+
+ if (!mTabChild)
+ return NS_ERROR_FAILURE;
+
+ // While a plugin has focus, selection change notification shouldn't be
+ // available.
+ if (NS_WARN_IF(mInputContext.mIMEState.mEnabled == IMEState::PLUGIN)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Note that selection change must be notified after text change if it occurs.
+ // Therefore, we don't need to query text content again here.
+ mContentCache.SetSelection(
+ this,
+ aIMENotification.mSelectionChangeData.mOffset,
+ aIMENotification.mSelectionChangeData.Length(),
+ aIMENotification.mSelectionChangeData.mReversed,
+ aIMENotification.mSelectionChangeData.GetWritingMode());
+
+ mTabChild->SendNotifyIMESelection(mContentCache, aIMENotification);
+
+ return NS_OK;
+}
+
+nsresult
+PuppetWidget::NotifyIMEOfMouseButtonEvent(
+ const IMENotification& aIMENotification)
+{
+ if (!mTabChild) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // While a plugin has focus, mouse button event notification shouldn't be
+ // available.
+ if (NS_WARN_IF(mInputContext.mIMEState.mEnabled == IMEState::PLUGIN)) {
+ return NS_ERROR_FAILURE;
+ }
+
+
+ bool consumedByIME = false;
+ if (!mTabChild->SendNotifyIMEMouseButtonEvent(aIMENotification,
+ &consumedByIME)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return consumedByIME ? NS_SUCCESS_EVENT_CONSUMED : NS_OK;
+}
+
+nsresult
+PuppetWidget::NotifyIMEOfPositionChange(const IMENotification& aIMENotification)
+{
+#ifndef MOZ_CROSS_PROCESS_IME
+ return NS_OK;
+#endif
+ if (NS_WARN_IF(!mTabChild)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_WARN_IF(!mContentCache.CacheEditorRect(this, &aIMENotification))) {
+ return NS_ERROR_FAILURE;
+ }
+ // While a plugin has focus, selection range isn't available. So, we don't
+ // need to cache it at that time.
+ if (mInputContext.mIMEState.mEnabled != IMEState::PLUGIN &&
+ NS_WARN_IF(!mContentCache.CacheSelection(this, &aIMENotification))) {
+ return NS_ERROR_FAILURE;
+ }
+ if (mIMEPreferenceOfParent.WantPositionChanged()) {
+ mTabChild->SendNotifyIMEPositionChange(mContentCache, aIMENotification);
+ } else {
+ mTabChild->SendUpdateContentCache(mContentCache);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::SetCursor(nsCursor aCursor)
+{
+ // Don't cache on windows, Windowless flash breaks this via async cursor updates.
+#if !defined(XP_WIN)
+ if (mCursor == aCursor && !mCustomCursor && !mUpdateCursor) {
+ return NS_OK;
+ }
+#endif
+
+ mCustomCursor = nullptr;
+
+ if (mTabChild &&
+ !mTabChild->SendSetCursor(aCursor, mUpdateCursor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mCursor = aCursor;
+ mUpdateCursor = false;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetWidget::SetCursor(imgIContainer* aCursor,
+ uint32_t aHotspotX, uint32_t aHotspotY)
+{
+ if (!aCursor || !mTabChild) {
+ return NS_OK;
+ }
+
+#if !defined(XP_WIN)
+ if (mCustomCursor == aCursor &&
+ mCursorHotspotX == aHotspotX &&
+ mCursorHotspotY == aHotspotY &&
+ !mUpdateCursor) {
+ return NS_OK;
+ }
+#endif
+
+ RefPtr<mozilla::gfx::SourceSurface> surface =
+ aCursor->GetFrame(imgIContainer::FRAME_CURRENT,
+ imgIContainer::FLAG_SYNC_DECODE);
+ if (!surface) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
+ surface->GetDataSurface();
+ if (!dataSurface) {
+ return NS_ERROR_FAILURE;
+ }
+
+ size_t length;
+ int32_t stride;
+ mozilla::UniquePtr<char[]> surfaceData =
+ nsContentUtils::GetSurfaceData(WrapNotNull(dataSurface), &length, &stride);
+
+ nsDependentCString cursorData(surfaceData.get(), length);
+ mozilla::gfx::IntSize size = dataSurface->GetSize();
+ if (!mTabChild->SendSetCustomCursor(cursorData, size.width, size.height, stride,
+ static_cast<uint8_t>(dataSurface->GetFormat()),
+ aHotspotX, aHotspotY, mUpdateCursor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mCursor = nsCursor(-1);
+ mCustomCursor = aCursor;
+ mCursorHotspotX = aHotspotX;
+ mCursorHotspotY = aHotspotY;
+ mUpdateCursor = false;
+
+ return NS_OK;
+}
+
+void
+PuppetWidget::ClearCachedCursor()
+{
+ nsBaseWidget::ClearCachedCursor();
+ mCustomCursor = nullptr;
+}
+
+nsresult
+PuppetWidget::Paint()
+{
+ MOZ_ASSERT(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
+
+ if (!GetCurrentWidgetListener())
+ return NS_OK;
+
+ LayoutDeviceIntRegion region = mDirtyRegion;
+
+ // reset repaint tracking
+ mDirtyRegion.SetEmpty();
+ mPaintTask.Revoke();
+
+ RefPtr<PuppetWidget> strongThis(this);
+
+ GetCurrentWidgetListener()->WillPaintWindow(this);
+
+ if (GetCurrentWidgetListener()) {
+#ifdef DEBUG
+ debug_DumpPaintEvent(stderr, this, region.ToUnknownRegion(),
+ "PuppetWidget", 0);
+#endif
+
+ if (mozilla::layers::LayersBackend::LAYERS_CLIENT == mLayerManager->GetBackendType()) {
+ // Do nothing, the compositor will handle drawing
+ if (mTabChild) {
+ mTabChild->NotifyPainted();
+ }
+ } else if (mozilla::layers::LayersBackend::LAYERS_BASIC == mLayerManager->GetBackendType()) {
+ RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(mDrawTarget);
+ if (!ctx) {
+ gfxDevCrash(LogReason::InvalidContext) << "PuppetWidget context problem " << gfx::hexa(mDrawTarget);
+ return NS_ERROR_FAILURE;
+ }
+ ctx->Rectangle(gfxRect(0,0,0,0));
+ ctx->Clip();
+ AutoLayerManagerSetup setupLayerManager(this, ctx,
+ BufferMode::BUFFER_NONE);
+ GetCurrentWidgetListener()->PaintWindow(this, region);
+ if (mTabChild) {
+ mTabChild->NotifyPainted();
+ }
+ }
+ }
+
+ if (GetCurrentWidgetListener()) {
+ GetCurrentWidgetListener()->DidPaintWindow();
+ }
+
+ return NS_OK;
+}
+
+void
+PuppetWidget::SetChild(PuppetWidget* aChild)
+{
+ MOZ_ASSERT(this != aChild, "can't parent a widget to itself");
+ MOZ_ASSERT(!aChild->mChild,
+ "fake widget 'hierarchy' only expected to have one level");
+
+ mChild = aChild;
+}
+
+NS_IMETHODIMP
+PuppetWidget::PaintTask::Run()
+{
+ if (mWidget) {
+ mWidget->Paint();
+ }
+ return NS_OK;
+}
+
+void
+PuppetWidget::PaintNowIfNeeded()
+{
+ if (IsVisible() && mPaintTask.IsPending()) {
+ Paint();
+ }
+}
+
+NS_IMPL_ISUPPORTS(PuppetWidget::MemoryPressureObserver, nsIObserver)
+
+NS_IMETHODIMP
+PuppetWidget::MemoryPressureObserver::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!mWidget) {
+ return NS_OK;
+ }
+
+ if (strcmp("memory-pressure", aTopic) == 0 &&
+ !NS_LITERAL_STRING("lowering-priority").Equals(aData)) {
+ if (!mWidget->mVisible && mWidget->mLayerManager &&
+ XRE_IsContentProcess()) {
+ mWidget->mLayerManager->ClearCachedResources();
+ }
+ }
+ return NS_OK;
+}
+
+void
+PuppetWidget::MemoryPressureObserver::Remove()
+{
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, "memory-pressure");
+ }
+ mWidget = nullptr;
+}
+
+bool
+PuppetWidget::NeedsPaint()
+{
+ // e10s popups are handled by the parent process, so never should be painted here
+ if (XRE_IsContentProcess() &&
+ Preferences::GetBool("browser.tabs.remote.desktopbehavior", false) &&
+ mWindowType == eWindowType_popup) {
+ NS_WARNING("Trying to paint an e10s popup in the child process!");
+ return false;
+ }
+
+ return mVisible;
+}
+
+float
+PuppetWidget::GetDPI()
+{
+ if (mDPI < 0) {
+ if (mTabChild) {
+ mTabChild->GetDPI(&mDPI);
+ } else {
+ mDPI = 96.0;
+ }
+ }
+
+ return mDPI;
+}
+
+double
+PuppetWidget::GetDefaultScaleInternal()
+{
+ if (mDefaultScale < 0) {
+ if (mTabChild) {
+ mTabChild->GetDefaultScale(&mDefaultScale);
+ } else {
+ mDefaultScale = 1;
+ }
+ }
+
+ return mDefaultScale;
+}
+
+int32_t
+PuppetWidget::RoundsWidgetCoordinatesTo()
+{
+ if (mRounding < 0) {
+ if (mTabChild) {
+ mTabChild->GetWidgetRounding(&mRounding);
+ } else {
+ mRounding = 1;
+ }
+ }
+
+ return mRounding;
+}
+
+void*
+PuppetWidget::GetNativeData(uint32_t aDataType)
+{
+ switch (aDataType) {
+ case NS_NATIVE_SHAREABLE_WINDOW: {
+ MOZ_ASSERT(mTabChild, "Need TabChild to get the nativeWindow from!");
+ mozilla::WindowsHandle nativeData = 0;
+ if (mTabChild) {
+ mTabChild->SendGetWidgetNativeData(&nativeData);
+ }
+ return (void*)nativeData;
+ }
+ case NS_NATIVE_WINDOW:
+ case NS_NATIVE_WIDGET:
+ case NS_NATIVE_DISPLAY:
+ // These types are ignored (see bug 1183828, bug 1240891).
+ break;
+ case NS_RAW_NATIVE_IME_CONTEXT:
+ MOZ_CRASH("You need to call GetNativeIMEContext() instead");
+ case NS_NATIVE_PLUGIN_PORT:
+ case NS_NATIVE_GRAPHIC:
+ case NS_NATIVE_SHELLWIDGET:
+ default:
+ NS_WARNING("nsWindow::GetNativeData called with bad value");
+ break;
+ }
+ return nullptr;
+}
+
+#if defined(XP_WIN)
+void
+PuppetWidget::SetNativeData(uint32_t aDataType, uintptr_t aVal)
+{
+ switch (aDataType) {
+ case NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW:
+ MOZ_ASSERT(mTabChild, "Need TabChild to send the message.");
+ if (mTabChild) {
+ mTabChild->SendSetNativeChildOfShareableWindow(aVal);
+ }
+ break;
+ default:
+ NS_WARNING("SetNativeData called with unsupported data type.");
+ }
+}
+#endif
+
+nsIntPoint
+PuppetWidget::GetChromeDimensions()
+{
+ if (!GetOwningTabChild()) {
+ NS_WARNING("PuppetWidget without Tab does not have chrome information.");
+ return nsIntPoint();
+ }
+ return GetOwningTabChild()->GetChromeDisplacement().ToUnknownPoint();
+}
+
+nsIntPoint
+PuppetWidget::GetWindowPosition()
+{
+ if (!GetOwningTabChild()) {
+ return nsIntPoint();
+ }
+
+ int32_t winX, winY, winW, winH;
+ NS_ENSURE_SUCCESS(GetOwningTabChild()->GetDimensions(0, &winX, &winY, &winW, &winH), nsIntPoint());
+ return nsIntPoint(winX, winY) + GetOwningTabChild()->GetClientOffset().ToUnknownPoint();
+}
+
+LayoutDeviceIntRect
+PuppetWidget::GetScreenBounds()
+{
+ return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size());
+}
+
+uint32_t PuppetWidget::GetMaxTouchPoints() const
+{
+ static uint32_t sTouchPoints = 0;
+ static bool sIsInitialized = false;
+ if (sIsInitialized) {
+ return sTouchPoints;
+ }
+ if (mTabChild) {
+ mTabChild->GetMaxTouchPoints(&sTouchPoints);
+ sIsInitialized = true;
+ }
+ return sTouchPoints;
+}
+
+void
+PuppetWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics)
+{
+ mTabChild->StartScrollbarDrag(aDragMetrics);
+}
+
+PuppetScreen::PuppetScreen(void *nativeScreen)
+{
+}
+
+PuppetScreen::~PuppetScreen()
+{
+}
+
+static ScreenConfiguration
+ScreenConfig()
+{
+ ScreenConfiguration config;
+ hal::GetCurrentScreenConfiguration(&config);
+ return config;
+}
+
+nsIntSize
+PuppetWidget::GetScreenDimensions()
+{
+ nsIntRect r = ScreenConfig().rect();
+ return nsIntSize(r.width, r.height);
+}
+
+NS_IMETHODIMP
+PuppetScreen::GetId(uint32_t *outId)
+{
+ *outId = 1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreen::GetRect(int32_t *outLeft, int32_t *outTop,
+ int32_t *outWidth, int32_t *outHeight)
+{
+ nsIntRect r = ScreenConfig().rect();
+ *outLeft = r.x;
+ *outTop = r.y;
+ *outWidth = r.width;
+ *outHeight = r.height;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreen::GetAvailRect(int32_t *outLeft, int32_t *outTop,
+ int32_t *outWidth, int32_t *outHeight)
+{
+ return GetRect(outLeft, outTop, outWidth, outHeight);
+}
+
+NS_IMETHODIMP
+PuppetScreen::GetPixelDepth(int32_t *aPixelDepth)
+{
+ *aPixelDepth = ScreenConfig().pixelDepth();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreen::GetColorDepth(int32_t *aColorDepth)
+{
+ *aColorDepth = ScreenConfig().colorDepth();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreen::GetRotation(uint32_t* aRotation)
+{
+ NS_WARNING("Attempt to get screen rotation through nsIScreen::GetRotation(). Nothing should know or care this in sandboxed contexts. If you want *orientation*, use hal.");
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+PuppetScreen::SetRotation(uint32_t aRotation)
+{
+ NS_WARNING("Attempt to set screen rotation through nsIScreen::GetRotation(). Nothing should know or care this in sandboxed contexts. If you want *orientation*, use hal.");
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMPL_ISUPPORTS(PuppetScreenManager, nsIScreenManager)
+
+PuppetScreenManager::PuppetScreenManager()
+{
+ mOneScreen = new PuppetScreen(nullptr);
+}
+
+PuppetScreenManager::~PuppetScreenManager()
+{
+}
+
+NS_IMETHODIMP
+PuppetScreenManager::ScreenForId(uint32_t aId,
+ nsIScreen** outScreen)
+{
+ NS_IF_ADDREF(*outScreen = mOneScreen.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreenManager::GetPrimaryScreen(nsIScreen** outScreen)
+{
+ NS_IF_ADDREF(*outScreen = mOneScreen.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreenManager::ScreenForRect(int32_t inLeft,
+ int32_t inTop,
+ int32_t inWidth,
+ int32_t inHeight,
+ nsIScreen** outScreen)
+{
+ return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+PuppetScreenManager::ScreenForNativeWidget(void* aWidget,
+ nsIScreen** outScreen)
+{
+ return GetPrimaryScreen(outScreen);
+}
+
+NS_IMETHODIMP
+PuppetScreenManager::GetNumberOfScreens(uint32_t* aNumberOfScreens)
+{
+ *aNumberOfScreens = 1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PuppetScreenManager::GetSystemDefaultScale(float *aDefaultScale)
+{
+ *aDefaultScale = 1.0f;
+ return NS_OK;
+}
+
+nsIWidgetListener*
+PuppetWidget::GetCurrentWidgetListener()
+{
+ if (!mPreviouslyAttachedWidgetListener ||
+ !mAttachedWidgetListener) {
+ return mAttachedWidgetListener;
+ }
+
+ if (mAttachedWidgetListener->GetView()->IsPrimaryFramePaintSuppressed()) {
+ return mPreviouslyAttachedWidgetListener;
+ }
+
+ return mAttachedWidgetListener;
+}
+
+void
+PuppetWidget::SetCandidateWindowForPlugin(
+ const CandidateWindowPosition& aPosition)
+{
+ if (!mTabChild) {
+ return;
+ }
+
+ mTabChild->SendSetCandidateWindowForPlugin(aPosition);
+}
+
+void
+PuppetWidget::ZoomToRect(const uint32_t& aPresShellId,
+ const FrameMetrics::ViewID& aViewId,
+ const CSSRect& aRect,
+ const uint32_t& aFlags)
+{
+ if (!mTabChild) {
+ return;
+ }
+
+ mTabChild->ZoomToRect(aPresShellId, aViewId, aRect, aFlags);
+}
+
+void
+PuppetWidget::LookUpDictionary(
+ const nsAString& aText,
+ const nsTArray<mozilla::FontRange>& aFontRangeArray,
+ const bool aIsVertical,
+ const LayoutDeviceIntPoint& aPoint)
+{
+ if (!mTabChild) {
+ return;
+ }
+
+ mTabChild->SendLookUpDictionary(nsString(aText), aFontRangeArray, aIsVertical, aPoint);
+}
+
+bool
+PuppetWidget::HasPendingInputEvent()
+{
+ if (!mTabChild) {
+ return false;
+ }
+
+ bool ret = false;
+
+ mTabChild->GetIPCChannel()->PeekMessages(
+ [&ret](const IPC::Message& aMsg) -> bool {
+ if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart)
+ == mozilla::dom::PBrowser::PBrowserStart) {
+ switch (aMsg.type()) {
+ case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
+ case mozilla::dom::PBrowser::Msg_SynthMouseMoveEvent__ID:
+ case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
+ case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
+ case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
+ case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
+ case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
+ case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
+ case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
+ case mozilla::dom::PBrowser::Msg_MouseEvent__ID:
+ case mozilla::dom::PBrowser::Msg_KeyEvent__ID:
+ ret = true;
+ return false; // Stop peeking.
+ }
+ }
+ return true;
+ }
+ );
+
+ return ret;
+}
+
+void
+PuppetWidget::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ bool aIsConsumed)
+{
+ if (NS_WARN_IF(mKeyEventInPluginCallbacks.IsEmpty())) {
+ return;
+ }
+ nsCOMPtr<nsIKeyEventInPluginCallback> callback =
+ mKeyEventInPluginCallbacks[0];
+ MOZ_ASSERT(callback);
+ mKeyEventInPluginCallbacks.RemoveElementAt(0);
+ callback->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+}
+
+nsresult
+PuppetWidget::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+ nsIKeyEventInPluginCallback* aCallback)
+{
+ if (NS_WARN_IF(!mTabChild)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ if (NS_WARN_IF(!mTabChild->SendOnWindowedPluginKeyEvent(aKeyEventData))) {
+ return NS_ERROR_FAILURE;
+ }
+ mKeyEventInPluginCallbacks.AppendElement(aCallback);
+ return NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY;
+}
+
+} // namespace widget
+} // namespace mozilla