/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim:expandtab:shiftwidth=4:tabstop=4: */ /* 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 "mozilla/ArrayUtils.h" #include "mozilla/MiscEvents.h" #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MOZ_X11 #include #include #include "gfxXlibSurface.h" #endif //MOZ_X11 #include "nsXULAppAPI.h" #include "prlink.h" #include "nsWindow.h" #include "mozqwidget.h" #include "nsIdleService.h" #include "nsRenderingContext.h" #include "nsIRollupListener.h" #include "nsWidgetsCID.h" #include "nsQtKeyUtils.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" #include "mozilla/Likely.h" #include "mozilla/layers/LayersTypes.h" #include "nsIWidgetListener.h" #include "ClientLayerManager.h" #include "BasicLayers.h" #include "nsIStringBundle.h" #include "nsGfxCIID.h" #include "imgIContainer.h" #include "nsGfxCIID.h" #include "nsIInterfaceRequestorUtils.h" #include "nsAutoPtr.h" #include "gfxQtPlatform.h" #include "nsIDOMWheelEvent.h" #include "GLContext.h" #ifdef MOZ_X11 #include "keysym2ucs.h" #endif #include "Layers.h" #include "GLContextProvider.h" using namespace mozilla; using namespace mozilla::gl; using namespace mozilla::widget; using namespace mozilla::gfx; using namespace mozilla::layers; using mozilla::gl::GLContext; #define kWindowPositionSlop 20 // Qt static const int WHEEL_DELTA = 120; static bool gGlobalsInitialized = false; static bool sAltGrModifier = false; static void find_first_visible_parent(QWindow* aItem, QWindow*& aVisibleItem); static bool is_mouse_in_window (MozQWidget* aWindow, double aMouseX, double aMouseY); static bool isContextMenuKeyEvent(const QKeyEvent *qe); static void InitKeyEvent(WidgetKeyboardEvent &aEvent, QKeyEvent *aQEvent); nsWindow::nsWindow() { LOG(("%s [%p]\n", __PRETTY_FUNCTION__, (void *)this)); mIsTopLevel = false; mIsDestroyed = false; mIsShown = false; mEnabled = true; mWidget = nullptr; mVisible = false; mActivatePending = false; mWindowType = eWindowType_child; mSizeState = nsSizeMode_Normal; mLastSizeMode = nsSizeMode_Normal; mQCursor = Qt::ArrowCursor; mNeedsResize = false; mNeedsMove = false; mListenForResizes = false; mNeedsShow = false; mTimerStarted = false; mMoveEvent.needDispatch = false; if (!gGlobalsInitialized) { gfxPlatform::GetPlatform(); gGlobalsInitialized = true; } memset(mKeyDownFlags, 0, sizeof(mKeyDownFlags)); mIsTransparent = false; mCursor = eCursor_standard; } nsWindow::~nsWindow() { LOG(("%s [%p]\n", __PRETTY_FUNCTION__, (void *)this)); Destroy(); } nsresult nsWindow::Create(nsIWidget *aParent, nsNativeWidget aNativeParent, const nsIntRect &aRect, nsDeviceContext *aContext, nsWidgetInitData *aInitData) { // only set the base parent if we're not going to be a dialog or a // toplevel nsIWidget *baseParent = aParent; // initialize all the common bits of this class BaseCreate(baseParent, aRect, aContext, aInitData); mVisible = true; // and do our common creation mParent = (nsWindow *)aParent; // save our bounds mBounds = aRect; // find native parent MozQWidget *parent = nullptr; if (aParent != nullptr) { parent = static_cast(aParent->GetNativeData(NS_NATIVE_WIDGET)); } else if (aNativeParent != nullptr) { parent = static_cast(aNativeParent); if (parent && mParent == nullptr) { mParent = parent->getReceiver(); } } LOG(("Create: nsWindow [%p] mWidget:[%p] parent:[%p], natPar:[%p] mParent:%p\n", (void *)this, (void*)mWidget, parent, aNativeParent, mParent)); // ok, create our QGraphicsWidget mWidget = createQWidget(parent, aInitData); if (!mWidget) { return NS_ERROR_OUT_OF_MEMORY; } // resize so that everything is set to the right dimensions Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false); // check if we should listen for resizes mListenForResizes = (aNativeParent || (aInitData && aInitData->mListenForResizes)); return NS_OK; } MozQWidget* nsWindow::createQWidget(MozQWidget* parent, nsWidgetInitData* aInitData) { const char *windowName = nullptr; Qt::WindowFlags flags = Qt::Widget; // ok, create our windows switch (mWindowType) { case eWindowType_dialog: windowName = "topLevelDialog"; flags = Qt::Dialog; break; case eWindowType_popup: windowName = "topLevelPopup"; flags = Qt::Popup; break; case eWindowType_toplevel: windowName = "topLevelWindow"; flags = Qt::Window; break; case eWindowType_invisible: windowName = "topLevelInvisible"; break; case eWindowType_child: case eWindowType_plugin: default: // sheet windowName = "paintArea"; break; } MozQWidget* widget = new MozQWidget(this, parent); if (!widget) { return nullptr; } widget->setObjectName(QString(windowName)); if (mWindowType == eWindowType_invisible) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 1, 0)) widget->setVisibility(QWindow::Hidden); #else widget->hide(); #endif } if (mWindowType == eWindowType_dialog) { widget->setModality(Qt::WindowModal); } widget->create(); // create a QGraphicsView if this is a new toplevel window LOG(("nsWindow::%s [%p] Created Window: %s, widget:%p, par:%p\n", __FUNCTION__, (void *)this, windowName, widget, parent)); return widget; } NS_IMETHODIMP nsWindow::Destroy(void) { if (mIsDestroyed || !mWidget) { return NS_OK; } LOG(("nsWindow::Destroy [%p]\n", (void *)this)); mIsDestroyed = true; /** Need to clean our LayerManager up while still alive */ if (mLayerManager) { mLayerManager->Destroy(); } mLayerManager = nullptr; // It is safe to call DestroyeCompositor several times (here and // in the parent class) since it will take effect only once. // The reason we call it here is because on gtk platforms we need // to destroy the compositor before we destroy the gdk window (which // destroys the the gl context attached to it). DestroyCompositor(); ClearCachedResources(); nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); if (rollupListener) { nsCOMPtr rollupWidget = rollupListener->GetRollupWidget(); if (static_cast(this) == rollupWidget) { rollupListener->Rollup(0, nullptr, nullptr); } } Show(false); // walk the list of children and call destroy on them. Have to be // careful, though -- calling destroy on a kid may actually remove // it from our child list, losing its sibling links. for (nsIWidget* kid = mFirstChild; kid; ) { nsIWidget* next = kid->GetNextSibling(); kid->Destroy(); kid = next; } // Destroy thebes surface now. Badness can happen if we destroy // the surface after its X Window. if (mWidget) { mWidget->dropReceiver(); // Call deleteLater instead of delete; Qt still needs the object // to be valid even after sending it a Close event. We could // also set WA_DeleteOnClose, but this gives us more control. mWidget->deleteLater(); } mWidget = nullptr; OnDestroy(); return NS_OK; } NS_IMETHODIMP nsWindow::Show(bool aState) { LOG(("nsWindow::Show [%p] state %d\n", (void *)this, aState)); if (aState == mIsShown) { return NS_OK; } // Clear our cached resources when the window is hidden. if (mIsShown && !aState) { ClearCachedResources(); } mIsShown = aState; if ((aState && !AreBoundsSane()) || !mWidget) { LOG(("\tbounds are insane or window hasn't been created yet\n")); mNeedsShow = true; return NS_OK; } if (aState) { if (mNeedsMove) { NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false); } else if (mNeedsResize) { NativeResize(mBounds.width, mBounds.height, false); } } else { // If someone is hiding this widget, clear any needing show flag. mNeedsShow = false; } NativeShow(aState); return NS_OK; } bool nsWindow::IsVisible() const { return mIsShown; } NS_IMETHODIMP nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) { if (!mWidget) { return NS_ERROR_FAILURE; } int32_t screenWidth = qApp->primaryScreen()->size().width(); int32_t screenHeight = qApp->primaryScreen()->size().height(); if (aAllowSlop) { if (*aX < (kWindowPositionSlop - mBounds.width)) *aX = kWindowPositionSlop - mBounds.width; if (*aX > (screenWidth - kWindowPositionSlop)) *aX = screenWidth - kWindowPositionSlop; if (*aY < (kWindowPositionSlop - mBounds.height)) *aY = kWindowPositionSlop - mBounds.height; if (*aY > (screenHeight - kWindowPositionSlop)) *aY = screenHeight - kWindowPositionSlop; } else { if (*aX < 0) *aX = 0; if (*aX > (screenWidth - mBounds.width)) *aX = screenWidth - mBounds.width; if (*aY < 0) *aY = 0; if (*aY > (screenHeight - mBounds.height)) *aY = screenHeight - mBounds.height; } return NS_OK; } NS_IMETHODIMP nsWindow::Move(double aX, double aY) { LOG(("nsWindow::Move [%p] %f %f\n", (void *)this, aX, aY)); int32_t x = NSToIntRound(aX); int32_t y = NSToIntRound(aY); if (mIsTopLevel) { SetSizeMode(nsSizeMode_Normal); } if (x == mBounds.x && y == mBounds.y) { return NS_OK; } mNeedsMove = false; // update the bounds QPoint pos(x, y); if (mIsTopLevel) { mWidget->setPosition(x, y); } else if (mWidget) { // the position of the widget is set relative to the parent // so we map the coordinates accordingly pos = mWidget->mapToGlobal(pos); mWidget->setPosition(pos); } mBounds.x = pos.x(); mBounds.y = pos.y(); NotifyRollupGeometryChange(); return NS_OK; } NS_IMETHODIMP nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) { mBounds.width = NSToIntRound(aWidth); mBounds.height = NSToIntRound(aHeight); if (!mWidget) return NS_OK; if (mIsShown) { if (AreBoundsSane()) { if (mIsTopLevel || mNeedsShow) NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, aRepaint); else NativeResize(mBounds.width, mBounds.height, aRepaint); // Does it need to be shown because it was previously insane? if (mNeedsShow) { NativeShow(true); } } else { // If someone has set this so that the needs show flag is false // and it needs to be hidden, update the flag and hide the // window. This flag will be cleared the next time someone // hides the window or shows it. It also prevents us from // calling NativeShow(false) excessively on the window which // causes unneeded X traffic. if (!mNeedsShow) { mNeedsShow = true; NativeShow(false); } } } else if (AreBoundsSane() && mListenForResizes) { // For widgets that we listen for resizes for (widgets created // with native parents) we apparently _always_ have to resize. I // dunno why, but apparently we're lame like that. NativeResize(mBounds.width, mBounds.height, aRepaint); } else { mNeedsResize = true; } // synthesize a resize event if this isn't a toplevel if (mIsTopLevel || mListenForResizes) { nsEventStatus status; DispatchResizeEvent(mBounds, status); } NotifyRollupGeometryChange(); return NS_OK; } NS_IMETHODIMP nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) { mBounds.x = NSToIntRound(aX); mBounds.y = NSToIntRound(aY); mBounds.width = NSToIntRound(aWidth); mBounds.height = NSToIntRound(aHeight); mPlaced = true; if (!mWidget) { return NS_OK; } // Has this widget been set to visible? if (mIsShown) { // Are the bounds sane? if (AreBoundsSane()) { // Yep? Resize the window NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, aRepaint); // Does it need to be shown because it was previously insane? if (mNeedsShow) NativeShow(true); } else { // If someone has set this so that the needs show flag is false // and it needs to be hidden, update the flag and hide the // window. This flag will be cleared the next time someone // hides the window or shows it. It also prevents us from // calling NativeShow(false) excessively on the window which // causes unneeded X traffic. if (!mNeedsShow) { mNeedsShow = true; NativeShow(false); } } } // If the widget hasn't been shown, mark the widget as needing to be // resized before it is shown else if (AreBoundsSane() && mListenForResizes) { // For widgets that we listen for resizes for (widgets created // with native parents) we apparently _always_ have to resize. I // dunno why, but apparently we're lame like that. NativeResize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, aRepaint); } else { mNeedsResize = true; mNeedsMove = true; } if (mIsTopLevel || mListenForResizes) { // synthesize a resize event nsEventStatus status; DispatchResizeEvent(mBounds, status); } if (aRepaint) { mWidget->renderLater(); } NotifyRollupGeometryChange(); return NS_OK; } NS_IMETHODIMP nsWindow::Enable(bool aState) { mEnabled = aState; return NS_OK; } bool nsWindow::IsEnabled() const { return mEnabled; } NS_IMETHODIMP nsWindow::SetFocus(bool aRaise) { // Make sure that our owning widget has focus. If it doesn't try to // grab it. Note that we don't set our focus flag in this case. LOGFOCUS((" SetFocus [%p]\n", (void *)this)); if (!mWidget) { return NS_ERROR_FAILURE; } if (mWidget->focusObject()) { return NS_OK; } // Because QGraphicsItem cannot get the focus if they are // invisible, we look up the chain, for the lowest visible // parent and focus that one QWindow* realFocusItem = nullptr; find_first_visible_parent(mWidget, realFocusItem); if (!realFocusItem || realFocusItem->focusObject()) { return NS_OK; } if (aRaise && mWidget) { // the raising has to happen on the view widget mWidget->raise(); } // XXXndeakin why is this here? It should dispatch only when the OS // notifies us. DispatchActivateEvent(); return NS_OK; } NS_IMETHODIMP nsWindow::ConfigureChildren(const nsTArray& aConfigurations) { for (uint32_t i = 0; i < aConfigurations.Length(); ++i) { const Configuration& configuration = aConfigurations[i]; nsWindow* w = static_cast(configuration.mChild); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); if (w->mBounds.Size() != configuration.mBounds.Size()) { w->Resize(configuration.mBounds.x, configuration.mBounds.y, configuration.mBounds.width, configuration.mBounds.height, true); } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) { w->Move(configuration.mBounds.x, configuration.mBounds.y); } } return NS_OK; } NS_IMETHODIMP nsWindow::Invalidate(const nsIntRect &aRect) { LOGDRAW(("Invalidate (rect) [%p,%p]: %d %d %d %d\n", (void *)this, (void*)mWidget,aRect.x, aRect.y, aRect.width, aRect.height)); if (!mWidget) { return NS_OK; } mWidget->renderLater(); return NS_OK; } nsIntPoint nsWindow::WidgetToScreenOffset() { NS_ENSURE_TRUE(mWidget, nsIntPoint(0,0)); QPoint origin(0, 0); origin = mWidget->mapToGlobal(origin); return nsIntPoint(origin.x(), origin.y()); } void* nsWindow::GetNativeData(uint32_t aDataType) { switch (aDataType) { case NS_NATIVE_WINDOW: case NS_NATIVE_WIDGET: { return mWidget; } case NS_NATIVE_SHAREABLE_WINDOW: { return mWidget ? (void*)mWidget->winId() : nullptr; } case NS_NATIVE_DISPLAY: { #ifdef MOZ_X11 return gfxQtPlatform::GetXDisplay(mWidget); #endif break; } case NS_NATIVE_PLUGIN_PORT: case NS_NATIVE_GRAPHIC: case NS_NATIVE_SHELLWIDGET: { break; } default: NS_WARNING("nsWindow::GetNativeData called with bad value"); return nullptr; } LOG(("nsWindow::%s [%p] aDataType:%i\n", __FUNCTION__, (void *)this, aDataType)); return nullptr; } NS_IMETHODIMP nsWindow::DispatchEvent(WidgetGUIEvent* aEvent, nsEventStatus& aStatus) { #ifdef DEBUG debug_DumpEvent(stdout, aEvent->widget, aEvent, nsAutoCString("something"), 0); #endif aStatus = nsEventStatus_eIgnore; // send it to the standard callback if (mWidgetListener) { aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); } return NS_OK; } NS_IMETHODIMP_(void) nsWindow::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { NS_ENSURE_TRUE_VOID(mWidget); // SetSoftwareKeyboardState uses mInputContext, // so, before calling that, record aContext in mInputContext. mInputContext = aContext; switch (mInputContext.mIMEState.mEnabled) { case IMEState::ENABLED: case IMEState::PASSWORD: case IMEState::PLUGIN: SetSoftwareKeyboardState(true, aAction); break; default: SetSoftwareKeyboardState(false, aAction); break; } } NS_IMETHODIMP_(InputContext) nsWindow::GetInputContext() { mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED; // Our qt widget looks like using only one context per process. // However, it's better to set the context's pointer. mInputContext.mNativeIMEContext = qApp->inputMethod(); return mInputContext; } NS_IMETHODIMP nsWindow::ReparentNativeWidget(nsIWidget *aNewParent) { NS_PRECONDITION(aNewParent, ""); MozQWidget* newParent = static_cast(aNewParent->GetNativeData(NS_NATIVE_WINDOW)); NS_ASSERTION(newParent, "Parent widget has a null native window handle"); if (mWidget) { mWidget->setParent(newParent); } return NS_OK; } NS_IMETHODIMP nsWindow::MakeFullScreen(bool aFullScreen) { NS_ENSURE_TRUE(mWidget, NS_ERROR_FAILURE); if (aFullScreen) { if (mSizeMode != nsSizeMode_Fullscreen) { mLastSizeMode = mSizeMode; } mSizeMode = nsSizeMode_Fullscreen; mWidget->showFullScreen(); } else { mSizeMode = mLastSizeMode; switch (mSizeMode) { case nsSizeMode_Maximized: mWidget->showMaximized(); break; case nsSizeMode_Minimized: mWidget->showMinimized(); break; case nsSizeMode_Normal: mWidget->showNormal(); break; default: mWidget->showNormal(); break; } } NS_ASSERTION(mLastSizeMode != nsSizeMode_Fullscreen, "mLastSizeMode should never be fullscreen"); return nsBaseWidget::MakeFullScreen(aFullScreen); } LayerManager* nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager, LayersBackend aBackendHint, LayerManagerPersistence aPersistence, bool* aAllowRetaining) { if (!mLayerManager && eTransparencyTransparent == GetTransparencyMode()) { mLayerManager = CreateBasicLayerManager(); } return nsBaseWidget::GetLayerManager(aShadowManager, aBackendHint, aPersistence, aAllowRetaining); } void nsWindow::UserActivity() { if (!mIdleService) { mIdleService = do_GetService("@mozilla.org/widget/idleservice;1"); } if (mIdleService) { mIdleService->ResetIdleTimeOut(0); } } uint32_t nsWindow::GetGLFrameBufferFormat() { if (mLayerManager && mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL) { return LOCAL_GL_RGB; } return LOCAL_GL_NONE; } TemporaryRef nsWindow::StartRemoteDrawing() { if (!mWidget) { return nullptr; } #ifdef MOZ_X11 Display* dpy = gfxQtPlatform::GetXDisplay(mWidget); Screen* screen = DefaultScreenOfDisplay(dpy); Visual* defaultVisual = DefaultVisualOfScreen(screen); gfxASurface* surf = new gfxXlibSurface(dpy, mWidget->winId(), defaultVisual, gfxIntSize(mWidget->width(), mWidget->height())); IntSize size(surf->GetSize().width, surf->GetSize().height); if (size.width <= 0 || size.height <= 0) { return nullptr; } return gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(surf, size); #else return nullptr; #endif } NS_IMETHODIMP nsWindow::SetCursor(nsCursor aCursor) { if (mCursor == aCursor && !mUpdateCursor) { return NS_OK; } mUpdateCursor = false; mCursor = aCursor; if (mWidget) { mWidget->SetCursor(mCursor); } return NS_OK; } NS_IMETHODIMP nsWindow::SetTitle(const nsAString& aTitle) { QString qStr(QString::fromUtf16((const ushort*)aTitle.BeginReading(), aTitle.Length())); mWidget->setTitle(qStr); return NS_OK; } // EVENTS void nsWindow::OnPaint() { LOGDRAW(("nsWindow::%s [%p]\n", __FUNCTION__, (void *)this)); nsIWidgetListener* listener = mAttachedWidgetListener ? mAttachedWidgetListener : mWidgetListener; if (!listener) { return; } listener->WillPaintWindow(this); switch (GetLayerManager()->GetBackendType()) { case mozilla::layers::LayersBackend::LAYERS_CLIENT: { nsIntRegion region(nsIntRect(0, 0, mWidget->width(), mWidget->height())); listener->PaintWindow(this, region); break; } default: NS_ERROR("Invalid layer manager"); } listener->DidPaintWindow(); } nsEventStatus nsWindow::moveEvent(QMoveEvent* aEvent) { LOG(("configure event [%p] %d %d\n", (void *)this, aEvent->pos().x(), aEvent->pos().y())); // can we shortcut? if (!mWidget || !mWidgetListener) return nsEventStatus_eIgnore; if ((mBounds.x == aEvent->pos().x() && mBounds.y == aEvent->pos().y())) { return nsEventStatus_eIgnore; } NotifyWindowMoved(aEvent->pos().x(), aEvent->pos().y()); return nsEventStatus_eConsumeNoDefault; } nsEventStatus nsWindow::resizeEvent(QResizeEvent* aEvent) { nsIntRect rect; // Generate XPFE resize event GetBounds(rect); rect.width = aEvent->size().width(); rect.height = aEvent->size().height(); mBounds.width = rect.width; mBounds.height = rect.height; nsEventStatus status; DispatchResizeEvent(rect, status); return status; } nsEventStatus nsWindow::mouseMoveEvent(QMouseEvent* aEvent) { UserActivity(); mMoveEvent.pos = aEvent->pos(); mMoveEvent.modifiers = aEvent->modifiers(); mMoveEvent.needDispatch = true; DispatchMotionToMainThread(); return nsEventStatus_eIgnore; } static void InitMouseEvent(WidgetMouseEvent& aMouseEvent, QMouseEvent* aEvent, int aClickCount) { aMouseEvent.refPoint.x = nscoord(aEvent->pos().x()); aMouseEvent.refPoint.y = nscoord(aEvent->pos().y()); aMouseEvent.InitBasicModifiers(aEvent->modifiers() & Qt::ControlModifier, aEvent->modifiers() & Qt::AltModifier, aEvent->modifiers() & Qt::ShiftModifier, aEvent->modifiers() & Qt::MetaModifier); aMouseEvent.clickCount = aClickCount; switch (aEvent->button()) { case Qt::LeftButton: aMouseEvent.button = WidgetMouseEvent::eLeftButton; break; case Qt::RightButton: aMouseEvent.button = WidgetMouseEvent::eRightButton; break; case Qt::MiddleButton: aMouseEvent.button = WidgetMouseEvent::eMiddleButton; break; default: break; } } static bool IsAcceptedButton(Qt::MouseButton button) { switch (button) { case Qt::LeftButton: case Qt::RightButton: case Qt::MiddleButton: return true; default: return false; } } nsEventStatus nsWindow::mousePressEvent(QMouseEvent* aEvent) { // The user has done something. UserActivity(); QPoint pos = aEvent->pos(); // we check against the widgets geometry, so use parent coordinates // for the check if (mWidget) pos = mWidget->mapToGlobal(pos); if (CheckForRollup(pos.x(), pos.y(), false)) return nsEventStatus_eIgnore; if (!IsAcceptedButton(aEvent->button())) { if (aEvent->button() == Qt::BackButton) return DispatchCommandEvent(nsGkAtoms::Back); if (aEvent->button() == Qt::ForwardButton) return DispatchCommandEvent(nsGkAtoms::Forward); return nsEventStatus_eIgnore; } WidgetMouseEvent event(true, NS_MOUSE_BUTTON_DOWN, this, WidgetMouseEvent::eReal); InitMouseEvent(event, aEvent, 1); nsEventStatus status = DispatchEvent(&event); // Right click on linux should also pop up a context menu. if (event.button == WidgetMouseEvent::eRightButton && MOZ_LIKELY(!mIsDestroyed)) { WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, WidgetMouseEvent::eReal); InitMouseEvent(contextMenuEvent, aEvent, 1); DispatchEvent(&contextMenuEvent, status); } return status; } nsEventStatus nsWindow::mouseReleaseEvent(QMouseEvent* aEvent) { // The user has done something. UserActivity(); if (!IsAcceptedButton(aEvent->button())) return nsEventStatus_eIgnore; WidgetMouseEvent event(true, NS_MOUSE_BUTTON_UP, this, WidgetMouseEvent::eReal); InitMouseEvent(event, aEvent, 1); return DispatchEvent(&event); } nsEventStatus nsWindow::mouseDoubleClickEvent(QMouseEvent* aEvent) { // The user has done something. UserActivity(); if (!IsAcceptedButton(aEvent->button())) return nsEventStatus_eIgnore; WidgetMouseEvent event(true, NS_MOUSE_DOUBLECLICK, this, WidgetMouseEvent::eReal); InitMouseEvent(event, aEvent, 2); return DispatchEvent(&event); } nsEventStatus nsWindow::focusInEvent(QFocusEvent* aEvent) { LOGFOCUS(("OnFocusInEvent [%p]\n", (void *)this)); if (!mWidget) { return nsEventStatus_eIgnore; } DispatchActivateEventOnTopLevelWindow(); LOGFOCUS(("Events sent from focus in event [%p]\n", (void *)this)); return nsEventStatus_eIgnore; } nsEventStatus nsWindow::focusOutEvent(QFocusEvent* aEvent) { LOGFOCUS(("OnFocusOutEvent [%p]\n", (void *)this)); if (!mWidget) { return nsEventStatus_eIgnore; } DispatchDeactivateEventOnTopLevelWindow(); LOGFOCUS(("Done with container focus out [%p]\n", (void *)this)); return nsEventStatus_eIgnore; } nsEventStatus nsWindow::keyPressEvent(QKeyEvent* aEvent) { LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this)); // The user has done something. UserActivity(); if (aEvent->key() == Qt::Key_AltGr) { sAltGrModifier = true; } CodeNameIndex codeNameIndex = ScanCodeToDOMCodeNameIndex(aEvent->nativeScanCode()); #ifdef MOZ_X11 // before we dispatch a key, check if it's the context menu key. // If so, send a context menu key event instead. if (isContextMenuKeyEvent(aEvent)) { WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, WidgetMouseEvent::eReal, WidgetMouseEvent::eContextMenuKey); //keyEventToContextMenuEvent(&event, &contextMenuEvent); return DispatchEvent(&contextMenuEvent); } uint32_t domCharCode = 0; uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key()); // get keymap and modifier map from the Xserver Display *display = gfxQtPlatform::GetXDisplay(mWidget); int x_min_keycode = 0, x_max_keycode = 0, xkeysyms_per_keycode; XDisplayKeycodes(display, &x_min_keycode, &x_max_keycode); XModifierKeymap *xmodmap = XGetModifierMapping(display); if (!xmodmap) return nsEventStatus_eIgnore; KeySym *xkeymap = XGetKeyboardMapping(display, x_min_keycode, x_max_keycode - x_min_keycode, &xkeysyms_per_keycode); if (!xkeymap) { XFreeModifiermap(xmodmap); return nsEventStatus_eIgnore; } // create modifier masks qint32 shift_mask = 0, shift_lock_mask = 0, caps_lock_mask = 0, num_lock_mask = 0; for (int i = 0; i < 8 * xmodmap->max_keypermod; ++i) { qint32 maskbit = 1 << (i / xmodmap->max_keypermod); KeyCode modkeycode = xmodmap->modifiermap[i]; if (modkeycode == NoSymbol) { continue; } quint32 mapindex = (modkeycode - x_min_keycode) * xkeysyms_per_keycode; for (int j = 0; j < xkeysyms_per_keycode; ++j) { KeySym modkeysym = xkeymap[mapindex + j]; switch (modkeysym) { case XK_Num_Lock: num_lock_mask |= maskbit; break; case XK_Caps_Lock: caps_lock_mask |= maskbit; break; case XK_Shift_Lock: shift_lock_mask |= maskbit; break; case XK_Shift_L: case XK_Shift_R: shift_mask |= maskbit; break; } } } // indicate whether is down or not bool shift_state = ((shift_mask & aEvent->nativeModifiers()) != 0) ^ (bool)(shift_lock_mask & aEvent->nativeModifiers()); bool capslock_state = (bool)(caps_lock_mask & aEvent->nativeModifiers()); // try to find a keysym that we can translate to a DOMKeyCode // this is needed because some of Qt's keycodes cannot be translated // TODO: use US keyboard keymap instead of localised keymap if (!domKeyCode && aEvent->nativeScanCode() >= (quint32)x_min_keycode && aEvent->nativeScanCode() <= (quint32)x_max_keycode) { int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode; for(int i = 0; (i < xkeysyms_per_keycode) && (domKeyCode == (quint32)NoSymbol); ++i) { domKeyCode = QtKeyCodeToDOMKeyCode(xkeymap[index + i]); } } // store character in domCharCode if (aEvent->text().length() && aEvent->text()[0].isPrint()) domCharCode = (int32_t) aEvent->text()[0].unicode(); KeyNameIndex keyNameIndex = domCharCode ? KEY_NAME_INDEX_PrintableKey : QtKeyCodeToDOMKeyNameIndex(aEvent->key()); // If the key isn't autorepeat, we need to send the initial down event if (!aEvent->isAutoRepeat() && !IsKeyDown(domKeyCode)) { // send the key down event SetKeyDownFlag(domKeyCode); WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this); InitKeyEvent(downEvent, aEvent); downEvent.keyCode = domKeyCode; downEvent.mKeyNameIndex = keyNameIndex; downEvent.mCodeNameIndex = codeNameIndex; nsEventStatus status = DispatchEvent(&downEvent); // DispatchEvent can Destroy us (bug 378273) if (MOZ_UNLIKELY(mIsDestroyed)) { qWarning() << "Returning[" << __LINE__ << "]: " << "Window destroyed"; return status; } // If prevent default on keydown, don't dispatch keypress event if (status == nsEventStatus_eConsumeNoDefault) { return nsEventStatus_eConsumeNoDefault; } } // Don't pass modifiers as NS_KEY_PRESS events. // Instead of selectively excluding some keys from NS_KEY_PRESS events, // we instead selectively include (as per MSDN spec // ( http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keypress%28VS.71%29.aspx ); // no official spec covers KeyPress events). if (aEvent->key() == Qt::Key_Shift || aEvent->key() == Qt::Key_Control || aEvent->key() == Qt::Key_Meta || aEvent->key() == Qt::Key_Alt || aEvent->key() == Qt::Key_AltGr) { return nsEventStatus_eIgnore; } // Look for specialized app-command keys switch (aEvent->key()) { case Qt::Key_Back: return DispatchCommandEvent(nsGkAtoms::Back); case Qt::Key_Forward: return DispatchCommandEvent(nsGkAtoms::Forward); case Qt::Key_Refresh: return DispatchCommandEvent(nsGkAtoms::Reload); case Qt::Key_Stop: return DispatchCommandEvent(nsGkAtoms::Stop); case Qt::Key_Search: return DispatchCommandEvent(nsGkAtoms::Search); case Qt::Key_Favorites: return DispatchCommandEvent(nsGkAtoms::Bookmarks); case Qt::Key_HomePage: return DispatchCommandEvent(nsGkAtoms::Home); case Qt::Key_Copy: case Qt::Key_F16: // F16, F20, F18, F14 are old keysyms for Copy Cut Paste Undo return DispatchContentCommandEvent(NS_CONTENT_COMMAND_COPY); case Qt::Key_Cut: case Qt::Key_F20: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_CUT); case Qt::Key_Paste: case Qt::Key_F18: case Qt::Key_F9: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_PASTE); case Qt::Key_F14: return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO); } // Qt::Key_Redo and Qt::Key_Undo are not available yet. if (aEvent->nativeVirtualKey() == 0xff66) { return DispatchContentCommandEvent(NS_CONTENT_COMMAND_REDO); } if (aEvent->nativeVirtualKey() == 0xff65) { return DispatchContentCommandEvent(NS_CONTENT_COMMAND_UNDO); } WidgetKeyboardEvent event(true, NS_KEY_PRESS, this); InitKeyEvent(event, aEvent); // If there is no charcode attainable from the text, try to // generate it from the keycode. Check shift state for case // Also replace the charcode if ControlModifier is the only // pressed Modifier if ((!domCharCode) && (QGuiApplication::keyboardModifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) { // get a character from X11 key map KeySym keysym = aEvent->nativeVirtualKey(); if (keysym) { domCharCode = (uint32_t) keysym2ucs(keysym); if (domCharCode == -1 || !QChar((quint32)domCharCode).isPrint()) { domCharCode = 0; } } // if Ctrl is pressed and domCharCode is not a ASCII character if (domCharCode > 0xFF && (QGuiApplication::keyboardModifiers() & Qt::ControlModifier)) { // replace Unicode character int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode; for (int i = 0; i < xkeysyms_per_keycode; ++i) { if (xkeymap[index + i] <= 0xFF && !shift_state) { domCharCode = (uint32_t) QChar::toLower((uint) xkeymap[index + i]); break; } } } } else { // The key event should cause a character input. // At that time, we need to reset the modifiers // because nsEditor will not accept a key event // for text input if one or more modifiers are set. event.modifiers &= ~(MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_META); } KeySym keysym = NoSymbol; int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode; for (int i = 0; i < xkeysyms_per_keycode; ++i) { if (xkeymap[index + i] == aEvent->nativeVirtualKey()) { if ((i % 2) == 0) { // shifted char keysym = xkeymap[index + i + 1]; break; } else { // unshifted char keysym = xkeymap[index + i - 1]; break; } } if (xkeysyms_per_keycode - 1 == i) { qWarning() << "Symbol '" << aEvent->nativeVirtualKey() << "' not found"; } } QChar unshiftedChar(domCharCode); long ucs = keysym2ucs(keysym); ucs = ucs == -1 ? 0 : ucs; QChar shiftedChar((uint)ucs); // append alternativeCharCodes if modifier is pressed // append an additional alternativeCharCodes if domCharCode is not a Latin character // and if one of these modifiers is pressed (i.e. Ctrl, Alt, Meta) if (domCharCode && (QGuiApplication::keyboardModifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) { event.charCode = domCharCode; event.keyCode = 0; AlternativeCharCode altCharCode(0, 0); // if character has a lower and upper representation if ((unshiftedChar.isUpper() || unshiftedChar.isLower()) && unshiftedChar.toLower() == shiftedChar.toLower()) { if (shift_state ^ capslock_state) { altCharCode.mUnshiftedCharCode = (uint32_t) QChar::toUpper((uint)domCharCode); altCharCode.mShiftedCharCode = (uint32_t) QChar::toLower((uint)domCharCode); } else { altCharCode.mUnshiftedCharCode = (uint32_t) QChar::toLower((uint)domCharCode); altCharCode.mShiftedCharCode = (uint32_t) QChar::toUpper((uint)domCharCode); } } else { altCharCode.mUnshiftedCharCode = (uint32_t) unshiftedChar.unicode(); altCharCode.mShiftedCharCode = (uint32_t) shiftedChar.unicode(); } // append alternative char code to event if ((altCharCode.mUnshiftedCharCode && altCharCode.mUnshiftedCharCode != domCharCode) || (altCharCode.mShiftedCharCode && altCharCode.mShiftedCharCode != domCharCode)) { event.alternativeCharCodes.AppendElement(altCharCode); } // check if the alternative char codes are latin-1 if (altCharCode.mUnshiftedCharCode > 0xFF || altCharCode.mShiftedCharCode > 0xFF) { altCharCode.mUnshiftedCharCode = altCharCode.mShiftedCharCode = 0; // find latin char for keycode KeySym keysym = NoSymbol; int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode; // find first shifted and unshifted Latin-Char in XKeyMap for (int i = 0; i < xkeysyms_per_keycode; ++i) { keysym = xkeymap[index + i]; if (keysym && keysym <= 0xFF) { if ((shift_state && (i % 2 == 1)) || (!shift_state && (i % 2 == 0))) { altCharCode.mUnshiftedCharCode = altCharCode.mUnshiftedCharCode ? altCharCode.mUnshiftedCharCode : keysym; } else { altCharCode.mShiftedCharCode = altCharCode.mShiftedCharCode ? altCharCode.mShiftedCharCode : keysym; } if (altCharCode.mUnshiftedCharCode && altCharCode.mShiftedCharCode) { break; } } } if (altCharCode.mUnshiftedCharCode || altCharCode.mShiftedCharCode) { event.alternativeCharCodes.AppendElement(altCharCode); } } } else { event.charCode = domCharCode; } if (xmodmap) { XFreeModifiermap(xmodmap); } if (xkeymap) { XFree(xkeymap); } event.keyCode = domCharCode ? 0 : domKeyCode; event.mKeyNameIndex = keyNameIndex; event.mCodeNameIndex = codeNameIndex; // send the key press event return DispatchEvent(&event); #else //:TODO: fix shortcuts hebrew for non X11, //see Bug 562195##51 // before we dispatch a key, check if it's the context menu key. // If so, send a context menu key event instead. if (isContextMenuKeyEvent(aEvent)) { WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this, WidgetMouseEvent::eReal, WidgetMouseEvent::eContextMenuKey); //keyEventToContextMenuEvent(&event, &contextMenuEvent); return DispatchEvent(&contextMenuEvent); } uint32_t domCharCode = 0; uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key()); if (aEvent->text().length() && aEvent->text()[0].isPrint()) { domCharCode = (int32_t) aEvent->text()[0].unicode(); } KeyNameIndex keyNameIndex = domCharCode ? KEY_NAME_INDEX_PrintableKey : QtKeyCodeToDOMKeyNameIndex(aEvent->key()); // If the key isn't autorepeat, we need to send the initial down event if (!aEvent->isAutoRepeat() && !IsKeyDown(domKeyCode)) { // send the key down event SetKeyDownFlag(domKeyCode); WidgetKeyboardEvent downEvent(true, NS_KEY_DOWN, this); InitKeyEvent(downEvent, aEvent); downEvent.keyCode = domKeyCode; downEvent.mKeyNameIndex = keyNameIndex; downEvent.mCodeNameIndex = codeNameIndex; nsEventStatus status = DispatchEvent(&downEvent); // If prevent default on keydown, don't dispatch keypress event if (status == nsEventStatus_eConsumeNoDefault) { return nsEventStatus_eConsumeNoDefault; } } WidgetKeyboardEvent event(true, NS_KEY_PRESS, this); InitKeyEvent(event, aEvent); event.charCode = domCharCode; event.keyCode = domCharCode ? 0 : domKeyCode; event.mKeyNameIndex = keyNameIndex; event.mCodeNameIndex = codeNameIndex; // send the key press event return DispatchEvent(&event); #endif } nsEventStatus nsWindow::keyReleaseEvent(QKeyEvent* aEvent) { LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this)); // The user has done something. UserActivity(); if (isContextMenuKeyEvent(aEvent)) { // er, what do we do here? DoDefault or NoDefault? return nsEventStatus_eConsumeDoDefault; } uint32_t domKeyCode = QtKeyCodeToDOMKeyCode(aEvent->key()); #ifdef MOZ_X11 if (!domKeyCode) { // get keymap from the Xserver Display *display = gfxQtPlatform::GetXDisplay(mWidget); int x_min_keycode = 0, x_max_keycode = 0, xkeysyms_per_keycode; XDisplayKeycodes(display, &x_min_keycode, &x_max_keycode); KeySym *xkeymap = XGetKeyboardMapping(display, x_min_keycode, x_max_keycode - x_min_keycode, &xkeysyms_per_keycode); if (aEvent->nativeScanCode() >= (quint32)x_min_keycode && aEvent->nativeScanCode() <= (quint32)x_max_keycode) { int index = (aEvent->nativeScanCode() - x_min_keycode) * xkeysyms_per_keycode; for(int i = 0; (i < xkeysyms_per_keycode) && (domKeyCode == (quint32)NoSymbol); ++i) { domKeyCode = QtKeyCodeToDOMKeyCode(xkeymap[index + i]); } } if (xkeymap) { XFree(xkeymap); } } #endif // MOZ_X11 // send the key event as a key up event WidgetKeyboardEvent event(true, NS_KEY_UP, this); InitKeyEvent(event, aEvent); if (aEvent->key() == Qt::Key_AltGr) { sAltGrModifier = false; } event.keyCode = domKeyCode; event.mKeyNameIndex = (aEvent->text().length() && aEvent->text()[0].isPrint()) ? KEY_NAME_INDEX_PrintableKey : QtKeyCodeToDOMKeyNameIndex(aEvent->key()); event.mCodeNameIndex = ScanCodeToDOMCodeNameIndex(aEvent->nativeScanCode()); // unset the key down flag ClearKeyDownFlag(event.keyCode); return DispatchEvent(&event); } nsEventStatus nsWindow::wheelEvent(QWheelEvent* aEvent) { // check to see if we should rollup WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this); wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE; // negative values for aEvent->delta indicate downward scrolling; // this is opposite Gecko usage. // TODO: Store the unused delta values due to fraction round and add it // to next event. The stored values should be reset by other // direction scroll event. int32_t delta = (int)(aEvent->delta() / WHEEL_DELTA) * -3; switch (aEvent->orientation()) { case Qt::Vertical: wheelEvent.deltaY = wheelEvent.lineOrPageDeltaY = delta; break; case Qt::Horizontal: wheelEvent.deltaX = wheelEvent.lineOrPageDeltaX = delta; break; default: Q_ASSERT(0); break; } wheelEvent.refPoint.x = nscoord(aEvent->pos().x()); wheelEvent.refPoint.y = nscoord(aEvent->pos().y()); wheelEvent.InitBasicModifiers(aEvent->modifiers() & Qt::ControlModifier, aEvent->modifiers() & Qt::AltModifier, aEvent->modifiers() & Qt::ShiftModifier, aEvent->modifiers() & Qt::MetaModifier); wheelEvent.time = 0; return DispatchEvent(&wheelEvent); } nsEventStatus nsWindow::showEvent(QShowEvent *) { LOG(("%s [%p]\n", __PRETTY_FUNCTION__,(void *)this)); mVisible = true; return nsEventStatus_eConsumeDoDefault; } nsEventStatus nsWindow::hideEvent(QHideEvent *) { LOG(("%s [%p]\n", __PRETTY_FUNCTION__,(void *)this)); mVisible = false; return nsEventStatus_eConsumeDoDefault; } nsEventStatus nsWindow::touchEvent(QTouchEvent* aEvent) { return nsEventStatus_eIgnore; } nsEventStatus nsWindow::tabletEvent(QTabletEvent* aEvent) { LOGFOCUS(("nsWindow::%s [%p]\n", __FUNCTION__, (void *)this)); return nsEventStatus_eIgnore; } // Helpers nsEventStatus nsWindow::DispatchEvent(WidgetGUIEvent* aEvent) { nsEventStatus status; DispatchEvent(aEvent, status); return status; } void nsWindow::DispatchActivateEvent(void) { if (mWidgetListener) { mWidgetListener->WindowActivated(); } } void nsWindow::DispatchDeactivateEvent(void) { if (mWidgetListener) { mWidgetListener->WindowDeactivated(); } } void nsWindow::DispatchActivateEventOnTopLevelWindow(void) { nsWindow* topLevelWindow = static_cast(GetTopLevelWidget()); if (topLevelWindow != nullptr) { topLevelWindow->DispatchActivateEvent(); } } void nsWindow::DispatchDeactivateEventOnTopLevelWindow(void) { nsWindow* topLevelWindow = static_cast(GetTopLevelWidget()); if (topLevelWindow != nullptr) { topLevelWindow->DispatchDeactivateEvent(); } } void nsWindow::DispatchResizeEvent(nsIntRect &aRect, nsEventStatus &aStatus) { aStatus = nsEventStatus_eIgnore; if (mWidgetListener && mWidgetListener->WindowResized(this, aRect.width, aRect.height)) { aStatus = nsEventStatus_eConsumeNoDefault; } } ///////////////////////////////////// OLD GECKO ECENTS need to Sort /////////////////// /* static */ bool isContextMenuKeyEvent(const QKeyEvent *qe) { uint32_t kc = QtKeyCodeToDOMKeyCode(qe->key()); if (qe->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier)) { return false; } bool isShift = qe->modifiers() & Qt::ShiftModifier; return (kc == NS_VK_F10 && isShift) || (kc == NS_VK_CONTEXT_MENU && !isShift); } /* static */void InitKeyEvent(WidgetKeyboardEvent &aEvent, QKeyEvent *aQEvent) { aEvent.InitBasicModifiers(aQEvent->modifiers() & Qt::ControlModifier, aQEvent->modifiers() & Qt::AltModifier, aQEvent->modifiers() & Qt::ShiftModifier, aQEvent->modifiers() & Qt::MetaModifier); aEvent.mIsRepeat = (aEvent.message == NS_KEY_DOWN || aEvent.message == NS_KEY_PRESS) && aQEvent->isAutoRepeat(); aEvent.time = 0; if (sAltGrModifier) { aEvent.modifiers |= (MODIFIER_CONTROL | MODIFIER_ALT); } // The transformations above and in qt for the keyval are not invertible // so link to the QKeyEvent (which will vanish soon after return from the // event callback) to give plugins access to hardware_keycode and state. // (An XEvent would be nice but the QKeyEvent is good enough.) aEvent.pluginEvent = (void *)aQEvent; } NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget, nsISupportsWeakReference) void nsWindow::ClearCachedResources() { if (mLayerManager && mLayerManager->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_BASIC) { mLayerManager->ClearCachedResources(); } for (nsIWidget* kid = mFirstChild; kid; ) { nsIWidget* next = kid->GetNextSibling(); static_cast(kid)->ClearCachedResources(); kid = next; } } NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent) { NS_ENSURE_ARG_POINTER(aNewParent); nsCOMPtr kungFuDeathGrip(this); nsIWidget* parent = GetParent(); if (parent) { parent->RemoveChild(this); } ReparentNativeWidget(aNewParent); aNewParent->AddChild(this); return NS_OK; } NS_IMETHODIMP nsWindow::SetModal(bool aModal) { LOG(("nsWindow::SetModal [%p] %d, widget[%p]\n", (void *)this, aModal, mWidget)); if (mWidget) { mWidget->setModality(aModal ? Qt::WindowModal : Qt::NonModal); } return NS_OK; } NS_IMETHODIMP nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, nsIWidget *aWidget, bool aActivate) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::SetSizeMode(int32_t aMode) { nsresult rv; LOG(("nsWindow::SetSizeMode [%p] %d\n", (void *)this, aMode)); if (aMode != nsSizeMode_Minimized) { mWidget->requestActivate(); } // Save the requested state. rv = nsBaseWidget::SetSizeMode(aMode); // return if there's no shell or our current state is the same as // the mode we were just set to. if (!mWidget || mSizeState == mSizeMode) { return rv; } switch (aMode) { case nsSizeMode_Maximized: mWidget->showMaximized(); break; case nsSizeMode_Minimized: mWidget->showMinimized(); break; case nsSizeMode_Fullscreen: mWidget->showFullScreen(); break; default: // nsSizeMode_Normal, really. mWidget->show(); break; } mSizeState = mSizeMode; return rv; } // Helper function to recursively find the first parent item that // is still visible (QGraphicsItem can be hidden even if they are // set to visible if one of their ancestors is invisible) /* static */ void find_first_visible_parent(QWindow* aItem, QWindow*& aVisibleItem) { NS_ENSURE_TRUE_VOID(aItem); aVisibleItem = nullptr; QWindow* parItem = nullptr; while (!aVisibleItem) { if (aItem->isVisible()) { aVisibleItem = aItem; } else { parItem = aItem->parent(); if (parItem) { aItem = parItem; } else { aItem->setVisible(true); aVisibleItem = aItem; } } } } NS_IMETHODIMP nsWindow::GetScreenBounds(nsIntRect &aRect) { aRect = nsIntRect(nsIntPoint(0, 0), mBounds.Size()); if (mIsTopLevel) { QPoint pos = mWidget->position(); aRect.MoveTo(pos.x(), pos.y()); } else { aRect.MoveTo(WidgetToScreenOffset()); } LOG(("GetScreenBounds %d %d | %d %d | %d %d\n", aRect.x, aRect.y, mBounds.width, mBounds.height, aRect.width, aRect.height)); return NS_OK; } NS_IMETHODIMP nsWindow::SetIcon(const nsAString& aIconSpec) { if (!mWidget) return NS_OK; nsCOMPtr iconFile; nsAutoCString path; nsTArray iconList; // Look for icons with the following suffixes appended to the base name. // The last two entries (for the old XPM format) will be ignored unless // no icons are found using the other suffixes. XPM icons are depricated. const char extensions[6][7] = { ".png", "16.png", "32.png", "48.png", ".xpm", "16.xpm" }; for (uint32_t i = 0; i < ArrayLength(extensions); i++) { // Don't bother looking for XPM versions if we found a PNG. if (i == ArrayLength(extensions) - 2 && iconList.Length()) break; nsAutoString extension; extension.AppendASCII(extensions[i]); ResolveIconName(aIconSpec, extension, getter_AddRefs(iconFile)); if (iconFile) { iconFile->GetNativePath(path); iconList.AppendElement(path); } } // leave the default icon intact if no matching icons were found if (iconList.Length() == 0) return NS_OK; return SetWindowIconList(iconList); } NS_IMETHODIMP nsWindow::CaptureMouse(bool aCapture) { LOG(("CaptureMouse %p\n", (void *)this)); if (!mWidget) return NS_OK; mWidget->setMouseGrabEnabled(aCapture); return NS_OK; } bool nsWindow::CheckForRollup(double aMouseX, double aMouseY, bool aIsWheel) { nsIRollupListener* rollupListener = GetActiveRollupListener(); nsCOMPtr rollupWidget; if (rollupListener) { rollupWidget = rollupListener->GetRollupWidget(); } if (!rollupWidget) { nsBaseWidget::gRollupListener = nullptr; return false; } bool retVal = false; MozQWidget *currentPopup = (MozQWidget *)rollupWidget->GetNativeData(NS_NATIVE_WINDOW); if (!is_mouse_in_window(currentPopup, aMouseX, aMouseY)) { bool rollup = true; if (aIsWheel) { rollup = rollupListener->ShouldRollupOnMouseWheelEvent(); retVal = true; } // if we're dealing with menus, we probably have submenus and // we don't want to rollup if the clickis in a parent menu of // the current submenu uint32_t popupsToRollup = UINT32_MAX; if (rollupListener) { nsAutoTArray widgetChain; uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain); for (uint32_t i=0; iGetNativeData(NS_NATIVE_WINDOW); if (is_mouse_in_window(currWindow, aMouseX, aMouseY)) { if (i < sameTypeCount) { rollup = false; } else { popupsToRollup = sameTypeCount; } break; } } // foreach parent menu widget } // if rollup listener knows about menus // if we've determined that we should still rollup, do it. if (rollup) { nsIntPoint pos(aMouseX, aMouseY); retVal = rollupListener->Rollup(popupsToRollup, &pos, nullptr); } } return retVal; } /* static */ bool is_mouse_in_window (MozQWidget* aWindow, double aMouseX, double aMouseY) { return aWindow->geometry().contains(aMouseX, aMouseY); } NS_IMETHODIMP nsWindow::GetAttention(int32_t aCycleCount) { LOG(("nsWindow::GetAttention [%p]\n", (void *)this)); return NS_ERROR_NOT_IMPLEMENTED; } nsEventStatus nsWindow::OnCloseEvent(QCloseEvent *aEvent) { if (!mWidgetListener) return nsEventStatus_eIgnore; mWidgetListener->RequestWindowClose(this); return nsEventStatus_eConsumeNoDefault; } inline bool is_latin_shortcut_key(quint32 aKeyval) { return ((Qt::Key_0 <= aKeyval && aKeyval <= Qt::Key_9) || (Qt::Key_A <= aKeyval && aKeyval <= Qt::Key_Z)); } nsEventStatus nsWindow::DispatchCommandEvent(nsIAtom* aCommand) { WidgetCommandEvent event(true, nsGkAtoms::onAppCommand, aCommand, this); nsEventStatus status; DispatchEvent(&event, status); return status; } nsEventStatus nsWindow::DispatchContentCommandEvent(int32_t aMsg) { WidgetContentCommandEvent event(true, aMsg, this); nsEventStatus status; DispatchEvent(&event, status); return status; } static void GetBrandName(nsXPIDLString& brandName) { nsCOMPtr bundleService = mozilla::services::GetStringBundleService(); nsCOMPtr bundle; if (bundleService) { bundleService->CreateBundle( "chrome://branding/locale/brand.properties", getter_AddRefs(bundle)); } if (bundle) { bundle->GetStringFromName( MOZ_UTF16("brandShortName"), getter_Copies(brandName)); } if (brandName.IsEmpty()) { brandName.AssignLiteral(MOZ_UTF16("Mozilla")); } } NS_IMETHODIMP nsWindow::SetWindowClass(const nsAString &xulWinType) { if (!mWidget) { return NS_ERROR_FAILURE; } nsXPIDLString brandName; GetBrandName(brandName); #ifdef MOZ_X11 XClassHint *class_hint = XAllocClassHint(); if (!class_hint) { return NS_ERROR_OUT_OF_MEMORY; } const char *role = nullptr; class_hint->res_name = ToNewCString(xulWinType); if (!class_hint->res_name) { XFree(class_hint); return NS_ERROR_OUT_OF_MEMORY; } class_hint->res_class = ToNewCString(brandName); if (!class_hint->res_class) { nsMemory::Free(class_hint->res_name); XFree(class_hint); return NS_ERROR_OUT_OF_MEMORY; } // Parse res_name into a name and role. Characters other than // [A-Za-z0-9_-] are converted to '_'. Anything after the first // colon is assigned to role; if there's no colon, assign the // whole thing to both role and res_name. for (char *c = class_hint->res_name; *c; c++) { if (':' == *c) { *c = 0; role = c + 1; } else if (!isascii(*c) || (!isalnum(*c) && ('_' != *c) && ('-' != *c))) *c = '_'; } class_hint->res_name[0] = toupper(class_hint->res_name[0]); if (!role) role = class_hint->res_name; QWindow *widget = mWidget; // If widget not show, handle might be null if (widget && widget->winId()) { XSetClassHint(gfxQtPlatform::GetXDisplay(widget), widget->winId(), class_hint); } nsMemory::Free(class_hint->res_class); nsMemory::Free(class_hint->res_name); XFree(class_hint); #endif return NS_OK; } void nsWindow::NativeResize(int32_t aWidth, int32_t aHeight, bool aRepaint) { LOG(("nsWindow::NativeResize [%p] %d %d\n", (void *)this, aWidth, aHeight)); mNeedsResize = false; mWidget->resize(aWidth, aHeight); if (aRepaint) { mWidget->renderLater(); } } void nsWindow::NativeResize(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight, bool aRepaint) { LOG(("nsWindow::NativeResize [%p] %d %d %d %d\n", (void *)this, aX, aY, aWidth, aHeight)); mNeedsResize = false; mNeedsMove = false; mWidget->setGeometry(aX, aY, aWidth, aHeight); if (aRepaint) { mWidget->renderLater(); } } void nsWindow::NativeShow(bool aAction) { if (aAction) { // On e10s, we never want the child process or plugin process // to go fullscreen because if we do the window because visible // do to disabled Qt-Xembed mWidget->show(); // unset our flag now that our window has been shown mNeedsShow = false; } else { mWidget->hide(); } } NS_IMETHODIMP nsWindow::SetHasTransparentBackground(bool aTransparent) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsWindow::GetHasTransparentBackground(bool& aTransparent) { aTransparent = mIsTransparent; return NS_OK; } void * nsWindow::SetupPluginPort(void) { NS_WARNING("Not implemented"); return nullptr; } nsresult nsWindow::SetWindowIconList(const nsTArray &aIconList) { QIcon icon; for (uint32_t i = 0; i < aIconList.Length(); ++i) { const char *path = aIconList[i].get(); LOG(("window [%p] Loading icon from %s\n", (void *)this, path)); icon.addFile(path); } mWidget->setIcon(icon); return NS_OK; } void nsWindow::SetDefaultIcon(void) { SetIcon(NS_LITERAL_STRING("default")); } void nsWindow::QWidgetDestroyed() { mWidget = nullptr; } NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide) { if (!mWidget) { // Nothing to hide return NS_ERROR_FAILURE; } // Sawfish, metacity, and presumably other window managers get // confused if we change the window decorations while the window // is visible. bool wasVisible = false; if (mWidget->isVisible()) { NativeShow(false); wasVisible = true; } if (wasVisible) { NativeShow(true); } return NS_OK; } ////////////////////////////////////////////////////////////////////// NS_IMETHODIMP_(bool) nsWindow::HasGLContext() { return false; } nsIWidget* nsWindow::GetParent(void) { return mParent; } float nsWindow::GetDPI() { return qApp->primaryScreen()->logicalDotsPerInch(); } void nsWindow::OnDestroy(void) { if (mOnDestroyCalled) { return; } mOnDestroyCalled = true; // release references to children and device context nsBaseWidget::OnDestroy(); // let go of our parent mParent = nullptr; nsCOMPtr kungFuDeathGrip = this; NotifyWindowDestroyed(); } bool nsWindow::AreBoundsSane(void) { if (mBounds.width > 0 && mBounds.height > 0) { return true; } return false; } void nsWindow::SetSoftwareKeyboardState(bool aOpen, const InputContextAction& aAction) { if (aOpen) { NS_ENSURE_TRUE_VOID(mInputContext.mIMEState.mEnabled != IMEState::DISABLED); // Ensure that opening the virtual keyboard is allowed for this specific // InputContext depending on the content.ime.strict.policy pref if (mInputContext.mIMEState.mEnabled != IMEState::PLUGIN && Preferences::GetBool("content.ime.strict_policy", false) && !aAction.ContentGotFocusByTrustedCause() && !aAction.UserMightRequestOpenVKB()) { return; } } if (aOpen) { qApp->inputMethod()->show(); } else { qApp->inputMethod()->hide(); } return; } void nsWindow::ProcessMotionEvent() { if (mMoveEvent.needDispatch) { WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, WidgetMouseEvent::eReal); event.refPoint.x = nscoord(mMoveEvent.pos.x()); event.refPoint.y = nscoord(mMoveEvent.pos.y()); event.InitBasicModifiers(mMoveEvent.modifiers & Qt::ControlModifier, mMoveEvent.modifiers & Qt::AltModifier, mMoveEvent.modifiers & Qt::ShiftModifier, mMoveEvent.modifiers & Qt::MetaModifier); event.clickCount = 0; DispatchEvent(&event); mMoveEvent.needDispatch = false; } mTimerStarted = false; }