Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/widget/windows/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 14 kB image not shown  

Quelle  nsNativeDragTarget.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 <stdio.h>
#include "nsIDragService.h"
#include "nsWidgetsCID.h"
#include "nsNativeDragTarget.h"
#include "nsDragService.h"
#include "nsINode.h"
#include "nsCOMPtr.h"

#include "nsIWidget.h"
#include "nsWindow.h"
#include "nsClipboard.h"
#include "KeyboardLayout.h"

#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/MouseEvents.h"

using namespace mozilla;
using namespace mozilla::widget;

// This is cached for Leave notification
static POINTL gDragLastPoint;

bool nsNativeDragTarget::gDragImageChanged = false;

/*
 * class nsNativeDragTarget
 */

nsNativeDragTarget::nsNativeDragTarget(nsIWidget* aWidget)
    : m_cRef(0),
      mEffectsAllowed(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK),
      mEffectsPreferred(DROPEFFECT_NONE),
      mTookOwnRef(false),
      mWidget(aWidget),
      mDropTargetHelper(nullptr) {
  mHWnd = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW);

  mDragService = do_GetService("@mozilla.org/widget/dragservice;1");
}

nsNativeDragTarget::~nsNativeDragTarget() {
  if (mDropTargetHelper) {
    mDropTargetHelper->Release();
    mDropTargetHelper = nullptr;
  }
}

// IUnknown methods - see iunknown.h for documentation
STDMETHODIMP
nsNativeDragTarget::QueryInterface(REFIID riid, void** ppv) {
  *ppv = nullptr;

  if (IID_IUnknown == riid || IID_IDropTarget == riid) *ppv = this;

  if (nullptr != *ppv) {
    ((LPUNKNOWN)*ppv)->AddRef();
    return S_OK;
  }

  return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG)
nsNativeDragTarget::AddRef(void) {
  ++m_cRef;
  NS_LOG_ADDREF(this, m_cRef, "nsNativeDragTarget"sizeof(*this));
  return m_cRef;
}

STDMETHODIMP_(ULONG) nsNativeDragTarget::Release(void) {
  --m_cRef;
  NS_LOG_RELEASE(this, m_cRef, "nsNativeDragTarget");
  if (0 != m_cRef) return m_cRef;

  delete this;
  return 0;
}

void nsNativeDragTarget::GetGeckoDragAction(DWORD grfKeyState,
                                            LPDWORD pdwEffect,
                                            uint32_t* aGeckoAction) {
  // If a window is disabled or a modal window is on top of it
  // (which implies it is disabled), then we should not allow dropping.
  if (!mWidget->IsEnabled()) {
    *pdwEffect = DROPEFFECT_NONE;
    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE;
    return;
  }

  // If the user explicitly uses a modifier key, they want the associated action
  // Shift + Control -> LINK, Shift -> MOVE, Ctrl -> COPY
  DWORD desiredEffect = DROPEFFECT_NONE;
  if ((grfKeyState & MK_CONTROL) && (grfKeyState & MK_SHIFT)) {
    desiredEffect = DROPEFFECT_LINK;
  } else if (grfKeyState & MK_SHIFT) {
    desiredEffect = DROPEFFECT_MOVE;
  } else if (grfKeyState & MK_CONTROL) {
    desiredEffect = DROPEFFECT_COPY;
  }

  // Determine the desired effect from what is allowed and preferred.
  if (!(desiredEffect &= mEffectsAllowed)) {
    // No modifier key effect is set which is also allowed, check
    // the preference of the data.
    desiredEffect = mEffectsPreferred & mEffectsAllowed;
    if (!desiredEffect) {
      // No preference is set, so just fall back to the allowed effect itself
      desiredEffect = mEffectsAllowed;
    }
  }

  // Otherwise we should specify the first available effect
  // from MOVE, COPY, or LINK.
  if (desiredEffect & DROPEFFECT_MOVE) {
    *pdwEffect = DROPEFFECT_MOVE;
    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE;
  } else if (desiredEffect & DROPEFFECT_COPY) {
    *pdwEffect = DROPEFFECT_COPY;
    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
  } else if (desiredEffect & DROPEFFECT_LINK) {
    *pdwEffect = DROPEFFECT_LINK;
    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
  } else {
    *pdwEffect = DROPEFFECT_NONE;
    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE;
  }
}

inline bool IsKeyDown(char key) { return GetKeyState(key) < 0; }

void nsNativeDragTarget::DispatchDragDropEvent(EventMessage aEventMessage,
                                               const POINTL& aPT) {
  WidgetDragEvent event(true, aEventMessage, mWidget);

  nsWindow* win = static_cast<nsWindow*>(mWidget);
  win->InitEvent(event);
  POINT cpos;

  cpos.x = aPT.x;
  cpos.y = aPT.y;

  if (mHWnd != nullptr) {
    ::ScreenToClient(mHWnd, &cpos);
    event.mRefPoint = LayoutDeviceIntPoint(cpos.x, cpos.y);
  } else {
    event.mRefPoint = LayoutDeviceIntPoint(0, 0);
  }

  ModifierKeyState modifierKeyState;
  modifierKeyState.InitInputEvent(event);

  nsDragSession* currSession =
      static_cast<nsDragSession*>(mDragService->GetCurrentSession(mWidget));
  if (currSession) {
    event.mInputSource = currSession->GetInputSource();
  } else {
    event.mInputSource = dom::MouseEvent_Binding::MOZ_SOURCE_MOUSE;
  }

  mWidget->DispatchInputEvent(&event);
}

void nsNativeDragTarget::ProcessDrag(EventMessage aEventMessage,
                                     DWORD grfKeyState, POINTL ptl,
                                     DWORD* pdwEffect) {
  // Before dispatching the event make sure we have the correct drop action set
  uint32_t geckoAction;
  GetGeckoDragAction(grfKeyState, pdwEffect, &geckoAction);

  // Set the current action into the Gecko specific type
  RefPtr<nsDragSession> currSession =
      static_cast<nsDragSession*>(mDragService->GetCurrentSession(mWidget));
  if (!currSession) {
    return;
  }

  currSession->SetDragAction(geckoAction);

  // Dispatch the event into Gecko
  DispatchDragDropEvent(aEventMessage, ptl);

  // If TakeChildProcessDragAction returns something other than
  // DRAGDROP_ACTION_UNINITIALIZED, it means that the last event was sent
  // to the child process and this event is also being sent to the child
  // process. In this case, use the last event's action instead.
  currSession->GetDragAction(&geckoAction);

  int32_t childDragAction = currSession->TakeChildProcessDragAction();
  if (childDragAction != nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
    geckoAction = childDragAction;
  }

  if (nsIDragService::DRAGDROP_ACTION_LINK & geckoAction) {
    *pdwEffect = DROPEFFECT_LINK;
  } else if (nsIDragService::DRAGDROP_ACTION_COPY & geckoAction) {
    *pdwEffect = DROPEFFECT_COPY;
  } else if (nsIDragService::DRAGDROP_ACTION_MOVE & geckoAction) {
    *pdwEffect = DROPEFFECT_MOVE;
  } else {
    *pdwEffect = DROPEFFECT_NONE;
  }

  if (aEventMessage != eDrop) {
    // Get the cached drag effect from the drag service, the data member should
    // have been set by whoever handled the WidgetGUIEvent or nsIDOMEvent on
    // drags.
    bool canDrop;
    currSession->GetCanDrop(&canDrop);
    if (!canDrop) {
      *pdwEffect = DROPEFFECT_NONE;
    }
  }

  // Clear the cached value
  currSession->SetCanDrop(false);
}

// IDropTarget methods
STDMETHODIMP
nsNativeDragTarget::DragEnter(LPDATAOBJECT pIDataSource, DWORD grfKeyState,
                              POINTL ptl, DWORD* pdwEffect) {
  if (!mDragService) {
    return E_FAIL;
  }

  mEffectsAllowed = *pdwEffect;
  AddLinkSupportIfCanBeGenerated(pIDataSource);

  // Drag and drop image helper
  if (GetDropTargetHelper()) {
    // We get a lot of crashes (often uncaught by our handler) later on during
    // DragOver calls, see bug 1465513. It looks like this might be because
    // we're not cleaning up previous drags fully and now released resources get
    // used. Calling IDropTargetHelper::DragLeave before DragEnter seems to fix
    // this for at least one reproduction of this crash.
    GetDropTargetHelper()->DragLeave();
    POINT pt = {ptl.x, ptl.y};
    GetDropTargetHelper()->DragEnter(mHWnd, pIDataSource, &pt, *pdwEffect);
  }

  // save a ref to this, in case the window is destroyed underneath us
  NS_ASSERTION(!mTookOwnRef, "own ref already taken!");
  this->AddRef();
  mTookOwnRef = true;

  // tell the drag service about this drag (it may have come from an
  // outside app).
  RefPtr<nsDragSession> session =
      static_cast<nsDragSession*>(mDragService->StartDragSession(mWidget));
  MOZ_ASSERT(session);

  void* tempOutData = nullptr;
  uint32_t tempDataLen = 0;
  nsresult loadResult = nsClipboard::GetNativeDataOffClipboard(
      pIDataSource, 0, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT),
      nullptr, &tempOutData, &tempDataLen);
  if (NS_SUCCEEDED(loadResult) && tempOutData) {
    mEffectsPreferred = *((DWORD*)tempOutData);
    free(tempOutData);
  } else {
    // We have no preference if we can't obtain it
    mEffectsPreferred = DROPEFFECT_NONE;
  }

  // Set the native data object into drag session
  session->SetIDataObject(pIDataSource);

  // Now process the native drag state and then dispatch the event
  ProcessDrag(eDragEnter, grfKeyState, ptl, pdwEffect);

  return S_OK;
}

void nsNativeDragTarget::AddLinkSupportIfCanBeGenerated(
    LPDATAOBJECT aIDataSource) {
  // If we don't have a link effect, but we can generate one, fix the
  // drop effect to include it.
  if (!(mEffectsAllowed & DROPEFFECT_LINK) && aIDataSource) {
    if (S_OK == ::OleQueryLinkFromData(aIDataSource)) {
      mEffectsAllowed |= DROPEFFECT_LINK;
    }
  }
}

STDMETHODIMP
nsNativeDragTarget::DragOver(DWORD grfKeyState, POINTL ptl, LPDWORD pdwEffect) {
  if (!mDragService) {
    return E_FAIL;
  }

  bool dragImageChanged = gDragImageChanged;
  gDragImageChanged = false;

  // If a LINK effect could be generated previously from a DragEnter(),
  // then we should include it as an allowed effect.
  mEffectsAllowed = (*pdwEffect) | (mEffectsAllowed & DROPEFFECT_LINK);

  RefPtr<nsDragSession> currentDragSession =
      static_cast<nsDragSession*>(mDragService->GetCurrentSession(mWidget));
  if (!currentDragSession) {
    return S_OK;  // Drag was canceled.
  }

  // without the AddRef() |this| can get destroyed in an event handler
  this->AddRef();

  // Drag and drop image helper
  if (GetDropTargetHelper()) {
    if (dragImageChanged) {
      // See comment in nsNativeDragTarget::DragEnter.
      GetDropTargetHelper()->DragLeave();
      // The drop helper only updates the image during DragEnter, so emulate
      // a DragEnter if the image was changed.
      POINT pt = {ptl.x, ptl.y};
      GetDropTargetHelper()->DragEnter(
          mHWnd, currentDragSession->GetDataObject(), &pt, *pdwEffect);
    }
    POINT pt = {ptl.x, ptl.y};
    GetDropTargetHelper()->DragOver(&pt, *pdwEffect);
  }

  ModifierKeyState modifierKeyState;
  currentDragSession->FireDragEventAtSource(eDrag,
                                            modifierKeyState.GetModifiers());
  // Now process the native drag state and then dispatch the event
  ProcessDrag(eDragOver, grfKeyState, ptl, pdwEffect);

  this->Release();

  return S_OK;
}

STDMETHODIMP
nsNativeDragTarget::DragLeave() {
  if (!mDragService) {
    return E_FAIL;
  }

  // Drag and drop image helper
  if (GetDropTargetHelper()) {
    GetDropTargetHelper()->DragLeave();
  }

  // dispatch the event into Gecko
  DispatchDragDropEvent(eDragExit, gDragLastPoint);

  nsCOMPtr<nsIDragSession> currentDragSession =
      mDragService->GetCurrentSession(mWidget);

  if (currentDragSession) {
    nsCOMPtr<nsINode> sourceNode;
    currentDragSession->GetSourceNode(getter_AddRefs(sourceNode));

    if (!sourceNode) {
      // We're leaving a window while doing a drag that was
      // initiated in a different app. End the drag session, since
      // we're done with it for now (until the user drags back into
      // mozilla).
      ModifierKeyState modifierKeyState;
      currentDragSession->EndDragSession(false,
                                         modifierKeyState.GetModifiers());
    }
  }

  // release the ref that was taken in DragEnter
  NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!");
  if (mTookOwnRef) {
    this->Release();
    mTookOwnRef = false;
  }

  return S_OK;
}

void nsNativeDragTarget::DragCancel() {
  // Cancel the drag session if we did DragEnter.
  if (mTookOwnRef) {
    if (GetDropTargetHelper()) {
      GetDropTargetHelper()->DragLeave();
    }
    if (mDragService) {
      ModifierKeyState modifierKeyState;
      RefPtr<nsIDragSession> session = mDragService->GetCurrentSession(mWidget);
      if (session) {
        session->EndDragSession(false, modifierKeyState.GetModifiers());
      }
    }
    this->Release();  // matching the AddRef in DragEnter
    mTookOwnRef = false;
  }
}

STDMETHODIMP
nsNativeDragTarget::Drop(LPDATAOBJECT pData, DWORD grfKeyState, POINTL aPT,
                         LPDWORD pdwEffect) {
  if (!mDragService) {
    return E_FAIL;
  }

  mEffectsAllowed = *pdwEffect;
  AddLinkSupportIfCanBeGenerated(pData);

  // Drag and drop image helper
  if (GetDropTargetHelper()) {
    POINT pt = {aPT.x, aPT.y};
    GetDropTargetHelper()->Drop(pData, &pt, *pdwEffect);
  }

  // Set the native data object into the drag service
  RefPtr<nsDragSession> currentDragSession =
      static_cast<nsDragSession*>(mDragService->GetCurrentSession(mWidget));
  if (!currentDragSession) {
    return S_OK;
  }
  currentDragSession->SetIDataObject(pData);

  // NOTE: ProcessDrag spins the event loop which may destroy arbitrary objects.
  // We use strong refs to prevent it from destroying these:
  RefPtr<nsNativeDragTarget> kungFuDeathGrip = this;

  // Now process the native drag state and then dispatch the event
  ProcessDrag(eDrop, grfKeyState, aPT, pdwEffect);

  currentDragSession =
      static_cast<nsDragSession*>(mDragService->GetCurrentSession(mWidget));
  if (!currentDragSession) {
    return S_OK;  // DragCancel() was called.
  }

  // Let the win drag session know whether it experienced
  // a drop event within the application. Drop will not oocur if the
  // drop landed outside the app. (used in tab tear off, bug 455884)
  currentDragSession->SetDroppedLocal();

  // Tell the drag session we're done with it.
  // Use GetMessagePos to get the position of the mouse at the last message
  // seen by the event loop. (Bug 489729)
  DWORD pos = ::GetMessagePos();
  POINT cpos;
  cpos.x = GET_X_LPARAM(pos);
  cpos.y = GET_Y_LPARAM(pos);
  currentDragSession->SetDragEndPoint(cpos.x, cpos.y);
  ModifierKeyState modifierKeyState;
  currentDragSession->EndDragSession(true, modifierKeyState.GetModifiers());

  // release the ref that was taken in DragEnter
  NS_ASSERTION(mTookOwnRef, "want to release own ref, but not taken!");
  if (mTookOwnRef) {
    this->Release();
    mTookOwnRef = false;
  }

  return S_OK;
}

/**
 * By lazy loading mDropTargetHelper we save 50-70ms of startup time
 * which is ~5% of startup time.
 */

IDropTargetHelper* nsNativeDragTarget::GetDropTargetHelper() {
  if (!mDropTargetHelper) {
    CoCreateInstance(CLSID_DragDropHelper, nullptr, CLSCTX_INPROC_SERVER,
                     IID_IDropTargetHelper, (LPVOID*)&mDropTargetHelper);
  }

  return mDropTargetHelper;
}

100%


¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.